├── .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: GNU ELPA 9 | #+html: GNU-devel ELPA 10 | #+html: MELPA 11 | #+html: MELPA Stable 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 | --------------------------------------------------------------------------------