├── .dir-locals.el
├── .elpaignore
├── .gitignore
├── CHANGELOG.org
├── LICENSE
├── README.org
├── avy-embark-collect.el
├── embark-consult.el
├── embark-org.el
├── embark.el
└── embark.texi
/.dir-locals.el:
--------------------------------------------------------------------------------
1 | ;;; Directory Local Variables
2 | ;;; For more information see (info "(emacs) Directory Variables")
3 |
4 | ((emacs-lisp-mode
5 | (show-trailing-whitespace . t)
6 | (indent-tabs-mode . nil)))
7 |
--------------------------------------------------------------------------------
/.elpaignore:
--------------------------------------------------------------------------------
1 | LICENSE
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.elc
2 | *-autoloads.el
3 | *-pkg.el
4 |
--------------------------------------------------------------------------------
/CHANGELOG.org:
--------------------------------------------------------------------------------
1 | #+title: Embark changelog
2 |
3 | * Version 1.1 (2024-04-18)
4 | - The =embark-consult= package contains a new exporter for
5 | =consult-location= targets (produced by several =consult= commands such
6 | as =consult-line=), which exports to a grep mode buffer. Users wishing
7 | to use the new grep mode exporter can use the following
8 | configuration:
9 | #+begin_src emacs-lisp
10 | (setf (alist-get 'consult-location embark-exporters-alist)
11 | #'embark-consult-export-location-grep)
12 | #+end_src
13 | The main reason for adding the new exporter is that users of the
14 | =wgrep= package will be able to make use of a feature that =wgrep= has
15 | and the built-in =occur-edit-mode= lacks: when editing search results
16 | you can add new lines to a result location. There are also some
17 | disadvantages of grep mode compared to occur mode (which is why the
18 | previously existing occur mode exporter continues to be the
19 | default): (1) =wgrep= is a third party package while =occur-edit-mode=
20 | is built-in; (2) occur mode buffers can list lines in any kind of
21 | buffer, but grep mode and =wgrep= are meant for lines of files
22 | exclusively.
23 | * Version 1.0 (2023-12-08)
24 | - You can now use around action hooks with multitarget actions (that
25 | you couldn't previously was an oversight).
26 | - Users of the =embark-consult= package can now use consult async search
27 | commands such as =consult-grep= as multitarget actions (through
28 | =embark-act-all=) to search a list of files. For example, you can use
29 | =consult-find= to search among file /names/ and once you have the
30 | relevant files in the minibuffer, you can use =embark-act-all= to
31 | search for some text in those files. When acting on buffers consult
32 | async search commands will search the associated file if there is
33 | one, or else the =default-directory= of the buffer.
34 | - =embark-bindings= and similar commands now show definition of keyboard
35 | macros.
36 | - =embark-org= now recognizes Org links in non-org buffers.
37 | - Now pressing RET in an =embark-collect= on a selection made by
38 | using =embark-select= in a normal buffer will take you to the location
39 | each target was collected from.
40 | - Some functions renamed for greater consistency (these functions are
41 | unlikely to be referred to in user's configuration):
42 | - =embark-target-completion-at-point= → =embark-target-completion-list-candidate=
43 | - =embark-target-top-minibuffer-completion= → =embark-target-top-minibuffer-candidate=
44 | - =embark-completions-buffer-candidates= → =embark-completion-list-candidates=
45 | * Version 0.23 (2023-09-19)
46 | - Added a mode line indicator showing the number of selected targets in
47 | the current buffer (contributed by @minad, thanks!)
48 | - Now =embark-select= can also be called as a top-level command, from
49 | outside =embark-act=. When called that way, it will select the first
50 | target at point.
51 | - =embark-org= now has support for acting on references to org headings
52 | in other buffers, by jumping to the heading first and then running
53 | the action. One source of references to org headings in other
54 | buffers are agenda views: each agenda item is such a reference. But
55 | this feature also supports some great third party commands which
56 | produce references to org headings, such as =org-ql-find= from the
57 | =org-ql= package or =consult-org-heading= from =consult=.
58 | - Renamed =embark-isearch= to =embark-isearch-forward= and added
59 | =embark-isearch-backward=.
60 | - =embark-become= now removes any invisible text from the minibuffer
61 | input on the grounds that users probably expect the target command
62 | to receive exactly the input they can see.
63 | - The meaning of the prefix argument in =embark-bindings= has flipped:
64 | now by default global key bindings are excluded and you can use =C-u=
65 | to include them.
66 | - If any candidate in an embark-collect buffer contains a newline,
67 | then candidates will be separated by horizontal lines. This is handy
68 | for the kill-ring, which you can browse by calling =embark-collect=
69 | from =yank-pop=.
70 | * Version 0.22.1 (2023-04-20)
71 | ** New feature: selections
72 | Now users can select several targets to make an ad hoc collection. The
73 | commands =embark-act-all=, =embark-export= and =embark-collect= will act on
74 | the selection if it is non-empty. To select or deselect a target use
75 | the =embark-select= action (bound to =SPC= in =embark-general-map=). If you
76 | have some targets selected, then using =embark-select= through
77 | =embark-act-all= will deselect them.
78 |
79 | Before this change the Embark Collect buffers had their own
80 | implementation of selections which has been removed. This is how to
81 | translate the old bindings to the new feature (which is available in
82 | all buffers, not just Embark Collect buffers!):
83 |
84 | | Task | Old binding | New binding |
85 | |--------------------+-------------+---------------|
86 | | Mark a candidate | m | a SPC |
87 | | Unmark a candidate | u | a SPC |
88 | | Unmark all | U | A SPC |
89 | | Mark all [1] | t | A SPC |
90 | | Toggle all marks | t | not available |
91 |
92 | [1] Marking all candidates (with either the old =t= or the new =A SPC=)
93 | requires that there are no marked candidates to begin with.
94 |
95 | In order to make room for the binding of =embark-select= to
96 | =SPC=, some other key bindings were moved:
97 |
98 | - =mark= in =embark-general-map= was moved to =C-SPC=.
99 | - =outline-mark-subtree= in =embark-heading-map= was moved to =C-SPC=.
100 | - =whitespace-cleanup-region= in =embark-region-map= was moved to =F=.
101 |
102 | * Version 0.21.1 (2020-01-30)
103 | - Finally started this changelog on 2023-04-20. Known issues with the
104 | changelog: it started very late, the first entry is not very
105 | informative.
106 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.org:
--------------------------------------------------------------------------------
1 | #+TITLE: Embark: Emacs Mini-Buffer Actions Rooted in Keymaps
2 | #+OPTIONS: d:nil
3 | #+EXPORT_FILE_NAME: embark.texi
4 | #+TEXINFO_DIR_CATEGORY: Emacs misc features
5 | #+TEXINFO_DIR_TITLE: Embark: (embark).
6 | #+TEXINFO_DIR_DESC: Emacs Mini-Buffer Actions Rooted in Keymaps
7 |
8 | #+html:
9 | #+html:
10 | #+html:
11 | #+html:
12 |
13 | * Overview
14 |
15 | Embark makes it easy to choose a command to run based on what is near
16 | point, both during a minibuffer completion session (in a way familiar
17 | to Helm or Counsel users) and in normal buffers. Bind the command
18 | =embark-act= to a key and it acts like prefix-key for a keymap of
19 | /actions/ (commands) relevant to the /target/ around point. With point on
20 | an URL in a buffer you can open the URL in a browser or eww or
21 | download the file it points to. If while switching buffers you spot an
22 | old one, you can kill it right there and continue to select another.
23 | Embark comes preconfigured with over a hundred actions for common
24 | types of targets such as files, buffers, identifiers, s-expressions,
25 | sentences; and it is easy to add more actions and more target types.
26 | Embark can also collect all the candidates in a minibuffer to an
27 | occur-like buffer or export them to a buffer in a major-mode specific
28 | to the type of candidates, such as dired for a set of files, ibuffer
29 | for a set of buffers, or customize for a set of variables.
30 |
31 | ** Acting on targets
32 |
33 | You can think of =embark-act= as a keyboard-based version of a
34 | right-click contextual menu. The =embark-act= command (which you should
35 | bind to a convenient key), acts as a prefix for a keymap offering you
36 | relevant /actions/ to use on a /target/ determined by the context:
37 |
38 | - In the minibuffer, the target is the current top completion
39 | candidate.
40 | - In the =*Completions*= buffer the target is the completion at point.
41 | - In a regular buffer, the target is the region if active, or else the
42 | file, symbol, URL, s-expression or defun at point.
43 |
44 | Multiple targets can be present at the same location and you can cycle
45 | between them by repeating the =embark-act= key binding. The type of
46 | actions offered depend on the type of the target. Here is a sample of
47 | a few of the actions offered in the default configuration:
48 |
49 | - For files you get offered actions like deleting, copying,
50 | renaming, visiting in another window, running a shell command on the
51 | file, etc.
52 | - For buffers the actions include switching to or killing the buffer.
53 | - For package names the actions include installing, removing or
54 | visiting the homepage.
55 | - For Emacs Lisp symbols the actions include finding the definition,
56 | looking up documentation, evaluating (which for a variable
57 | immediately shows the value, but for a function lets you pass it
58 | some arguments first). There are some actions specific to variables,
59 | such as setting the value directly or though the customize system,
60 | and some actions specific to commands, such as binding it to a key.
61 |
62 | By default when you use =embark-act= if you don't immediately select an
63 | action, after a short delay Embark will pop up a buffer showing a list
64 | of actions and their corresponding key bindings. If you are using
65 | =embark-act= outside the minibuffer, Embark will also highlight the
66 | current target. These behaviors are configurable via the variable
67 | =embark-indicators=. Instead of selecting an action via its key binding,
68 | you can select it by name with completion by typing =C-h= after
69 | =embark-act=.
70 |
71 | Everything is easily configurable: determining the current target,
72 | classifying it, and deciding which actions are offered for each type
73 | in the classification. The above introduction just mentions part of
74 | the default configuration.
75 |
76 | Configuring which actions are offered for a type is particularly easy
77 | and requires no programming: the variable =embark-keymap-alist=
78 | associates target types with variables containing keymaps, and those
79 | keymaps containing bindings for the actions. (To examine the available
80 | categories and their associated keymaps, you can use =C-h v
81 | embark-keymap-alist= or customize that variable.) For example, in the
82 | default configuration the type =file= is associated with the symbol
83 | =embark-file-map=. That symbol names a keymap with single-letter key
84 | bindings for common Emacs file commands, for instance =c= is bound to
85 | =copy-file=. This means that if you are in the minibuffer after running
86 | a command that prompts for a file, such as =find-file= or =rename-file=,
87 | you can copy a file by running =embark-act= and then pressing =c=.
88 |
89 | These action keymaps are very convenient but not strictly necessary
90 | when using =embark-act=: you can use any command that reads from the
91 | minibuffer as an action and the target of the action will be inserted
92 | at the first minibuffer prompt. After running =embark-act= all of your
93 | key bindings and even =execute-extended-command= can be used to run a
94 | command. For example, if you want to replace all occurrences of the
95 | symbol at point, just use =M-%= as the action, there is no need to bind
96 | =query-replace= in one of Embark's keymaps. Also, those action keymaps
97 | are normal Emacs keymaps and you should feel free to bind in them
98 | whatever commands you find useful as actions and want to be available
99 | through convenient bindings.
100 |
101 | The actions in =embark-general-map= are available no matter what type
102 | of completion you are in the middle of. By default this includes
103 | bindings to save the current candidate in the kill ring and to insert
104 | the current candidate in the previously selected buffer (the buffer
105 | that was current when you executed a command that opened up the
106 | minibuffer).
107 |
108 | Emacs's minibuffer completion system includes metadata indicating the
109 | /category/ of what is being completed. For example, =find-file='s
110 | metadata indicates a category of =file= and =switch-to-buffer='s metadata
111 | indicates a category of =buffer=. Embark has the related notion of the
112 | /type/ of a target for actions, and by default when category metadata
113 | is present it is taken to be the type of minibuffer completion
114 | candidates when used as targets. Emacs commands often do not set
115 | useful category metadata so the [[https://github.com/minad/marginalia][Marginalia]] package, which supplies
116 | this missing metadata, is highly recommended for use with Embark.
117 |
118 | Embark's default configuration has actions for the following target
119 | types: files, buffers, symbols, packages, URLs, bookmarks, and as a
120 | somewhat special case, actions for when the region is active. You can
121 | read about the [[https://github.com/oantolin/embark/wiki/Default-Actions][default actions and their key bindings]] on the GitHub
122 | project wiki.
123 |
124 | ** The default action on a target
125 |
126 | Embark has a notion of default action for a target:
127 |
128 | - If the target is a minibuffer completion candidate, then the default
129 | action is whatever command opened the minibuffer in the first place.
130 | For example if you run =kill-buffer=, then the default action will be
131 | to kill buffers.
132 | - If the target comes from a regular buffer (i.e., not a minibuffer),
133 | then the default action is whatever is bound to =RET= in the keymap of
134 | actions for that type of target. For example, in Embark's default
135 | configuration for a URL found at point the default action is
136 | =browse-url=, because =RET= is bound to =browse-url= in the =embark-url-map=
137 | keymap.
138 |
139 | To run the default action you can press =RET= after running =embark-act=.
140 | Note that if there are several different targets at a given location,
141 | each has its own default action, so first cycle to the target you want
142 | and then press =RET= to run the corresponding default action.
143 |
144 | There is also =embark-dwim= which runs the default action for the first
145 | target found. It's pretty handy in non-minibuffer buffers: with
146 | Embark's default configuration it will:
147 |
148 | - Open the file at point.
149 | - Open the URL at point in a web browser (using the =browse-url=
150 | command).
151 | - Compose a new email to the email address at point.
152 | - In an Emacs Lisp buffer, if point is on an opening parenthesis or
153 | right after a closing one, it will evaluate the corresponding
154 | expression.
155 | - Go to the definition of an Emacs Lisp function, variable or macro at
156 | point.
157 | - Find the file corresponding to an Emacs Lisp library at point.
158 |
159 | ** Working with sets of possible targets
160 |
161 | Besides acting individually on targets, Embark lets you work
162 | collectively on a set of target /candidates/. For example, while you are
163 | in the minibuffer the candidates are simply the possible completions
164 | of your input. Embark provides three main commands to work on candidate
165 | sets:
166 |
167 | - The =embark-act-all= command runs the same action on each of the
168 | current candidates. It is just like using =embark-act= on each
169 | candidate in turn. (Because you can easily act on many more
170 | candidates than you meant to, by default Embark asks you to confirm
171 | uses of =embark-act-all=; you can turn this off by setting the user
172 | option =embark-confirm-act-all= to =nil=.)
173 |
174 | - The =embark-collect= command produces a buffer listing all the current
175 | candidates, for you to peruse and run actions on at your leisure.
176 | The candidates are displayed as a list showing additional
177 | annotations. If any of the candidates contain newlines, then
178 | horizontal lines are used to separate candidates.
179 |
180 | The Embark Collect buffer is somewhat "dired-like": you can select
181 | and deselect candidates through =embark-select= (available as an
182 | action in =embark-act=, bound to =SPC=; but you could also give it a
183 | global key binding). In an Embark Collect buffer =embark-act= is bound
184 | to =a= and =embark-act-all= is bound to =A=; =embark-act-all= will act on
185 | all currently marked candidates if there any, and will act on all
186 | candidates if none are marked. In particular, this means that =a SPC=
187 | will toggle whether the candidate at point is selected, and =A SPC=
188 | will select all candidates if none are selected, or deselect all
189 | selected candidates if there are some.
190 |
191 | - The =embark-export= command tries to open a buffer in an appropriate
192 | major mode for the set of candidates. If the candidates are files
193 | export produces a Dired buffer; if they are buffers, you get an
194 | Ibuffer buffer; and if they are packages you get a buffer in
195 | package menu mode.
196 |
197 | If you use the grepping commands from the [[https://github.com/minad/consult/][Consult]] package,
198 | =consult-grep=, =consult-git-grep= or =consult-ripgrep=, then you should
199 | install the =embark-consult= package, which adds support for exporting a
200 | list of grep results to an honest grep-mode buffer, on which you can
201 | even use [[https://github.com/mhayashi1120/Emacs-wgrep][wgrep]] if you wish.
202 |
203 | When in doubt choosing between exporting and collecting, a good rule
204 | of thumb is to always prefer =embark-export= since when an exporter to a
205 | special major mode is available for a given type of target, it will be
206 | more featureful than an Embark collect buffer, and if no such exporter
207 | is configured the =embark-export= command falls back to the generic
208 | =embark-collect=.
209 |
210 | These commands are always available as "actions" (although they do not
211 | act on just the current target but on all candidates) for =embark-act=
212 | and are bound to =A=, =S= (for "snapshot"), and =E=, respectively, in
213 | =embark-general-map=. This means that you do not have to bind your own
214 | key bindings for these (although you can, of course!), just a key
215 | binding for =embark-act=.
216 |
217 | In Embark Collect or Embark Export buffers that were obtained by
218 | running =embark-collect= or =embark-export= from within a minibuffer
219 | completion session, =g= is bound to a command that restarts the
220 | completion session, that is, the command that opened the minibuffer is
221 | run again and the minibuffer contents restored. You can then interact
222 | normally with the command, perhaps editing the minibuffer contents,
223 | and, if you wish, you can rerun =embark-collect= or =embark-export= to get
224 | an updated buffer.
225 |
226 | *** Selecting some targets to make an ad hoc candidate set
227 |
228 | The commands for working with sets of candidates just described,
229 | namely =embark-act-all=, =embark-export= and =embark-collect= by default
230 | work with all candidates defined in the current context. For example,
231 | in the minibuffer they operate on all currently completion candidates,
232 | or in a dired buffer they work on all marked files (or all files if
233 | none are marked). Embark also has a notion of /selection/, where you can
234 | accumulate an ad hoc list of targets for these commands to work on.
235 |
236 | The selection is controlled by using the =embark-select= action, bound
237 | to =SPC= in =embark-general-map= so that it is always available (you can
238 | also give =embark-select= a global key binding if you wish; when called
239 | directly, not as an action for =embark-act=, it will select the first
240 | target at point). Calling this action on a target toggles its
241 | membership in the current buffer's Embark selection; that is, it adds
242 | it to selection if not selected and removes it from the selection if
243 | it was selected. Whenever the selection for a buffer is non-empty, the
244 | commands =embark-act-all=, =embark-export= and =embark-collect= will act on
245 | the selection.
246 |
247 | To deselect all selected targets, you can use the =embark-select= action
248 | through =embark-act-all=, since this will run =embark-select= on each
249 | member of the current selection. Similarly if no targets are selected
250 | and you are in a minibuffer completion session, running =embark-select=
251 | from =embark-act-all= will select all the current completion candidates.
252 |
253 | By default, whenever some targets are selected in the current buffer,
254 | a count of selected targets appears in the mode line. This can be
255 | turned off or customized through the =embark-selection-indicator= user
256 | option.
257 |
258 | The selection functionality is supported in every buffer:
259 |
260 | - In the minibuffer this gives a convenient way to act on several
261 | completion candidates that don't follow any simple pattern: just go
262 | through the completions selecting the ones you want, then use
263 | =embark-act-all=. For example, you could attach several files at once
264 | to an email.
265 | - For Embark Collect buffers this functionality enables a dired-like
266 | workflow, in which you mark various candidates and apply an action
267 | to all at once. (It supersedes a previous ad hoc dired-like
268 | interface that was implemented only in Embark Collect buffers, with
269 | a slightly different interface.)
270 | - In a eww buffer you could use this to select various links you wish
271 | to follow up on, and then collect them into a buffer. Similarly,
272 | while reading Emacs's info manual you could select some symbols you
273 | want to read more about and export them to an =apropos-mode= buffer.
274 | - You can use selections in regular text or programming buffers to do
275 | complex editing operations. For example, if you have three
276 | paragraphs scattered over a file and you want to bring them
277 | together, you can select each one, insert them all somewhere and
278 | finally delete all of them (from their original locations).
279 |
280 | *** =embark-live= a live-updating variant of =embark-collect=
281 |
282 | Finally, there is also an =embark-live= variant of the =embark-collect=
283 | command which automatically updates the collection after each change
284 | in the source buffer. Users of a completion UI that automatically
285 | updates and displays the candidate list (such as Vertico, Icomplete,
286 | Fido-mode, or MCT) will probably not want to use
287 | =embark-live= from the minibuffer as they will then have two live
288 | updating displays of the completion candidates!
289 |
290 | A more likely use of =embark-live= is to be called from a regular buffer
291 | to display a sort of live updating "table of contents" for the buffer.
292 | This depends on having appropriate candidate collectors configured in
293 | =embark-candidate-collectors=. There are not many in Embark's default
294 | configuration, but you can try this experiment: open a dired buffer in
295 | a directory that has very many files, mark a few, and run =embark-live=.
296 | You'll get an Embark Collect buffer containing only the marked files,
297 | which updates as you mark or unmark files in dired. To make
298 | =embark-live= genuinely useful other candidate collectors are required.
299 | The =embark-consult= package (documented near the end of this manual)
300 | contains a few: one for imenu items and one for outline headings as
301 | used by =outline-minor-mode=. Those collectors really do give
302 | =embark-live= a table-of-contents feel.
303 |
304 | ** Switching to a different command without losing what you've typed
305 |
306 | Embark also has the =embark-become= command which is useful for when
307 | you run a command, start typing at the minibuffer and realize you
308 | meant a different command. The most common case for me is that I run
309 | =switch-to-buffer=, start typing a buffer name and realize I haven't
310 | opened the file I had in mind yet! I'll use this situation as a
311 | running example to illustrate =embark-become=. When this happens I can,
312 | of course, press =C-g= and then run =find-file= and open the file, but
313 | this requires retyping the portion of the file name you already
314 | typed. This process can be streamlined with =embark-become=: while still
315 | in the =switch-to-buffer= you can run =embark-become= and effectively
316 | make the =switch-to-buffer= command become =find-file= for this run.
317 |
318 | You can bind =embark-become= to a key in =minibuffer-local-map=, but it is
319 | also available as an action under the letter =B= (uppercase), so you
320 | don't need a binding if you already have one for =embark-act=. So,
321 | assuming I have =embark-act= bound to, say, =C-.=, once I realize I
322 | haven't open the file I can type =C-. B C-x C-f= to have
323 | =switch-to-buffer= become =find-file= without losing what I have already
324 | typed in the minibuffer.
325 |
326 | But for even more convenience, =embark-become= offers shorter key
327 | bindings for commands you are likely to want the current command to
328 | become. When you use =embark-become= it looks for the current command in
329 | all keymaps named in the list =embark-become-keymaps= and then activates
330 | all keymaps that contain it. For example, the default value of
331 | =embark-become-keymaps= contains a keymap =embark-become-file+buffer-map=
332 | with bindings for several commands related to files and buffers, in
333 | particular, it binds =switch-to-buffer= to =b= and =find-file= to =f=. So when
334 | I accidentally try to switch to a buffer for a file I haven't opened
335 | yet, =embark-become= finds that the command I ran, =switch-to-buffer=, is
336 | in the keymap =embark-become-file+buffer-map=, so it activates that
337 | keymap (and any others that also contain a binding for
338 | =switch-to-buffer=). The end result is that I can type =C-. B f= to
339 | switch to =find-file=.
340 |
341 | * Quick start
342 |
343 | The easiest way to install Embark is from GNU ELPA, just run =M-x
344 | package-install RET embark RET=. (It is also available on MELPA.) It is
345 | highly recommended to also install [[https://github.com/minad/marginalia][Marginalia]] (also available on GNU
346 | ELPA), so that Embark can offer you preconfigured actions in more
347 | contexts. For =use-package= users, the following is a very reasonable
348 | starting configuration:
349 |
350 | #+begin_src emacs-lisp
351 | (use-package marginalia
352 | :ensure t
353 | :config
354 | (marginalia-mode))
355 |
356 | (use-package embark
357 | :ensure t
358 |
359 | :bind
360 | (("C-." . embark-act) ;; pick some comfortable binding
361 | ("C-;" . embark-dwim) ;; good alternative: M-.
362 | ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings'
363 |
364 | :init
365 |
366 | ;; Optionally replace the key help with a completing-read interface
367 | (setq prefix-help-command #'embark-prefix-help-command)
368 |
369 | ;; Show the Embark target at point via Eldoc. You may adjust the
370 | ;; Eldoc strategy, if you want to see the documentation from
371 | ;; multiple providers. Beware that using this can be a little
372 | ;; jarring since the message shown in the minibuffer can be more
373 | ;; than one line, causing the modeline to move up and down:
374 |
375 | ;; (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target)
376 | ;; (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly)
377 |
378 | :config
379 |
380 | ;; Hide the mode line of the Embark live/completions buffers
381 | (add-to-list 'display-buffer-alist
382 | '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
383 | nil
384 | (window-parameters (mode-line-format . none)))))
385 |
386 | ;; Consult users will also want the embark-consult package.
387 | (use-package embark-consult
388 | :ensure t ; only need to install it, embark loads it after consult if found
389 | :hook
390 | (embark-collect-mode . consult-preview-at-point-mode))
391 | #+end_src
392 |
393 | About the suggested key bindings for =embark-act= and =embark-dwim=:
394 | - Those key bindings are unlikely to work in the terminal, but
395 | terminal users are probably well aware of this and will know to
396 | select different bindings.
397 | - The suggested =C-.= binding is used by default in (at least some
398 | installations of) GNOME to input emojis, and Emacs doesn't even get
399 | a chance to respond to the binding. You can select a different key
400 | binding for =embark-act= or use =ibus-setup= to change the shortcut for
401 | emoji insertion (Emacs 29 uses =C-x 8 e e=, in case you want to set
402 | the same one system-wide).
403 | - The suggested alternative of =M-.= for =embark-dwim= is bound by default
404 | to =xref-find-definitions=. That is a very useful command but
405 | overwriting it with =embark-dwim= is sensible since in Embark's
406 | default configuration, =embark-dwim= will also find the definition of
407 | the identifier at point. (Note that =xref-find-definitions= with a
408 | prefix argument prompts you for an identifier, =embark-dwim= does not
409 | cover this case).
410 |
411 | Other Embark commands such as =embark-act-all=, =embark-become=,
412 | =embark-collect=, and =embark-export= can be run through =embark-act= as
413 | actions bound to =A=, =B=, =S= (for "snapshot"), and =E= respectively, and
414 | thus don't really need a dedicated key binding, but feel free to bind
415 | them directly if you so wish. If you do choose to bind them directly,
416 | you'll probably want to bind them in =minibuffer-local-map=, since they
417 | are most useful in the minibuffer (in fact, =embark-become= only works
418 | in the minibuffer).
419 |
420 | The command =embark-dwim= executes the default action at point. Another good
421 | keybinding for =embark-dwim= is =M-.= since =embark-dwim= acts like
422 | =xref-find-definitions= on the symbol at point. =C-.= can be seen as a
423 | right-click context menu at point and =M-.= acts like left-click. The
424 | keybindings are mnemonic, both act at the point (=.=).
425 |
426 | Embark needs to know what your minibuffer completion system considers
427 | to be the list of candidates and which one is the current candidate.
428 | Embark works out of the box if you use Emacs's default tab completion,
429 | the built-in =icomplete-mode= or =fido-mode=, or the third-party packages
430 | [[https://github.com/minad/vertico][Vertico]] or [[https://github.com/abo-abo/swiper][Ivy]].
431 |
432 | 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
433 | those packages include comprehensive functionality for acting on
434 | minibuffer completion candidates. (Embark does come with Ivy
435 | integration despite this.)
436 |
437 | * Advanced configuration
438 | ** Showing information about available targets and actions
439 |
440 | By default, if you run =embark-act= and do not immediately select an
441 | action, after a short delay Embark will pop up a buffer called =*Embark
442 | Actions*= containing a list of available actions with their key
443 | bindings. You can scroll that buffer with the mouse of with the usual
444 | commands =scroll-other-window= and =scroll-other-window-down= (bound by
445 | default to =C-M-v= and =C-M-S-v=).
446 |
447 | That functionality is provided by the =embark-mixed-indicator=, but
448 | Embark has other indicators that can provide information about the
449 | target and its type, what other targets you can cycle to, and which
450 | actions have key bindings in the action map for the current type of
451 | target. Any number of indicators can be active at once and the user
452 | option =embark-indicators= should be set to a list of the desired
453 | indicators.
454 |
455 | Embark comes with the following indicators:
456 |
457 | - =embark-minimal-indicator=: shows a messages in the echo area or
458 | minibuffer prompt showing the current target and the types of all
459 | targets starting with the current one.
460 |
461 | - =embark-highlight-indicator=: highlights the target at point; on by
462 | default.
463 |
464 | - =embark-verbose-indicator=: displays a table of actions and their key
465 | bindings in a buffer; this is not on by default, in favor of the
466 | mixed indicator described next.
467 |
468 | - =embark-mixed-indicator=: starts out by behaving as the minimal
469 | indicator but after a short delay acts as the verbose indicator;
470 | this is on by default.
471 |
472 | - =embark-isearch-highlight-indicator=: this only does something when
473 | the current target is the symbol at point, in which case it
474 | lazily highlights all occurrences of that symbol in the current
475 | buffer, like isearch; also on by default.
476 |
477 | Users of the popular [[https://github.com/justbur/emacs-which-key][which-key]] package may prefer to use the
478 | =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
479 | definition from the wiki into your configuration and customize the
480 | =embark-indicators= user option to exclude the mixed and verbose
481 | indicators and to include =embark-which-key-indicator=.
482 |
483 | If you use [[https://github.com/minad/vertico][Vertico]], there is an even easier way to get a
484 | =which-key=-like display that also lets you use completion to narrow
485 | down the list of alternatives, described at the end of the next
486 | section.
487 |
488 | ** Selecting commands via completions instead of key bindings
489 |
490 | As an alternative to reading the list of actions in the verbose or
491 | mixed indicators (see the previous section for a description of
492 | these), you can press the =embark-help-key=, which is =C-h= by default
493 | (but you may prefer =?= to free up =C-h= for use as a prefix) after
494 | running =embark-act=. Pressing the help key will prompt you for the name
495 | of an action with completion (but feel free to enter a command that is
496 | not among the offered candidates!), and will also remind you of the
497 | key bindings. You can press =embark-keymap-prompter-key=, which is =@= by
498 | default, at the prompt and then one of the key bindings to enter the
499 | name of the corresponding action.
500 |
501 | You may think that with the =*Embark Actions*= buffer popping up to
502 | remind you of the key bindings you'd never want to use completion to
503 | select an action by name, but personally I find that typing a small
504 | portion of the action name to narrow down the list of candidates feels
505 | significantly faster than visually scanning the entire list of actions.
506 |
507 | If you find you prefer selecting actions that way, you can configure
508 | embark to always prompt you for actions by setting the variable
509 | =embark-prompter= to =embark-completing-read-prompter=.
510 |
511 | On the other hand, you may wish to continue using key bindings for the
512 | actions you perform most often, and to use completion only to explore
513 | what further actions are available or when you've forgotten a key
514 | binding. In that case, you may prefer to use the minimal indicator,
515 | which does not pop-up an =*Embark Actions*= buffer at all, and to use
516 | the =embark-help-key= whenever you need help. This unobtrusive setup is
517 | achieved with the following configuration:
518 |
519 | #+begin_src emacs-lisp
520 | (setq embark-indicators
521 | '(embark-minimal-indicator ; default is embark-mixed-indicator
522 | embark-highlight-indicator
523 | embark-isearch-highlight-indicator))
524 | #+end_src
525 |
526 | [[https://github.com/minad/vertico][Vertico]] users may wish to configure a grid display for the actions and
527 | key-bindings, reminiscent of the popular package [[https://github.com/justbur/emacs-which-key][which-key]], but, of
528 | course, enhanced by the use of completion to narrow the list of
529 | commands. In order to get the grid display, put the following in your
530 | Vertico configuration:
531 |
532 | #+begin_src emacs-lisp
533 | (add-to-list 'vertico-multiform-categories '(embark-keybinding grid))
534 | (vertico-multiform-mode)
535 | #+end_src
536 |
537 | This will make the available keys be shown in a compact grid like in
538 | =which-key=. The =vertico-multiform-mode= also enables keys such as =M-V=,
539 | =M-G=, =M-B=, and =M-U= for manually switching between layouts in Vertico
540 | buffers.
541 |
542 | *** Selecting commands via completion outside of Embark
543 |
544 | If you like this completion interface for exploring key bindings for
545 | Embark actions, you may want to use it elsewhere in Emacs. You can use
546 | Embark's completion-based command prompter to list:
547 |
548 | - key bindings under a prefix,
549 | - local key bindings, or
550 | - all key bindings.
551 |
552 | To use it for key bindings under a prefix (you can use this to replace
553 | the =which-key= package, for example), use this configuration:
554 |
555 | #+begin_src emacs-lisp
556 | (setq prefix-help-command #'embark-prefix-help-command)
557 | #+end_src
558 |
559 | Now, when you have started on a prefix sequence such as =C-x= or =C-c=,
560 | pressing =C-h= will bring up the Embark version of the built-in
561 | =prefix-help-command=, which will list the keys under that prefix and
562 | their bindings, and lets you select the one you wanted with completion,
563 | or by key binding if you press =embark-keymap-prompter-key=.
564 |
565 | To list local or global key bindings, use the command =embark-bindings=.
566 | You can bind that to =C-h b=, which is the default key binding for the
567 | built-in =describe-bindings= command, which this command can replace. By
568 | default, =embark-bindings= lists local key bindings, typically those
569 | bound in the major mode keymap; to get global bindings as well, call
570 | it with a =C-u= prefix argument.
571 |
572 | ** Quitting the minibuffer after an action
573 |
574 | By default, if you call =embark-act= from the minibuffer it quits the
575 | minibuffer after performing the action. You can change this by setting
576 | the user option =embark-quit-after-action= to =nil=. Having =embark-act= /not/
577 | quit the minibuffer can be useful to turn commands into little "thing
578 | managers". For example, you can use =find-file= as a little file manager
579 | or =describe-package= as a little package manager: you can run those
580 | commands, perform a series of actions, and then quit the command.
581 |
582 | If you want to control the quitting behavior in a fine-grained manner
583 | depending on the action, you can set =embark-quit-after-action= to an
584 | alist, associating commands to either =t= for quitting or =nil= for not
585 | quitting. When using an alist, you can use the special key =t= to
586 | specify the default behavior. For example, to specify that by default
587 | actions should not quit the minibuffer but that using =kill-buffer= as
588 | an action should quit, you can use the following configuration:
589 |
590 | #+begin_src emacs-lisp
591 | (setq embark-quit-after-action '((kill-buffer . t) (t . nil)))
592 | #+end_src
593 |
594 | The variable =embark-quit-after-action= only specifies a default, that
595 | is, it only controls whether or not =embark-act= quits the minibuffer
596 | when you call it without a prefix argument, and you can select the
597 | opposite behavior to what the variable says by calling =embark-act= with
598 | =C-u=. Also note that both the variable =embark-quit-after-action= and =C-u=
599 | have no effect when you call =embark-act= outside the minibuffer.
600 |
601 | If you find yourself using the quitting and non-quitting variants of
602 | =embark-act= about equally often, independently of the action, you may
603 | prefer to simply have separate commands for them instead of a single
604 | command that you call with =C-u= half the time. You could, for example,
605 | keep the default exiting behavior of =embark-act= and define a
606 | non-quitting version as follows:
607 |
608 | #+begin_src emacs-lisp
609 | (defun embark-act-noquit ()
610 | "Run action but don't quit the minibuffer afterwards."
611 | (interactive)
612 | (let ((embark-quit-after-action nil))
613 | (embark-act)))
614 | #+end_src
615 |
616 | ** Running some setup after injecting the target
617 |
618 | You can customize what happens after the target is inserted at the
619 | minibuffer prompt of an action. There are
620 | =embark-target-injection-hooks=, that are run by default after injecting
621 | the target into the minibuffer. The variable
622 | =embark-target-injection-hooks= is an alist associating commands to
623 | their setup hooks. There are two special keys: if no setup hook is
624 | specified for a given action, the hook associated to =t= is run; and the
625 | hook associated to =:always= is run regardless of the action. (This
626 | variable used to have the less explicit name of
627 | =embark-setup-action-hooks=, so please update your configuration.)
628 |
629 | For example, consider using =shell-command= as an action during file
630 | completion. It would be useful to insert a space before the target
631 | file name and to leave the point at the beginning, so you can
632 | immediately type the shell command to run on that file. That's why in
633 | Embark's default configuration there is an entry in
634 | =embark-target-injection-hooks= associating =shell-command= to a hook that
635 | includes =embark--shell-prep=, a simple helper function that quotes all
636 | the spaces in the file name, inserts an extra space at the beginning
637 | of the line and leaves point to the left of it.
638 |
639 | Now, the preparation that =embark--shell-prep= does would be useless if
640 | Embark did what it normally does after it inserts the target of the
641 | action at the minibuffer prompt, which is to "press =RET=" for you,
642 | accepting the target as is; if Embark did that for =shell-command= you
643 | wouldn't get a chance to type in the command to execute! That is why
644 | in Embark's default configuration the entry for =shell-command= in
645 | =embark-target-injection-hooks= also contains the function
646 | =embark--allow-edit=.
647 |
648 | Embark used to have a dedicated variable =embark-allow-edit-actions= to
649 | which you could add commands for which Embark should forgo pressing
650 | =RET= for you after inserting the target. Since its effect can also be
651 | achieved via the general =embark-target-injection-hooks= mechanism, that
652 | variable has been removed to simplify Embark. Be sure to update your
653 | configuration; if you had something like:
654 |
655 | #+begin_src emacs-lisp
656 | (add-to-list 'embark-allow-edit-actions 'my-command)
657 | #+end_src
658 |
659 | you should replace it with:
660 |
661 | #+begin_src emacs-lisp
662 | (push 'embark--allow-edit
663 | (alist-get 'my-command embark-target-injection-hooks))
664 | #+end_src
665 |
666 |
667 | Also note that while you could abuse =embark--allow-edit= so that you
668 | have to confirm "dangerous" actions such as =delete-file=, it is better
669 | to implement confirmation by adding the =embark--confirm= function to
670 | the appropriate entry of a different hook alist, namely,
671 | =embark-pre-action-hooks=.
672 |
673 | Besides =embark--allow-edit=, Embark comes with another function that is
674 | of general utility in action setup hooks: =embark--ignore-target=. Use
675 | it for commands that do prompt you in the minibuffer but for which
676 | inserting the target would be inappropriate. This is not a common
677 | situation but does occasionally arise. For example it is used by
678 | default for =shell-command-on-region=: that command is used as an action
679 | for region targets, and it prompts you for a shell command; you
680 | typically do /not/ want the target, that is the contents of the region,
681 | to be entered at that prompt!
682 |
683 | ** Running hooks before, after or around an action
684 |
685 | Embark has three variables, =embark-pre-action-hooks=,
686 | =embark-post-action-hooks= and =embark-around-action-hooks=, which are
687 | alists associating commands to hooks that should run before or after
688 | or as around advice for the command when used as an action. As with
689 | =embark-target-injection-hooks=, there are two special keys for the
690 | alists: =t= designates the default hook to run when no specific hook is
691 | specified for a command; and the hook associated to =:always= runs
692 | regardless.
693 |
694 | The default values of those variables are fairly extensive, adding
695 | creature comforts to make running actions a smooth experience. Embark
696 | comes with several functions intended to be added to these hooks, and
697 | used in the default values of =embark-pre-action-hooks=,
698 | =embark-post-action-hooks= and =embark-around-action-hooks=.
699 |
700 | For pre-action hooks:
701 |
702 | - =embark--confirm= :: Prompt the user for confirmation before executing
703 | the action. This is used be default for commands deemed "dangerous",
704 | or, more accurately, hard to undo, such as =delete-file= and
705 | =kill-buffer=.
706 |
707 | - =embark--unmark-target= :: Unmark the active region. Use this for
708 | commands you want to act on the region contents but without the
709 | region being active. The default configuration uses this function as
710 | a pre-action hook for =occur= and =query-replace=, for example, so that
711 | you can use them as actions with region targets to search the whole
712 | buffer for the text contained in the region. Without this pre-action
713 | hook using =occur= as an action for a region target would be
714 | pointless: it would search for the the region contents /in the
715 | region/, (typically, due to the details of regexps) finding only one
716 | match!
717 |
718 | - =embark--beginning-of-target= :: Move to the beginning of the target
719 | (for targets that report bounds). This is used by default for
720 | backward motion commands such as =backward-sexp=, so that they don't
721 | accidentally leave you on the current target.
722 |
723 | - =embark--end-of-target= :: Move to the end of the target. This is used
724 | similarly to the previous function, but also for commands that act
725 | on the last s-expression like =eval-last-sexp=. This allow you to act
726 | on an s-expression from anywhere inside it and still use
727 | =eval-last-sexp= as an action.
728 |
729 | - =embark--xref-push-markers= :: Push the current location on the xref
730 | marker stack. Use this for commands that take you somewhere and for
731 | which you'd like to be able to come back to where you were using
732 | =xref-pop-marker-stack=. This is used by default for =find-library=.
733 |
734 | For post-action hooks:
735 |
736 | - =embark--restart= :: Restart the command currently prompting in the
737 | minibuffer, so that the list of completion candidates is updated.
738 | This is useful as a post action hook for commands that delete or
739 | rename a completion candidate; for example the default value of
740 | =embark-post-action-hooks= uses it for =delete-file=, =kill-buffer=,
741 | =rename-file=, =rename-buffer=, etc.
742 |
743 | For around-action hooks:
744 |
745 | - =embark--mark-target= :: Save existing mark and point location, mark
746 | the target and run the action. Most targets at point outside the
747 | minibuffer report which region of the buffer they correspond to
748 | (this is the information used by =embark-highlight-indicator= to
749 | know what portion of the buffer to highlight); this function marks
750 | that region. It is useful as an around action hook for commands that
751 | expect a region to be marked, for example, it is used by default for
752 | =indent-region= so that it works on s-expression targets, or for
753 | =fill-region= so that it works on paragraph targets.
754 |
755 | - =embark--cd= :: Run the action with =default-directory= set to the
756 | directory associated to the current target. The target should be of
757 | type =file=, =buffer=, =bookmark= or =library=, and the associated directory
758 | is what you'd expect in each case.
759 |
760 | - =embark--narrow-to-target= :: Run the action with buffer narrowed to
761 | current target. Use this as an around hook to localize the effect of
762 | actions that don't already work on just the region. In the default
763 | configuration it is used for =repunctuate-sentences=.
764 |
765 | - =embark--save-excursion= :: Run the action restoring point at the end.
766 | The current default configuration doesn't use this but it is
767 | available for users.
768 |
769 | ** Creating your own keymaps
770 |
771 | All internal keymaps are defined with the standard helper macro
772 | =defvar-keymap=. For example a simple version of the file action keymap
773 | could be defined as follows:
774 |
775 | #+BEGIN_SRC emacs-lisp
776 | (defvar-keymap embark-file-map
777 | :doc "Example keymap with a few file actions"
778 | :parent embark-general-map
779 | "d" #'delete-file
780 | "r" #'rename-file
781 | "c" #'copy-file)
782 | #+END_SRC
783 |
784 | These action keymaps are perfectly normal Emacs
785 | keymaps. You may want to inherit from the =embark-general-map= if you
786 | want to access the default Embark actions. Note that =embark-collect=
787 | and =embark-export= are also made available via =embark-general-map=.
788 |
789 | ** Defining actions for new categories of targets
790 |
791 | It is easy to configure Embark to provide actions for new types of
792 | targets, either in the minibuffer or outside it. I present below two
793 | very detailed examples of how to do this. At several points I'll
794 | explain more than one way to proceed, typically with the easiest
795 | option first. I include the alternative options since there will be
796 | similar situations where the easiest option is not available.
797 |
798 | *** New minibuffer target example - tab-bar tabs
799 |
800 | As an example let us take a look at the [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Tab-Bars.html][tab bars]]. I'll explain how
801 | to configure Embark to offer tab-specific actions when you use the
802 | tab-bar-mode commands that mention tabs by name. The configuration
803 | explained here is now built-in to Embark (and Marginalia), but it's
804 | still a good self-contained example. In order to setup up tab actions
805 | you would need to: (1) make sure Embark knows those commands deal with
806 | tabs, (2) define a keymap for tab actions and configure Embark so it
807 | knows that's the keymap you want.
808 |
809 | **** Telling Embark about commands that prompt for tabs by name
810 |
811 | For step (1), it would be great if the =tab-bar-mode= commands reported
812 | the completion category =tab= when asking you for a tab with
813 | completion. (All built-in Emacs commands that prompt for file names,
814 | for example, do have metadata indicating that they want a =file=.) They
815 | do not, unfortunately, and I will describe a couple of ways to deal
816 | with this.
817 |
818 | Maybe the easiest thing is to configure [[https://github.com/minad/marginalia][Marginalia]] to enhance those
819 | commands. All of the =tab-bar-*-tab-by-name= commands have the words
820 | "tab by name" in the minibuffer prompt, so you can use:
821 |
822 | #+begin_src emacs-lisp
823 | (add-to-list 'marginalia-prompt-categories '("tab by name" . tab))
824 | #+end_src
825 |
826 | That's it! But in case you are ever in a situation where you don't
827 | already have commands that prompt for the targets you want, I'll
828 | describe how writing your own command with appropriate =category=
829 | metadata looks:
830 |
831 | #+begin_src emacs-lisp
832 | (defun my-select-tab-by-name (tab)
833 | (interactive
834 | (list
835 | (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab)))
836 | (tab-bar-tabs))
837 | (user-error "No tabs found"))))
838 | (completing-read
839 | "Tabs: "
840 | (lambda (string predicate action)
841 | (if (eq action 'metadata)
842 | '(metadata (category . tab))
843 | (complete-with-action
844 | action tab-list string predicate)))))))
845 | (tab-bar-select-tab-by-name tab))
846 | #+end_src
847 |
848 | As you can see, the built-in support for setting the category
849 | meta-datum is not very easy to use or pretty to look at. To help with
850 | this I recommend the =consult--read= function from the excellent
851 | [[https://github.com/minad/consult/][Consult]] package. With that function we can rewrite the command as
852 | follows:
853 |
854 | #+begin_src emacs-lisp
855 | (defun my-select-tab-by-name (tab)
856 | (interactive
857 | (list
858 | (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab)))
859 | (tab-bar-tabs))
860 | (user-error "No tabs found"))))
861 | (consult--read tab-list
862 | :prompt "Tabs: "
863 | :category 'tab))))
864 | (tab-bar-select-tab-by-name tab))
865 | #+end_src
866 |
867 | Much nicer! No matter how you define the =my-select-tab-by-name=
868 | command, the first approach with Marginalia and prompt detection has
869 | the following advantages: you get the =tab= category for all the
870 | =tab-bar-*-bar-by-name= commands at once, also, you enhance built-in
871 | commands, instead of defining new ones.
872 |
873 | **** Defining and configuring a keymap for tab actions
874 |
875 | Let's say we want to offer select, rename and close actions for tabs
876 | (in addition to Embark general actions, such as saving the tab name to
877 | the kill-ring, which you get for free). Then this will do:
878 |
879 | #+begin_src emacs-lisp
880 | (defvar-keymap embark-tab-actions
881 | :doc "Keymap for actions for tab-bar tabs (when mentioned by name)."
882 | :parent embark-general-map
883 | "s" #'tab-bar-select-tab-by-name
884 | "r" #'tab-bar-rename-tab-by-name
885 | "k" #'tab-bar-close-tab-by-name)
886 |
887 | (add-to-list 'embark-keymap-alist '(tab . embark-tab-actions))
888 | #+end_src
889 |
890 | What if after using this for a while you feel closing the tab
891 | without confirmation is dangerous? You have a couple of options:
892 |
893 | 1. You can keep using the =tab-bar-close-tab-by-name= command, but have
894 | Embark ask you for confirmation:
895 | #+begin_src emacs-lisp
896 | (push #'embark--confirm
897 | (alist-get 'tab-bar-close-tab-by-name
898 | embark-pre-action-hooks))
899 | #+end_src
900 |
901 | 2. You can write your own command that prompts for confirmation and
902 | use that instead of =tab-bar-close-tab-by-name= in the above keymap:
903 | #+begin_src emacs-lisp
904 | (defun my-confirm-close-tab-by-name (tab)
905 | (interactive "sTab to close: ")
906 | (when (y-or-n-p (format "Close tab '%s'? " tab))
907 | (tab-bar-close-tab-by-name tab)))
908 | #+end_src
909 |
910 | Notice that this is a command you can also use directly from =M-x=
911 | independently of Embark. Using it from =M-x= leaves something to be
912 | desired, though, since you don't get completion for the tab names.
913 | You can fix this if you wish as described in the previous section.
914 |
915 | *** New target example in regular buffers - short Wikipedia links
916 |
917 | Say you want to teach Embark to treat text of the form
918 | =wikipedia:Garry_Kasparov= in any regular buffer as a link to Wikipedia,
919 | with actions to open the Wikipedia page in eww or an external browser
920 | or to save the URL of the page in the kill-ring. We can take advantage
921 | of the actions that Embark has preconfigured for URLs, so all we need
922 | to do is teach Embark that =wikipedia:Garry_Kasparov= stands for the URL
923 | =https://en.wikipedia.org/wiki/Garry_Kasparov=.
924 |
925 | You can be as fancy as you want with the recognized syntax. Here, to
926 | keep the example simple, I'll assume the link matches the regexp
927 | =wikipedia:[[:alnum:]_]+=. We will write a function that looks for a
928 | match surrounding point, and returns a dotted list of the form ='(url
929 | URL-OF-THE-PAGE START . END)= where =START= and =END= are the buffer
930 | positions bounding the target, and are used by Embark to highlight it
931 | if you have =embark-highlight-indicator= included in the list
932 | =embark-indicators=. (There are a couple of other options for the return
933 | value of a target finder: the bounding positions are optional and a
934 | single target finder is allowed to return multiple targets; see the
935 | documentation for =embark-target-finders= for details.)
936 |
937 | #+begin_src emacs-lisp
938 | (defun my-short-wikipedia-link ()
939 | "Target a link at point of the form wikipedia:Page_Name."
940 | (save-excursion
941 | (let* ((start (progn (skip-chars-backward "[:alnum:]_:") (point)))
942 | (end (progn (skip-chars-forward "[:alnum:]_:") (point)))
943 | (str (buffer-substring-no-properties start end)))
944 | (save-match-data
945 | (when (string-match "wikipedia:\\([[:alnum:]_]+\\)" str)
946 | `(url
947 | ,(format "https://en.wikipedia.org/wiki/%s"
948 | (match-string 1 str))
949 | ,start . ,end))))))
950 |
951 | (add-to-list 'embark-target-finders 'my-short-wikipedia-link)
952 | #+end_src
953 |
954 | * How does Embark call the actions?
955 |
956 | Embark actions are normal Emacs commands, that is, functions with an
957 | interactive specification. In order to execute an action, Embark
958 | calls the command with =call-interactively=, so the command reads user
959 | input exactly as if run directly by the user. For example the
960 | command may open a minibuffer and read a string
961 | (=read-from-minibuffer=) or open a completion interface
962 | (=completing-read=). If this happens, Embark takes the target string
963 | and inserts it automatically into the minibuffer, simulating user
964 | input this way. After inserting the string, Embark exits the
965 | minibuffer, submitting the input. (The immediate minibuffer exit can
966 | be disabled for specific actions in order to allow editing the
967 | input; this is done by adding the =embark--allow-edit= function to the
968 | appropriate entry of =embark-target-injection-hooks=). Embark inserts
969 | the target string at the first minibuffer opened by the action
970 | command, and if the command happens to prompt the user for input
971 | more than once, the user still interacts with the second and further
972 | prompts in the normal fashion. Note that if a command does not
973 | prompt the user for input in the minibuffer, Embark still allows you
974 | to use it as an action, but of course, never inserts the target
975 | anywhere. (There are plenty of examples in the default configuration
976 | of commands that do not prompt the user bound to keys in the action
977 | maps, most of the region actions, for instance.)
978 |
979 | This is how Embark manages to reuse normal commands as actions. The
980 | mechanism allows you to use as Embark actions commands that were not
981 | written with Embark in mind (and indeed almost all actions that are
982 | bound by default in Embark's action keymaps are standard Emacs
983 | commands). It also allows you to write new custom actions in such a
984 | way that they are useful even without Embark.
985 |
986 | Emacs has a variable =y-or-n-p-use-read-key=, which when set to =t=
987 | causes =y-or-n-p= to use =read-key= instead of =read-from-minibuffer=.
988 | Setting =y-or-n-p-use-read-key= to =t= is recommended for Embark users
989 | because it keeps Embark from attempting to insert the target at a
990 | =y-or-n-p= prompt, which would almost never be sensible. Also consider
991 | this as a warning to structure your own action commands so that if
992 | they use =y-or-n-p=, they do so only after the prompting for the
993 | target.
994 |
995 | Here is a simple example illustrating the various ways of reading
996 | input from the user mentioned above. Bind the following commands to
997 | the =embark-symbol-map= to be used as actions, then put the point on
998 | some symbol and run them with =embark-act=:
999 |
1000 | #+begin_src emacs-lisp
1001 | (defun example-action-command1 ()
1002 | (interactive)
1003 | (message "The input was `%s'." (read-from-minibuffer "Input: ")))
1004 |
1005 | (defun example-action-command2 (arg input1 input2)
1006 | (interactive "P\nsInput 1: \nsInput 2: ")
1007 | (message "The first input %swas `%s', and the second was `%s'."
1008 | (if arg "truly " "")
1009 | input1
1010 | input2))
1011 |
1012 | (defun example-action-command3 ()
1013 | (interactive)
1014 | (message "Your selection was `%s'."
1015 | (completing-read "Select: " '("E" "M" "B" "A" "R" "K"))))
1016 |
1017 | (defun example-action-command4 ()
1018 | (interactive)
1019 | (message "I don't prompt you for input and thus ignore the target!"))
1020 |
1021 | (keymap-set embark-symbol-map "X 1" #'example-action-command1)
1022 | (keymap-set embark-symbol-map "X 2" #'example-action-command2)
1023 | (keymap-set embark-symbol-map "X 3" #'example-action-command3)
1024 | (keymap-set embark-symbol-map "X 4" #'example-action-command4)
1025 | #+end_src
1026 |
1027 | Also note that if you are using the key bindings to call actions,
1028 | you can pass prefix arguments to actions in the normal way. For
1029 | example, you can use =C-u X2= with the above demonstration actions to
1030 | make the message printed by =example-action-command2= more emphatic.
1031 | This ability to pass prefix arguments to actions is useful for some
1032 | actions in the default configuration, such as
1033 | =embark-shell-command-on-buffer=.
1034 |
1035 | ** Non-interactive functions as actions
1036 |
1037 | Alternatively, Embark does support one other type of action: a
1038 | non-interactive function of a single argument. The target is passed
1039 | as argument to the function. For example:
1040 |
1041 | #+begin_src emacs-lisp
1042 | (defun example-action-function (target)
1043 | (message "The target was `%s'." target))
1044 |
1045 | (keymap-set embark-symbol-map "X 4" #'example-action-function)
1046 | #+end_src
1047 |
1048 | Note that normally binding non-interactive functions in a keymap is
1049 | useless, since when attempting to run them using the key binding you
1050 | get an error message similar to "Wrong type argument: commandp,
1051 | example-action-function". In general it is more flexible to write
1052 | any new Embark actions as commands, that is, as interactive
1053 | functions, because that way you can also run them directly, without
1054 | Embark. But there are a couple of reasons to use non-interactive
1055 | functions as actions:
1056 |
1057 | 1. You may already have the function lying around, and it is
1058 | convenient to simply reuse it.
1059 |
1060 | 2. For command actions the targets can only be simple string, with
1061 | no text properties. For certain advanced uses you may want the
1062 | action to receive a string /with/ some text properties, or even a
1063 | non-string target.
1064 |
1065 | * Embark, Marginalia and Consult
1066 |
1067 | Embark cooperates well with the [[https://github.com/minad/marginalia][Marginalia]] and [[https://github.com/minad/consult][Consult]] packages.
1068 | Neither of those packages is a dependency of Embark, but both are
1069 | highly recommended companions to Embark, for opposite reasons:
1070 | Marginalia greatly enhances Embark's usefulness, while Embark can help
1071 | enhance Consult.
1072 |
1073 | In the remainder of this section I'll explain what exactly Marginalia
1074 | does for Embark, and what Embark can do for Consult.
1075 |
1076 | ** Marginalia
1077 |
1078 | Embark comes with actions for symbols (commands, functions, variables
1079 | with actions such as finding the definition, looking up the
1080 | documentation, evaluating, etc.) in the =embark-symbol-map= keymap, and
1081 | for packages (actions like install, delete, browse url, etc.) in the
1082 | =embark-package-keymap=.
1083 |
1084 | Unfortunately Embark does not automatically offers you these keymaps
1085 | when relevant, because many built-in Emacs commands don't report
1086 | accurate category metadata. For example, a command like
1087 | =describe-package=, which reads a package name from the minibuffer,
1088 | does not have metadata indicating this fact.
1089 |
1090 | In an earlier Embark version, there were functions to supply this
1091 | missing metadata, but they have been moved to Marginalia, which
1092 | augments many Emacs command to report accurate category metadata.
1093 | Simply activating =marginalia-mode= allows Embark to offer you the
1094 | package and symbol actions when appropriate again. Candidate
1095 | annotations in the Embark collect buffer are also provided by the
1096 | Marginalia package:
1097 |
1098 | - If you install Marginalia and activate =marginalia-mode=, Embark
1099 | Collect buffers will use the Marginalia annotations automatically.
1100 |
1101 | - If you don't install Marginalia, you will see only the annotations
1102 | that come with Emacs (such as key bindings in =M-x=, or the unicode
1103 | characters in =C-x 8 RET=).
1104 |
1105 | ** Consult
1106 |
1107 | The excellent Consult package provides many commands that use
1108 | minibuffer completion, via the =completing-read= function; plenty of its
1109 | commands can be considered enhanced versions of built-in Emacs
1110 | commands, and some are completely new functionality. One common
1111 | enhancement provided in all commands for which it makes sense is
1112 | preview functionality, for example =consult-buffer= will show you a
1113 | quick preview of a buffer before you actually switch to it.
1114 |
1115 | If you use both Consult and Embark you should install the
1116 | =embark-consult= package which provides integration between the two. It
1117 | provides exporters for several Consult commands and also tweaks the
1118 | behavior of many Consult commands when used as actions with =embark-act=
1119 | in subtle ways that you may not even notice, but make for a smoother
1120 | experience. You need only install it to get these benefits: Embark
1121 | will automatically load it after Consult if found.
1122 |
1123 | The =embark-consult= package provides the following exporters:
1124 |
1125 | - You can use =embark-export= from =consult-line=, =consult-outline=, or
1126 | =consult-mark= to obtain an =occur-mode= buffer. As with the built-in
1127 | =occur= command you use that buffer to jump to a match and after that,
1128 | you can then use =next-error= and =previous-error= to navigate to other
1129 | matches. You can also press =e= to activate =occur-edit-mode= and edit
1130 | the matches in place!
1131 |
1132 | - You can export from any of the Consult asynchronous search commands,
1133 | =consult-grep=, =consult-git-grep=, or =consult-ripgrep= to get a
1134 | =grep-mode= buffer. Here too you can use =next-error= and =previous-error=
1135 | to navigate among matches, and, if you install the [[http://github.com/mhayashi1120/Emacs-wgrep/raw/master/wgrep.el ][wgrep]] package,
1136 | you can use it to edit the matches in place.
1137 |
1138 | In both cases, pressing =g= will rerun the Consult command you had
1139 | exported from and re-enter the input you had typed (which is similar
1140 | to reverting but a little more flexible). You can then proceed to
1141 | re-export if that's what you want, but you can also edit the input
1142 | changing the search terms or simply cancel if you see you are done
1143 | with that search.
1144 |
1145 | The =embark-consult= also contains some candidates collectors that allow
1146 | you to run =embark-live= to get a live-updating table of contents for
1147 | your buffer:
1148 |
1149 | - =embark-consult-outline-candidates= produces the outline headings of
1150 | the current buffer, using =consult-outline=.
1151 | - =embark-consult-imenu-candidates= produces the imenu items of
1152 | the current buffer, using =consult-imenu=.
1153 | - =embark-consult-imenu-or-outline-candidates= is a simple combination
1154 | of the two previous functions: it produces imenu items in buffers
1155 | deriving from =prog-mode= and otherwise outline headings.
1156 |
1157 | The way to configure =embark-live= (or =embark-collect= and =embark-export=
1158 | for that matter) to use one of these function is to add it at the end
1159 | of the =embark-candidate-collectors= list. The =embark-consult= package by
1160 | default adds the last one, which seems to be the most sensible
1161 | default.
1162 |
1163 | Besides those exporters and candidate collectors, the =embark-consult=
1164 | package provides many subtle tweaks and small integrations between
1165 | Embark and Consult. Some examples are:
1166 |
1167 | - When used as actions, the asynchronous search commands will search
1168 | only the files associated to the targets: if the targets /are/ files,
1169 | it searches those files; for buffers it will search either the
1170 | associated file if there is one, else all files in the buffer's
1171 | =default-directory=; for bookmarks it will search the file they point
1172 | to, same for Emacs Lisp libraries. This is particularly powerful
1173 | when using =embark-act-all= to act on multiple files at once, for
1174 | example you can use =consult-find= to search among file /names/ and then
1175 | =embark-act-all= and =consult-grep= to search within the matching files.
1176 |
1177 | - For all other target types, those that do not have a sensible
1178 | notion of associated file, a Consult search command (asynchronous
1179 | or not) will search for the text of the target but leave the
1180 | minibuffer open so you can interact with the Consult command.
1181 |
1182 | - =consult-imenu= will search for the target and take you directly to
1183 | the location if it matches a unique imenu entry, otherwise it will
1184 | leave the minibuffer open so you can navigate among the matches.
1185 |
1186 | * Related Packages
1187 |
1188 | There are several packages that offer functionality similar
1189 | to Embark's.
1190 |
1191 | - Acting on minibuffer completion candidates :: The popular Ivy and
1192 | Helm packages have support for acting on the completion candidates
1193 | of commands written using their APIs, and there is an extensive
1194 | ecosystem of packages meant for Helm and for Ivy (the Ivy ones
1195 | usually have "counsel" in the name) providing commands and
1196 | appropriate actions.
1197 | - Acting on things at point :: The built-in =context-menu-mode= provides
1198 | a mouse-driven context-sensitive configurable menu. The =do-at-point=
1199 | package by Philip Kaludercic (available on GNU ELPA), on the other
1200 | hand is keyboard-driven.
1201 | - Collecting completion candidates into a buffer :: The Ivy package
1202 | has the command =ivy-occur= which is similar to =embark-collect=. As
1203 | with Ivy actions, =ivy-occur= only works for commands written using
1204 | the Ivy API.
1205 |
1206 | * Resources
1207 |
1208 | If you want to learn more about how others have used Embark here are
1209 | some links to read:
1210 |
1211 | - [[https://karthinks.com/software/fifteen-ways-to-use-embark/][Fifteen ways to use Embark]], a blog post by Karthik Chikmagalur.
1212 | - [[https://protesilaos.com/dotemacs/][Protesilaos Stavrou's dotemacs]], look for the section called
1213 | "Extended minibuffer actions and more (embark.el and
1214 | prot-embark.el)"
1215 |
1216 | And some videos to watch:
1217 |
1218 | - [[https://protesilaos.com/codelog/2021-01-09-emacs-embark-extras/][Embark and my extras]] by Protesilaos Stavrou.
1219 | - [[https://youtu.be/qpoQiiinCtY][Embark -- Key features and tweaks]] by Raoul Comninos on the
1220 | Emacs-Elements YouTube channel.
1221 | - [[https://youtu.be/WsxXr1ncukY][Livestreamed: Adding an Embark context action to send a stream
1222 | message]] by Sacha Chua.
1223 | - [[https://youtu.be/qk2Is_sC8Lk][System Crafters Live! - The Many Uses of Embark]] by David Wilson.
1224 | - [[https://youtu.be/5ffb2at2d7w][Using Emacs Episode 80 - Vertico, Marginalia, Consult and Embark]] by
1225 | Mike Zamansky.
1226 |
1227 | * Contributions
1228 |
1229 | Contributions to Embark are very welcome. There is a [[https://github.com/oantolin/embark/issues/95][wish list]] for
1230 | actions, target finders, candidate collectors and exporters. For other
1231 | ideas you have for Embark, feel free to open an issue on the [[https://github.com/oantolin/embark/issues][issue
1232 | tracker]]. Any neat configuration tricks you find might be a good fit
1233 | for the [[https://github.com/oantolin/embark/wiki][wiki]].
1234 |
1235 | Code contributions are very welcome too, but since Embark is now on
1236 | GNU ELPA, copyright assignment to the FSF is required before you can
1237 | contribute code.
1238 |
1239 | * Acknowledgments
1240 |
1241 | While I, Omar Antolín Camarena, have written most of the Embark code
1242 | and remain very stubborn about some of the design decisions, Embark
1243 | has received substantial help from a number of other people which this
1244 | document has neglected to mention for far too long. In particular,
1245 | Daniel Mendler has been absolutely invaluable, implementing several
1246 | important features, and providing a lot of useful advice.
1247 |
1248 | Code contributions:
1249 |
1250 | - [[https://github.com/minad][Daniel Mendler]]
1251 | - [[https://github.com/clemera/][Clemens Radermacher]]
1252 | - [[https://codeberg.org/jao/][José Antonio Ortega Ruiz]]
1253 | - [[https://github.com/iyefrat][Itai Y. Efrat]]
1254 | - [[https://github.com/a13][a13]]
1255 | - [[https://github.com/jakanakaevangeli][jakanakaevangeli]]
1256 | - [[https://github.com/mihakam][mihakam]]
1257 | - [[https://github.com/leungbk][Brian Leung]]
1258 | - [[https://github.com/karthink][Karthik Chikmagalur]]
1259 | - [[https://github.com/roshanshariff][Roshan Shariff]]
1260 | - [[https://github.com/condy0919][condy0919]]
1261 | - [[https://github.com/DamienCassou][Damien Cassou]]
1262 | - [[https://github.com/JimDBh][JimDBh]]
1263 |
1264 | Advice and useful discussions:
1265 |
1266 | - [[https://github.com/minad][Daniel Mendler]]
1267 | - [[https://gitlab.com/protesilaos/][Protesilaos Stavrou]]
1268 | - [[https://github.com/clemera/][Clemens Radermacher]]
1269 | - [[https://github.com/hmelman/][Howard Melman]]
1270 | - [[https://github.com/astoff][Augusto Stoffel]]
1271 | - [[https://github.com/bdarcus][Bruce d'Arcus]]
1272 | - [[https://github.com/jdtsmith][JD Smith]]
1273 | - [[https://github.com/karthink][Karthik Chikmagalur]]
1274 | - [[https://github.com/jakanakaevangeli][jakanakaevangeli]]
1275 | - [[https://github.com/iyefrat][Itai Y. Efrat]]
1276 | - [[https://github.com/mohkale][Mohsin Kaleem]]
1277 |
--------------------------------------------------------------------------------
/avy-embark-collect.el:
--------------------------------------------------------------------------------
1 | ;;; avy-embark-collect.el --- Use avy to jump to Embark Collect entries -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2020-2025 Omar Antolín Camarena
4 |
5 | ;; Author: Omar Antolín Camarena
6 | ;; Keywords: convenience
7 | ;; Version: 0.3
8 | ;; URL: https://github.com/oantolin/embark
9 | ;; Package-Requires: ((emacs "25.1") (embark "0.9") (avy "0.5"))
10 |
11 | ;; This program is free software; you can redistribute it and/or modify
12 | ;; it under the terms of the GNU General Public License as published by
13 | ;; the Free Software Foundation, either version 3 of the License, or
14 | ;; (at your option) any later version.
15 |
16 | ;; This program is distributed in the hope that it will be useful,
17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | ;; GNU General Public License for more details.
20 |
21 | ;; You should have received a copy of the GNU General Public License
22 | ;; along with this program. If not, see .
23 |
24 | ;;; Commentary:
25 |
26 | ;; This packages provides two commands, `avy-embark-collect-choose' and
27 | ;; `avy-embark-collect-act', that use avy to jump to an Embark Collect
28 | ;; entry and choose it or act on it, respectively.
29 |
30 | ;;; Code:
31 |
32 | (require 'avy)
33 | (require 'embark)
34 | (eval-when-compile (require 'subr-x))
35 |
36 | (defun avy-embark-collect--candidates ()
37 | "Collect all visible Embark collect candidates."
38 | (let (candidates)
39 | (avy-dowindows current-prefix-arg ; avy-dowindows binds wnd! 🤯
40 | (when (derived-mode-p 'embark-collect-mode)
41 | (dolist (pair (avy--find-visible-regions
42 | (window-start) (window-end wnd t)))
43 | (save-excursion
44 | (goto-char (car pair))
45 | (when (button-at (point))
46 | (push (cons (point) wnd) candidates))
47 | (while (and (condition-case nil (forward-button 1)
48 | (error nil))
49 | (< (point) (cdr pair)))
50 | (push (cons (point) wnd) candidates))))))
51 | (nreverse candidates)))
52 |
53 | (defun avy-embark-collect--act (pt)
54 | "Act on the completion at PT."
55 | (unwind-protect
56 | (save-excursion
57 | (goto-char pt)
58 | (embark-act))
59 | (select-window (cdr (ring-ref avy-ring 0)))
60 | t))
61 |
62 | (defun avy-embark-collect--choose (pt)
63 | "Choose on the completion at PT."
64 | (unwind-protect (push-button pt)
65 | (select-window (cdr (ring-ref avy-ring 0)))
66 | t))
67 |
68 | (defun avy-embark-collect--jump (action dispatch-alist)
69 | "Jump to a visible Embark Collect candidate and perform ACTION.
70 | Other actions are listed in the DISPATCH-ALIST."
71 | (interactive)
72 | (avy-with avy-embark-collect-choose
73 | (let ((avy-action action)
74 | (avy-dispatch-alist dispatch-alist))
75 | (avy-process (avy-embark-collect--candidates)))))
76 |
77 | ;;;###autoload
78 | (defun avy-embark-collect-choose ()
79 | "Choose an Embark Collect candidate."
80 | (interactive)
81 | (avy-embark-collect--jump #'avy-embark-collect--choose
82 | '((?e . avy-embark-collect--act)
83 | (?p . avy-action-goto))))
84 |
85 | ;;;###autoload
86 | (defun avy-embark-collect-act ()
87 | "Act on an Embark Collect candidate."
88 | (interactive)
89 | (avy-embark-collect--jump #'avy-embark-collect--act
90 | '((?e . avy-embark-collect--choose)
91 | (?p . avy-action-goto))))
92 |
93 | (provide 'avy-embark-collect)
94 | ;;; avy-embark-collect.el ends here
95 |
--------------------------------------------------------------------------------
/embark-consult.el:
--------------------------------------------------------------------------------
1 | ;;; embark-consult.el --- Consult integration for Embark -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2021-2025 Free Software Foundation, Inc.
4 |
5 | ;; Author: Omar Antolín Camarena
6 | ;; Maintainer: Omar Antolín Camarena
7 | ;; Keywords: convenience
8 | ;; Version: 1.1
9 | ;; URL: https://github.com/oantolin/embark
10 | ;; Package-Requires: ((emacs "28.1") (compat "30") (embark "1.1") (consult "1.8"))
11 |
12 | ;; This program is free software; you can redistribute it and/or modify
13 | ;; it under the terms of the GNU General Public License as published by
14 | ;; the Free Software Foundation, either version 3 of the License, or
15 | ;; (at your option) any later version.
16 |
17 | ;; This program is distributed in the hope that it will be useful,
18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 | ;; GNU General Public License for more details.
21 |
22 | ;; You should have received a copy of the GNU General Public License
23 | ;; along with this program. If not, see .
24 |
25 | ;;; Commentary:
26 |
27 | ;; This package provides integration between Embark and Consult. The package
28 | ;; will be loaded automatically by Embark.
29 |
30 | ;; Some of the functionality here was previously contained in Embark
31 | ;; itself:
32 |
33 | ;; - Support for consult-buffer, so that you get the correct actions
34 | ;; for each type of entry in consult-buffer's list.
35 |
36 | ;; - Support for consult-line, consult-outline, consult-mark and
37 | ;; consult-global-mark, so that the insert and save actions don't
38 | ;; include a weird unicode character at the start of the line, and so
39 | ;; you can export from them to an occur buffer (where occur-edit-mode
40 | ;; works!).
41 |
42 | ;; Just load this package to get the above functionality, no further
43 | ;; configuration is necessary.
44 |
45 | ;; Additionally this package contains some functionality that has
46 | ;; never been in Embark: access to Consult preview from auto-updating
47 | ;; Embark Collect buffer that is associated to an active minibuffer
48 | ;; for a Consult command. For information on Consult preview, see
49 | ;; Consult's info manual or its readme on GitHub.
50 |
51 | ;; If you always want the minor mode enabled whenever it possible use:
52 |
53 | ;; (add-hook 'embark-collect-mode-hook #'consult-preview-at-point-mode)
54 |
55 | ;; If you don't want the minor mode automatically on and prefer to
56 | ;; trigger the consult previews manually use this instead:
57 |
58 | ;; (keymap-set embark-collect-mode-map "C-j"
59 | ;; #'consult-preview-at-point)
60 |
61 | ;;; Code:
62 |
63 | (require 'embark)
64 | (require 'consult)
65 |
66 | (eval-when-compile
67 | (require 'cl-lib))
68 |
69 | ;;; Consult preview from Embark Collect buffers
70 |
71 | (defun embark-consult--collect-candidate ()
72 | "Return candidate at point in collect buffer."
73 | (cadr (embark-target-collect-candidate)))
74 |
75 | (add-hook 'consult--completion-candidate-hook #'embark-consult--collect-candidate)
76 |
77 | ;;; Support for consult-location
78 |
79 | (defun embark-consult--strip (string)
80 | "Strip substrings marked with the `consult-strip' property from STRING."
81 | (if (text-property-not-all 0 (length string) 'consult-strip nil string)
82 | (let ((end (length string)) (pos 0) (chunks))
83 | (while (< pos end)
84 | (let ((next (next-single-property-change pos 'consult-strip string end)))
85 | (unless (get-text-property pos 'consult-strip string)
86 | (push (substring string pos next) chunks))
87 | (setq pos next)))
88 | (apply #'concat (nreverse chunks)))
89 | string))
90 |
91 | (defun embark-consult--target-strip (type target)
92 | "Remove the unicode suffix character from a TARGET of TYPE."
93 | (cons type (embark-consult--strip target)))
94 |
95 | (setf (alist-get 'consult-location embark-transformer-alist)
96 | #'embark-consult--target-strip)
97 |
98 | (defun embark-consult-goto-location (target)
99 | "Jump to consult location TARGET."
100 | (consult--jump (car (consult--get-location target)))
101 | (pulse-momentary-highlight-one-line (point)))
102 |
103 | (setf (alist-get 'consult-location embark-default-action-overrides)
104 | #'embark-consult-goto-location)
105 |
106 | (defun embark-consult-export-location-occur (lines)
107 | "Create an occur mode buffer listing LINES.
108 | The elements of LINES should be completion candidates with
109 | category `consult-line'."
110 | (let ((buf (generate-new-buffer "*Embark Export Occur*"))
111 | (mouse-msg "mouse-2: go to this occurrence")
112 | (inhibit-read-only t)
113 | last-buf)
114 | (with-current-buffer buf
115 | (dolist (line lines)
116 | (pcase-let*
117 | ((`(,loc . ,num) (consult--get-location line))
118 | ;; the text properties added to the following strings are
119 | ;; taken from occur-engine
120 | (lineno (propertize
121 | (format "%7d:" num)
122 | 'occur-prefix t
123 | ;; Allow insertion of text at the end
124 | ;; of the prefix (for Occur Edit mode).
125 | 'front-sticky t
126 | 'rear-nonsticky t
127 | 'read-only t
128 | 'occur-target loc
129 | 'follow-link t
130 | 'help-echo mouse-msg
131 | 'font-lock-face list-matching-lines-prefix-face
132 | 'mouse-face 'highlight))
133 | (contents (propertize (embark-consult--strip line)
134 | 'occur-target loc
135 | 'occur-match t
136 | 'follow-link t
137 | 'help-echo mouse-msg
138 | 'mouse-face 'highlight))
139 | (nl (propertize "\n" 'occur-target loc))
140 | (this-buf (marker-buffer loc)))
141 | (unless (eq this-buf last-buf)
142 | (insert (propertize
143 | (format "lines from buffer: %s\n" this-buf)
144 | 'face list-matching-lines-buffer-name-face
145 | 'read-only t))
146 | (setq last-buf this-buf))
147 | (insert lineno contents nl)))
148 | (goto-char (point-min))
149 | (occur-mode))
150 | (pop-to-buffer buf)))
151 |
152 | (defun embark-consult-export-location-grep (lines)
153 | "Create a grep mode buffer listing LINES.
154 | Any LINES that come from a buffer which is not visiting a file
155 | will be excluded from the grep buffer, since grep mode only works
156 | with files. The elements of LINES should be completion
157 | candidates with category `consult-location'. No matches will be
158 | highlighted in the exported buffer, since the `consult-location'
159 | candidates do not carry that information."
160 | (let (non-file-buffers)
161 | (embark-consult--export-grep
162 | :header "Exported line search results (file-backed buffers only):\n\n"
163 | :lines lines
164 | :insert
165 | (lambda (lines)
166 | (let ((count 0))
167 | (dolist (line lines)
168 | (pcase-let* ((`(,loc . ,num) (consult--get-location line))
169 | (lineno (format "%d" num))
170 | (contents (embark-consult--strip line))
171 | (buffer (marker-buffer loc))
172 | (file (buffer-file-name buffer)))
173 | (if (null file)
174 | (cl-pushnew buffer non-file-buffers)
175 | (insert (file-relative-name file) ":" lineno ":" contents "\n")
176 | (cl-incf count))))
177 | count))
178 | :footer
179 | (lambda ()
180 | (when non-file-buffers
181 | (let ((start (goto-char (point-max))))
182 | (insert "\nSome results were in buffers with no associated file"
183 | " and are missing\nfrom the exported result:\n")
184 | (dolist (buf non-file-buffers)
185 | (insert "- " (buffer-name buf) "\n"))
186 | (insert "\nEither save the buffers or use the"
187 | " `embark-consult-export-location-occur'\nexporter.")
188 | (message "This exporter does not support non-file buffers: %s"
189 | non-file-buffers)
190 | (add-text-properties
191 | start (point-max)
192 | '(read-only t wgrep-footer t front-sticky t))))))))
193 |
194 | (defun embark-consult--upgrade-markers ()
195 | "Upgrade consult-location cheap markers to real markers.
196 | This function is meant to be added to `embark-collect-mode-hook'."
197 | (when (eq embark--type 'consult-location)
198 | (dolist (entry tabulated-list-entries)
199 | (when (car entry)
200 | (consult--get-location (car entry))))))
201 |
202 | ;; Set default `occur-mode' based exporter for consult-line,
203 | ;; consult-line-multi, consult-outline and alike Another option is
204 | ;; using grep-mode by using `embark-consult-export-location-grep'
205 | (setf (alist-get 'consult-location embark-exporters-alist)
206 | #'embark-consult-export-location-occur)
207 | (cl-pushnew #'embark-consult--upgrade-markers embark-collect-mode-hook)
208 |
209 | ;;; Support for consult-grep
210 |
211 | (defvar grep-mode-line-matches)
212 | (defvar grep-num-matches-found)
213 | (declare-function wgrep-setup "ext:wgrep")
214 |
215 | (defvar-keymap embark-consult-rerun-map
216 | :doc "A keymap with a binding for `embark-rerun-collect-or-export'."
217 | :parent nil
218 | "g" #'embark-rerun-collect-or-export)
219 |
220 | (cl-defun embark-consult--export-grep (&key header lines insert footer)
221 | "Create a grep mode buffer listing LINES.
222 | The HEADER string is inserted at the top of the buffer. The
223 | function INSERT is called to insert the LINES and should return a
224 | count of the matches (there may be more than one match per line).
225 | The function FOOTER is called to insert a footer."
226 | (let ((buf (generate-new-buffer "*Embark Export Grep*")))
227 | (with-current-buffer buf
228 | (grep-mode)
229 | (setq-local mode-line-process grep-mode-line-matches)
230 | (let ((inhibit-read-only t))
231 | (insert (propertize header 'wgrep-header t 'front-sticky t))
232 | (dlet ((compilation-filter-start (point)))
233 | (setq-local grep-num-matches-found (funcall insert lines))
234 | (goto-char (point-max))
235 | ;; Emacs 30 feature `grep-use-headings'
236 | (when (and (bound-and-true-p grep-use-headings)
237 | (fboundp 'grep--heading-filter))
238 | (grep--heading-filter))
239 | (funcall footer)
240 | (goto-char (point-min))))
241 | ;; Make this buffer current for next/previous-error
242 | (setq next-error-last-buffer buf)
243 | ;; Set up keymap before possible wgrep-setup, so that wgrep
244 | ;; restores our binding too when the user finishes editing.
245 | (use-local-map (make-composed-keymap
246 | embark-consult-rerun-map
247 | (current-local-map)))
248 | ;; TODO Wgrep 3.0 and development versions use different names for the
249 | ;; parser variable.
250 | (defvar wgrep-header/footer-parser)
251 | (defvar wgrep-header&footer-parser)
252 | (setq-local wgrep-header/footer-parser #'ignore
253 | wgrep-header&footer-parser #'ignore)
254 | (when (fboundp 'wgrep-setup) (wgrep-setup)))
255 | (pop-to-buffer buf)))
256 |
257 | (defun embark-consult-export-grep (lines)
258 | "Create a grep mode buffer listing LINES.
259 | The elements of LINES should be completion candidates with
260 | category `consult-grep'."
261 | (embark-consult--export-grep
262 | :header "Exported grep results:\n\n"
263 | :lines lines
264 | :insert
265 | (lambda (lines)
266 | (dolist (line lines) (insert line "\n"))
267 | (goto-char (point-min))
268 | (let ((count 0) prop)
269 | (while (setq prop (text-property-search-forward
270 | 'face 'consult-highlight-match t))
271 | (cl-incf count)
272 | (put-text-property (prop-match-beginning prop)
273 | (prop-match-end prop)
274 | 'font-lock-face
275 | 'match))
276 | count))
277 | :footer #'ignore))
278 |
279 | (defun embark-consult-goto-grep (location)
280 | "Go to LOCATION, which should be a string with a grep match."
281 | (consult--jump (consult--grep-position location))
282 | (pulse-momentary-highlight-one-line (point)))
283 |
284 | (setf (alist-get 'consult-grep embark-default-action-overrides)
285 | #'embark-consult-goto-grep)
286 | (setf (alist-get 'consult-grep embark-exporters-alist)
287 | #'embark-consult-export-grep)
288 |
289 | ;;; Support for consult-xref
290 |
291 | (declare-function consult-xref "ext:consult-xref")
292 | (declare-function xref--show-xref-buffer "xref")
293 | (declare-function xref-pop-to-location "xref")
294 | (defvar xref-auto-jump-to-first-xref)
295 | (defvar consult-xref--fetcher)
296 |
297 | (defun embark-consult-export-xref (items)
298 | "Create an xref buffer listing ITEMS."
299 | (cl-flet ((xref-items (items)
300 | (mapcar (lambda (item) (get-text-property 0 'consult-xref item))
301 | items)))
302 | (let ((fetcher consult-xref--fetcher)
303 | (input (minibuffer-contents)))
304 | (set-buffer
305 | (xref--show-xref-buffer
306 | (lambda ()
307 | (let ((candidates (funcall fetcher)))
308 | (if (null (cdr candidates))
309 | candidates
310 | (catch 'xref-items
311 | (minibuffer-with-setup-hook
312 | (lambda ()
313 | (insert input)
314 | (add-hook
315 | 'minibuffer-exit-hook
316 | (lambda ()
317 | (throw 'xref-items
318 | (xref-items
319 | (or
320 | (plist-get
321 | (embark--maybe-transform-candidates)
322 | :candidates)
323 | (user-error "No candidates for export")))))
324 | nil t))
325 | (consult-xref fetcher))))))
326 | `((fetched-xrefs . ,(xref-items items))
327 | (window . ,(embark--target-window))
328 | (auto-jump . ,xref-auto-jump-to-first-xref)
329 | (display-action)))))))
330 |
331 | (setf (alist-get 'consult-xref embark-exporters-alist)
332 | #'embark-consult-export-xref)
333 |
334 | (defun embark-consult-xref (cand)
335 | "Default action override for `consult-xref', open CAND xref location."
336 | (xref-pop-to-location (get-text-property 0 'consult-xref cand)))
337 |
338 | (setf (alist-get 'consult-xref embark-default-action-overrides)
339 | #'embark-consult-xref)
340 |
341 | ;;; Support for consult-find and consult-locate
342 |
343 | (setf (alist-get '(file . consult-find) embark-default-action-overrides
344 | nil nil #'equal)
345 | #'find-file)
346 |
347 | (setf (alist-get '(file . consult-locate) embark-default-action-overrides
348 | nil nil #'equal)
349 | #'find-file)
350 |
351 | ;;; Support for consult-isearch-history
352 |
353 | (setf (alist-get 'consult-isearch-history embark-transformer-alist)
354 | #'embark-consult--target-strip)
355 |
356 | ;;; Support for consult-man and consult-info
357 |
358 | (defun embark-consult-man (cand)
359 | "Default action override for `consult-man', open CAND man page."
360 | (man (get-text-property 0 'consult-man cand)))
361 |
362 | (setf (alist-get 'consult-man embark-default-action-overrides)
363 | #'embark-consult-man)
364 |
365 | (declare-function consult-info--action "ext:consult-info")
366 |
367 | (defun embark-consult-info (cand)
368 | "Default action override for `consult-info', open CAND info manual."
369 | (consult-info--action cand)
370 | (pulse-momentary-highlight-one-line (point)))
371 |
372 | (setf (alist-get 'consult-info embark-default-action-overrides)
373 | #'embark-consult-info)
374 |
375 | (setf (alist-get 'consult-info embark-transformer-alist)
376 | #'embark-consult--target-strip)
377 |
378 | ;;; Bindings for consult commands in embark keymaps
379 |
380 | (keymap-set embark-become-file+buffer-map "C b" #'consult-buffer)
381 | (keymap-set embark-become-file+buffer-map "C 4 b" #'consult-buffer-other-window)
382 |
383 | ;;; Support for Consult search commands
384 |
385 | (defvar-keymap embark-consult-sync-search-map
386 | :doc "Keymap for Consult sync search commands"
387 | :parent nil
388 | "o" #'consult-outline
389 | "i" 'consult-imenu
390 | "I" 'consult-imenu-multi
391 | "l" #'consult-line
392 | "L" #'consult-line-multi)
393 |
394 | (defvar-keymap embark-consult-async-search-map
395 | :doc "Keymap for Consult async search commands"
396 | :parent nil
397 | "g" #'consult-grep
398 | "r" #'consult-ripgrep
399 | "G" #'consult-git-grep
400 | "f" #'consult-find
401 | "F" #'consult-locate)
402 |
403 | (defvar embark-consult-search-map
404 | (keymap-canonicalize
405 | (make-composed-keymap embark-consult-sync-search-map
406 | embark-consult-async-search-map))
407 | "Keymap for all Consult search commands.")
408 |
409 | (fset 'embark-consult-sync-search-map embark-consult-sync-search-map)
410 | (keymap-set embark-become-match-map "C" 'embark-consult-sync-search-map)
411 |
412 | (cl-pushnew 'embark-consult-async-search-map embark-become-keymaps)
413 |
414 | (fset 'embark-consult-search-map embark-consult-search-map)
415 | (keymap-set embark-general-map "C" 'embark-consult-search-map)
416 |
417 | (map-keymap
418 | (lambda (_key cmd)
419 | (cl-pushnew 'embark--unmark-target
420 | (alist-get cmd embark-pre-action-hooks))
421 | (cl-pushnew 'embark--allow-edit
422 | (alist-get cmd embark-target-injection-hooks)))
423 | embark-consult-search-map)
424 |
425 | (defun embark-consult--unique-match (&rest _)
426 | "If there is a unique matching candidate, accept it.
427 | This is intended to be used in `embark-target-injection-hooks'."
428 | (let ((candidates (cdr (embark-minibuffer-candidates))))
429 | (if (or (null candidates) (cdr candidates))
430 | (embark--allow-edit)
431 | (delete-minibuffer-contents)
432 | (insert (car candidates)))))
433 |
434 | (dolist (cmd '(consult-outline consult-imenu consult-imenu-multi))
435 | (setf (alist-get cmd embark-target-injection-hooks)
436 | (remq 'embark--allow-edit
437 | (alist-get cmd embark-target-injection-hooks)))
438 | (cl-pushnew #'embark-consult--unique-match
439 | (alist-get cmd embark-target-injection-hooks)))
440 |
441 | (cl-defun embark-consult--async-search-dwim
442 | (&key action type target candidates &allow-other-keys)
443 | "DWIM when using a Consult async search command as an ACTION.
444 | If the TYPE of the target(s) has a notion of associated
445 | file (files, buffers, libraries and some bookmarks do), then run
446 | the ACTION with `consult-project-function' set to nil, and search
447 | only the files associated to the TARGET or CANDIDATES. For other
448 | types, run the ACTION with TARGET or CANDIDATES as initial input."
449 | (if-let ((file-fn (cdr (assq type embark--associated-file-fn-alist))))
450 | (let (consult-project-function)
451 | (funcall action
452 | (delq nil (mapcar file-fn (or candidates (list target))))))
453 | (funcall action nil (or target (string-join candidates " ")))))
454 |
455 | (map-keymap
456 | (lambda (_key cmd)
457 | (unless (eq cmd #'consult-locate)
458 | (cl-pushnew cmd embark-multitarget-actions)
459 | (cl-pushnew #'embark-consult--async-search-dwim
460 | (alist-get cmd embark-around-action-hooks))))
461 | embark-consult-async-search-map)
462 |
463 | ;;; Tables of contents for buffers: imenu and outline candidate collectors
464 |
465 | (defun embark-consult-outline-candidates ()
466 | "Collect all outline headings in the current buffer."
467 | (cons 'consult-location (consult--outline-candidates)))
468 |
469 | (autoload 'consult-imenu--items "consult-imenu")
470 |
471 | (defun embark-consult-imenu-candidates ()
472 | "Collect all imenu items in the current buffer."
473 | (cons 'imenu (mapcar #'car (consult-imenu--items))))
474 |
475 | (declare-function consult-imenu--group "ext:consult-imenu")
476 |
477 | (defun embark-consult--imenu-group-function (type prop)
478 | "Return a suitable group-function for imenu.
479 | TYPE is the completion category.
480 | PROP is the metadata property.
481 | Meant as :after-until advice for `embark-collect--metadatum'."
482 | (when (and (eq type 'imenu) (eq prop 'group-function))
483 | (consult-imenu--group)))
484 |
485 | (advice-add #'embark-collect--metadatum :after-until
486 | #'embark-consult--imenu-group-function)
487 |
488 | (defun embark-consult-imenu-or-outline-candidates ()
489 | "Collect imenu items in prog modes buffer or outline headings otherwise."
490 | (if (derived-mode-p 'prog-mode)
491 | (embark-consult-imenu-candidates)
492 | (embark-consult-outline-candidates)))
493 |
494 | (setf (alist-get 'imenu embark-default-action-overrides) 'consult-imenu)
495 |
496 | (add-to-list 'embark-candidate-collectors
497 | #'embark-consult-imenu-or-outline-candidates
498 | 'append)
499 |
500 | (provide 'embark-consult)
501 | ;;; embark-consult.el ends here
502 |
--------------------------------------------------------------------------------
/embark-org.el:
--------------------------------------------------------------------------------
1 | ;;; embark-org.el --- Embark targets and actions for Org Mode -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2022-2025 Free Software Foundation, Inc.
4 |
5 | ;; This program is free software; you can redistribute it and/or modify
6 | ;; it under the terms of the GNU General Public License as published by
7 | ;; the Free Software Foundation, either version 3 of the License, or
8 | ;; (at your option) any later version.
9 |
10 | ;; This program is distributed in the hope that it will be useful,
11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | ;; GNU General Public License for more details.
14 |
15 | ;; You should have received a copy of the GNU General Public License
16 | ;; along with this program. If not, see .
17 |
18 | ;;; Commentary:
19 |
20 | ;; This package configures the Embark package for use in Org Mode
21 | ;; buffers. It teaches Embark a number of Org related targets and
22 | ;; appropriate actions. Currently it has table cells, whole tables,
23 | ;; source blocks and links. Targets to add: headings (Embark already
24 | ;; has generic support for outlines, so we just nee to add Org
25 | ;; specific actions), timestamps, etc.
26 |
27 | ;;; Code:
28 |
29 | (require 'embark)
30 | (require 'org)
31 | (require 'org-element)
32 |
33 | ;;; Basic target finder for Org
34 |
35 | ;; There are very many org element and objects types, we'll only
36 | ;; recognize those for which there are specific actions we can put in
37 | ;; a keymap, or even if there aren't any specific actions, if it's
38 | ;; important to be able to kill, delete or duplicate (embark-insert)
39 | ;; them conveniently. I'll start conservatively and we can add more
40 | ;; later
41 |
42 | (defconst embark-org--types
43 | '(
44 | babel-call
45 | ;; bold
46 | ;; center-block
47 | ;; citation
48 | ;; citation-reference
49 | ;; clock
50 | ;; code
51 | ;; comment
52 | ;; comment-block
53 | ;; diary-sexp
54 | ;; drawer
55 | ;; dynamic-block
56 | ;; entity
57 | ;; example-block
58 | ;; export-block
59 | ;; export-snippet
60 | ;; fixed-width
61 | footnote-definition
62 | footnote-reference
63 | ;; headline ; the bounds include the entire subtree!
64 | ;; horizontal-rule
65 | ;; inline-babel-call
66 | inline-src-block
67 | ;; inlinetask
68 | ;; italic
69 | item
70 | ;; keyword
71 | ;; latex-environment
72 | ;; latex-fragment
73 | ;; line-break
74 | link
75 | ;; macro
76 | ;; node-property
77 | ;; paragraph ; the existing general support seems fine
78 | plain-list
79 | ;; planning
80 | ;; property-drawer
81 | ;; quote-block
82 | ;; radio-target
83 | ;; section
84 | ;; special-block
85 | src-block
86 | ;; statistics-cookie
87 | ;; strike-through
88 | ;; subscript
89 | ;; superscript
90 | table ; supported via a specific target finder
91 | table-cell
92 | ;; table-row ; we'll put row & column actions in the cell map
93 | ;; target ; I think there are no useful actions for radio targets
94 | timestamp
95 | ;; underline
96 | ;; verbatim
97 | ;; verse-block
98 | )
99 | "Supported Org object and element types.")
100 |
101 | (defun embark-org-target-element-context ()
102 | "Target all Org elements or objects around point."
103 | (when (derived-mode-p 'org-mode)
104 | (cl-loop
105 | for elt = (org-element-lineage (org-element-context) embark-org--types t)
106 | then (org-element-lineage elt embark-org--types)
107 | while elt
108 | ;; clip bounds to narrowed portion of buffer
109 | for begin = (max (org-element-property :begin elt) (point-min))
110 | for end = (min (org-element-property :end elt) (point-max))
111 | for target = (buffer-substring begin end)
112 | ;; Adjust table-cell to exclude final |. (Why is that there?)
113 | ;; Note: We are not doing this as an embark transformer because we
114 | ;; want to adjust the bounds too.
115 | ;; TODO? If more adjustments like this become necessary, add a
116 | ;; nice mechanism for doing them.
117 | when (and (eq (car elt) 'table-cell) (string-suffix-p "|" target))
118 | do (setq target (string-trim (string-remove-suffix "|" target))
119 | end (1- end))
120 | collect `(,(intern (format "org-%s" (car elt))) ,target ,begin . ,end))))
121 |
122 | (unless (memq 'embark-org-target-element-context embark-target-finders)
123 | (if-let ((tail (memq 'embark-target-active-region embark-target-finders)))
124 | (push 'embark-org-target-element-context (cdr tail))
125 | (push 'embark-org-target-element-context embark-target-finders)))
126 |
127 | ;;; Custom Org actions
128 |
129 | (defvar org-export-with-toc)
130 |
131 | (defun embark-org-copy-as-markdown (start end)
132 | "Export the region from START to END to markdown and save on the `kill-ring'."
133 | (interactive "r")
134 | (require 'ox)
135 | (kill-new
136 | (let (org-export-with-toc)
137 | (string-trim
138 | (org-export-string-as (buffer-substring-no-properties start end) 'md t))))
139 | (deactivate-mark))
140 |
141 | (add-to-list 'embark-pre-action-hooks
142 | '(embark-org-copy-as-markdown embark--mark-target))
143 |
144 | (keymap-set embark-region-map "M" #'embark-org-copy-as-markdown) ; good idea?
145 |
146 | ;;; Tables
147 |
148 | (dolist (motion '(org-table-move-cell-up org-table-move-cell-down
149 | org-table-move-cell-left org-table-move-cell-right
150 | org-table-move-row org-table-move-column
151 | org-table-move-row-up org-table-move-row-down
152 | org-table-move-column-left org-table-move-column-right))
153 | (add-to-list 'embark-repeat-actions motion))
154 |
155 | (dolist (cmd '(org-table-eval-formula org-table-edit-field))
156 | (push 'embark--ignore-target (alist-get cmd embark-target-injection-hooks)))
157 |
158 | (defvar-keymap embark-org-table-cell-map
159 | :doc "Keymap for actions the current cells, column or row of an Org table."
160 | :parent embark-general-map
161 | "RET" #'org-table-align ; harmless default
162 | "" #'org-table-move-cell-up
163 | "" #'org-table-move-cell-down
164 | "" #'org-table-move-cell-left
165 | "" #'org-table-move-cell-right
166 | "d" #'org-table-kill-row
167 | "c" #'org-table-copy-down
168 | "D" #'org-table-delete-column ; capital = column
169 | "^" #'org-table-move-row-up
170 | "v" #'org-table-move-row-down
171 | "<" #'org-table-move-column-left
172 | ">" #'org-table-move-column-right
173 | "o" #'org-table-insert-row
174 | "O" #'org-table-insert-column ; capital = column
175 | "h" #'org-table-insert-hline
176 | "=" #'org-table-eval-formula
177 | "e" #'org-table-edit-field
178 | "g" #'org-table-recalculate)
179 |
180 | (defvar-keymap embark-org-table-map
181 | :doc "Keymap for actions on entire Org table."
182 | :parent embark-general-map
183 | "RET" #'org-table-align ; harmless default
184 | "=" #'org-table-edit-formulas
185 | "s" #'org-table-sort-lines
186 | "t" #'org-table-transpose-table-at-point
187 | "c" #'org-table-convert
188 | "f" #'org-table-follow-field-mode
189 | "y" #'org-table-paste-rectangle
190 | "d" #'org-table-toggle-formula-debugger
191 | "o" #'org-table-toggle-coordinate-overlays
192 | "g" #'org-table-iterate
193 | "e" #'org-table-export)
194 |
195 | (push 'embark--ignore-target ; prompts for file name
196 | (alist-get 'org-table-export embark-target-injection-hooks))
197 |
198 | (add-to-list 'embark-keymap-alist '(org-table embark-org-table-map))
199 |
200 | (add-to-list 'embark-keymap-alist '(org-table-cell embark-org-table-cell-map))
201 |
202 | ;;; Links
203 |
204 | ;; The link support has a slightly complicated design in order to
205 | ;; achieve the following goals:
206 |
207 | ;; 1. RET should simply be org-open-at-point
208 |
209 | ;; 2. When the link is to a file, URL, email address or elisp
210 | ;; expression or command, we want to offer the user actions for
211 | ;; that underlying type.
212 |
213 | ;; 3. Even in those cases, we still want some actions to apply to the
214 | ;; entire link including description: actions to copy the link as
215 | ;; markdown, or just the link description or target.
216 |
217 | ;; So the strategy is as follows (illustrated with file links):
218 |
219 | ;; - The target will be just the file, without the description and
220 | ;; also without the "file:" prefix nor the "::line-number or search"
221 | ;; suffix. That way, file actions will correctly apply to it.
222 |
223 | ;; - The type will not be 'file, but 'org-file-link; that way we can
224 | ;; register a keymap for 'org-file-link that inherits from both
225 | ;; embark-org-link-map (with RET bound to org-open-at-point and a
226 | ;; few other generic link actions) and embark-file-map.
227 |
228 | ;; - The commands to copy the link at point in some format will be
229 | ;; written as commands that act on the Org link at point. This way
230 | ;; they are independently (plausibly) useful, and we circumvent the
231 | ;; problem that the whole Org link is not actually the target (just
232 | ;; the inner file is!).
233 |
234 | ;; Alternative design I considered: separate each target into two, a
235 | ;; whole link target which includes the description and brackets and
236 | ;; what not; and an "inner target" which is just the file or URL or
237 | ;; whatever. Cons of this approach: much target cycling is required!
238 | ;; First of all, an unadorned embark-dwim definitely should be
239 | ;; org-open-at-point, which means the whole link target would need
240 | ;; priority. That means that any file, URL, etc. actions would require
241 | ;; you to cycle first. This sounds very inconvenient, the above
242 | ;; slightly more complex design allows both whole-link and inner
243 | ;; target actions to work without cycling.
244 |
245 | (defun embark-org-target-link ()
246 | "Target Org link at point.
247 | This targets Org links in any buffer, not just buffers in
248 | `org-mode' or `org-agenda-mode'. Org links in any buffer can be
249 | opened with `org-open-at-point-global', which is the default
250 | Embark action for Org links."
251 | (pcase (org-in-regexp org-link-any-re)
252 | (`(,start . ,end)
253 | ;; We won't recognize unadorned http(s) or mailto links, as those
254 | ;; already have target finders (but if these links have either a
255 | ;; description, double brackets or angle brackets, then we do
256 | ;; recognize them as org links)
257 | (unless (save-excursion (goto-char start) (looking-at "http\\|mailto"))
258 | `(org-link ,(buffer-substring start end) ,start . ,end)))))
259 |
260 | (let ((tail (memq 'embark-target-active-region embark-target-finders)))
261 | (cl-pushnew 'embark-org-target-link (cdr tail)))
262 |
263 | (autoload 'org-attach-dir "org-attach")
264 |
265 | (defun embark-org--refine-link-type (_type target)
266 | "Refine type of link TARGET if we have more specific actions available."
267 | (when (string-match org-link-any-re target)
268 | (let ((target (or (match-string-no-properties 2 target)
269 | (match-string-no-properties 0 target))))
270 | (cond
271 | ((string-prefix-p "http" target)
272 | (cons 'org-url-link target))
273 | ((string-prefix-p "mailto:" target)
274 | (cons 'org-email-link (string-remove-prefix "mailto:" target)))
275 | ((string-prefix-p "file:" target)
276 | (cons 'org-file-link
277 | (replace-regexp-in-string
278 | "::.*" "" (string-remove-prefix "file:" target))))
279 | ((string-prefix-p "attachment:" target)
280 | (cons 'org-file-link
281 | (expand-file-name
282 | (replace-regexp-in-string
283 | "::.*" "" (string-remove-prefix "attachment:" target))
284 | (org-attach-dir))))
285 | ((string-match-p "^[./]" target)
286 | (cons 'org-file-link (abbreviate-file-name (expand-file-name target))))
287 | ((string-prefix-p "elisp:(" target)
288 | (cons 'org-expression-link (string-remove-prefix "elisp:" target)))
289 | ((string-prefix-p "elisp:" target)
290 | (cons 'command (string-remove-prefix "elisp:" target)))
291 | (t (cons 'org-link target))))))
292 |
293 | (add-to-list 'embark-transformer-alist
294 | '(org-link . embark-org--refine-link-type))
295 |
296 | (defmacro embark-org-define-link-copier (name formula description)
297 | "Define a command that copies the Org link at point according to FORMULA.
298 | The command's name is formed by appending NAME to
299 | embark-org-copy-link. The docstring includes the DESCRIPTION of
300 | what part or in what format the link is copied."
301 | `(defun ,(intern (format "embark-org-copy-link-%s" name)) ()
302 | ,(format "Copy to the kill-ring the Org link at point%s." description)
303 | (interactive)
304 | (when (org-in-regexp org-link-any-re)
305 | (let* ((full (match-string-no-properties 0))
306 | (target (or (match-string-no-properties 2)
307 | (match-string-no-properties 0)))
308 | (description (match-string-no-properties 3))
309 | (kill ,formula))
310 | (ignore full target description)
311 | (when kill
312 | (message "Saved '%s'" kill)
313 | (kill-new kill))))))
314 |
315 | (embark-org-define-link-copier in-full full " in full")
316 | (embark-org-define-link-copier description description "'s description")
317 | (embark-org-define-link-copier target target "'s target")
318 |
319 | (defalias 'embark-org-copy-link-inner-target #'kill-new
320 | "Copy inner part of the Org link at point's target.
321 | For mailto and elisp links, the inner part is the portion of the
322 | target after `mailto:' or `elisp:'.
323 |
324 | For file links the inner part is the file name, without the
325 | `file:' prefix and without `::' suffix (used for line numbers,
326 | IDs or search terms).
327 |
328 | For URLs the inner part is the whole target including the `http:'
329 | or `https:' prefix. For any other type of link the inner part is
330 | also the whole target.")
331 |
332 | (defvar-keymap embark-org-link-copy-map
333 | :doc "Keymap for different ways to copy Org links to the kill-ring.
334 |
335 | You should bind w in this map to your most frequently used link
336 | copying function. The default is for w to copy the \"inner
337 | target\" (see `embark-org-copy-link-inner-target'); which is also
338 | bound to i."
339 | :parent nil
340 | "w" #'embark-org-copy-link-inner-target
341 | "f" #'embark-org-copy-link-in-full
342 | "d" #'embark-org-copy-link-description
343 | "t" #'embark-org-copy-link-target
344 | "i" #'embark-org-copy-link-inner-target
345 | "m" #'embark-org-copy-as-markdown)
346 |
347 | (fset 'embark-org-link-copy-map embark-org-link-copy-map)
348 |
349 | (defvar-keymap embark-org-link-map
350 | :doc "Keymap for actions on Org links."
351 | :parent embark-general-map
352 | "RET" #'org-open-at-point-global
353 | "'" #'org-insert-link
354 | "n" #'org-next-link
355 | "p" #'org-previous-link
356 | "w" #'embark-org-link-copy-map)
357 |
358 | (dolist (motion '(org-next-link org-previous-link))
359 | (cl-pushnew motion embark-repeat-actions))
360 |
361 | ;; The reason for this is left as an exercise to the reader.
362 | ;; Solution: Na ryvfc gnetrg znl cebzcg gur hfre sbe fbzrguvat!
363 | (cl-pushnew 'embark--ignore-target
364 | (alist-get 'org-open-at-point embark-target-injection-hooks))
365 | (cl-pushnew 'embark--ignore-target
366 | (alist-get 'org-insert-link embark-target-injection-hooks))
367 |
368 | (add-to-list 'embark-keymap-alist
369 | '(org-link embark-org-link-map))
370 | (add-to-list 'embark-keymap-alist
371 | '(org-url-link embark-org-link-map embark-url-map))
372 | (add-to-list 'embark-keymap-alist
373 | '(org-email-link embark-org-link-map embark-email-map))
374 | (add-to-list 'embark-keymap-alist
375 | '(org-file-link embark-org-link-map embark-file-map))
376 | (add-to-list 'embark-keymap-alist
377 | '(org-expression-link embark-org-link-map embark-expression-map))
378 |
379 | ;;; Org headings
380 |
381 | (defun embark-org--refine-heading (type target)
382 | "Refine TYPE of heading TARGET in Org buffers."
383 | (cons
384 | (if (derived-mode-p 'org-mode) 'org-heading type)
385 | target))
386 |
387 | (add-to-list 'embark-transformer-alist '(heading . embark-org--refine-heading))
388 |
389 | (defvar-keymap embark-org-heading-map
390 | :doc "Keymap for actions on Org headings."
391 | :parent embark-heading-map
392 | "RET" #'org-todo
393 | "TAB" #'org-cycle
394 | "t" #'org-todo
395 | "s" #'org-schedule
396 | "d" #'org-deadline
397 | "," #'org-priority
398 | ":" #'org-set-tags-command
399 | "P" #'org-set-property
400 | "D" #'org-delete-property
401 | "k" #'org-cut-subtree
402 | "N" #'org-narrow-to-subtree
403 | "T" #'org-tree-to-indirect-buffer
404 | "" #'org-do-promote
405 | "" #'org-do-demote
406 | "o" #'org-sort
407 | "r" #'org-refile
408 | "R" #'embark-org-refile-here
409 | "I" #'org-clock-in
410 | "O" #'org-clock-out
411 | "a" #'org-archive-subtree-default-with-confirmation
412 | "h" #'org-insert-heading-respect-content
413 | "H" #'org-insert-todo-heading-respect-content
414 | "l" #'org-store-link
415 | "j" #'embark-org-insert-link-to)
416 |
417 | (dolist (cmd '(org-todo org-metaright org-metaleft org-metaup org-metadown
418 | org-shiftmetaleft org-shiftmetaright org-cycle org-shifttab))
419 | (cl-pushnew cmd embark-repeat-actions))
420 |
421 | (dolist (cmd '(org-set-tags-command org-set-property
422 | org-delete-property org-refile org-schedule))
423 | (cl-pushnew 'embark--ignore-target
424 | (alist-get cmd embark-target-injection-hooks)))
425 |
426 | (add-to-list 'embark-keymap-alist '(org-heading embark-org-heading-map))
427 |
428 | ;;; Source blocks
429 |
430 | (defun embark-org-copy-block-contents ()
431 | "Save contents of source block at point to the `kill-ring'."
432 | (interactive)
433 | (when (org-in-src-block-p)
434 | (let ((contents (nth 2 (org-src--contents-area (org-element-at-point)))))
435 | (with-temp-buffer
436 | (insert contents)
437 | (org-do-remove-indentation)
438 | (kill-new (buffer-substring (point-min) (point-max)))))))
439 |
440 | (defvar-keymap embark-org-src-block-map
441 | :doc "Keymap for actions on Org source blocks."
442 | :parent embark-general-map
443 | "RET" #'org-babel-execute-src-block
444 | "C-SPC" #'org-babel-mark-block
445 | "TAB" #'org-indent-block
446 | "c" #'embark-org-copy-block-contents
447 | "h" #'org-babel-check-src-block
448 | "k" #'org-babel-remove-result-one-or-many
449 | "p" #'org-babel-previous-src-block
450 | "n" #'org-babel-next-src-block
451 | "t" #'org-babel-tangle
452 | "s" #'org-babel-switch-to-session
453 | "l" #'org-babel-load-in-session
454 | "'" #'org-edit-special
455 | "/" #'org-babel-demarcate-block
456 | "N" #'org-narrow-to-block)
457 |
458 | (cl-defun embark-org--at-block-head
459 | (&rest rest &key run bounds &allow-other-keys)
460 | "Save excursion and RUN the action at the head of the current block.
461 | If BOUNDS are given, use them to locate the block (useful for
462 | when acting on a selection of blocks). Applies RUN to the REST
463 | of the arguments."
464 | (save-excursion
465 | (when bounds (goto-char (car bounds)))
466 | (org-babel-goto-src-block-head)
467 | (apply run rest)))
468 |
469 | (cl-pushnew #'embark-org--at-block-head
470 | (alist-get 'org-indent-block embark-around-action-hooks))
471 |
472 | (dolist (motion '(org-babel-next-src-block org-babel-previous-src-block))
473 | (add-to-list 'embark-repeat-actions motion))
474 |
475 | (dolist (cmd '(org-babel-execute-maybe
476 | org-babel-lob-execute-maybe
477 | org-babel-execute-src-block
478 | org-babel-execute-src-block-maybe
479 | org-babel-execute-buffer
480 | org-babel-execute-subtree))
481 | (cl-pushnew #'embark--ignore-target
482 | (alist-get cmd embark-target-injection-hooks)))
483 |
484 | (add-to-list 'embark-keymap-alist '(org-src-block embark-org-src-block-map))
485 |
486 | ;;; Inline source blocks
487 |
488 | (defvar-keymap embark-org-inline-src-block-map
489 | :doc "Keymap for actions on Org inline source blocks."
490 | :parent embark-general-map
491 | "RET" #'org-babel-execute-src-block
492 | "'" #'org-edit-inline-src-code
493 | "k" #'org-babel-remove-inline-result)
494 |
495 | (add-to-list 'embark-keymap-alist
496 | '(org-inline-src-block embark-org-inline-src-block-map))
497 |
498 | ;;; Babel calls
499 |
500 | (defvar-keymap embark-org-babel-call-map
501 | :doc "Keymap for actions on Org babel calls."
502 | :parent embark-general-map
503 | "RET" #'org-babel-lob-execute-maybe
504 | "k" #'org-babel-remove-result)
505 |
506 | (add-to-list 'embark-keymap-alist
507 | '(org-babel-call embark-org-babel-call-map))
508 |
509 | ;;; List items
510 |
511 | (defvar-keymap embark-org-item-map
512 | :doc "Keymap for actions on Org list items."
513 | :parent embark-general-map
514 | "RET" #'org-toggle-checkbox
515 | "c" #'org-toggle-checkbox
516 | "t" #'org-toggle-item
517 | "n" #'org-next-item
518 | "p" #'org-previous-item
519 | "" #'org-outdent-item
520 | "" #'org-indent-item
521 | "" #'org-move-item-up
522 | "" #'org-move-item-down
523 | ">" #'org-indent-item-tree
524 | "<" #'org-outdent-item-tree)
525 |
526 | (dolist (cmd '(org-toggle-checkbox
527 | org-toggle-item
528 | org-next-item
529 | org-previous-item
530 | org-outdent-item
531 | org-indent-item
532 | org-move-item-up
533 | org-move-item-down
534 | org-indent-item-tree
535 | org-outdent-item-tree))
536 | (add-to-list 'embark-repeat-actions cmd))
537 |
538 | (add-to-list 'embark-keymap-alist '(org-item embark-org-item-map))
539 |
540 | ;;; Org plain lists
541 |
542 | (defvar-keymap embark-org-plain-list-map
543 | :doc "Keymap for actions on plain Org lists."
544 | :parent embark-general-map
545 | "RET" #'org-list-repair
546 | "r" #'org-list-repair
547 | "s" #'org-sort-list
548 | "b" #'org-cycle-list-bullet
549 | "t" #'org-list-make-subtree
550 | "c" #'org-toggle-checkbox)
551 |
552 | (add-to-list 'embark-repeat-actions 'org-cycle-list-bullet)
553 |
554 | (add-to-list 'embark-keymap-alist '(org-plain-list embark-org-plain-list-map))
555 |
556 | (cl-defun embark-org--toggle-checkboxes
557 | (&rest rest &key run type &allow-other-keys)
558 | "Around action hook for `org-toggle-checkbox'.
559 | See `embark-around-action-hooks' for the keyword arguments RUN and TYPE.
560 | REST are the remaining arguments."
561 | (apply (if (eq type 'org-plain-list) #'embark--mark-target run)
562 | :type type
563 | rest))
564 |
565 | (cl-pushnew #'embark-org--toggle-checkboxes
566 | (alist-get 'org-toggle-checkbox embark-around-action-hooks))
567 |
568 | ;;; "Encode" region using Org export in place
569 |
570 | (defvar-keymap embark-org-export-in-place-map
571 | :doc "Keymap for actions which replace the region by an exported version."
572 | :parent embark-general-map
573 | "m" #'org-md-convert-region-to-md
574 | "h" #'org-html-convert-region-to-html
575 | "a" #'org-ascii-convert-region-to-ascii
576 | "l" #'org-latex-convert-region-to-latex)
577 |
578 | (fset 'embark-org-export-in-place-map embark-org-export-in-place-map)
579 |
580 | (keymap-set embark-encode-map "o" 'embark-org-export-in-place-map)
581 |
582 | ;;; References to Org headings, such as agenda items
583 |
584 | ;; These are targets that represent an org heading but not in the
585 | ;; current buffer, instead they have a text property named
586 | ;; `org-marker' that points to the actual heading.
587 |
588 | (defun embark-org-target-agenda-item ()
589 | "Target Org agenda item at point."
590 | (when (and (derived-mode-p 'org-agenda-mode)
591 | (get-text-property (line-beginning-position) 'org-marker))
592 | (let ((start (+ (line-beginning-position) (current-indentation)))
593 | (end (line-end-position)))
594 | `(org-heading ,(buffer-substring start end) ,start . ,end))))
595 |
596 | (let ((tail (memq 'embark-org-target-element-context embark-target-finders)))
597 | (cl-pushnew 'embark-org-target-agenda-item (cdr tail)))
598 |
599 | (cl-defun embark-org--at-heading
600 | (&rest rest &key run target &allow-other-keys)
601 | "RUN the action at the location of the heading TARGET refers to.
602 | The location is given by the `org-marker' text property of
603 | target. Applies RUN to the REST of the arguments."
604 | (if-let ((marker (get-text-property 0 'org-marker target)))
605 | (org-with-point-at marker
606 | (apply run :target target rest))
607 | (apply run :target target rest)))
608 |
609 | (cl-defun embark-org-goto-heading (&key target &allow-other-keys)
610 | "Jump to the org heading TARGET refers to."
611 | (when-let ((marker (get-text-property 0 'org-marker target)))
612 | (pop-to-buffer (marker-buffer marker))
613 | (widen)
614 | (goto-char marker)
615 | (org-reveal)
616 | (pulse-momentary-highlight-one-line)))
617 |
618 | (defun embark-org-heading-default-action (target)
619 | "Default action for Org headings.
620 | There are two types of heading TARGETs: the heading at point in a
621 | normal org buffer, and references to org headings in some other
622 | buffer (for example, org agenda items). For references the
623 | default action is to jump to the reference, and for the heading
624 | at point, the default action is whatever is bound to RET in
625 | `embark-org-heading-map', or `org-todo' if RET is unbound."
626 | (if (get-text-property 0 'org-marker target)
627 | (embark-org-goto-heading :target target)
628 | (command-execute
629 | (or (keymap-lookup embark-org-heading-map "RET") #'org-todo))))
630 |
631 | (defconst embark-org--invisible-jump-to-heading
632 | '(org-tree-to-indirect-buffer
633 | org-refile
634 | org-clock-in
635 | org-clock-out
636 | org-archive-subtree-default-with-confirmation
637 | org-store-link)
638 | "Org heading actions which won't display the heading's buffer.")
639 |
640 | (defconst embark-org--no-jump-to-heading
641 | '(embark-org-insert-link-to embark-org-refile-here)
642 | "Org heading actions which shouldn't be executed with point at the heading.")
643 |
644 | (setf (alist-get 'org-heading embark-default-action-overrides)
645 | #'embark-org-heading-default-action)
646 |
647 | (map-keymap
648 | (lambda (_key cmd)
649 | (unless (or (where-is-internal cmd (list embark-general-map))
650 | (memq cmd embark-org--no-jump-to-heading)
651 | (memq cmd embark-org--invisible-jump-to-heading))
652 | (cl-pushnew 'embark-org-goto-heading
653 | (alist-get cmd embark-pre-action-hooks))))
654 | embark-org-heading-map)
655 |
656 | (dolist (cmd embark-org--invisible-jump-to-heading)
657 | (cl-pushnew 'embark-org--at-heading
658 | (alist-get cmd embark-around-action-hooks)))
659 |
660 | (defun embark-org--in-source-window (target function)
661 | "Call FUNCTION, in the source window, on TARGET's `org-marker'.
662 |
663 | If TARGET does not have an `org-marker' property a `user-error'
664 | is signaled. In case the TARGET comes from an org agenda buffer
665 | and the `other-window-for-scrolling' is an org mode buffer, then
666 | the FUNCTION is called with that other window temporarily
667 | selected; otherwise the FUNCTION is called in the selected
668 | window."
669 | (if-let ((marker (get-text-property 0 'org-marker target)))
670 | (with-selected-window
671 | (or (and (derived-mode-p 'org-agenda-mode)
672 | (let ((window (ignore-errors (other-window-for-scrolling))))
673 | (with-current-buffer (window-buffer window)
674 | (when (derived-mode-p 'org-mode) window))))
675 | (selected-window))
676 | (funcall function marker))
677 | (user-error "The target is an org heading rather than a reference to one")))
678 |
679 | (defun embark-org-refile-here (target)
680 | "Refile the heading at point in the source window to TARGET.
681 |
682 | If TARGET is an agenda item and `other-window-for-scrolling' is
683 | displaying an org mode buffer, then that is the source window.
684 | If TARGET is a minibuffer completion candidate, then the source
685 | window is the window selected before the command that opened the
686 | minibuffer ran."
687 | (embark-org--in-source-window target
688 | (lambda (marker)
689 | (org-refile nil nil
690 | ;; The RFLOC argument:
691 | (list
692 | ;; Name
693 | (org-with-point-at marker
694 | (nth 4 (org-heading-components)))
695 | ;; File
696 | (buffer-file-name (marker-buffer marker))
697 | ;; nil
698 | nil
699 | ;; Position
700 | marker)))))
701 |
702 | (defun embark-org-insert-link-to (target)
703 | "Insert a link to the TARGET in the source window.
704 |
705 | If TARGET is an agenda item and `other-window-for-scrolling' is
706 | displaying an org mode buffer, then that is the source window.
707 | If TARGET is a minibuffer completion candidate, then the source
708 | window is the window selected before the command that opened the
709 | minibuffer ran."
710 | (embark-org--in-source-window target
711 | (lambda (marker)
712 | (org-with-point-at marker (org-store-link nil t))
713 | (org-insert-all-links 1 "" ""))))
714 |
715 | (provide 'embark-org)
716 | ;;; embark-org.el ends here
717 |
--------------------------------------------------------------------------------