├── .gitignore ├── LICENSE ├── README.org ├── calc.el ├── eshell └── alias ├── experiments ├── avy-completion.el ├── avy-grille.el ├── cicio-mode.el ├── consult-eww-source.el ├── drag-region.el ├── embark-elfeed.el ├── embark-extra-indicators.el ├── embark-kmacro.el ├── embark-this-buffer.el ├── eshell-repl.el ├── gobble-whitespace.el ├── grille.el ├── init.el ├── library-support.el ├── mc-prompt-once.el └── regexpect.el ├── init.el ├── my-lisp ├── among.el ├── aoc.el ├── apt-progress.el ├── arXiv.el ├── block-undo.el ├── calc-exchange-rates.el ├── calc-rref.el ├── calc-simplex.el ├── ecomplete-extras.el ├── eshell-extras.el ├── eval-region-advice.el ├── ffap-eshell.el ├── gptel-extras.el ├── help-extras.el ├── isearch-extras.el ├── man-help.el ├── message-extras.el ├── narrow-extras.el ├── org-async-init.el ├── org-extras.el ├── org-ql-usual-files.el ├── shr-heading.el ├── text-extras.el ├── tmp-buffer.el ├── topaz-paste.el ├── vc-extras.el ├── vimgolf.el ├── visiting-buffer.el ├── webjump-extras.el └── window-extras.el └── viper /.gitignore: -------------------------------------------------------------------------------- 1 | /elpa/ 2 | *.elc 3 | /.sx/ 4 | bbdb 5 | bookmarks 6 | projects 7 | tramp 8 | /auto-save-list/ 9 | /url/ 10 | /history 11 | /transient 12 | /network-security.data 13 | /session.* 14 | /eshell/* 15 | !/eshell/alias 16 | /games/ 17 | /speed-type/ 18 | /recentf 19 | /server 20 | /eln-cache 21 | forge-database.sqlite 22 | nov-places 23 | persist/ 24 | multisession/ 25 | emojis/emojione-v2.2.6-22/ 26 | request/ 27 | -------------------------------------------------------------------------------- /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 | * Welcome! 2 | 3 | This is my personal Emacs configuration. I tend to prefer learning how 4 | to use built-in packages for any given task. If I get a small itch to 5 | scratch, I'll typically write my own tiny package for it. For itches 6 | too big to handle on my own I do go shopping for external packages. 7 | 8 | There is also code I wind up writing with no itch of my own to 9 | scratch: I like reading [[https://reddit.com/r/emacs][r/emacs]] and writing little functions for 10 | people there. The functions I like and think I might use someday 11 | become tiny packages here. Writing small snippets for other people is 12 | probably my favorite way of learning Emacs, because it takes me down 13 | paths I don't think I would explore on my own. 14 | 15 | * Some packages I've bundled separately 16 | 17 | I intend to publish the most reusable parts of my configuration as 18 | separate packages. So far that is: 19 | 20 | - [[https://github.com/oantolin/embark][embark]] 21 | - [[https://github.com/oantolin/orderless][orderless]] 22 | - [[https://github.com/oantolin/icomplete-vertical][icomplete-vertical]] 23 | - [[https://github.com/oantolin/math-delimiters][math-delimiters]] 24 | - [[https://github.com/oantolin/placeholder][placeholder]] 25 | - [[https://github.com/oantolin/epithet][epithet]] 26 | - [[https://github.com/oantolin/completing-history][completing-history]] 27 | - [[https://github.com/oantolin/restricto][restricto]] 28 | - [[https://github.com/oantolin/live-completions][live-completions]] 29 | 30 | * Tiny packages of interest that live here 31 | 32 | Of the packages I haven't published separately, I think these are 33 | probably the most useful: 34 | 35 | - block-undo :: make executions of keyboard macros undo in a single step! 36 | - eval-region-advice :: have Emacs Lisp evaluation commands like 37 | =eval-last-sexp= and =eval-defun= call =eval-region= if the region is 38 | active (=eval-region= does not have a key binding by default and with 39 | this tiny package it doesn't need one). 40 | - shr-heading :: commands to move to the next and or previous heading 41 | in a shr buffer (shr buffers show rendered HTML and are used by eww 42 | and elfeed, for example), as well as imenu support so you can 43 | instantly jump to a heading with completion. 44 | - ffap-eshell :: better directory guessing for file names that happen 45 | to be printed out in an eshell buffer. 46 | - visiting-buffer :: tweak the =delete-file= and =rename-file= commands 47 | (as well as their =vc-= counterparts) so that they also delete or 48 | rename any open buffer visiting the file, and, in case of renaming 49 | an Emacs Lisp library, also adjust the =provide= and the header line 50 | if necessary. 51 | - ecomplete-extras :: commands to add or remove an email address from 52 | ecomplete's database. 53 | - message-extras :: a function to keep you from sending emails without 54 | a subject line or without an attachment if you mention one in the 55 | email body; also, functons to manage sending email from several 56 | different accounts: a command to cycle the from address among your 57 | accounts, and a function to set the SMTP server to match the from 58 | address. 59 | 60 | * Themes 61 | 62 | For many years I kept trying Emacs themes and always went back to the 63 | default theme. I don't understand why people don't like the default 64 | theme, to me it is clearly better than almost all others. :) 65 | 66 | I finally found themes I like better than the default: Protesilaos 67 | Stavrou's [[https://gitlab.com/protesilaos/modus-themes][modus themes]]! 68 | 69 | * What's missing from this repo? 70 | 71 | I separated the personal part of my configuration to publish this. The 72 | personal part doesn't cover that much: email addresses and signatures, 73 | org capture templates, org agenda configuration, and the org-publish 74 | configuration for my academic website, basically. In particular most 75 | of my org mode configuration is in this public part. 76 | -------------------------------------------------------------------------------- /calc.el: -------------------------------------------------------------------------------- 1 | ;;; Mode settings stored by Calc on Tue Sep 13 02:30:06 2016 2 | (setq calc-prefer-frac t) 3 | ;;; End of mode settings 4 | -------------------------------------------------------------------------------- /eshell/alias: -------------------------------------------------------------------------------- 1 | alias xx for-each embark-open-externally $* 2 | alias ff for-each find-file $* 3 | -------------------------------------------------------------------------------- /experiments/avy-completion.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | (require 'avy) 4 | (require 'cl-lib) 5 | 6 | (defun avy-completion--candidate (pt) 7 | "Return the completion candidate at PT." 8 | (goto-char pt) 9 | (let ((end (next-single-property-change pt 'mouse-face))) 10 | (buffer-substring-no-properties pt end))) 11 | 12 | (defun avy-action-complete (pt) 13 | "Enter completion candidate at PT into the minibuffer." 14 | (let ((where completion-base-position) 15 | (candidate (avy-completion--candidate pt))) 16 | (select-window (active-minibuffer-window)) 17 | (apply #'delete-region where) 18 | (insert candidate))) 19 | 20 | (defun avy-action-choose (pt) 21 | "Choose completion at PT." 22 | (goto-char pt) 23 | (choose-completion)) 24 | 25 | (defun avy-action-save (pt) 26 | "Save completion candidate at PT in the kill-ring." 27 | (kill-new (avy-completion--candidate pt)) 28 | (select-window (active-minibuffer-window))) 29 | 30 | (defun avy-action-insert (pt) 31 | "Insert completion candidate at PT into the previous buffer." 32 | (let ((candidate (avy-completion--candidate pt))) 33 | (select-window (active-minibuffer-window)) 34 | (with-minibuffer-selected-window 35 | (when (use-region-p) 36 | (delete-region (region-beginning) (region-end))) 37 | (insert candidate)))) 38 | 39 | (defun avy-completion () 40 | "Jump to a completion candidate." 41 | (interactive) 42 | (let ((wnd (get-buffer-window "*Completions*" 0))) 43 | (if wnd 44 | (with-current-buffer "*Completions*" 45 | (avy-with avy-completion 46 | (let ((avy-action #'avy-action-choose) 47 | (avy-dispatch-alist '((?c . avy-action-complete) 48 | (?i . avy-action-insert) 49 | (?w . avy-action-save) 50 | (?m . avy-action-goto)))) 51 | (avy-process 52 | (save-excursion 53 | (cl-loop initially (goto-char (point-min)) 54 | do (next-completion 1) until (eobp) 55 | collect (cons (point) wnd))))))) 56 | (user-error "No *Completions* windows")))) 57 | 58 | (provide 'avy-completion) 59 | -------------------------------------------------------------------------------- /experiments/avy-grille.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | (require 'avy) 4 | (require 'grille) 5 | 6 | (defun avy-action-complete (pt) 7 | "Enter completion candidate at PT into the minibuffer." 8 | (grille--insert (button-at pt))) 9 | 10 | (defun avy-action-choose (pt) 11 | "Choose completion at PT." 12 | (grille--choose (button-at pt))) 13 | 14 | (declare-function embark-act "embark") 15 | 16 | (defun avy-action-embark-act (pt) 17 | "Act on the completion at PT." 18 | (goto-char pt) 19 | (embark-act)) 20 | 21 | (defun avy-grille-command (action dispatch-alist) 22 | "Jump to a completion candidate." 23 | (let ((wnd (or (get-buffer-window "*Grille*" 0) 24 | (get-buffer-window "*Embark Live Occur*" 0)))) 25 | (if wnd 26 | (with-current-buffer (window-buffer wnd) 27 | (avy-with avy-completion 28 | (let ((avy-action action) 29 | (avy-dispatch-alist dispatch-alist)) 30 | (avy-process 31 | (save-excursion 32 | (goto-char (point-min)) 33 | (let ((btns `((,(point) . ,wnd)))) 34 | (forward-button 1 t) 35 | (while (not (bobp)) 36 | (push (cons (point) wnd) btns) 37 | (forward-button 1 t)) 38 | (nreverse btns))))))) 39 | (user-error "No *Grille* or *Embark Live Occur* windows")))) 40 | 41 | (defun avy-grille-choose () 42 | "Choose a completion candidate." 43 | (interactive) 44 | (avy-grille-command #'avy-action-choose 45 | '((?c . avy-action-complete) 46 | (?x . avy-action-embark-act) 47 | (?m . avy-action-goto)))) 48 | 49 | (defun avy-grille-embark-act () 50 | "Act on a completion candidate." 51 | (interactive) 52 | (avy-grille-command #'avy-action-embark-act 53 | '((?c . avy-action-complete) 54 | (?x . avy-action-choose) 55 | (?m . avy-action-goto)))) 56 | 57 | (provide 'avy-grille) 58 | -------------------------------------------------------------------------------- /experiments/cicio-mode.el: -------------------------------------------------------------------------------- 1 | ;;; cicio-mode.el --- Major mode for cicio programs -*- lexical-binding: t; -*- 2 | 3 | (add-to-list 'auto-mode-alist '("\\.ci\\'" . cicio-mode)) 4 | 5 | (require 'clojure-mode) 6 | 7 | (defconst cicio-font-lock-keywords 8 | (let ((keywords (copy-tree clojure-font-lock-keywords))) 9 | (setf (elt keywords 2) 10 | `(,(concat "(\\(?:[a-z\.-]+/\\)?\\(def\[a-z\-*!?\]*-?\\)" 11 | ;; Function declarations 12 | "\\>" 13 | ;; Any whitespace 14 | "[ \r\n\t]*" 15 | ;; Possibly type or metadata 16 | "\\(?:#?^\\(?:{[^}]*}\\|\\sw+\\)[ \r\n\t]*\\)*" 17 | "\\(\\sw+\\)?") 18 | (1 font-lock-keyword-face) 19 | (2 font-lock-function-name-face nil t))) 20 | keywords)) 21 | 22 | (defvar-keymap cicio-mode-map 23 | :doc "Keymap for Cicio mode." 24 | "C-c C-l" #'cicio-to-lua 25 | "C-c C-c" #'run-cicio 26 | "C-c C-r" #'cicio-runfile 27 | "C-M-x" #'cicio-eval-defun 28 | "C-M-q" #'indent-pp-sexp) 29 | 30 | (defvar inferior-cicio-mode-map 31 | :doc "Keymap for Inferior Cicio mode." 32 | "C-c C-r" #'inferior-cicio-runfile 33 | "C-c C-s" #'inferior-cicio-switch-to-source 34 | "C-M-q" #'indent-pp-sexp) 35 | 36 | (define-derived-mode cicio-mode clojure-mode "Cicio" 37 | "Major mode for editing Cicio programs. 38 | 39 | \\{cicio-mode-map}" 40 | (dolist (ind-forms '((nil def) 41 | (1 unless repeat-until using) 42 | (2 fold loop))) 43 | (let ((ind (car ind-forms))) 44 | (dolist (form (cdr ind-forms)) 45 | (put-clojure-indent form ind)))) 46 | (setf (car font-lock-defaults) 'cicio-font-lock-keywords)) 47 | 48 | (define-derived-mode inferior-cicio-mode comint-mode "Inferior Cicio" 49 | "Major mode for interacting with an inferior Cicio interpreter." 50 | (set-syntax-table clojure-mode-syntax-table) 51 | (clojure-font-lock-setup) 52 | (setf (car font-lock-defaults) 'cicio-font-lock-keywords)) 53 | 54 | (defun cicio-to-lua (arg) 55 | "Convert current buffer to Lua. With prefix arg, skip indenting." 56 | (interactive "P") 57 | (save-buffer) 58 | (shell-command (format "cicio -d %s" buffer-file-name) "*cicio-to-lua*") 59 | (switch-to-buffer-other-window "*cicio-to-lua*") 60 | (lua-mode) 61 | (unless arg (indent-region (point-min) (point-max)))) 62 | 63 | (defun cicio-runfile () 64 | "Switch to REPL and reload file" 65 | (interactive) 66 | (save-buffer) 67 | (let ((buffer (current-buffer))) 68 | (run-cicio) 69 | (setq-local cicio-source-buffer buffer) 70 | (inferior-cicio-runfile))) 71 | 72 | (defun inferior-cicio-runfile () 73 | "(Re)load associated source file" 74 | (interactive) 75 | (goto-char (point-max)) 76 | (if (boundp 'cicio-source-buffer) 77 | (let ((file (buffer-file-name cicio-source-buffer))) 78 | (comint-send-string "*cicio*" 79 | (format "(runfile \"%s\")(io.write \";; loaded %s\")nil\n" 80 | file (file-name-nondirectory file)))) 81 | (message "No associated source buffer"))) 82 | 83 | (defun inferior-cicio-switch-to-source () 84 | "Switch to associated source buffer" 85 | (interactive) 86 | (if (boundp 'cicio-source-buffer) 87 | (switch-to-buffer cicio-source-buffer) 88 | (message "No associated source buffer"))) 89 | 90 | (defun cicio-eval-defun () 91 | "Send the top-level form containing point to cicio." 92 | (interactive) 93 | (save-excursion 94 | (when (beginning-of-defun) 95 | (let ((beg (point))) 96 | (end-of-defun) 97 | (comint-send-region "*cicio*" beg (point)))))) 98 | 99 | (defun run-cicio () 100 | "Run an inferior cicio process, input and output via buffer `*cicio*'." 101 | (interactive) 102 | (switch-to-buffer (make-comint "cicio" "cicio")) 103 | (inferior-cicio-mode)) 104 | 105 | (provide 'cicio-mode) 106 | -------------------------------------------------------------------------------- /experiments/consult-eww-source.el: -------------------------------------------------------------------------------- 1 | ;; consult-eww-source.el --- eww bookmark source for consult-buffer -*- lexical-binding: t; -*- 2 | 3 | (require 'consult) 4 | (require 'eww) 5 | 6 | ;;; consult-buffer source 7 | ;; Taken with very minor modifications from the Consult wiki 8 | (defvar consult--source-eww 9 | (list 10 | :name "Eww" 11 | :narrow ?e 12 | :category 'eww-bookmark 13 | :action (lambda (bm) 14 | (eww-browse-url (get-text-property 0 'url bm))) 15 | :items (lambda () 16 | (eww-read-bookmarks) 17 | (mapcar (lambda (bm) 18 | (propertize 19 | (plist-get bm :title) 20 | 'url (plist-get bm :url))) 21 | eww-bookmarks)))) 22 | 23 | ;;; annotate with URL 24 | (add-to-list 'consult-buffer-sources 'consult--source-eww 'append) 25 | 26 | (defun annotate-eww-bookmark (bm) 27 | (concat 28 | (propertize " " 'display `(space :align-to (- right 50))) 29 | (propertize (get-text-property 0 'url bm) 'face 'completions-annotations))) 30 | 31 | (defvar marginalia-annotator-registry) 32 | (with-eval-after-load 'marginalia 33 | (add-to-list 'marginalia-annotator-registry 34 | '(eww-bookmark annotate-eww-bookmark builtin none))) 35 | 36 | ;;; Have Embark treat them as just URLs 37 | (defun transform-eww-bookmark-to-url (target) 38 | (if (eq (car target) 'eww-bookmark) 39 | (cons 'url (get-text-property 0 'url (cdr target))) 40 | target)) 41 | 42 | (with-eval-after-load 'embark 43 | (advice-add 'embark--refine-multi-category 44 | :filter-return #'transform-eww-bookmark-to-url)) 45 | 46 | (provide 'consult-eww-source) 47 | -------------------------------------------------------------------------------- /experiments/drag-region.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | ;; TODO: make it work with rectangular regions 4 | 5 | (let (contents reversed-p) 6 | (defun drag-region--kill () 7 | (when (and (not (eq this-command 'drag-region-mode)) 8 | (region-active-p)) 9 | (let ((beg (region-beginning)) 10 | (end (region-end))) 11 | (setq contents (buffer-substring beg end) 12 | reversed-p (< beg end)) 13 | (delete-region beg end)))) 14 | (defun drag-region--yank () 15 | (when contents 16 | (set-mark (point)) 17 | (insert contents) 18 | (setq contents nil) 19 | (when reversed-p 20 | (exchange-point-and-mark)) 21 | (setq deactivate-mark nil)))) 22 | 23 | (define-minor-mode drag-region-mode 24 | "Drag the region around as you move." 25 | :lighter " drag" 26 | (if drag-region-mode 27 | (progn 28 | (add-hook 'pre-command-hook #'drag-region--kill) 29 | (add-hook 'post-command-hook #'drag-region--yank)) 30 | (remove-hook 'pre-command-hook #'drag-region--kill) 31 | (remove-hook 'post-command-hook #'drag-region--yank) 32 | (deactivate-mark))) 33 | 34 | (provide 'drag-region) 35 | -------------------------------------------------------------------------------- /experiments/embark-elfeed.el: -------------------------------------------------------------------------------- 1 | ;;; embark-elfeed.el --- Target feed entry URLs in elfeed -*- lexical-binding: t; -*- 2 | 3 | (require 'elfeed) 4 | (require 'embark) 5 | 6 | (defun embark-elfeed-target-url () 7 | "Target the URL of the elfeed entry at point." 8 | (when-let (((derived-mode-p 'elfeed-search-mode)) 9 | (entry (elfeed-search-selected :ignore-region)) 10 | (url (elfeed-entry-link entry))) 11 | `(url ,url ,(line-beginning-position) . ,(line-end-position)))) 12 | 13 | (defun embark-elfeed-url-candidates () 14 | "Target the URLs of the selected elfeed entries." 15 | (when-let (((derived-mode-p 'elfeed-search-mode)) 16 | (entries (elfeed-search-selected)) 17 | (urls (mapcar #'elfeed-entry-link entries))) 18 | (cons 'url urls))) 19 | 20 | (add-to-list 'embark-target-finders #'embark-elfeed-target-url) 21 | (add-to-list 'embark-candidate-collectors #'embark-elfeed-url-candidates) 22 | 23 | (provide 'embark-elfeed) 24 | -------------------------------------------------------------------------------- /experiments/embark-extra-indicators.el: -------------------------------------------------------------------------------- 1 | ;; embark-extra-indicators.el --- Which-key and minibuffer embark indicators -*- lexical-binding: t; -*- 2 | 3 | (defvar which-key--buffer) 4 | (declare-function which-key--show-keymap "ext:which-key") 5 | 6 | (defun embark-which-key-indicator () 7 | "An embark indicator that displays keymaps using which-key. 8 | The which-key help message will show the type and value of the 9 | current target followed by an ellipsis if there are further 10 | targets." 11 | (lambda (&optional keymap targets prefix) 12 | (if (null keymap) 13 | (which-key--hide-popup-ignore-command) 14 | (which-key--show-keymap 15 | (if (eq (plist-get (car targets) :type) 'embark-become) 16 | "Become" 17 | (format "Act on %s '%s'%s" 18 | (plist-get (car targets) :type) 19 | (embark--truncate-target (plist-get (car targets) :target)) 20 | (if (cdr targets) "…" ""))) 21 | (if prefix 22 | (pcase (lookup-key keymap prefix 'accept-default) 23 | ((and (pred keymapp) km) km) 24 | (_ (key-binding prefix 'accept-default))) 25 | keymap) 26 | nil nil t (lambda (binding) 27 | (not (string-suffix-p "-argument" (cdr binding)))))))) 28 | 29 | (defun embark-minibuffer-indicator () 30 | "An embark indicator for the minibuffer that shows the target in an overlay." 31 | (let ((indicator-overlay)) 32 | (lambda (&optional keymap targets _prefix) 33 | (if (null keymap) 34 | (when indicator-overlay 35 | (delete-overlay indicator-overlay)) 36 | (when (minibufferp) 37 | (unless indicator-overlay 38 | (setq indicator-overlay (make-overlay 39 | (minibuffer-prompt-end) (point-max))) 40 | (overlay-put indicator-overlay 'face 'highlight)) 41 | (overlay-put indicator-overlay 42 | 'display (plist-get (car targets) :target))))))) 43 | 44 | (provide 'embark-extra-indicators) 45 | -------------------------------------------------------------------------------- /experiments/embark-kmacro.el: -------------------------------------------------------------------------------- 1 | ;;; embark-kmacro.el --- Embark support for Hyperbole key series -*- lexical-binding: t; -*- 2 | 3 | (require 'embark) 4 | 5 | (autoload 'kmacro-push-ring "kmacro") 6 | (autoload 'kmacro-name-last-macro "kmacro") 7 | (autoload 'kmacro-bind-to-key "kmacro") 8 | 9 | (defun embark-kmacro-target () 10 | "Target a textual kmacro in braces." 11 | (save-excursion 12 | (let ((beg (progn (skip-chars-backward "^{}\n") (point))) 13 | (end (progn (skip-chars-forward "^{}\n") (point)))) 14 | (when (and (eq (char-before beg) ?{) (eq (char-after end) ?})) 15 | `(kmacro ,(buffer-substring-no-properties beg end) 16 | . (,(1- beg) . ,(1+ end))))))) 17 | 18 | (add-to-list 'embark-target-finders 'embark-kmacro-target) 19 | 20 | (defun embark-kmacro-run (arg kmacro) 21 | (interactive "p\nsKmacro: ") 22 | (kmacro-call-macro arg t nil (kbd kmacro))) 23 | 24 | (defun embark-kmacro-save (kmacro) 25 | (interactive "sKmacro: ") 26 | (kmacro-push-ring) 27 | (setq last-kbd-macro (kbd kmacro))) 28 | 29 | (defun embark-kmacro-name (kmacro name) 30 | (interactive "sKmacro: \nSName: ") 31 | (let ((last-kbd-macro (kbd kmacro))) 32 | (kmacro-name-last-macro name))) 33 | 34 | (defun embark-kmacro-bind (kmacro) 35 | (interactive "sKmacro: \n") 36 | (let ((last-kbd-macro (kbd kmacro))) 37 | (kmacro-bind-to-key nil))) 38 | 39 | (defvar-keymap embark-kmacro-map 40 | :doc "Actions on kmacros." 41 | "RET" #'embark-kmacro-run 42 | "s" #'embark-kmacro-save 43 | "n" #'embark-kmacro-name 44 | "b" #'embark-kmacro-bind) 45 | 46 | (let ((file-tail (cl-member 'file embark-keymap-alist :key #'car))) 47 | (setcdr file-tail (cons '(kmacro . embark-kmacro-map) (cdr file-tail)))) 48 | 49 | (provide 'embark-kmacro) 50 | -------------------------------------------------------------------------------- /experiments/embark-this-buffer.el: -------------------------------------------------------------------------------- 1 | ;; embark-this-buffer.el --- Embark whole buffer target & actions -*- lexical-binding: t; -*- 2 | 3 | (require 'embark) 4 | 5 | (declare-function 'transpose-windows "ext:window-extras") 6 | 7 | (defvar-keymap this-buffer-map 8 | :doc "Commands to act on current file or buffer." 9 | :parent embark-general-map 10 | "RET" #'eval-buffer 11 | "l" #'load-file 12 | "b" #'byte-compile-file 13 | "e" #'eval-buffer 14 | "r" #'rename-file 15 | "d" #'delete-file 16 | "W" #'write-file 17 | "v r" #'vc-rename-file 18 | "v d" #'vc-delete-file 19 | "n" #'diff-buffer-with-file ; n for new 20 | "C-=" #'ediff-buffers 21 | "=" #'ediff-files 22 | "$" #'ispell 23 | "!" #'shell-command 24 | "&" #'async-shell-command 25 | "x" #'embark-open-externally ; useful for PDFs 26 | "c" #'copy-file 27 | "k" #'kill-current-buffer 28 | "z" #'bury-buffer 29 | "q" #'quit-window 30 | "|" #'embark-shell-command-on-buffer 31 | "g" #'revert-buffer 32 | "p" #'pwd 33 | "h" #'mark-whole-buffer 34 | "<" #'previous-buffer 35 | ">" #'next-buffer 36 | "t" #'transpose-windows) 37 | 38 | (cl-pushnew 'embark--allow-edit (alist-get 'write-file embark-target-injection-hooks)) 39 | 40 | (dolist (cmd '(previous-buffer next-buffer transpose-windows)) 41 | (cl-pushnew cmd embark-repeat-actions)) 42 | 43 | (defun embark-on-this-buffer () 44 | "Run `embark-act' on the current buffer." 45 | (interactive) 46 | (let ((embark-target-finders 47 | (list (lambda () (cons 'this-buffer (buffer-name))))) 48 | (embark-keymap-alist '((this-buffer . this-buffer-map)))) 49 | (embark-act))) 50 | 51 | (provide 'embark-this-buffer) 52 | -------------------------------------------------------------------------------- /experiments/eshell-repl.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | ;; to use run: emacs --batch -l path/to/eshell-repl.el 4 | 5 | (require 'em-prompt) 6 | 7 | (let ((dir default-directory)) 8 | (add-hook 'eshell-directory-change-hook 9 | (lambda () (setq dir default-directory))) 10 | (while t 11 | (let* ((default-directory dir) 12 | (command (read-from-minibuffer (funcall eshell-prompt-function)))) 13 | (princ (eshell-command-result command))))) 14 | -------------------------------------------------------------------------------- /experiments/gobble-whitespace.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | (defun gobble-whitespace (skip-chars) 4 | (lambda (old-fun n &optional killflag) 5 | (let ((beg (point))) 6 | (if (or (and (use-region-p) delete-active-region) 7 | (/= n 1) 8 | (progn (funcall skip-chars " \t\r\n\v") 9 | (= beg (point)))) 10 | (funcall old-fun n killflag) 11 | (when (eq skip-chars #'skip-chars-backward) 12 | (goto-char (or (text-property-any (point) beg 'read-only nil) beg))) 13 | (delete-region beg (point)))))) 14 | 15 | (defalias 'gobble-whitespace-forward (gobble-whitespace #'skip-chars-forward)) 16 | (defalias 'gobble-whitespace-backward (gobble-whitespace #'skip-chars-backward)) 17 | 18 | (define-minor-mode global-gobble-whitespace-mode 19 | "Make chacter deletion commands gobble whitespace" 20 | :global t 21 | (if global-gobble-whitespace-mode 22 | (progn 23 | (advice-add 'delete-forward-char :around #'gobble-whitespace-forward) 24 | (advice-add 'delete-backward-char :around #'gobble-whitespace-backward)) 25 | (advice-remove 'delete-forward-char #'gobble-whitespace-forward) 26 | (advice-remove 'delete-backward-char #'gobble-whitespace-backward))) 27 | 28 | (provide 'gobble-whitespace) 29 | -------------------------------------------------------------------------------- /experiments/grille.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | (eval-when-compile 4 | (require 'cl-lib) 5 | (require 'subr-x)) 6 | 7 | (defcustom grille-limit 50 8 | "Maximum number of candidates" 9 | :type 'integer 10 | :group 'grille) 11 | 12 | (defvar grille--columns nil) 13 | 14 | (defvar grille-button-keymap 15 | `(keymap (9 . grille--insert) ,@button-map)) 16 | 17 | (define-button-type 'grille-button 18 | 'face 'default 19 | 'action 'grille--choose 20 | 'keymap grille-button-keymap) 21 | 22 | (defun grille--boundaries () 23 | (let ((contents (minibuffer-contents)) 24 | (pt (- (point) (minibuffer-prompt-end)))) 25 | (completion-boundaries 26 | (substring contents 0 pt) 27 | minibuffer-completion-table 28 | minibuffer-completion-predicate 29 | (substring contents pt)))) 30 | 31 | (defun grille--insert (completion) 32 | (interactive) 33 | (when-let ((label (button-label completion)) 34 | (mini (active-minibuffer-window))) 35 | (select-window mini) 36 | (pcase-let ((origin (minibuffer-prompt-end)) 37 | (`(,beg . ,end) (grille--boundaries))) 38 | (delete-region (+ origin beg) (+ (point) end)) 39 | (goto-char (+ origin beg)) 40 | (insert label)))) 41 | 42 | (defun grille--choose (completion) 43 | (interactive) 44 | (grille--insert completion) 45 | (unless (= (car (grille--boundaries)) ; new boundaries? 46 | (- (point) (minibuffer-prompt-end))) 47 | (exit-minibuffer))) 48 | 49 | (defun grille--revert (&rest _) 50 | "Revert the grid completions buffer." 51 | (while-no-input 52 | (with-current-buffer "*Grille*" 53 | (revert-buffer)))) 54 | 55 | (defun grille--list () 56 | "Get list of completions." 57 | (when-let ((mini (active-minibuffer-window))) 58 | (with-selected-window mini 59 | (let* ((all (completion-all-completions 60 | (minibuffer-contents) 61 | minibuffer-completion-table 62 | minibuffer-completion-predicate 63 | (- (point) (minibuffer-prompt-end)))) 64 | (last (last all))) 65 | (when last (setcdr last nil)) 66 | all)))) 67 | 68 | (defun grille--select-columns () 69 | "Compute reasonable number of columns." 70 | (let ((all (grille--list)) 71 | (width (frame-width))) 72 | (/ width 73 | (or (cl-loop repeat grille-limit 74 | for entry in all 75 | maximize (min (+ (length entry) 3) width)) 76 | width)))) 77 | 78 | (defun grille--set-format () 79 | (setq tabulated-list-format 80 | (apply #'vector 81 | (cl-loop with width = (/ (frame-width) 82 | grille--columns) 83 | repeat grille--columns 84 | collect `("Completions" ,width nil))))) 85 | 86 | (defun grille--entries () 87 | (let ((all (grille--list))) 88 | (cl-loop repeat grille-limit 89 | while all 90 | collect 91 | (list 92 | nil 93 | (apply #'vector 94 | (cl-loop repeat grille--columns 95 | collect `(,(or (pop all) "") 96 | type grille-button))))))) 97 | 98 | (define-derived-mode grille-mode 99 | tabulated-list-mode "Grid" 100 | "Grid of completions.") 101 | 102 | (defvar-keymap grille-mode-map 103 | "" #'forward-button 104 | "" #'backward-button 105 | "s" #'isearch-forward) 106 | 107 | (defun grille (&optional columns) 108 | "Pop up a live-updating grid with current completions." 109 | (interactive "P") 110 | (when (minibufferp) 111 | (with-current-buffer (get-buffer-create "*Grille*") 112 | (grille-mode) 113 | (setq-local grille--columns 114 | (if columns 115 | (prefix-numeric-value columns) 116 | (grille--select-columns))) 117 | (grille--set-format) 118 | (setq tabulated-list-entries #'grille--entries) 119 | (setq minibuffer-scroll-window 120 | (display-buffer (current-buffer) 121 | '((display-buffer-reuse-window 122 | display-buffer-at-bottom)))) 123 | (while-no-input (tabulated-list-print))) 124 | (add-hook 'after-change-functions #'grille--revert nil t))) 125 | 126 | (defun grille-completing-read (&rest args) 127 | (run-with-idle-timer 0.05 nil #'grille) 128 | (apply #'completing-read-default args)) 129 | 130 | (defun grille-switch-to () 131 | "Switch to completions grid." 132 | (interactive) 133 | (let ((window (or (get-buffer-window "*Grille*" 0) 134 | (progn (grille) 135 | (get-buffer-window "*Grille*" 0))))) 136 | (when window (select-window window)))) 137 | 138 | (defun grille-forward-char-or-switch-to (&optional N) 139 | "Move forward if possible, else switch to completions." 140 | (interactive "P") 141 | (if (and (eobp) (not N)) 142 | (grille-switch-to) 143 | (forward-char (prefix-numeric-value N)))) 144 | 145 | (provide 'grille) 146 | -------------------------------------------------------------------------------- /experiments/init.el: -------------------------------------------------------------------------------- 1 | ;;; Packages or keybindings that I am not currently using, but whose 2 | ;;; configuration I still sometimes want to load 3 | 4 | (use-package slime 5 | :ensure t 6 | :defer t 7 | :bind 8 | (:map slime-editing-map 9 | ([remap display-local-help] . slime-describe-symbol) 10 | ([remap embark-pp-eval-defun] . slime-compile-defun) 11 | ([remap pp-macroexpand-expression] . slime-expand-1) 12 | ([remap pp-eval-expression] . slime-interactive-eval) 13 | ([remap xref-find-definitions] . slime-edit-definition)) 14 | :config 15 | (setq inferior-lisp-program "sbcl") 16 | (defun just-use-cirf (completions start end) 17 | (funcall completion-in-region-function start end completions)) 18 | (advice-add 'slime-display-or-scroll-completions :override #'just-use-cirf)) 19 | 20 | (use-package slime-repl 21 | :defer t 22 | :bind (:map slime-repl-mode-map ("DEL") (""))) 23 | 24 | (use-package selected 25 | :ensure t 26 | :diminish selected-minor-mode 27 | :bind 28 | (:map selected-keymap 29 | ("x" . exchange-point-and-mark) 30 | ("b" . backward-sexp) 31 | ("SPC" . mark-sexp) 32 | ("f" . forward-sexp) 33 | ("," . backward-sentence) 34 | ("." . mark-end-of-sentence) 35 | (";" . forward-sentence) 36 | ("<" . backward-paragraph) 37 | ("h" . mark-paragraph) 38 | (">" . forward-paragraph) 39 | ("B" . beginning-of-defun) 40 | ("d" . mark-defun) 41 | ("F" . end-of-defun) 42 | ("w" . kill-ring-save) 43 | ("t" . transpose-regions) 44 | ("m" . apply-macro-to-region-lines) 45 | ("i" . indent-rigidly) 46 | ("=" . count-words-region) 47 | ("e" . eval-region) 48 | ("|" . shell-command-on-region) 49 | ("o" . org-table-convert-region) 50 | ("n" . narrow-to-region) 51 | ("s" . whitespace-cleanup-region) 52 | ("r" . rot13-region) 53 | ("S" . sort-lines) 54 | ("R" . reverse-region) 55 | ("*" . calc-grab-region)) 56 | :init 57 | (selected-global-mode)) 58 | 59 | (use-package gcmh 60 | :ensure t 61 | :demand t 62 | :diminish 63 | :config (gcmh-mode)) 64 | 65 | (use-package rainbow-delimiters 66 | :ensure t 67 | :hook 68 | (prog-mode . rainbow-delimiters-mode-enable)) 69 | 70 | (bind-keys 71 | :prefix "C-c f" 72 | :prefix-map file-ops-map 73 | :prefix-docstring "Keymap for file operations" 74 | ("c" . copy-file) 75 | ("d" . delete-file) 76 | ("x" . embark-open-externally) 77 | ("r" . rename-file) 78 | ("m" . make-directory) 79 | ("D" . delete-directory) 80 | ("/" . cd) 81 | ("." . pwd) 82 | ("=" . ediff) 83 | ("b" . byte-compile-file) 84 | ("B" . byte-recompile-directory)) 85 | 86 | (bind-keys 87 | :prefix "C-c f e" 88 | :prefix-map ediff-ops-map 89 | :prefix-docstring "Keymap for launching ediff" 90 | ("f" . ediff-files) 91 | ("3" . ediff-files3) 92 | ("d" . ediff-directories) 93 | ("c" . ediff-current-file) 94 | ("b" . ediff-buffers)) 95 | 96 | (bind-keys 97 | :prefix "C-x p" 98 | :prefix-map pkg-ops-map 99 | :prefix-docstring "Keymap for package operations" 100 | ("l" . list-packages) 101 | ("n" . package-list-packages-no-fetch) 102 | ("i" . package-install) 103 | ("d" . package-delete) 104 | ("a" . package-autoremove) 105 | ("g" . package-refresh-contents) 106 | ("r" . package-reinstall) 107 | ("h" . describe-package)) 108 | 109 | (use-package avy-grille 110 | :bind 111 | (:map minibuffer-local-completion-map 112 | ("'" . avy-grille-choose) 113 | ("\"" . avy-grille-embark-act))) 114 | 115 | (use-package grille 116 | :bind 117 | (:map minibuffer-local-completion-map 118 | ("M-q" . grille) 119 | ("" . grille-forward-char-or-switch-to) 120 | ("" . grille-switch-to)) 121 | (:map grille-mode-map 122 | (";" . embark-act) 123 | ("'" . avy-grille)) 124 | :commands grille-completing-read 125 | :custom 126 | (completing-read-function #'grille-completing-read) 127 | :hook 128 | (grille-mode 129 | . (lambda () 130 | (let ((buffer (current-buffer)) 131 | (mini (active-minibuffer-window))) 132 | (when mini 133 | (with-selected-window mini 134 | (embark--cache-info buffer))) 135 | (add-hook 'tabulated-list-revert-hook 136 | (lambda () 137 | (setq default-directory 138 | (with-selected-window mini 139 | (embark--default-directory)))) 140 | nil t))))) 141 | 142 | (use-package live-completions 143 | :demand t 144 | :load-path "~/my-elisp-packages/live-completions" 145 | :bind (:map minibuffer-local-completion-map 146 | ("C-v" . live-completions-set-columns)) 147 | :config (live-completions-mode)) 148 | 149 | (use-package icomplete 150 | :demand t 151 | :config (icomplete-mode) 152 | :bind (:map icomplete-minibuffer-map 153 | ("" . icomplete-forward-completions) 154 | ("C-n" . icomplete-forward-completions) 155 | ("" . icomplete-backward-completions) 156 | ("C-p" . icomplete-backward-completions) 157 | ("TAB" . minibuffer-force-complete) 158 | ("C-M-i" . minibuffer-complete) 159 | ("M-RET" . exit-minibuffer)) 160 | :hook 161 | (icomplete-minibuffer-setup . visual-line-mode) 162 | :custom 163 | (icomplete-show-matches-on-no-input t) 164 | (icomplete-prospects-height 5) 165 | (icomplete-separator " ⋮ ") 166 | (icomplete-hide-common-prefix nil) 167 | :config 168 | (advice-add 'icomplete-vertical-minibuffer-teardown 169 | :after #'visual-line-mode)) 170 | 171 | (use-package icomplete-vertical 172 | :ensure t 173 | :demand t 174 | ;; :load-path "~/my-elisp-packages/icomplete-vertical" 175 | :bind (:map icomplete-minibuffer-map 176 | ("C-v" . icomplete-vertical-toggle)) 177 | :config (icomplete-vertical-mode)) 178 | 179 | (use-package simple ; defines completion-list-mode 180 | :bind 181 | (:map completion-list-mode-map 182 | ("TAB" . insert-completion)) 183 | :config 184 | (defun insert-completion (&optional event) 185 | "Insert current completion into the minibuffer. 186 | If EVENT, use EVENT’s position to determine the starting position." 187 | (interactive (list last-nonmenu-event)) 188 | (let ((completion-no-auto-exit t)) 189 | (choose-completion event))) 190 | :hook 191 | (completion-list-mode . force-truncate-lines)) 192 | 193 | (use-package restricto 194 | :demand t 195 | :load-path "~/my-elisp-packages/restricto" 196 | :bind (:map minibuffer-local-completion-map 197 | ("SPC" . restricto-narrow) 198 | ("S-SPC" . restricto-widen)) 199 | :config (restricto-mode)) 200 | 201 | (use-package regexpect 202 | :demand t 203 | :config 204 | (cl-flet ((string-fix-p (u v) 205 | (or (string-prefix-p u v) (string-suffix-p u v))) 206 | (remfix (u v) 207 | (let ((pre (string-prefix-p u v))) 208 | (substring v (if pre 1 0) (if pre nil -1)))) 209 | (rx-seq (fmt seq) 210 | (mapconcat 211 | (if (stringp fmt) (lambda (x) (format fmt x)) fmt) 212 | seq ".?*")) 213 | (format-unless (fmt pat) 214 | (lambda (x) 215 | (if (string-match-p pat x) x (format fmt x))))) 216 | (defun my-regexp-converter (pattern) 217 | (cond 218 | ((string-match-p " " pattern) 219 | (rx-seq (format-unless "\\(%s\\)" "\\\\(") 220 | (mapcar #'my-regexp-converter (split-string pattern)))) 221 | ((string-fix-p "=" pattern) (regexp-quote (remfix "=" pattern))) 222 | ((string-fix-p "," pattern) (remfix "," pattern)) 223 | ((string-match-p "^!." pattern) 224 | (rx-to-string 225 | `(seq 226 | (group string-start) ; highlight nothing! 227 | (zero-or-more 228 | (or ,@(cl-loop for i from 1 below (length pattern) 229 | collect `(seq ,(substring pattern 1 i) 230 | (or (not (any ,(aref pattern i))) 231 | string-end))))) 232 | string-end))) 233 | ((string-match-p "^{.*}$" pattern) 234 | (rx-seq "\\(%c\\)" (substring pattern 1 -1))) 235 | ((and minibuffer-completing-file-name 236 | (string-match-p "[-.]" pattern)) 237 | (rx-seq "\\<\\(%s\\)" (split-string pattern "[-.]" t))) 238 | ((string-match-p "[/-]" pattern) 239 | (rx-seq "\\<\\(%s\\)" (split-string pattern "[/-]" t))) 240 | ((string-fix-p "." pattern) 241 | (rx-seq "\\<\\(%c\\)" (remfix "." pattern))) 242 | (t pattern)))) 243 | :custom (regexpect-converter #'my-regexp-converter)) 244 | 245 | -------------------------------------------------------------------------------- /experiments/library-support.el: -------------------------------------------------------------------------------- 1 | ;; library-support.el --- Embark & Marginalia support for elisp libraries -*- lexical-binding: t; -*- 2 | 3 | (require 'marginalia) 4 | (require 'embark) 5 | (require 'lisp-mnt) 6 | 7 | (add-to-list 'marginalia-prompt-categories '("\\<[lL]ibrary\\>" . library)) 8 | 9 | (defvar library-summary-cache (make-hash-table :test #'equal)) 10 | 11 | (defun library-summary (library) 12 | (setq library (string-remove-suffix ".elc" library)) 13 | (or (gethash library library-summary-cache) 14 | (setf (gethash library library-summary-cache) 15 | (when-let ((path (ffap-el-mode library)) 16 | (summary (let ((inhibit-message t)) (lm-summary path)))) 17 | (concat (propertize " " 'display '(space :align-to (- right 60))) 18 | (propertize summary 'face 'completions-annotations)))))) 19 | 20 | (add-to-list 'marginalia-annotator-registry 21 | '(library library-summary builtin none)) 22 | 23 | ;; override Embark's target finder 24 | (defun embark-target-library-at-point () 25 | "Target the Emacs Lisp library name at point." 26 | (when-let (filename (thing-at-point 'filename)) 27 | (when (ffap-el-mode filename) 28 | `(library ,filename . ,(bounds-of-thing-at-point 'filename))))) 29 | 30 | (defvar-keymap embark-library-map 31 | :doc "Keymap for operations on Emacs Lisp libraries." 32 | "RET" #'find-library 33 | "l" #'load-library 34 | "f" #'find-library 35 | "h" #'finder-commentary 36 | "a" #'apropos-library 37 | "w" #'locate-library) 38 | 39 | (add-to-list 'embark-keymap-alist '(library . embark-library-map)) 40 | 41 | (provide 'library-support) 42 | -------------------------------------------------------------------------------- /experiments/mc-prompt-once.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | (defun mc-prompt-once-advice (fn &rest args) 4 | (setq mc--this-command (lambda () (interactive) (apply fn args))) 5 | (apply fn args)) 6 | 7 | (defun mc-prompt-once (&rest fns) 8 | (dolist (fn fns) 9 | (advice-add fn :around #'mc-prompt-once-advice))) 10 | 11 | (defun mc--reset-read-prompts () 12 | (setq mc--read-char nil 13 | mc--read-quoted-char nil 14 | mc--ivy-completing-read nil)) 15 | 16 | (defvar mc--ivy-completing-read nil) 17 | 18 | (defun mc-support-ivy (fn &rest args) 19 | (if (not multiple-cursors-mode) 20 | (apply fn args) 21 | (unless mc--ivy-completing-read 22 | (setq mc--ivy-completing-read (apply fn args))) 23 | mc--ivy-completing-read)) 24 | 25 | (with-eval-after-load 'ivy 26 | (advice-add 'ivy-completing-read :around #'mc-support-ivy)) 27 | 28 | (with-eval-after-load 'misc (mc-prompt-once #'zap-up-to-char)) 29 | 30 | (with-eval-after-load 'smartparens (mc-prompt-once #'sp-rewrap-sexp)) 31 | 32 | (defun prompt-insert-string (s) 33 | "Prompt for a string to insert in the buffer (meant to be used with multiple-cursors)." 34 | (interactive "MInsert: ") 35 | (insert s)) 36 | 37 | (provide 'mc-prompt-once) 38 | -------------------------------------------------------------------------------- /experiments/regexpect.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | (defgroup regexpect nil 4 | "Completion style that converts a string into a regexp and matches it." 5 | :group 'completion) 6 | 7 | (defcustom regexpect-converter #'identity 8 | "The function used to convert an input string into a regexp." 9 | :type 'function 10 | :group 'regexpect) 11 | 12 | (defcustom regexpect-smart-case t 13 | "Whether to use smart case. 14 | If this variable is t, then case-sensitivity is decided as 15 | follows: if any component contains upper case letters, the 16 | matches are case sensitive; otherwise case-insensitive. This 17 | like the behavior of `isearch' when `search-upper-case' is 18 | non-nil. 19 | 20 | On the other hand, if this variable is nil, then case-sensitivity 21 | is determined by the values of `completion-ignore-case', 22 | `read-file-name-completion-ignore-case' and 23 | `read-buffer-completion-ignore-case', as usual for completion." 24 | :type 'boolean 25 | :group 'regexpect) 26 | 27 | (defun regexpect--internal (string table &optional pred) 28 | (save-match-data 29 | (let* ((limit (car (completion-boundaries string table pred ""))) 30 | (prefix (substring string 0 limit)) 31 | (pattern (substring string limit)) 32 | (regexp (funcall regexpect-converter pattern)) 33 | (completion-regexp-list (list regexp)) 34 | (completion-ignore-case 35 | (if regexpect-smart-case 36 | (isearch-no-upper-case-p regexp t) 37 | completion-ignore-case))) 38 | (list 39 | (condition-case nil 40 | (all-completions prefix table pred) 41 | (invalid-regexp nil)) 42 | regexp 43 | prefix)))) 44 | 45 | (defun regexpect-all-completions (string table pred _point) 46 | "Convert STRING to a regexp and find entries TABLE matching it all. 47 | The predicate PRED is used to constrain the entries in TABLE. The 48 | matching portions of each candidate are highlighted. 49 | This function is part of the `regexpect' completion style." 50 | (cl-destructuring-bind (completions regexp prefix) 51 | (regexpect--internal string table pred) 52 | (when completions 53 | (nconc 54 | (save-match-data 55 | (cl-loop for original in completions 56 | for string = (copy-sequence original) do 57 | (string-match regexp string) 58 | (cl-loop 59 | for (x y) on (or (cddr (match-data)) (match-data)) by #'cddr 60 | when x do 61 | (font-lock-prepend-text-property 62 | x y 63 | 'face 'completions-common-part 64 | string)) 65 | collect string)) 66 | (length prefix))))) 67 | 68 | (defun regexpect-try-completion (string table pred point &optional _metadata) 69 | "Complete STRING to unique matching entry in TABLE. 70 | This uses `regexpect-all-completions' to find matches for 71 | STRING in TABLE among entries satisfying PRED. If there is only 72 | one match, it completes to that match. If there are no matches, 73 | it returns nil. In any other case it \"completes\" STRING to 74 | itself, without moving POINT. This function is part of the 75 | `regexpect' completion style." 76 | (cl-destructuring-bind (completions regexp prefix) 77 | (regexpect--internal string table pred) 78 | (cond 79 | ((null completions) nil) 80 | ((null (cdr completions)) 81 | (let ((full (concat prefix (car completions)))) 82 | (cons full (length full)))) 83 | ((string= regexp (regexp-quote regexp)) 84 | (completion-substring-try-completion string table pred point)) 85 | (t (cons string point))))) 86 | 87 | (add-to-list 'completion-styles-alist 88 | '(regexpect 89 | regexpect-try-completion regexpect-all-completions 90 | "Convert pattern to regexp and match it.")) 91 | 92 | (provide 'regexpect) 93 | -------------------------------------------------------------------------------- /init.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t -*- 2 | 3 | ;;; Customize thinks it knows better than me 4 | 5 | (setq custom-file (make-temp-file "emacs-custom-")) 6 | 7 | ;;; GUI 8 | 9 | (setopt 10 | inhibit-startup-screen t 11 | initial-scratch-message nil 12 | menu-bar-mode nil 13 | tool-bar-mode nil 14 | scroll-bar-mode nil 15 | use-dialog-box nil 16 | ring-bell-function #'ignore 17 | cursor-type 'bar 18 | tab-bar-show nil 19 | tab-bar-close-button-show nil) 20 | 21 | (set-face-attribute 'variable-pitch nil :family "URW Bookman") 22 | 23 | (custom-set-faces 24 | '(Info-quoted ((t :inherit fixed-pitch))) 25 | '(fringe ((t :background unspecified)))) 26 | 27 | (set-fontset-font "fontset-default" 'emoji 28 | (font-spec :family "Noto Color Emoji")) 29 | 30 | ;;; package.el & use-package setup 31 | 32 | (setq package-archives 33 | '(("melpa" . "https://melpa.org/packages/") 34 | ("gnu" . "https://elpa.gnu.org/packages/") 35 | ("nongnu" . "https://elpa.nongnu.org/nongnu/"))) 36 | 37 | (use-package diminish :ensure t :defer t) 38 | 39 | (use-package bind-key 40 | :bind ("C-h y" . describe-personal-keybindings)) 41 | 42 | (setopt use-package-vc-prefer-newest t) 43 | 44 | (add-to-list 'load-path "~/.emacs.d/my-lisp/") 45 | (add-to-list 'load-path "~/.private/") 46 | 47 | ;;; misc 48 | 49 | (dolist (cmd '(narrow-to-region 50 | upcase-region 51 | downcase-region 52 | dired-find-alternate-file 53 | LaTeX-narrow-to-environment 54 | TeX-narrow-to-group 55 | narrow-to-page 56 | set-goal-column 57 | scroll-left 58 | scroll-right)) 59 | (put cmd 'disabled nil)) 60 | (put 'suspend-frame 'disabled t) 61 | 62 | (setopt 63 | use-package-enable-imenu-support t 64 | set-mark-command-repeat-pop t 65 | tab-always-indent 'complete 66 | current-language-environment "UTF-8" 67 | after-save-hook '(executable-make-buffer-file-executable-if-script-p) 68 | column-number-indicator-zero-based nil 69 | scroll-preserve-screen-position t 70 | make-backup-files nil 71 | save-interprogram-paste-before-kill t 72 | sentence-end-double-space nil 73 | cycle-spacing-actions '(delete-all-space just-one-space restore) 74 | words-include-escapes t 75 | indent-tabs-mode nil 76 | standard-indent 2 77 | view-read-only t 78 | kill-read-only-ok t 79 | kill-whole-line t 80 | truncate-lines t 81 | history-delete-duplicates t 82 | kill-do-not-save-duplicates t 83 | default-input-method "TeX" 84 | default-transient-input-method "TeX" 85 | password-cache-expiry 300 86 | debugger-stack-frame-as-list t 87 | split-width-threshold 140 88 | y-or-n-p-use-read-key t 89 | use-short-answers t 90 | async-shell-command-display-buffer nil 91 | revert-without-query '("") 92 | recenter-positions '(top middle bottom) 93 | display-time-default-load-average nil 94 | native-comp-async-report-warnings-errors 'silent 95 | grep-use-headings t) 96 | 97 | (bind-keys 98 | ("C-d" . delete-forward-char) 99 | ("M-K" . kill-paragraph) 100 | ("M-H" . mark-paragraph) ; for REPLs where I use M-h for consult-history 101 | ("M-Z" . zap-to-char) 102 | ("C-x k" . kill-current-buffer) 103 | ("C-x q" . quit-window) 104 | ("C-x /" . pwd) 105 | ("C-x C-/" . cd) 106 | ("C-x C-p" . list-packages) 107 | ("M-r" . kmacro-start-macro-or-insert-counter) 108 | ("M-m" . kmacro-end-or-call-macro) 109 | ("C-x C-k i" . insert-kbd-macro) 110 | ("M-i" . back-to-indentation) 111 | ("M-s k" . keep-lines) 112 | ("M-s f" . flush-lines) 113 | ("M-s c" . count-matches) 114 | ("C-;" . comment-dwim) 115 | ("C-z" . query-replace-regexp) 116 | ("C-<" . delete-indentation) 117 | ("M-\\" . cycle-spacing) 118 | ("C-h p" . describe-package) ; swap these two 119 | ("C-h P" . finder-by-keyword) 120 | ("C-\\" . activate-transient-input-method) ; swap these two, too 121 | ("C-x \\" . toggle-input-method) 122 | ("C-x M-c" . restart-emacs) 123 | ([remap list-buffers] . electric-buffer-list) 124 | ([remap count-words-region] . count-words) 125 | ("C-M-o" . up-list) 126 | ("C-o" . split-line) 127 | ("M-o" . other-window) 128 | ("M-R" . raise-sexp) 129 | ("M-T" . transpose-lines) 130 | ("C-x C-t" . transpose-sentences) 131 | ("C-x M-t" . transpose-paragraphs) 132 | ([remap apropos-command] . apropos)) 133 | 134 | (bind-keys :prefix-map insert-pair-map 135 | :prefix "M-[" 136 | ([t] . insert-pair)) 137 | 138 | (defun insert-pair-numeric-prefix (args) 139 | (cons (prefix-numeric-value (car args)) (cdr args))) 140 | 141 | (advice-add 'insert-pair :filter-args #'insert-pair-numeric-prefix) 142 | 143 | (bind-keys :prefix-map toggle-map 144 | :prefix "C-c x" 145 | :prefix-docstring "Keymap for commands that toggle settings." 146 | ("c" . column-number-mode) 147 | ("d" . toggle-debug-on-error) 148 | ("t" . toggle-frame-tab-bar) 149 | ("f" . follow-mode) 150 | ("w" . whitespace-mode) 151 | ("v" . variable-pitch-mode) 152 | ("r" . visible-mode)) 153 | 154 | (bind-keys :prefix-map time-map 155 | :prefix "C-c t" 156 | :prefix-docstring "Keymap for commands that deal with time." 157 | ("w" . world-clock) 158 | ("t" . display-time-mode) 159 | ("c" . calendar) 160 | ("o" . org-timer-set-timer) 161 | ("p" . org-timer-pause-or-continue) 162 | ("s" . org-timer-stop)) 163 | 164 | (use-package package 165 | :defer t 166 | :config 167 | (defun just-package-names (fn &rest args) 168 | (cl-letf (((symbol-function 'package-desc-full-name) 169 | (lambda (pkg) (symbol-name (package-desc-name pkg))))) 170 | (apply fn args))) 171 | (advice-add 'package-menu--list-to-prompt :around 'just-package-names)) 172 | 173 | ;;; packages 174 | 175 | (use-package doric-themes 176 | :ensure t 177 | :bind 178 | ("C-c x b" . doric-themes-toggle) 179 | :custom 180 | (doric-themes-to-toggle '(doric-wind doric-water)) 181 | :init 182 | (doric-themes-select 'doric-wind)) 183 | 184 | (use-package spacious-padding 185 | :ensure t 186 | :custom 187 | (spacious-padding-widths 188 | '( :internal-border-width 15 189 | :header-line-width 4 190 | :mode-line-width 3 ; half the default 191 | :tab-width 4 192 | :right-divider-width 15 ; half the default 193 | :scroll-bar-width 8)) 194 | :init (spacious-padding-mode)) 195 | 196 | (use-package face-remap 197 | :defer t 198 | :diminish buffer-face-mode) 199 | 200 | (use-package imenu 201 | :defer t 202 | :custom (imenu-space-replacement nil)) 203 | 204 | (use-package info 205 | :bind 206 | (:map Info-mode-map 207 | ("{" . backward-paragraph) 208 | ("}" . forward-paragraph))) 209 | 210 | (use-package custom 211 | :hook 212 | (Custom-mode . configure-imenu-Custom) 213 | :config 214 | (defun configure-imenu-Custom () 215 | (setq imenu-generic-expression 216 | '(("Faces" "^\\(?:Show\\|Hide\\) \\(.*\\) face: \\[sample\\]" 1) 217 | ("Variables" "^\\(?:Show Value\\|Hide\\) \\([^:\n]*\\)" 1))))) 218 | 219 | (use-package recentf 220 | :custom 221 | (recentf-max-saved-items 50) 222 | :init 223 | (recentf-mode)) 224 | 225 | (use-package repeat 226 | :init (repeat-mode) 227 | :config 228 | (put 'other-window 'repeat-map nil)) 229 | 230 | (use-package misc 231 | :bind 232 | ("M-z" . zap-up-to-char) 233 | ("M-F" . forward-to-word) 234 | ("M-B" . backward-to-word) 235 | ("C-M-<" . mark-beginning-of-buffer) 236 | ("C-M->" . mark-end-of-buffer) 237 | ("C-S-l" . copy-from-above-command) 238 | ("C-S-d" . duplicate-dwim)) 239 | 240 | (use-package rect 241 | :bind 242 | (:map rectangle-mark-mode-map 243 | ("t" . string-rectangle) 244 | ("o" . open-rectangle) 245 | ("c" . clear-rectangle) 246 | ("n" . rectangle-number-lines) 247 | ("x" . rectangle-exchange-point-and-mark) 248 | ("*" . calc-grab-rectangle) 249 | (":" . calc-grab-sum-down) 250 | ("_" . calc-grab-sum-across) 251 | (" " . delete-whitespace-rectangle))) 252 | 253 | (use-package visiting-buffer) 254 | 255 | (use-package text-extras 256 | :bind 257 | ("M-Q" . unfill-paragraph) 258 | ("C-S-o" . copy-word-from-above) 259 | ("C-S-K" . duplicate-line-kill-word) 260 | ("M-L" . mark-line) 261 | ("M-C" . mark-char) 262 | ("M-@" . mark-my-word) 263 | ("M-g r" . goto-random-line) 264 | ("C-M--" . kill-inside-sexp) 265 | ("C-M-=" . mark-inside-sexp) 266 | ("M-U" . unwrap-sexp) 267 | ("M-S" . unwrap-mark-sexp) 268 | ("C-|" . pipe-region) 269 | ("C-S-f" . forward-to-whitespace) 270 | ("C-S-b" . backward-to-whitespace) 271 | ("M-W" . mark-non-whitespace) 272 | ("M-M" . apply-macro-to-rest-of-paragraph) 273 | ("M-;" . dabbrev-next) 274 | ("C-M-;" . dabbrev-complete-next) 275 | ("C-c u" . text-to-clipboard) 276 | ([remap upcase-word] . upcase-dwiw) 277 | ([remap downcase-word] . downcase-dwiw) 278 | ([remap capitalize-word] . capitalize-dwiw) 279 | :commands 280 | echo-area-tooltips) 281 | 282 | (use-package placeholder 283 | :vc (:url "https://github.com/oantolin/placeholder.git") 284 | :bind 285 | ("M-_" . placeholder-insert) 286 | ("C-S-n" . placeholder-forward) 287 | ("C-S-p" . placeholder-backward)) 288 | 289 | (use-package topaz-paste 290 | :commands topaz-paste-region topaz-paste-buffer) 291 | 292 | (use-package gptel 293 | :ensure t 294 | :bind 295 | (:prefix-map global-gptel-map :prefix "C-c i" 296 | ("c" . gptel) 297 | ("s" . gptel-send) 298 | ("r" . gptel-rewrite) 299 | ("a" . gptel-add) 300 | ("m" . gptel-mode)) 301 | :custom 302 | (gptel-org-branching-context t) 303 | :config 304 | (pop gptel--known-backends) ; remove the default ChatGPT backend 305 | (gptel-make-openai "Groq" 306 | :host "api.groq.com" 307 | :endpoint "/openai/v1/chat/completions" 308 | :stream t 309 | :key gptel-api-key 310 | :models '(llama-3.3-70b-versatile 311 | llama-3.1-8b-instant 312 | llama3-70b-8192 313 | llama3-8b-8192 314 | mixtral-8x7b-32768 315 | deepseek-r1-distill-llama-70b 316 | deepseek-r1-distill-qwen-32b 317 | gemma2-9b-it)) 318 | (setq gptel-model 'gemini-2.0-flash 319 | gptel-backend 320 | (gptel-make-gemini "Gemini" :key gptel-api-key :stream t))) 321 | 322 | (use-package gptel-extras 323 | :bind 324 | (:map global-gptel-map 325 | ("q" . gptel-extras-mini) 326 | ("d" . gptel-extras-define))) 327 | 328 | (use-package isearch-extras 329 | :custom 330 | (search-whitespace-regexp ".*?") 331 | (isearch-allow-scroll 'unlimited) 332 | (isearch-lazy-count t) 333 | :bind 334 | ("M-n" . isearch-next) 335 | ("M-p" . isearch-previous) 336 | (:map isearch-mode-map 337 | ("M-c") ; free up for capitalize-dwim, still bound to M-s c 338 | ("M-r") ; free up for recording kmacros, still bound to M-s r 339 | ("C-z" . isearch-query-replace) 340 | ("" . isearch-exit-at-end) 341 | ([remap isearch-abort] . isearch-cancel) 342 | ("" . isearch-delete-wrong) 343 | ("C-M-w" . isearch-yank-region)) 344 | :hook 345 | (isearch-mode-end . isearch-exit-at-start)) 346 | 347 | (use-package math-delimiters 348 | :vc (:url "https://github.com/oantolin/math-delimiters.git") 349 | :bind 350 | (:map toggle-map 351 | ("$" . math-delimiters-toggle)) 352 | :commands 353 | math-delimiters-no-dollars 354 | math-delimiters-insert) 355 | 356 | (use-package block-undo 357 | :config 358 | (advice-add 'message-insert-signature :around #'block-undo)) 359 | 360 | (use-package help-extras 361 | :bind 362 | ("C-h h" . show-help) 363 | :commands cotd) 364 | 365 | (use-package window-extras 366 | :bind 367 | (:map ctl-x-4-map 368 | ("s" . toggle-window-split) 369 | ("t" . transpose-windows)) 370 | (:map ctl-x-5-map 371 | ("s" . screenshot)) 372 | (:map toggle-map 373 | ("s" . toggle-mode-line))) 374 | 375 | (use-package minibuffer 376 | :bind 377 | (:map minibuffer-local-completion-map 378 | ("" . minibuffer-force-complete) 379 | ("SPC") ("?")) 380 | :custom 381 | (completion-styles '(orderless)) 382 | (completion-category-defaults nil) 383 | (completion-category-overrides '((file (styles basic partial-completion)))) 384 | (read-file-name-completion-ignore-case t) 385 | (read-buffer-completion-ignore-case t) 386 | (completion-ignore-case t) 387 | (enable-recursive-minibuffers t) 388 | (minibuffer-eldef-shorten-default t) 389 | (resize-mini-windows t) 390 | (minibuffer-prompt-properties 391 | '(read-only t cursor-intangible t face minibuffer-prompt)) 392 | :init 393 | (minibuffer-depth-indicate-mode) 394 | (minibuffer-electric-default-mode) 395 | :hook 396 | (minibuffer-setup . cursor-intangible-mode) 397 | :config 398 | (defun stealthily (fn &rest args) 399 | "Apply FN to ARGS while inhibiting modification hooks." 400 | (let ((inhibit-modification-hooks t)) 401 | (apply fn args))) 402 | (advice-add 'minibuf-eldef-setup-minibuffer :around #'stealthily)) 403 | 404 | (use-package orderless 405 | :ensure t 406 | :demand t 407 | :config 408 | (defun prefixes-for-separators (pattern _index _total) 409 | (when (string-match-p "^[^][^\\+*]*[./-][^][\\+*$]*$" pattern) 410 | (cons 'orderless-prefixes pattern))) 411 | (cl-pushnew '(?` . orderless-regexp) orderless-affix-dispatch-alist) 412 | :custom 413 | (orderless-style-dispatchers 414 | '(orderless-affix-dispatch prefixes-for-separators))) 415 | 416 | (use-package vertico 417 | :ensure t 418 | :bind 419 | (:map vertico-map 420 | ("C-" . vertico-really-exit-input) ; must-match is a tyrant 421 | ("DEL" . vertico-directory-delete-char) 422 | ("C-M-d" . consult-dir) 423 | ("C-M-j" . consult-dir-jump-file) 424 | ("M-q" . vertico-quick-exit)) 425 | (:map vertico-multiform-map 426 | ("M-H" . vertico-multiform-flat) ; H for horizontal 427 | ("M-P" . vertico-multiform-buffer)) ; P for panoramic 428 | :custom 429 | (vertico-multiform-categories 430 | '((embark-keybinding grid) 431 | (command flat) 432 | (file grid))) 433 | (vertico-multiform-commands 434 | '((org-set-tags-command grid) 435 | (org-agenda-set-tags grid) 436 | (TeX-command-master flat))) 437 | :init 438 | (vertico-mode) 439 | :config 440 | (defalias 'vertico-really-exit-input #'exit-minibuffer) 441 | (vertico-multiform-mode) ; I use these for text editing 442 | (keymap-unset vertico-multiform-map "M-F") 443 | (keymap-unset vertico-multiform-map "M-B")) 444 | 445 | (use-package marginalia 446 | :ensure t 447 | :bind 448 | (:map minibuffer-local-map 449 | ("M-A" . marginalia-cycle)) 450 | :init 451 | (marginalia-mode) 452 | :config 453 | (defun marginalia--file-owner (attrs) ; Only display UID 454 | "Return file owner given ATTRS." 455 | (let ((uid (file-attribute-user-id attrs))) 456 | (when (/= (user-uid) uid) 457 | (or (user-login-name uid) uid))))) 458 | 459 | (use-package corfu 460 | :ensure t 461 | :bind 462 | (:map corfu-map ("SPC" . corfu-insert-separator)) 463 | :init 464 | (global-corfu-mode)) 465 | 466 | (use-package cape 467 | :ensure t 468 | :bind ("C-" . cape-prefix-map) 469 | :init 470 | (add-hook 'completion-at-point-functions #'cape-dabbrev) 471 | (add-hook 'completion-at-point-functions #'cape-file) 472 | (add-hook 'completion-at-point-functions #'cape-elisp-block)) 473 | 474 | (use-package ecomplete-extras 475 | :bind 476 | ("C-x M" . compose-mail-to) 477 | :commands 478 | add-email-to-ecomplete 479 | remove-email-from-ecomplete) 480 | 481 | (use-package arXiv 482 | :commands arXiv-pdf arXiv-show arXiv-capture 483 | :init 484 | (defvar-keymap arXiv-map 485 | "p" #'arXiv-pdf 486 | "s" #'arXiv-show 487 | "w" #'arXiv-copy-url 488 | "c" #'arXiv-capture) 489 | (fset 'arXiv-map arXiv-map)) 490 | 491 | (use-package scimago 492 | :vc (:url "https://github.com/oantolin/scimago.git") 493 | :commands scimago-copy-quartiles scimago-show-quartiles 494 | :bind 495 | ("C-c q" . scimago-copy-quartiles)) 496 | 497 | (use-package embark 498 | :ensure t 499 | :bind 500 | ("C-." . embark-act) 501 | ("M-." . embark-dwim) 502 | ("C-h b" . embark-bindings) 503 | ("C-h B" . embark-bindings-at-point) 504 | ("C-h M" . embark-bindings-in-keymap) 505 | ("C-h E" . embark-on-last-message) 506 | (:map completion-list-mode-map 507 | ("." . embark-act)) 508 | (:map embark-collect-mode-map 509 | ("a") ; I don't like my own default :) 510 | ("." . embark-act) 511 | ("F" . consult-focus-lines)) 512 | (:map embark-package-map 513 | ("t" . try)) 514 | (:map embark-identifier-map 515 | ("(" . insert-parentheses) 516 | ("[" . insert-pair-map)) 517 | (:map embark-expression-map 518 | ("(" . insert-parentheses) 519 | ("[" . insert-pair-map)) 520 | (:map embark-region-map 521 | ("(" . insert-parentheses) 522 | ("[" . insert-pair-map) 523 | ("=" . quick-calc)) 524 | (:map embark-email-map 525 | ("+" . add-email-to-ecomplete) 526 | ("\\" . remove-email-from-ecomplete)) 527 | (:map embark-encode-map 528 | ("p" . topaz-paste-region)) 529 | (:map embark-url-map 530 | ("a" . arXiv-map)) 531 | (:map embark-general-map 532 | ("D" . gptel-extras-define)) 533 | :custom 534 | (embark-quit-after-action nil) 535 | (prefix-help-command #'embark-prefix-help-command) 536 | (embark-indicators '(embark-minimal-indicator 537 | embark-highlight-indicator 538 | embark-isearch-highlight-indicator)) 539 | (embark-cycle-key ".") 540 | (embark-help-key "?") 541 | (embark-confirm-act-all nil) 542 | :config 543 | ;; want sentence and paragraph targets in more modes 544 | (embark-define-thingatpt-target sentence 545 | text-mode help-mode Info-mode man-common mastodon-mode 546 | lem-mode emacs-news-view-mode) 547 | (embark-define-thingatpt-target paragraph 548 | text-mode help-mode Info-mode man-common mastodon-mode 549 | lem-mode emacs-news-view-mode) 550 | (setq embark-candidate-collectors 551 | (cl-substitute 'embark-sorted-minibuffer-candidates 552 | 'embark-minibuffer-candidates 553 | embark-candidate-collectors)) 554 | (dolist (cmd '(markdown-insert-code 555 | markdown-insert-italic 556 | markdown-insert-bold 557 | cdlatex-math-modify 558 | TeX-font)) 559 | (push #'embark--mark-target (alist-get cmd embark-around-action-hooks))) 560 | (push #'embark--xref-push-marker 561 | (alist-get 'find-file embark-pre-action-hooks)) 562 | (add-to-list 'embark-keymap-alist '(ecomplete . embark-email-map)) 563 | (defun embark-on-last-message (arg) 564 | "Act on the last message displayed in the echo area." 565 | (interactive "P") 566 | (with-current-buffer "*Messages*" 567 | (goto-char (1- (point-max))) 568 | (embark-act arg)))) 569 | 570 | (use-package embark-consult :ensure t :defer t) 571 | 572 | (use-package embark-org 573 | :bind 574 | (:map embark-org-link-map 575 | ("a" . arXiv-map)) 576 | (:map embark-org-heading-map 577 | ("a" . org-archive-subtree-default)) ; skip confirmation 578 | (:map embark-org-src-block-map 579 | ("e" . org-edit-special))) 580 | 581 | (use-package consult-dir 582 | :ensure t 583 | :bind 584 | (:map minibuffer-local-filename-completion-map 585 | ("C-M-d" . consult-dir) 586 | ("C-M-j" . consult-dir-jump-file))) 587 | 588 | (use-package consult 589 | :ensure t 590 | :bind 591 | ("M-y" . consult-yank-pop) 592 | ("M-g l" . consult-line) 593 | ("M-g L" . consult-line-multi) 594 | ("M-g i" . consult-imenu) 595 | ("M-g o" . consult-outline) 596 | ("M-g a" . consult-org-agenda) 597 | ("M-g I" . consult-imenu-multi) 598 | ("M-g m" . consult-mark) 599 | ("M-g k" . consult-global-mark) 600 | ("M-s g" . consult-grep) 601 | ("M-s G" . consult-git-grep) 602 | ("M-s r" . consult-ripgrep) 603 | ("M-s i" . consult-info) 604 | ("M-g f" . consult-find) 605 | ("M-X" . consult-mode-command) 606 | ("C-c b" . consult-buffer) 607 | ("C-c 4 b" . consult-buffer-other-window) 608 | ("M-s K" . consult-keep-lines) 609 | ("M-s F" . consult-focus-lines) 610 | ("C->" . consult-register-store) 611 | ("C-," . consult-register-load) 612 | ("C-M-," . consult-register) 613 | (:map minibuffer-local-map 614 | ("M-h" . consult-history) 615 | ("M-r") ("M-s")) 616 | (:map consult-narrow-map 617 | ("C-<" . consult-narrow-help)) 618 | (:map isearch-mode-map 619 | ("M-g l" . consult-line)) 620 | :custom 621 | (register-preview-function #'consult-register-format) 622 | (consult-narrow-key "<") 623 | (xref-show-xrefs-function #'consult-xref) 624 | (xref-show-definitions-function #'consult-xref) 625 | :hook 626 | ((embark-collect-mode completion-list-mode) . consult-preview-at-point-mode) 627 | :config 628 | (advice-add #'register-preview :override #'consult-register-window) 629 | (setf (alist-get 'log-edit-mode consult-mode-histories) 630 | 'log-edit-comment-ring)) 631 | 632 | (use-package consult-imenu 633 | :defer t 634 | :config 635 | (setf 636 | (alist-get 637 | ?k (plist-get (alist-get 'emacs-lisp-mode consult-imenu-config) :types)) 638 | '("Keymaps" font-lock-variable-name-face))) 639 | 640 | (use-package webjump 641 | :bind 642 | ("C-c w" . webjump)) 643 | 644 | (use-package webjump-extras 645 | :after webjump 646 | :demand t 647 | :bind 648 | ("C-c W" . webjump-reload) 649 | :config 650 | (webjump-reload)) 651 | 652 | (use-package tmp-buffer 653 | :bind ("C-c n" . tmp-buffer)) 654 | 655 | (use-package narrow-extras 656 | :bind 657 | (:map ctl-x-map 658 | ("C-n" . narrow-or-widen-dwim)) 659 | (:map narrow-map 660 | ("s" . narrow-to-sexp) 661 | ("l" . narrow-to-sexp) ; alias for Org mode 662 | ("r" . narrow-to-region) 663 | ("." . narrow-to-point))) 664 | 665 | (use-package beginend 666 | :ensure t 667 | :diminish beginend-global-mode 668 | :config 669 | (dolist (mode beginend-modes) (diminish (cdr mode))) 670 | (beginend-global-mode)) 671 | 672 | (use-package avy 673 | :ensure t 674 | :bind 675 | (("M-j" . avy-goto-char-timer) 676 | ([remap goto-line] . avy-goto-line)) 677 | (:map isearch-mode-map 678 | ("M-q" . avy-isearch)) 679 | :config 680 | (add-to-list 'avy-dispatch-alist '(?\, . avy-action-goto)) 681 | (defun avy-embark-act (pt) 682 | "Use Embark to act on the item at PT." 683 | (unwind-protect 684 | (save-excursion 685 | (goto-char pt) 686 | (embark-act)) 687 | (select-window 688 | (cdr (ring-ref avy-ring 0))) 689 | t)) 690 | (add-to-list 'avy-dispatch-alist '(?\. . avy-embark-act)) 691 | (defun avy-action-exchange (pt) 692 | "Exchange sexp at PT with the one at point." 693 | (set-mark pt) 694 | (transpose-sexps 0)) 695 | (add-to-list 'avy-dispatch-alist '(?e . avy-action-exchange))) 696 | 697 | (use-package link-hint 698 | :ensure t 699 | :bind 700 | ("C-S-j" . link-hint-open-link)) 701 | 702 | (use-package paren 703 | :custom 704 | (show-paren-context-when-offscreen t) 705 | :init 706 | (show-paren-mode)) 707 | 708 | (use-package find-func 709 | :init 710 | (find-function-setup-keys)) 711 | 712 | (use-package text-mode 713 | :hook 714 | (text-mode . variable-pitch-mode) 715 | :config 716 | (modify-syntax-entry ?\" "\"" text-mode-syntax-table)) 717 | 718 | (use-package outline 719 | :defer t 720 | :diminish outline-minor-mode 721 | :hook (prog-mode . outline-minor-mode)) 722 | 723 | (use-package emacs-news-view-mode 724 | :defer t 725 | :config 726 | ;; NEWS files use single quotes around elisp symbols. I think those 727 | ;; are the only files I view in outline-mode, but if I find others 728 | ;; then I might modify the syntax only locally in NEWS files. 729 | (modify-syntax-entry ?' "\"" emacs-news-view-mode-syntax-table)) 730 | 731 | (use-package eldoc :defer t :diminish) 732 | 733 | (use-package diff-mode 734 | :bind 735 | (:map diff-mode-map 736 | ("M-o") 737 | ("v" . vc-next-action) 738 | ("/" . diff-split-hunk))) 739 | 740 | (use-package ediff 741 | :defer t 742 | :custom 743 | (ediff-merge-split-window-function 'split-window-horizontally) 744 | (ediff-split-window-function 'split-window-horizontally) 745 | (ediff-window-setup-function 'ediff-setup-windows-plain)) 746 | 747 | (use-package olivetti 748 | :ensure t 749 | :custom-face 750 | (olivetti-fringe ((t :background unspecified))) 751 | :bind 752 | ("C-c x o" . olivetti-mode)) 753 | 754 | (use-package shr 755 | :bind 756 | (:map shr-map 757 | ("v")) ; don't override view-source with a useless synonym for RET 758 | :custom 759 | (shr-use-colors nil) 760 | (shr-image-animate nil)) 761 | 762 | (use-package shr-heading 763 | :commands 764 | shr-heading-setup-imenu 765 | shr-heading-next 766 | shr-heading-previous) 767 | 768 | (use-package eww 769 | :bind 770 | (:map eww-mode-map 771 | ("{" . backward-paragraph) 772 | ("}" . forward-paragraph) 773 | ("C-c C-p" . shr-heading-previous) 774 | ("C-c C-n" . shr-heading-next)) 775 | :custom 776 | (eww-bookmarks-directory "~/.private/") 777 | :hook 778 | (eww-mode . shr-heading-setup-imenu) 779 | (eww-mode . echo-area-tooltips) 780 | :config 781 | (modify-syntax-entry ?\“ "(”" eww-mode-syntax-table) 782 | (modify-syntax-entry ?\” ")“" eww-mode-syntax-table) 783 | (defun pdfs-are-binary (fn &rest args) 784 | (let ((buffer-file-coding-system 'binary)) 785 | (apply fn args))) 786 | (advice-add 'eww-display-pdf :around #'pdfs-are-binary)) 787 | 788 | (use-package latex 789 | :ensure auctex 790 | :bind 791 | (:map LaTeX-mode-map 792 | ("$" . math-delimiters-insert) 793 | ("C-=" . TeX-font) 794 | ([remap next-error]) 795 | ([remap previous-error]) 796 | ("M-g M-n" . TeX-next-error) 797 | ("M-g M-p" . TeX-previous-error) 798 | ("M-n" . next-error) 799 | ("M-p" . previous-error)) 800 | :custom 801 | (TeX-save-query nil) 802 | (TeX-source-correlate-mode t) 803 | (TeX-source-correlate-start-server t) 804 | :hook 805 | (LaTeX-mode . make-backslash-a-prefix-in-LaTeX) 806 | (LaTeX-mode . turn-on-cdlatex) 807 | (LaTeX-mode . prettify-symbols-mode) 808 | (LaTeX-mode . add-prettify-symbols-hook) 809 | :config 810 | (defun LaTeX-outline-name () 811 | "Guess a name for the current header line." 812 | (save-excursion 813 | (search-forward "{" nil t) 814 | (let ((start (point))) 815 | (backward-char) 816 | (condition-case nil 817 | (with-syntax-table (TeX-search-syntax-table ?\{ ?\}) 818 | (forward-sexp) 819 | (backward-char)) 820 | (error (forward-sentence))) 821 | (replace-regexp-in-string 822 | "[\n\r][ ]+" " " 823 | (buffer-substring start (point)))))) 824 | (defun make-backslash-a-prefix-in-LaTeX () 825 | "Set the syntax class of \\ to ' in LaTeX buffers." 826 | (modify-syntax-entry ?\\ "'" LaTeX-mode-syntax-table)) 827 | (defun toggle-prettify-symbols () 828 | "Toggle `prettify-symbols-mode'." 829 | (prettify-symbols-mode 'toggle)) 830 | (defun add-prettify-symbols-hook () 831 | "Add toggling `prettify-symbols-mode' to local value of `visible-mode-hook'." 832 | (add-hook 'visible-mode-hook 'toggle-prettify-symbols nil t)) 833 | (setcdr (assq 'output-pdf TeX-view-program-selection) 834 | '("PDF Tools"))) 835 | 836 | (use-package cdlatex 837 | :ensure t 838 | :diminish 839 | :diminish org-cdlatex-mode 840 | :bind (:map cdlatex-mode-map ("$") ("(") ("[") ("{")) 841 | :custom 842 | (cdlatex-math-modify-alist '((?B "\\mathbb" nil t nil nil) 843 | (?k "\\mathfrak" nil t nil nil))) 844 | (cdlatex-math-symbol-alist '((?+ "\\cup" "\\oplus" "\\bigoplus") 845 | (?& "\\wedge" "\\cap") 846 | (?* "\\times" "\\otimes") 847 | (?o "\\omega" "\\circ") 848 | (?x "\\chi" "\\xrightarrow")))) 849 | 850 | (use-package reftex 851 | :ensure t 852 | :after latex 853 | :hook (LaTeX-mode . reftex-mode) 854 | :custom 855 | (reftex-plug-into-AUCTeX t) 856 | (reftex-ref-macro-prompt nil) 857 | (reftex-label-alist 858 | '(("theorem" ?T "thm:" "~\\ref{%s}" t ("theorem") -3) 859 | ("lemma" ?L "lem:" "~\\ref{%s}" t ("lemma") -3) 860 | ("proposition" ?P "prop:" "~\\ref{%s}" t ("proposition") -3) 861 | ("corollary" ?C "cor:" "~\\ref{%s}" t ("corollary") -3) 862 | ("remark" ?R "rem:" "~\\ref{%s}" t ("remark") -3) 863 | ("definition" ?D "defn:" "~\\ref{%s}" t ("definition") -3)))) 864 | 865 | (use-package pdf-tools 866 | :ensure t 867 | :bind 868 | (:map pdf-view-mode-map 869 | ("i" . consult-imenu) 870 | ("t" . pdf-view-themed-minor-mode) 871 | ("d" . pdf-view-midnight-minor-mode) 872 | ("s n" . "nsbp") 873 | ([remap scroll-up-command] . pdf-view-scroll-up-or-next-page) 874 | ([remap scroll-down-command] . pdf-view-scroll-down-or-previous-page)) 875 | :init 876 | (pdf-loader-install) 877 | :hook 878 | (pdf-view-mode . pdf-view-themed-minor-mode) 879 | :config 880 | (add-hook 'TeX-after-compilation-finished-functions 881 | #'TeX-revert-document-buffer) 882 | (defun default-1-page (fn prop &optional winprops) 883 | (or (funcall fn prop winprops) 884 | (and (eq prop 'page) 1))) 885 | (advice-add 'image-mode-window-get :around #'default-1-page)) 886 | 887 | (use-package pdf-annot 888 | :defer t 889 | :custom 890 | (pdf-annot-minor-mode-map-prefix "a") 891 | (pdf-annot-list-format '((page . 3) (type . 7) (contents . 200))) 892 | (pdf-annot-activate-created-annotations t)) 893 | 894 | (use-package pdf-outline 895 | :defer t 896 | :custom 897 | (pdf-outline-imenu-use-flat-menus t) 898 | :config 899 | (defun pdf-outline-indent (fn link &optional labels) 900 | (let ((item (funcall fn link labels)) 901 | (indent pdf-outline-buffer-indent)) 902 | (cons (concat (make-string (* indent (1- (alist-get 'depth link))) ?\s) 903 | (car item)) 904 | (cdr item)))) 905 | (advice-add 'pdf-outline-imenu-create-item :around #'pdf-outline-indent)) 906 | 907 | (use-package dired 908 | :bind 909 | (:map dired-mode-map 910 | ("e" . dired-open-externally)) 911 | :custom 912 | (dired-dwim-target t) 913 | (dired-listing-switches "-alGh") 914 | :hook 915 | (dired-mode . dired-hide-details-mode) 916 | :config 917 | (defun dired-open-externally (&optional arg) 918 | "Open marked or current file in operating system's default application." 919 | (interactive "P") 920 | (dired-map-over-marks 921 | (embark-open-externally (dired-get-filename)) 922 | arg))) 923 | 924 | (use-package comint 925 | :custom 926 | (comint-prompt-read-only t) 927 | :bind 928 | (:map comint-mode-map 929 | ("M-h" . consult-history) 930 | ("M-r") ("M-s"))) 931 | 932 | (use-package man-help) 933 | 934 | (use-package eshell-extras 935 | :commands 936 | eshell/in-term 937 | eshell/for-each 938 | interactive-cd) 939 | 940 | (use-package eshell 941 | :bind 942 | ("C-!" . eshell) 943 | :config 944 | (setenv "PAGER" "cat") 945 | (setq eshell-modules-list (delq 'eshell-banner eshell-modules-list))) 946 | 947 | (use-package esh-mode 948 | :bind 949 | (:map eshell-mode-map 950 | ("C-c d" . interactive-cd) 951 | ("M-q" . quit-window) 952 | ([remap display-local-help] . man))) 953 | 954 | (use-package em-hist 955 | :bind 956 | (:map eshell-hist-mode-map 957 | ("M-h" . consult-history) 958 | ("M-r") ("M-s")) 959 | :custom (eshell-hist-ignoredups t)) 960 | 961 | (use-package ffap-eshell :after (eshell ffap)) 962 | 963 | (use-package shell 964 | :bind (:map shell-mode-map 965 | ([remap display-local-help] . man) 966 | ("C-c d" . interactive-cd))) 967 | 968 | (use-package apt-progress 969 | :after (:any eshell shell)) 970 | 971 | (use-package sh-script 972 | :bind (:map sh-mode-map 973 | ([remap display-local-help] . man))) 974 | 975 | (use-package term 976 | :bind 977 | (:map term-mode-map 978 | ("C-c d" . interactive-cd) 979 | ("M-h" . consult-history) 980 | ("M-r") ("M-s")) 981 | (:map term-raw-map 982 | ("C-c d" . interactive-cd) 983 | ("M-h" . consult-history) 984 | ("M-r") ("M-s"))) 985 | 986 | (use-package vc-extras 987 | :commands 988 | clear-log-edit-buffer 989 | log-view-save-commit-hash 990 | vc-git-commit) 991 | 992 | (use-package log-edit 993 | :bind 994 | (:map log-edit-mode-map 995 | ("M-h" . consult-history) 996 | ("M-r") ("M-s")) 997 | :hook 998 | (log-edit-mode . turn-on-auto-fill) 999 | :config 1000 | (advice-add 'consult-history :before #'clear-log-edit-buffer) 1001 | (remove-hook 'log-edit-hook #'log-edit-show-files)) 1002 | 1003 | (use-package log-view 1004 | :bind 1005 | (:map log-view-mode-map 1006 | ("w" . log-view-save-commit-hash))) 1007 | 1008 | (use-package vc 1009 | :bind 1010 | (:map vc-prefix-map 1011 | ("R" . vc-rename-file) 1012 | ("d" . vc-dir-root) 1013 | ("c" . vc-git-commit))) 1014 | 1015 | (use-package vc-dir 1016 | :bind 1017 | (:map vc-dir-mode-map 1018 | ("r" . vc-revert) 1019 | ("c" . vc-git-commit))) 1020 | 1021 | (use-package smerge-mode 1022 | :defer t 1023 | :custom 1024 | (smerge-command-prefix "\C-xc") 1025 | :config 1026 | (named-let process ((keymap smerge-basic-map)) 1027 | (map-keymap 1028 | (lambda (_key cmd) 1029 | (if (keymapp cmd) 1030 | (process cmd) 1031 | (when (consp cmd) 1032 | (setq cmd (cdr cmd))) 1033 | (when (symbolp cmd) 1034 | (put cmd 'repeat-map 'smerge-basic-map)))) 1035 | keymap))) 1036 | 1037 | (use-package magit 1038 | :ensure t 1039 | :bind ("C-x v b d" . magit-branch-delete)) 1040 | 1041 | (use-package markdown-mode 1042 | :ensure t 1043 | :bind 1044 | (:map markdown-mode-map 1045 | ("C-=" . markdown-mode-style-map)) 1046 | :hook 1047 | (markdown-mode . turn-on-visual-line-mode) 1048 | :custom 1049 | (markdown-hide-markup t) 1050 | :custom-face 1051 | (markdown-metadata-key-face ((t (:inherit default)))) 1052 | (markdown-metadata-value-face 1053 | ((t (:inherit default :foreground unspecified)))) 1054 | :config 1055 | (fset 'markdown-mode-style-map markdown-mode-style-map) 1056 | (modify-syntax-entry ?\" "\"" markdown-mode-syntax-table)) 1057 | 1058 | (use-package org 1059 | :ensure t 1060 | :bind 1061 | (("C-c c" . org-capture) 1062 | ("C-c a" . org-agenda) 1063 | ("C-c s" . org-store-link) 1064 | ("C-c C" . org-clock-goto)) 1065 | (:map org-mode-map 1066 | ("C-,") ; I use this for consult-register-load 1067 | ("C-c C-=" . org-cycle-agenda-files) 1068 | ("$" . math-delimiters-insert) 1069 | ("C-$" . ispell-complete-word) 1070 | ("C-=" . org-emphasize) 1071 | ("M-g o" . consult-org-heading) 1072 | ("C-M-" . org-insert-subheading) 1073 | ("C-M-S-" . org-insert-todo-subheading)) 1074 | :custom 1075 | (org-ellipsis "…") 1076 | (org-refile-use-outline-path 'file) 1077 | (org-outline-path-complete-in-steps nil) 1078 | (org-refile-allow-creating-parent-nodes 'confirm) 1079 | (org-startup-indented t) 1080 | (org-highlight-latex-and-related '(latex script entities)) 1081 | (org-export-with-smart-quotes t) 1082 | (org-confirm-babel-evaluate nil) 1083 | (org-export-async-init-file "~/.emacs.d/my-lisp/org-async-init.el") 1084 | (org-special-ctrl-a/e t) 1085 | (org-hide-emphasis-markers t) 1086 | (org-hide-leading-stars t) 1087 | (org-pretty-entities t) 1088 | (org-entities-user '(("newline" "\\newline" nil "
" "\n" "\n" "⏎"))) 1089 | (org-preview-latex-image-directory "~/.cache/ltximg/") 1090 | (org-tags-column 0) 1091 | (org-auto-align-tags nil) 1092 | (org-use-speed-commands t) 1093 | (org-cycle-emulate-tab 'whitestart) 1094 | (org-agenda-skip-timestamp-if-done t) 1095 | (org-agenda-skip-scheduled-if-done t) 1096 | (org-agenda-skip-deadline-if-done t) 1097 | (org-agenda-start-on-weekday nil) 1098 | (org-log-into-drawer t) 1099 | (org-agenda-use-time-grid nil) 1100 | :hook 1101 | (org-mode . turn-on-org-cdlatex) 1102 | (org-mode . ediff-with-org-show-all) 1103 | (org-mode . turn-on-auto-fill) 1104 | (org-mode . org-tweak-syntax-table) 1105 | (org-mode . add-pretty-entities-hook) 1106 | (org-mode . echo-area-tooltips) 1107 | :config 1108 | (cl-pushnew 'org-habit org-modules) 1109 | (defun ediff-with-org-show-all () 1110 | "Expand all headings prior to ediffing org buffers." 1111 | (add-hook 'ediff-prepare-buffer-hook #'org-fold-show-all nil t)) 1112 | (defun add-pretty-entities-hook () 1113 | "Add `org-toggle-pretty-entities' to local value of `visible-mode-hook'." 1114 | (add-hook 'visible-mode-hook 'org-toggle-pretty-entities nil t)) 1115 | (setopt org-structure-template-alist 1116 | (append org-structure-template-alist 1117 | '(("thm" . "theorem") 1118 | ("pf" . "proof") 1119 | ("lem" . "lemma") 1120 | ("cor" . "corollary") 1121 | ("def" . "definition") 1122 | ("rem" . "remark") 1123 | ("exer" . "exercise") 1124 | ("prop" . "proposition") 1125 | ("el" . "src emacs-lisp")))) 1126 | (setopt org-latex-default-packages-alist 1127 | (cl-set-difference org-latex-default-packages-alist 1128 | '("fontenc" "textcomp") 1129 | :test #'equal)) 1130 | (setopt org-latex-packages-alist 1131 | (cons '("AUTO" "babel" t ("pdflatex")) org-latex-packages-alist)) 1132 | (when (executable-find "texi2pdf") 1133 | (setopt org-latex-pdf-process '("texi2pdf %f"))) 1134 | (defun org-tweak-syntax-table () 1135 | (cl-loop for (ch cl) in '((?< ".") (?> ".") (?\\ "'") (?' "'")) 1136 | do (modify-syntax-entry ch cl org-mode-syntax-table))) 1137 | (defun when-in-org-do-as-the-organs-do (fn) 1138 | (if (derived-mode-p 'org-mode) (org-open-at-point) (funcall fn))) 1139 | (advice-add 'org-open-at-point-global 1140 | :around #'when-in-org-do-as-the-organs-do) 1141 | (org-link-set-parameters 1142 | "org-title" 1143 | :store (defun store-org-title-link () 1144 | "Store a link to the org file visited in the current buffer. 1145 | Use the #+TITLE as the link description. The link is only stored 1146 | if `org-store-link' is called from the #+TITLE line." 1147 | (when (and (derived-mode-p 'org-mode) 1148 | (save-excursion 1149 | (beginning-of-line) 1150 | (looking-at "#\\+\\(?:TITLE\\|title\\):"))) 1151 | (org-link-store-props 1152 | :type "file" 1153 | :link (concat "file:" (buffer-file-name)) 1154 | :description (cadar (org-collect-keywords '("TITLE")))))))) 1155 | 1156 | (use-package org-config :after org) ; private package 1157 | 1158 | (use-package org-ql 1159 | :ensure t 1160 | :bind 1161 | ("M-s q" . org-ql-find) 1162 | ("M-s n" . org-ql-find-in-org-directory) 1163 | ("M-s s" . org-ql-search) 1164 | ("M-s v" . org-ql-view)) 1165 | 1166 | (use-package org-ql-usual-files 1167 | :bind 1168 | ("M-s u" . org-ql-usual-files-find) 1169 | ("M-s l" . org-ql-usual-files-open-link)) 1170 | 1171 | (use-package org-ql-completing-read 1172 | :bind 1173 | (:map org-ql-completing-read-map 1174 | ([remap embark-collect]))) 1175 | 1176 | (use-package org-indent 1177 | :bind 1178 | (:map toggle-map 1179 | ("i" . org-indent-mode))) 1180 | 1181 | (use-package org-modern 1182 | :ensure t 1183 | :after org 1184 | :demand t 1185 | :bind 1186 | (:map toggle-map 1187 | ("m" . org-modern-mode)) 1188 | :custom 1189 | (org-modern-star 'replace) 1190 | (org-modern-checkbox '((?X . "☑") 1191 | (?- . #("☐–" 0 2 (composition ((2))))) 1192 | (? . "☐"))) 1193 | :custom-face 1194 | (org-modern-label 1195 | ((t :height 0.8 :width condensed :weight regular 1196 | :underline nil :inherit fixed-pitch))) 1197 | :config 1198 | (global-org-modern-mode)) 1199 | 1200 | (use-package citeproc :ensure t :defer t) 1201 | 1202 | (use-package jinx 1203 | :ensure t 1204 | :diminish 1205 | :hook 1206 | (emacs-startup . global-jinx-mode) 1207 | :custom 1208 | (jinx-languages "en es") 1209 | :bind 1210 | ("M-$" . jinx-correct) 1211 | ("C-M-$" . jinx-languages) 1212 | (:map jinx-overlay-map ; I use M-n & M-p for isearch-{next/previous} 1213 | ("M-n") ("M-p"))) 1214 | 1215 | (use-package try :ensure t :defer t) 1216 | 1217 | (use-package logos 1218 | :ensure t 1219 | :custom 1220 | (logos-outlines-are-pages t) 1221 | :bind 1222 | ([remap forward-page] . logos-forward-page-dwim) 1223 | ([remap backward-page] . logos-backward-page-dwim) 1224 | (:map narrow-map 1225 | ("n" . logos-narrow-dwim))) 1226 | 1227 | (use-package keycast 1228 | :ensure t 1229 | :bind (:map toggle-map 1230 | ("k" . keycast-mode-line-mode) 1231 | ("h" . keycast-header-line-mode)) 1232 | :config 1233 | (defun store-action-key+cmd (cmd) 1234 | (force-mode-line-update t) 1235 | (setq this-command cmd 1236 | keycast--this-command-keys (this-single-command-keys) 1237 | keycast--this-command-desc cmd)) 1238 | (advice-add 'embark-keymap-prompter :filter-return #'store-action-key+cmd) 1239 | (defun force-keycast-update (&rest _) (keycast--update)) 1240 | (advice-add 'embark-act :before #'force-keycast-update)) 1241 | 1242 | ;;; email packages 1243 | 1244 | (use-package email-config) ; private package 1245 | 1246 | (use-package gnus 1247 | :bind 1248 | ("C-c g" . gnus)) 1249 | 1250 | (fset 'goto-map goto-map) 1251 | 1252 | (use-package gnus-group 1253 | :bind 1254 | (:map gnus-group-mode-map 1255 | ("M-g" . goto-map) 1256 | ("M-&") ("M-n") ("M-p") ; Gnus taking over useful keybindings 1257 | ("C-&" . gnus-group-universal-argument) 1258 | ("T" . gnus-group-get-new-news-this-group))) 1259 | 1260 | (use-package gnus-art 1261 | :bind 1262 | (:map gnus-article-mode-map 1263 | ("C-h b") ; come on Gnus, that key binding is sacred! 1264 | ("M-&") ; also pretty important 1265 | ("C-&" . gnus-summary-universal-argument) 1266 | ("M-g" . goto-map) 1267 | ("{" . backward-paragraph) 1268 | ("}" . forward-paragraph))) 1269 | 1270 | (use-package gnus-sum 1271 | :bind 1272 | (:map gnus-summary-mode-map 1273 | ("M-&") ; also pretty important 1274 | ("C-&" . gnus-summary-universal-argument) 1275 | ("M-i") ; I use this for back-to-indentation 1276 | ("M-g" . goto-map) ; rescan is also on Z G, and I use that prefix a lot! 1277 | ("M-a" . gnus-symbolic-argument))) 1278 | 1279 | (use-package ecomplete 1280 | :defer t 1281 | :custom 1282 | (ecomplete-database-file "~/.private/ecompleterc") 1283 | :config 1284 | (setq completion-category-defaults nil)) 1285 | 1286 | (use-package message 1287 | :bind (:map message-mode-map ("M-n")) 1288 | :custom 1289 | (message-signature nil) 1290 | (message-mail-alias-type 'ecomplete) 1291 | (message-self-insert-commands nil) 1292 | (message-expand-name-standard-ui t) 1293 | ;; all-user-mail-addresses-regexp is defined in email-config 1294 | (message-alternative-emails all-user-mail-addresses-regexp) 1295 | (message-fill-column nil) 1296 | :hook 1297 | (message-mode . turn-on-visual-line-mode)) 1298 | 1299 | (use-package message-extras 1300 | :after message 1301 | :bind 1302 | (:map message-mode-map 1303 | ("C-c x e" . cycle-from-address)) 1304 | :commands set-smtp-server 1305 | :hook 1306 | (message-send . set-smtp-server) 1307 | (message-send . message-lint) 1308 | :config 1309 | (defvar-keymap cycle-from-address-repeat-map 1310 | "e" #'cycle-from-address) 1311 | (put 'cycle-from-address 'repeat-map 'cycle-from-address-repeat-map)) 1312 | 1313 | ;;; applications 1314 | 1315 | (use-package sx 1316 | :ensure t 1317 | :defer t 1318 | :init 1319 | (defalias 'sx #'sx-tab-all-questions) 1320 | :custom 1321 | (sx-cache-directory "~/.private/sx") 1322 | :custom-face 1323 | (sx-question-mode-content-face ((t (:background unspecified))))) 1324 | 1325 | (use-package nov :ensure t :mode ("\\.epub\\'" . nov-mode)) 1326 | 1327 | (use-package osm 1328 | :ensure t 1329 | :defer t 1330 | :custom 1331 | (osm-tile-directory "~/.cache/osm")) 1332 | 1333 | (use-package ement 1334 | :ensure t 1335 | :custom 1336 | (ement-notify-notification-predicates nil) ; stop DESKTOP notifications 1337 | (ement-room-send-message-filter #'ement-room-send-org-filter) 1338 | (ement-room-compose-method 'compose-buffer) 1339 | (ement-room-compose-buffer-window-auto-height-min 5) 1340 | :config 1341 | (defun dumb-quotes (fn &rest args) 1342 | (let (org-export-with-smart-quotes) 1343 | (apply fn args))) 1344 | (advice-add #'ement-room-send-org-filter :around #'dumb-quotes) 1345 | :bind 1346 | (:prefix-map global-ement-map :prefix "C-c e" 1347 | ("c" . ement-connect) 1348 | ("d" . ement-disconnect) 1349 | ("l" . ement-room-list) 1350 | ("r" . ement-view-room) 1351 | ("k" . ement-kill-buffers) 1352 | ("n" . ement-notify-switch-to-notifications-buffer) 1353 | ("m" . ement-notify-switch-to-mentions-buffer)) 1354 | (:map ement-room-mode-map ; One of my keyboards has no 1355 | ("e" . ement-room-dispatch-edit-message) 1356 | ("p" . ement-room-goto-prev) 1357 | ("n" . ement-room-goto-next) 1358 | ("{" . backward-paragraph) 1359 | ("}" . forward-paragraph) 1360 | ("<" . beginning-of-buffer) 1361 | (">" . end-of-buffer))) 1362 | 1363 | (use-package mastodon 1364 | :ensure t 1365 | :bind 1366 | (:prefix-map global-mastodon-map :prefix "C-c m" 1367 | ("h" . mastodon) 1368 | ("t" . mastodon-toot) 1369 | ("n" . mastodon-notifications-get) 1370 | ("b" . mastodon-profile-view-bookmarks) 1371 | ("p" . mastodon-profile-my-profile) 1372 | ("l" . mastodon-views-view-list-timeline) 1373 | ("s" . mastodon-search-query) 1374 | ("u" . mastodon-url-lookup) 1375 | ("L" . mastodon-views-view-lists) 1376 | ("@" . mastodon-notifications-get-mentions)) 1377 | (:map mastodon-mode-map 1378 | ("{" . backward-paragraph) 1379 | ("}" . forward-paragraph)) 1380 | :hook 1381 | (mastodon-mode . mastodon-async-mode) 1382 | (mastodon-mode . mastodon-recenter-positions) 1383 | (mastodon-mode . variable-pitch-mode) 1384 | (mastodon-toot-mode . turn-on-visual-line-mode) 1385 | :config 1386 | (defun mastodon-recenter-positions () 1387 | (setq-local recenter-positions '(bottom middle top)))) 1388 | 1389 | (use-package lem 1390 | :ensure t 1391 | :bind 1392 | ("C-c l" . lem) 1393 | (:map lem-mode-map 1394 | ("{" . backward-paragraph) 1395 | ("}" . forward-paragraph))) 1396 | 1397 | (use-package fedi-config :after (:any mastodon lem)) ; private package 1398 | 1399 | ;;; major modes for programming languages 1400 | 1401 | (use-package elisp-mode 1402 | :config 1403 | (add-to-list 1404 | 'lisp-imenu-generic-expression 1405 | '("Keymaps" "^\\s-*(defvar-keymap\\s-+\\(\\(?:\\sw\\|\\s_\\)+\\)" 1) 1406 | t)) 1407 | 1408 | (use-package python 1409 | :defer t 1410 | :custom 1411 | (python-shell-interpreter "python3")) 1412 | 1413 | (use-package scheme 1414 | :defer t 1415 | :custom 1416 | (scheme-program-name "guile")) 1417 | 1418 | (use-package cperl-mode 1419 | :mode "\\.\\([pP]\\([Llm]\\|erl\\|od\\)\\|al\\)\\'" 1420 | :interpreter "\\(mini\\)?perl5?" 1421 | :bind 1422 | (:map cperl-mode-map 1423 | ([remap display-local-help] . cperl-perldoc))) 1424 | 1425 | (use-package sly 1426 | :ensure t 1427 | :bind 1428 | (:map sly-editing-mode-map 1429 | ([remap display-local-help] . sly-describe-symbol) 1430 | ([remap embark-pp-eval-defun] . sly-compile-defun) 1431 | ([remap pp-macroexpand-expression] . sly-expand-1) 1432 | ([remap pp-eval-expression] . sly-interactive-eval) 1433 | ([remap xref-find-definitions] . sly-edit-definition)) 1434 | (:map sly-prefix-map 1435 | ("I") ("E") ; C-c LETTER are reserved! 1436 | ("C-v i" . sly-inspect) 1437 | ("C-v e" . sly-edit-value)) 1438 | :custom 1439 | (inferior-lisp-program "sbcl") 1440 | :hook 1441 | (sly-mode . turn-off-sly-symbol-completion-mode) 1442 | (sly-connected . increase-elision-length) 1443 | :config 1444 | (defun turn-off-sly-symbol-completion-mode () 1445 | (sly-symbol-completion-mode -1)) 1446 | (defun increase-elision-length () 1447 | (sly-eval 1448 | '(cl:setf (cl:cdr (cl:assoc 'slynk:*string-elision-length* 1449 | slynk:*slynk-pprint-bindings*)) 1450 | 1000)))) 1451 | 1452 | (use-package sly-mrepl 1453 | :bind 1454 | (:map sly-mrepl-mode-map 1455 | ([remap display-local-help] . sly-describe-symbol) 1456 | ([remap embark-pp-eval-defun] . sly-compile-defun) 1457 | ([remap pp-macroexpand-expression] . sly-expand-1) 1458 | ([remap pp-eval-expression] . sly-interactive-eval) 1459 | ([remap xref-find-definitions] . sly-edit-definition) 1460 | ("M-h" . consult-history))) 1461 | 1462 | (use-package sly-package-fu 1463 | :bind 1464 | (:map sly-prefix-map 1465 | ("C-s i" . sly-import-symbol-at-point) 1466 | ("C-s x" . sly-export-symbol-at-point)) 1467 | :config 1468 | (defun sly-package-fu-fix-keys () ; C-c LETTER are reserved! 1469 | (keymap-unset sly-mode-map "C-c i") 1470 | (keymap-unset sly-mode-map "C-c x")) 1471 | (advice-add 'sly-package-fu-init :after #'sly-package-fu-fix-keys)) 1472 | 1473 | (use-package sly-trace-dialog 1474 | :bind 1475 | (:map sly-trace-dialog-shortcut-mode-map 1476 | ("C-c T") ; C-c LETTER are reserved! 1477 | ("C-c M-t" . sly-trace-dialog) 1478 | ("C-c C-M-t" . sly-toggle-fancy-trace))) 1479 | 1480 | (use-package j-mode 1481 | :ensure t 1482 | :mode "\\.j\\'" 1483 | :bind 1484 | ("C-c j" . j-console) 1485 | (:map j-mode-map 1486 | ([remap display-local-help] . j-help-lookup-symbol)) 1487 | :custom-face 1488 | (j-verb-face 1489 | ((t (:foreground unspecified :inherit font-lock-function-name-face)))) 1490 | (j-other-face ((t (:foreground unspecified :inherit font-lock-keyword-face)))) 1491 | (j-adverb-face ((t (:foreground unspecified :inherit font-lock-type-face)))) 1492 | (j-conjunction-face 1493 | ((t (:foreground unspecified :inherit font-lock-preprocessor-face))))) 1494 | 1495 | (use-package ngnk-cli 1496 | :vc (:url "https://github.com/oantolin/ngnk-mode.git") 1497 | :bind 1498 | ("C-c k" . run-ngnk) 1499 | :custom 1500 | (ngnk-mark-line-continuations t)) 1501 | 1502 | (use-package ngnk-mode 1503 | :vc (:url "https://github.com/oantolin/ngnk-mode.git") 1504 | :mode "\\.k\\'" 1505 | :hook 1506 | (ngnk-mode . require-final-newline) 1507 | :config 1508 | (defun require-final-newline () 1509 | (setq-local require-final-newline t))) 1510 | 1511 | (use-package bqn-mode 1512 | :ensure t 1513 | :bind 1514 | ("C-c B" . bqn-comint-bring) 1515 | (:map bqn-mode-map 1516 | ("C-c C-c" . bqn-comint-eval-dwim) 1517 | ("C-c C-b" . bqn-comint-eval-buffer) 1518 | ("C-c C-s" . bqn-comint-send-dwim) 1519 | ("C-c C-q" . bqn-glyph-mode-show-glyphs) 1520 | ("C-c C-k" . bqn-keymap-mode-show-keyboard) 1521 | ([remap display-local-help] . bqn-help-symbol-info-at-point)) 1522 | (:map bqn-comint-mode-map 1523 | ("C-c C-q" . bqn-glyph-mode-show-glyphs) 1524 | ("C-c C-k" . bqn-keymap-mode-show-keyboard) 1525 | ([remap display-local-help] . bqn-help-symbol-info-at-point)) 1526 | :custom 1527 | (bqn-interpreter-arguments 1528 | (list "-e" 1529 | (format "BQNLib ⇐ {𝕨•Import%S•file.At𝕩∾%S}" 1530 | (expand-file-name "~/code/bqn-libs") ".bqn") 1531 | "-r")) 1532 | :hook 1533 | (bqn-comint-mode . use-bqn--eldoc) 1534 | (bqn-comint-mode . turn-on-eldoc-mode) 1535 | :config 1536 | (defun use-bqn--eldoc () 1537 | (setq-local eldoc-documentation-function #'bqn--eldoc))) 1538 | 1539 | (use-package gap-mode 1540 | :ensure t 1541 | :custom 1542 | (gap-executable "/usr/bin/gap")) 1543 | -------------------------------------------------------------------------------- /my-lisp/among.el: -------------------------------------------------------------------------------- 1 | ;;; among.el --- Words among letters -*- lexical-binding: t; -*- 2 | 3 | (defun among (letters) 4 | (interactive "sAmong: ") 5 | (save-excursion 6 | (goto-char (point-min)) 7 | (with-output-to-temp-buffer "*among*" 8 | (while (search-forward-regexp (format "^[%s]+$" letters) nil t) 9 | (let ((word (buffer-substring-no-properties 10 | (save-excursion 11 | (beginning-of-line) 12 | (point)) 13 | (point)))) 14 | (when (cl-loop for c across word 15 | always (<= (cl-count c word) 16 | (cl-count c letters))) 17 | (princ (format "%2d %s\n" (length word) word))))))) 18 | (with-current-buffer "*among*" 19 | (let ((inhibit-read-only t)) 20 | (sort-numeric-fields 1 (point-min) (point-max)) 21 | (reverse-region (point-min) (point-max)) 22 | (set-buffer-modified-p nil))) 23 | (pop-to-buffer "*among*")) 24 | 25 | (provide 'among) 26 | -------------------------------------------------------------------------------- /my-lisp/aoc.el: -------------------------------------------------------------------------------- 1 | ;;; aoc.el --- Download AoC puzzle inputs -*- lexical-binding: t; -*- 2 | 3 | ;; This is function is based on the function from this blog post: 4 | ;; https://arjenwiersma.nl/posts/2021-11-13-Advent-of-Code-Helper/ 5 | 6 | ;; In particular, I had no idea what a session cookie was before 7 | ;; reading that! 8 | 9 | (require 'url) 10 | 11 | (defun aoc (year day) 12 | "Download AoC input for DAY of YEAR; open the puzzle in a browser. 13 | DAY and YEAR default to the current date during December, but 14 | outside December, or with a prefix argument, you are prompted for 15 | them." 16 | (interactive 17 | (let ((today (decode-time nil "EST"))) 18 | (if (or current-prefix-arg 19 | (not (= (decoded-time-month today) 12))) 20 | (mapcar #'read-number '("Year: " "Day: ")) 21 | (list (decoded-time-year today) (decoded-time-day today))))) 22 | (let ((url-request-extra-headers 23 | `(("Cookie" 24 | . ,(concat "session=" 25 | (funcall 26 | (plist-get 27 | (car (auth-source-search :host "adventofcode.com")) 28 | :secret)))))) 29 | (url (format "https://adventofcode.com/%d/day/%d" year day))) 30 | (with-temp-file (format "~/code/advent-of-code/%d/day%02d.txt" year day) 31 | (url-insert-file-contents (concat url "/input"))) 32 | (browse-url url))) 33 | 34 | (provide 'aoc) 35 | -------------------------------------------------------------------------------- /my-lisp/apt-progress.el: -------------------------------------------------------------------------------- 1 | ;;; apt-progress.el --- Render apt progress bars in eshell buffers -*- lexical-binding: t; -*- 2 | 3 | (require 'ansi-color) 4 | 5 | (defun apt-progress-fix (begin end &optional _) 6 | (let ((end (copy-marker end))) 7 | (save-excursion 8 | (goto-char begin) 9 | (while (search-forward-regexp "\^[7\\(.*?\\)\^[8" end t) 10 | (let ((text (match-string 1))) 11 | (replace-match "") 12 | (when (string-match "Progress: \\[ *[0-9]+%\\]" text) 13 | (message "%s" (match-string 0 text)))))))) 14 | 15 | (advice-add 'ansi-color-apply-on-region :before 'apt-progress-fix) 16 | 17 | (provide 'apt-progress) 18 | 19 | -------------------------------------------------------------------------------- /my-lisp/arXiv.el: -------------------------------------------------------------------------------- 1 | ;; arXiv.el --- Misc commands for arXiv -*- lexical-binding: t; -*- 2 | 3 | (defun arXiv--id (paper) 4 | "Return the arXiv id of PAPER. 5 | A ragtag bunch of ad hoc formats are recognized for PAPER." 6 | (setq paper (replace-regexp-in-string "_" "." paper)) ; Mastodon 7 | (cl-loop for pattern in 8 | '("^%s$" 9 | "^arXiv:%s\\(?:/.*\\)?$" 10 | "^https?://arxiv.org/\\(?:abs\\|pdf\\)/%s\\(?:\\.pdf\\)?$" 11 | "^https://mathstodon.xyz/tags/arXiv.%s$") 12 | when (string-match (format pattern "\\([0-9v.]+\\)") paper) 13 | return (match-string 1 paper))) 14 | 15 | (defun arXiv-pdf (paper &optional external) 16 | "Open the PDF version of PAPER from the arXiv in Emacs. 17 | A ragtag bunch of ad hoc formats are recognized for PAPER. 18 | If EXTERNAL is non-nil use an external browser." 19 | (interactive "sarXiv paper: \nP") 20 | (when (and (not external) (fboundp 'pdf-tools-install)) 21 | (pdf-tools-install)) 22 | (let ((pdf (format "https://arxiv.org/pdf/%s.pdf" (arXiv--id paper)))) 23 | (if external 24 | (browse-url pdf) 25 | (eww pdf)))) 26 | 27 | (defun arXiv-copy-url (paper) 28 | "Copy PAPER's URL to the kill-ring." 29 | (interactive "sarXiv paper: ") 30 | (kill-new (format "https://arxiv.org/abs/%s" (arXiv--id paper)))) 31 | 32 | (defvar url-http-end-of-headers) 33 | 34 | (require 'dom) 35 | 36 | (defun arXiv--with-metadata (paper fn) 37 | "Download and parse arXiv metadata for PAPER. 38 | Call FN on an alist with keys `title', `authors', `url', `id' and 39 | `abstract'." 40 | (url-retrieve 41 | (format "http://export.arxiv.org/api/query?id_list=%s" (arXiv--id paper)) 42 | (lambda (_) 43 | (goto-char url-http-end-of-headers) 44 | (forward-line 2) 45 | (let* ((xml (libxml-parse-xml-region (point))) 46 | (entry (car (dom-by-tag xml 'entry))) 47 | (title (dom-text (dom-by-tag entry 'title))) 48 | (abstract (dom-text (dom-by-tag entry 'summary))) 49 | (authors (mapcar #'dom-texts (dom-by-tag entry 'author))) 50 | (url (dom-text (dom-by-tag entry 'id))) 51 | (id (string-remove-prefix "http://arxiv.org/abs/" url))) 52 | (funcall fn `((title . ,title) (authors . ,authors) 53 | (abstract . ,abstract) (url . ,url) (id . ,id))))))) 54 | 55 | (defun arXiv-show (paper) 56 | "Popup a buffer with the title, authors and abstract of PAPER. 57 | A ragtag bunch of ad hoc formats are recognized for PAPER." 58 | (interactive "sarXiv paper: ") 59 | (arXiv--with-metadata 60 | paper 61 | (lambda (data) 62 | (let-alist data 63 | (with-output-to-temp-buffer (format "*arXiv:%s*" .id) 64 | (princ (format "%s\nBy: %s\n\n%s\n" 65 | .title (string-join .authors ", ") .abstract))))))) 66 | 67 | (defun arXiv-capture (paper) 68 | "Capture Org entry for arXiv PAPER at point. 69 | (This depends on the `org-capture' template \"a\" just inserting 70 | the kill ring head. 😬)" 71 | (interactive "sarXiv paper: ") 72 | (arXiv--with-metadata 73 | paper 74 | (lambda (data) 75 | (let-alist data 76 | (kill-new (format "[[%s][%s]]\nBy: %s\n\n%s\n" 77 | .url .title (string-join .authors ", ") .abstract))) 78 | (org-capture nil "a")))) 79 | 80 | (provide 'arXiv) 81 | -------------------------------------------------------------------------------- /my-lisp/block-undo.el: -------------------------------------------------------------------------------- 1 | ;;; block-undo.el --- Undo keyboard macros in a single step -*- lexical-binding: t; -*- 2 | 3 | (defun block-undo (fn &rest args) 4 | "Apply FN to ARGS in such a way that it can be undone in a single step." 5 | (undo-boundary) 6 | (with-undo-amalgamate 7 | (apply fn args))) 8 | 9 | (dolist (fn '(kmacro-call-macro 10 | kmacro-exec-ring-item 11 | apply-macro-to-region-lines)) 12 | (advice-add fn :around #'block-undo)) 13 | 14 | (provide 'block-undo) 15 | -------------------------------------------------------------------------------- /my-lisp/calc-exchange-rates.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | (defun calc-exchange-rates () 4 | "Look up exchange rates online and set them up as calc units." 5 | (with-current-buffer 6 | (url-retrieve-synchronously "https://api.openrates.io/latest") 7 | (let* ((exch (json-read)) 8 | (base (intern (cdr (assq 'base exch))))) 9 | (setq math-additional-units 10 | (cons 11 | (list base nil "Base currency") 12 | (cl-loop for (curr . rate) in (cdr (assq 'rates exch)) 13 | collect 14 | (list curr (format "%f %s" (/ rate) base) 15 | (format "Exchange rate %s=>%s" curr base)))) 16 | math-units-table nil)))) 17 | 18 | (provide 'calc-exchange-rates) 19 | -------------------------------------------------------------------------------- /my-lisp/calc-rref.el: -------------------------------------------------------------------------------- 1 | ;;; calc-rref.el --- Row-reduce echelon form for Calc -*- lexical-binding: t; -*- 2 | 3 | (require 'calc-mtx) 4 | 5 | (defun calc-rref (arg) 6 | "Compute the reduce row echelon form of a matrix" 7 | (interactive "P") 8 | (calc-slow-wrapper 9 | (calc-unary-op "rref" 'calcFunc-rref arg))) 10 | 11 | (defun calcFunc-rref (m) 12 | "Compute the reduce row echelon form of a matrix" 13 | (if (math-matrixp m) 14 | (math-with-extra-prec 2 (rref-raw m)) 15 | (math-reject-arg m 'matrixp))) 16 | 17 | ;; Algorithm from http://rosettacode.org/wiki/Reduced_row_echelon_form 18 | (defun rref-raw (orig-m) 19 | (let* ((m (math-copy-matrix orig-m)) 20 | (rows (1- (length m))) 21 | (cols (1- (length (nth 1 m)))) 22 | (lead 1) 23 | (r 1)) 24 | (catch 'done 25 | (while (and (<= r rows) (<= lead cols)) 26 | (let ((i r)) 27 | (while (math-zerop (nth lead (nth i m))) 28 | (setq i (1+ i)) 29 | (when (> i rows) 30 | (setq i r lead (1+ lead)) 31 | (when (> lead cols) (throw 'done m)))) 32 | (setq m (math-swap-rows m i r)) 33 | (let ((pivot (nth lead (nth r m))) (i 1)) 34 | (unless (math-zerop pivot) 35 | (let ((j lead)) 36 | (while (<= j cols) 37 | (setcar (nthcdr j (nth r m)) 38 | (math-div (nth j (nth r m)) pivot)) 39 | (setq j (1+ j))))) 40 | (while (<= i rows) 41 | (unless (= i r) 42 | (let ((j lead) (c (nth lead (nth i m)))) 43 | (while (<= j cols) 44 | (setcar (nthcdr j (nth i m)) 45 | (math-sub (nth j (nth i m)) 46 | (math-mul c (nth j (nth r m))))) 47 | (setq j (1+ j))))) 48 | (setq i (1+ i))))) 49 | (setq r (1+ r) lead (1+ lead))) 50 | m))) 51 | 52 | (bind-keys 53 | :map calc-mode-map 54 | ("v !" . calc-rref) 55 | ("V !" . calc-rref)) 56 | 57 | (provide 'calc-rref) 58 | -------------------------------------------------------------------------------- /my-lisp/calc-simplex.el: -------------------------------------------------------------------------------- 1 | ;;; calc-simplex.el --- Simplex method for Calc -*- lexical-binding: t; -*- 2 | 3 | (defun display-index (i cols phase-1) 4 | (cond 5 | ((not phase-1) i) 6 | ((eq i 'vec) 'vec) 7 | ((= i cols) 0) 8 | ((> i cols) (- i 1)) 9 | (t i)) ) 10 | 11 | (defun actual-basis (basis cols phase-1) 12 | (mapcar 13 | (lambda (i) 14 | (cond 15 | ((not phase-1) i) 16 | ((eq i 'vec) 'vec) 17 | ((= i 0) cols) 18 | ((>= i cols) (+ i 1)) 19 | (t i))) 20 | basis)) 21 | 22 | (defmath dict ((matrixp A) (vectorp b) (vectorp c) 23 | (vectorp basis) &optional phase-1) 24 | "Dictionary for basis variables with indices in `basis'" 25 | (interactive "p" 4 "dict") 26 | (setq basis (actual-basis basis (subscr (mdims A) 2) phase-1)) 27 | (message "Actual basis: %s" basis) 28 | (let* ((big-A (trn (append (trn A) (idn 1 (subscr (mdims A) 1))))) 29 | (n (subscr (mdims big-A) 2)) 30 | (B (mcol big-A basis))) 31 | (let ((B-1 (inv B)) 32 | (non-basis (vdiff (index n) basis)) 33 | (cc (append c (cvec 0 (- n (vlen c)))))) 34 | (let ((A_N (mcol big-A non-basis)) 35 | (c_B (mrow cc basis)) 36 | (c_N (mrow cc non-basis))) 37 | (vconcat 38 | (* B-1 (trn (cons b (trn (- A_N))))) 39 | (list 40 | (let ((c_B*B-1 (* c_B B-1))) 41 | (vconcat 42 | (* c_B*B-1 b) 43 | (- c_N (* c_B*B-1 A_N)))))))))) 44 | 45 | (defmath pivot ((vectorp basis) (natnum enters) (natnum exits)) 46 | "Replace `exits' with `enters' in `basis'" 47 | (interactive 3 "pivot") 48 | (vunion (vdiff basis (cvec exits 1)) (cvec enters 1))) 49 | 50 | (defun format-pos (a) 51 | (pcase a 52 | (`(frac ,p ,q) (format "\\frac{%s}{%s}" p q)) 53 | (`(float ,x ,n) (format "%g" (* x (expt 10 n)))) 54 | (_ (format "%s" a)))) 55 | 56 | (defun format-val (a) 57 | (if (math-zerop a) 58 | "" 59 | (format "%s%s" 60 | (if (math-negp a) "-" "") 61 | (format-pos (math-abs a))))) 62 | 63 | (defun format-term (a j) 64 | (if (math-zerop a) 65 | "& " 66 | (format "& %s%s x_%d" 67 | (if (math-negp a) "-" "+") 68 | (if (math-equal (math-abs a) 1) 69 | "" 70 | (format-pos (math-abs a))) 71 | j))) 72 | 73 | (defmath latexdict ((matrixp dict) (vectorp basis) &optional phase-1) 74 | "Render a dictionary in LaTeX" 75 | (interactive "p" 2 "latexdict") 76 | (let ((m (- (vlen dict) 1)) 77 | (n (- (vlen (mrow dict 1)) 1))) 78 | (setq basis (actual-basis basis n phase-1)) 79 | (let ((non-basis (vdiff (index (+ m n)) basis))) 80 | (let ((s (with-output-to-string 81 | (princ "\\[\\begin{array}{*{10}{r}}\n") 82 | (for ((i 1 (+ m 1))) 83 | (princ 84 | (format 85 | "%s = & %s " 86 | (if (= i (+ m 1)) 87 | (if phase-1 " w " " z ") 88 | (format "x_%d" 89 | (display-index (subscr basis i) n phase-1))) 90 | (format-val (subscr dict i 1)))) 91 | (for ((j 1 n)) 92 | (unless (and phase-1 (= (subscr non-basis j) n)) 93 | (princ 94 | (format-term 95 | (subscr dict i (+ j 1)) 96 | (display-index (subscr non-basis j) n phase-1))))) 97 | (when phase-1 98 | (let ((j (cl-position n non-basis))) 99 | (when j 100 | (princ (format-term (subscr dict i (+ j 1)) 0))))) 101 | (princ "\\\\\n")) 102 | (princ "\\end{array}\\]")))) 103 | (kill-new s) 104 | 'ok)))) 105 | 106 | (bind-keys 107 | :map calc-mode-map 108 | ("z d" . calc-dict) 109 | ("z p" . calc-pivot) 110 | ("z l" . calc-latexdict)) 111 | 112 | (provide 'calc-simplex) 113 | -------------------------------------------------------------------------------- /my-lisp/ecomplete-extras.el: -------------------------------------------------------------------------------- 1 | ;;; ecomplete-extras.el --- A few extra function for ecomplete -*- lexical-binding: t; -*- 2 | 3 | (require 'ecomplete) 4 | 5 | (defun email--name+address (email) 6 | "Return a pair of the name and address for an EMAIL." 7 | (let (name) 8 | (when (string-match "^\\(?:\\(.*\\) \\)?<\\(.*\\)>$" email) 9 | (setq name (match-string 1 email) 10 | email (match-string 2 email))) 11 | (cons name email))) 12 | 13 | (defun ecomplete--read-address () 14 | "Read an email address with completion." 15 | (unless ecomplete-database (ecomplete-setup)) 16 | (completing-read "Email address: " (ecomplete-completion-table 'mail))) 17 | 18 | (defun add-email-to-ecomplete (email) 19 | "Add email address to ecomplete's database." 20 | (interactive "sEmail address: ") 21 | (pcase-let ((`(,name . ,email) (email--name+address email))) 22 | (unless name (setq name (read-string "Name: "))) 23 | (ecomplete-add-item 24 | 'mail email 25 | (format (cond ((equal name "") "%s%s") 26 | ((string-match-p "^\\(?:[A-Za-z0-9 ]*\\|\".*\"\\)$" name) 27 | "%s <%s>") 28 | (t "\"%s\" <%s>")) 29 | name email)) 30 | (ecomplete-save))) 31 | 32 | (defun remove-email-from-ecomplete (email) 33 | "Remove email address from ecomplete's database." 34 | (interactive (list (ecomplete--read-address))) 35 | (when-let ((email (cdr (email--name+address email))) 36 | (entry (ecomplete-get-item 'mail email))) 37 | (setf (cdr (assq 'mail ecomplete-database)) 38 | (remove entry (cdr (assq 'mail ecomplete-database)))) 39 | (ecomplete-save))) 40 | 41 | (defun compose-mail-to (address) 42 | "Compose email to ADDRESS from ecomplete's database." 43 | (interactive (list (ecomplete--read-address))) 44 | (compose-mail address)) 45 | 46 | (provide 'ecomplete-extras) 47 | -------------------------------------------------------------------------------- /my-lisp/eshell-extras.el: -------------------------------------------------------------------------------- 1 | ;; eshell-extras.el --- Miscellaneous eshell commands -*- lexical-binding: t; -*- 2 | 3 | (defun interactive-cd (dir) 4 | "Prompt for a directory and cd to it." 5 | (interactive "Dcd ") 6 | (let ((inhibit-read-only t)) 7 | (insert (concat "cd " (shell-quote-argument dir)))) 8 | (pcase major-mode 9 | ('shell-mode (comint-send-input)) 10 | ('eshell-mode (eshell-send-input)) 11 | ('term-mode (term-send-input)))) 12 | 13 | (defun eshell/for-each (cmd &rest args) 14 | "Run command once for each argument." 15 | (let ((fn (intern cmd)) 16 | (dir default-directory)) 17 | (dolist (arg (flatten-tree args)) 18 | (let ((default-directory dir)) 19 | (funcall fn arg))))) 20 | 21 | (defun eshell/in-term (prog &rest args) 22 | "Run shell command in term buffer." 23 | (switch-to-buffer (apply #'make-term prog prog nil args)) 24 | (term-mode) 25 | (term-char-mode)) 26 | 27 | ;; The built-in version of the following function doesn't ensure you 28 | ;; get an eshell buffer in the right directory! 29 | (defun eshell-bookmark-jump (bookmark) 30 | "Default bookmark handler for Eshell buffers." 31 | (let ((default-directory (bookmark-prop-get bookmark 'location))) 32 | (eshell '(4)))) 33 | 34 | (provide 'eshell-extras) 35 | -------------------------------------------------------------------------------- /my-lisp/eval-region-advice.el: -------------------------------------------------------------------------------- 1 | ;;; eval-region-advice.el --- Advise eval commands to use region if active -*- lexical-binding: t; -*- 2 | 3 | (defun eval-region-if-active (arg) 4 | "Advice for evaluation commands, to have them call 5 | `eval-region' when the region is activate." 6 | (when (use-region-p) 7 | (eval-region (region-beginning) (region-end) arg) 8 | (deactivate-mark) 9 | t)) 10 | 11 | (dolist (fn '(eval-print-last-sexp eval-last-sexp eval-defun)) 12 | (advice-add fn :before-until #'eval-region-if-active)) 13 | 14 | (provide 'eval-region-advice) 15 | -------------------------------------------------------------------------------- /my-lisp/ffap-eshell.el: -------------------------------------------------------------------------------- 1 | ;;; ffap-eshell.el --- Extra rules for ffap -*- lexical-binding: t; -*- 2 | 3 | ;;; Improved directory guessing in eshell buffers: 4 | 5 | (require 'ffap) 6 | (require 'eshell) 7 | 8 | (defun guess-directory-from-face () 9 | (save-excursion 10 | (backward-paragraph) 11 | (forward-char) 12 | (when (eq (get-text-property (point) 'face) 'eshell-ls-directory) 13 | (buffer-substring-no-properties 14 | (point) (next-single-property-change (point) 'face))))) 15 | 16 | (defun guess-directory-from-prompt () 17 | (save-excursion 18 | (eshell-previous-prompt 1) 19 | (end-of-line) 20 | (thing-at-point 'filename))) 21 | 22 | (defun ffap-eshell-mode (name) 23 | (seq-find #'file-exists-p 24 | (mapcar (lambda (dir) (expand-file-name name dir)) 25 | (delq nil (list default-directory 26 | (guess-directory-from-face) 27 | (guess-directory-from-prompt)))))) 28 | 29 | (setf (alist-get 'eshell-mode ffap-alist) #'ffap-eshell-mode) 30 | 31 | (provide 'ffap-eshell) 32 | -------------------------------------------------------------------------------- /my-lisp/gptel-extras.el: -------------------------------------------------------------------------------- 1 | ;;; gptel-extras.el --- Other commands to use LLMs -*- lexical-binding: t; -*- 2 | 3 | (require 'gptel-context) 4 | 5 | (defun gptel-extras--show-response (response info) 6 | "Display RESPONSE in the echo area or a buffer, depending on length. 7 | 8 | For use as a `:callback' with `gptel-request'." 9 | (cond 10 | ((not response) 11 | (message "gptel-mini failed with message: %s" (plist-get info :status))) 12 | ((< (length response) 250) 13 | (message "%s" response)) 14 | (t (pop-to-buffer (get-buffer-create "*gptel-mini*")) 15 | (let ((inhibit-read-only t)) 16 | (erase-buffer) 17 | (insert response) 18 | (fill-region (point-min) (point-max))) 19 | (goto-char (point-min)) 20 | (special-mode)))) 21 | 22 | (defun gptel-extras-mini (prompt) 23 | "Display the LLM's response to PROMPT. 24 | If the region is active, it is included as context. If the response is 25 | short, it is shown in the echo area; otherwise, it is displayed in a 26 | buffer." 27 | (interactive "sAsk LLM: ") 28 | (when (string= prompt "") (user-error "A prompt is required.")) 29 | (when (use-region-p) 30 | (gptel-context--add-region 31 | (current-buffer) (region-beginning) (region-end))) 32 | (gptel-request prompt :callback #'gptel-extras--show-response)) 33 | 34 | (defun gptel-extras-define (term) 35 | "Use an LLM to define a TERM." 36 | (interactive "sLookup: ") 37 | (when (string= term "") (user-error "A term to define is required.")) 38 | (gptel-request (format "Explain this very briefly: %S" term) 39 | :callback #'gptel-extras--show-response)) 40 | 41 | (provide 'gptel-extras) 42 | -------------------------------------------------------------------------------- /my-lisp/help-extras.el: -------------------------------------------------------------------------------- 1 | ;;; help-extras.el --- Miscellaneous help-related commands -*- lexical-binding: t; -*- 2 | 3 | (defun command-of-the-day () 4 | "Show the documentation for a random command." 5 | (interactive) 6 | (let (commands) 7 | (mapatoms (lambda (s) (when (commandp s) (push s commands)))) 8 | (describe-function (nth (random (length commands)) commands)) 9 | (with-current-buffer "*Help*" 10 | (setq help-xref-stack-item '(command-of-the-day))))) 11 | 12 | (defalias 'cotd #'command-of-the-day) 13 | 14 | (defun show-help () 15 | "Show the *Help* buffer." 16 | (interactive) 17 | (display-buffer "*Help*")) 18 | 19 | (provide 'help-extras) 20 | -------------------------------------------------------------------------------- /my-lisp/isearch-extras.el: -------------------------------------------------------------------------------- 1 | ;;; isearch-extras.el --- Some isearch tweaks -*- lexical-binding: t; -*- 2 | 3 | (defun isearch-exit-at-start () 4 | "Exit search at the beginning of the current match." 5 | (unless (or isearch-mode-end-hook-quit 6 | (bound-and-true-p isearch-suspended) 7 | (not isearch-forward) 8 | (not isearch-other-end) 9 | (and (boundp 'avy-command) 10 | (eq avy-command 'avy-isearch))) 11 | (goto-char isearch-other-end))) 12 | 13 | (defun isearch-exit-at-end () 14 | "Exit search at the end of the current match." 15 | (interactive) 16 | (let ((isearch-other-end (point))) 17 | (isearch-exit)) 18 | (unless isearch-forward (goto-char isearch-other-end))) 19 | 20 | (defun isearch-delete-wrong () 21 | "Revert to previous successful search." 22 | (interactive) 23 | (while (or (not isearch-success) isearch-error) 24 | (isearch-pop-state)) 25 | (isearch-update)) 26 | 27 | (defun isearch-yank-region () 28 | "If the region is active add it to the isearch search string. 29 | Either bind this to a key in `isearch-mode-map' or add it to 30 | `isearch-mode-hook'." 31 | (interactive) 32 | (when (use-region-p) 33 | (isearch-yank-string 34 | (buffer-substring-no-properties 35 | (region-beginning) (region-end))) 36 | (deactivate-mark))) 37 | 38 | (defun isearch-replace-regexp (&optional from-here) 39 | "Regexp isearch followed by query-replace. 40 | A more interactive replacement for `query-replace-regexp': it 41 | starts with a regexp isearch from the top of the buffer, which 42 | allows you to verify visually you typed the correct regexp; upon 43 | exiting the isearch runs a query-replace operation; and finally 44 | restores point. 45 | 46 | If called when the region is active, it first narrows to the 47 | region. When FROM-HERE is non-nil (interactively, if called with 48 | a prefix argument), the search and replacements are done from 49 | point until the end of the buffer." 50 | (interactive "P") 51 | (save-excursion 52 | (save-restriction 53 | (unless from-here (goto-char (point-min))) 54 | (when (use-region-p) 55 | (narrow-to-region (region-beginning) (region-end)) 56 | (deactivate-mark)) 57 | (let ((isearch-mode-end-hook 58 | (lambda () 59 | (unless isearch-mode-end-hook-quit 60 | (let (isearch-mode-end-hook) 61 | (unless from-here (isearch-beginning-of-buffer)) 62 | (isearch-query-replace-regexp)))))) 63 | (isearch-forward-regexp))))) 64 | 65 | (defun isearch-next () 66 | "Go to next isearch match." 67 | (interactive) 68 | (let (isearch-lazy-highlight) 69 | (isearch-repeat 'forward)) 70 | (isearch-exit)) 71 | 72 | (defun isearch-previous () 73 | "Go to previous isearch match." 74 | (interactive) 75 | (let (isearch-lazy-highlight) 76 | (isearch-repeat 'backward)) 77 | (isearch-exit)) 78 | 79 | (provide 'isearch-extras) 80 | -------------------------------------------------------------------------------- /my-lisp/man-help.el: -------------------------------------------------------------------------------- 1 | ;;; man-help.el --- Use man for help in shell command prompts -*- lexical-binding: t; -*- 2 | 3 | 4 | (defun use-man-for-local-help (fn &rest args) 5 | "Remap `display-local-help' to `man' in the minibuffer. 6 | Intended as `:around' advice for `read-shell-command'." 7 | (minibuffer-with-setup-hook 8 | (lambda () 9 | (use-local-map 10 | (define-keymap :parent (current-local-map) 11 | " " #'man))) 12 | (apply fn args))) 13 | 14 | (advice-add 'read-shell-command :around #'use-man-for-local-help) 15 | 16 | (provide 'man-help) 17 | -------------------------------------------------------------------------------- /my-lisp/message-extras.el: -------------------------------------------------------------------------------- 1 | ;;; message-extras.el --- Miscellaneous commands for email -*- lexical-binding: t; -*- 2 | 3 | (require 'message) 4 | (require 'smtpmail) 5 | 6 | ;; The functions here depend on the following variable, which should 7 | ;; be set to an alist associating each user mail address with its smtp 8 | ;; data (a plist with keys :server, :type and :port). I set mine in a 9 | ;; private configuration package. 10 | (defvar all-user-mail-addresses) 11 | 12 | (defun cycle-from-address () 13 | "Cycle between my email addresses." 14 | (interactive) 15 | (save-excursion 16 | (let ((from (cadr 17 | (mail-extract-address-components 18 | (message-field-value "From")))) 19 | (emails (mapcar #'car all-user-mail-addresses))) 20 | (message-goto-from) 21 | (delete-region (point) (search-backward ":")) 22 | (insert ": " 23 | (message-make-from 24 | user-full-name 25 | (elt emails (mod (1+ (seq-position emails from)) 26 | (length emails)))))))) 27 | 28 | (defun set-smtp-server () 29 | "Set the stmp server according to the from field. 30 | Add to `message-send-hook'." 31 | (when-let* ((from (cadr 32 | (mail-extract-address-components 33 | (message-field-value "From")))) 34 | (server (cdr (assoc from all-user-mail-addresses)))) 35 | (setq smtpmail-smtp-user (or (plist-get server :user) from) 36 | smtpmail-smtp-server (plist-get server :server) 37 | smtpmail-stream-type (plist-get server :type) 38 | smtpmail-smtp-service (plist-get server :port)))) 39 | 40 | (defun message-lint () 41 | "Check for missing subject or attachments. 42 | Add to `message-send-hook'." 43 | (unless (message-field-value "Subject") 44 | (message-goto-subject) 45 | (user-error "Add a subject line")) 46 | (cl-flet ((containsp (re) (save-excursion 47 | (message-goto-body) 48 | (re-search-forward re nil t)))) 49 | (when (and (containsp "attach\\|adjunt") 50 | (not (containsp "disposition=attachment")) 51 | (not (y-or-n-p "Send mail without attachments? "))) 52 | (user-error "Attach some files")))) 53 | 54 | (provide 'message-extras) 55 | -------------------------------------------------------------------------------- /my-lisp/narrow-extras.el: -------------------------------------------------------------------------------- 1 | ;;; narrow-extras.el --- Miscellaneous narrowing commands -*- lexical-binding: t; -*- 2 | 3 | (declare-function org-edit-src-code 'org-src) 4 | (declare-function org-edit-src-exit 'org-src) 5 | (declare-function TeX-narrow-to-group 'tex) 6 | (declare-function LaTeX-narrow-to-environment 'latex) 7 | 8 | (defun narrow-or-widen-dwim (p) 9 | "Widen if buffer is narrowed, narrow-dwim otherwise. 10 | Dwim means: region, org-src-block, org-subtree, or defun, 11 | whichever applies first. Narrowing to org-src-block actually 12 | calls `org-edit-src-code'. 13 | 14 | With prefix P, don't widen, just narrow even if buffer is 15 | already narrowed." 16 | (interactive "P") 17 | (declare (interactive-only)) 18 | (cond ((and (buffer-narrowed-p) (not p)) (widen)) 19 | ((and (bound-and-true-p org-src-mode) (not p)) 20 | (org-edit-src-exit)) 21 | ((region-active-p) 22 | (narrow-to-region (region-beginning) (region-end))) 23 | ((derived-mode-p 'org-mode) 24 | (or (ignore-errors (org-edit-src-code)) 25 | (ignore-errors (org-narrow-to-block)) 26 | (org-narrow-to-subtree))) 27 | ((derived-mode-p 'latex-mode) 28 | (LaTeX-narrow-to-environment)) 29 | ((derived-mode-p 'tex-mode) 30 | (TeX-narrow-to-group)) 31 | (t (narrow-to-defun)))) 32 | 33 | (defun narrow-to-point () 34 | "Narrow to point, useful for yanking a rectangle." 35 | (interactive) 36 | (narrow-to-region (point) (point))) 37 | 38 | (defun narrow-to-sexp () 39 | "Narrow to sexp containing point." 40 | (interactive) 41 | (narrow-to-region 42 | (save-excursion (up-list -1 t t) (point)) 43 | (save-excursion (up-list +1 t t) (point)))) 44 | 45 | (provide 'narrow-extras) 46 | -------------------------------------------------------------------------------- /my-lisp/org-async-init.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | (require 'package) 4 | (setq package-enable-at-startup nil) 5 | (package-initialize) 6 | 7 | (setq make-backup-files nil) 8 | 9 | (require 'org) 10 | (require 'ox) 11 | (require 'ox-beamer) 12 | 13 | (when (executable-find "latexmk") 14 | (setq org-latex-pdf-process '("latexmk -pdf %f"))) 15 | 16 | (setq org-export-with-smart-quotes t 17 | org-confirm-babel-evaluate nil) 18 | 19 | (add-to-list 'load-path "~/.private/") 20 | (add-to-list 'load-path "~/.emacs.d/my-lisp") 21 | 22 | (require 'org-config) 23 | -------------------------------------------------------------------------------- /my-lisp/org-extras.el: -------------------------------------------------------------------------------- 1 | ;;; org-extras.el --- Miscellaneous Org extras -*- lexical-binding: t; -*- 2 | 3 | ;;; arXiv links 4 | 5 | (require 'org) 6 | 7 | (defun org-extras--link-exporter 8 | (path-regexp url-format title-format-1 &optional title-format-2) 9 | (lambda (path description backend) 10 | (when (and (memq backend '(latex html)) 11 | (string-match path-regexp path)) 12 | (format (pcase backend 13 | ((or 'latex 'beamer) "\\href{%s}{%s}") 14 | ('html "%s")) 15 | (format url-format (match-string 1 path)) 16 | (or description 17 | (format (pcase backend 18 | ((or 'latex 'beamer) "\\texttt{%s}") 19 | ('html "%s")) 20 | (format (if (match-string 2 path) 21 | title-format-2 22 | title-format-1) 23 | (match-string 1 path) 24 | (match-string 2 path)))))))) 25 | 26 | (let ((arxiv-regexp "^\\(.*?\\)\\(?:/\\(.*\\)\\)?$") 27 | (arxiv-url-format "https://arxiv.org/abs/%s")) 28 | (org-link-set-parameters 29 | "arXiv" 30 | :face 'org-link 31 | :follow (lambda (path in-emacs) 32 | (when-let (((string-match arxiv-regexp path)) 33 | (url (format arxiv-url-format (match-string 1 path)))) 34 | (if in-emacs (eww url) (browse-url url)))) 35 | :export (org-extras--link-exporter 36 | arxiv-regexp arxiv-url-format "arXiv:%s" "arXiv:%s [%s]"))) 37 | 38 | ;;; doi links 39 | 40 | (defun beamer-is-latex (args) 41 | "Treat Beamer backend same as LaTeX." 42 | (pcase-let ((`(,path ,desc ,backend ,info) args)) 43 | `(,path ,desc ,(if (eq backend 'beamer) 'latex backend) ,info))) 44 | 45 | (defun default-doi-desc (args) 46 | "If no description is given use doi:... format." 47 | (pcase-let ((`(,path ,desc ,backend ,info) args)) 48 | `(,path ,(or desc (concat "doi:" path)) ,backend ,info))) 49 | 50 | (advice-add 'org-link-doi-export :filter-args #'beamer-is-latex) 51 | (advice-add 'org-link-doi-export :filter-args #'default-doi-desc) 52 | 53 | ;;; Inline JavaScript 54 | 55 | (add-to-list 'org-src-lang-modes '("inline-js" . javascript)) 56 | 57 | (defvar org-babel-default-header-args:inline-js 58 | '((:results . "html") 59 | (:exports . "results"))) 60 | 61 | (defun org-babel-execute:inline-js (body _params) 62 | (format "" body)) 63 | 64 | ;;; Source blocks for declaring LaTeX macros 65 | 66 | (add-to-list 'org-src-lang-modes '("latex-macros" . latex)) 67 | 68 | (defvar org-babel-default-header-args:latex-macros 69 | '((:results . "raw") 70 | (:exports . "results"))) 71 | 72 | (defun prefix-all-lines (pre body) 73 | (with-temp-buffer 74 | (insert body) 75 | (string-insert-rectangle (point-min) (point-max) pre) 76 | (buffer-string))) 77 | 78 | (defun org-babel-execute:latex-macros (body _params) 79 | (concat 80 | (prefix-all-lines "#+LATEX_HEADER: " body) 81 | "\n#+HTML_HEAD_EXTRA:
\\(\n" 82 | (prefix-all-lines "#+HTML_HEAD_EXTRA: " body) 83 | "\n#+HTML_HEAD_EXTRA: \\)
\n")) 84 | 85 | (defun no-short-tags (tags) 86 | "Do not offer very short tags as completion candidates. 87 | Use as `:filter-return' advice for `org-get-buffer-tags'." 88 | (mapcar (lambda (group) 89 | (seq-filter (lambda (tag) (> (length tag) 2)) group)) 90 | tags)) 91 | 92 | (advice-add 'org-get-buffer-tags :filter-return #'no-short-tags) 93 | 94 | (provide 'org-extras) 95 | -------------------------------------------------------------------------------- /my-lisp/org-ql-usual-files.el: -------------------------------------------------------------------------------- 1 | ;;; org-ql-usual-files --- Search in the usual files -*- lexical-binding: t; -*- 2 | 3 | ;; I usually want to search both agenda files and refile targets 4 | 5 | (require 'org-ql-find) 6 | (require 'org-capture) 7 | 8 | (defcustom org-ql-usual-files 9 | (seq-union 10 | (seq-union (mapcan #'seq-copy (mapcar #'car org-refile-targets)) 11 | org-agenda-files) 12 | (cl-loop for (_ _ _ (type file . _) _) in org-capture-templates 13 | when (string-prefix-p "file" (symbol-name type)) 14 | collect (abbreviate-file-name 15 | (expand-file-name file org-directory)))) 16 | "Org files I usually want to search with `org-ql-find'." 17 | :type '(repeat file) 18 | :group 'org-ql) 19 | 20 | (defun org-ql-usual-files () 21 | "Return list of org files I usually want to search. 22 | This returns `org-ql-usual-files' plus the current buffer if 23 | it's in `org-mode'." 24 | (append 25 | (when (and (derived-mode-p 'org-mode) 26 | (not (and (buffer-file-name) 27 | (member (abbreviate-file-name (buffer-file-name)) 28 | org-ql-usual-files)))) 29 | (list (current-buffer))) 30 | org-ql-usual-files)) 31 | 32 | (defun org-ql-usual-files-find () 33 | "Call `org-ql-find' on current buffer and `org-ql-usual-files'." 34 | (interactive) 35 | (org-ql-find (org-ql-usual-files) :widen t)) 36 | 37 | (defun org-ql-usual-files-open-link () 38 | "Call `org-ql-open-link' on current buffer and `org-ql-usual-files'." 39 | (interactive) 40 | (org-ql-open-link (org-ql-usual-files))) 41 | 42 | (provide 'org-ql-usual-files) 43 | -------------------------------------------------------------------------------- /my-lisp/shr-heading.el: -------------------------------------------------------------------------------- 1 | ;;; shr-heading.el --- Navigation by heading -*- lexical-binding: t; -*- 2 | 3 | (defun shr-heading-next (&optional arg) 4 | "Move forward by ARG headings (any h1-h4). 5 | If ARG is negative move backwards, ARG defaults to 1." 6 | (interactive "p") 7 | (unless arg (setq arg 1)) 8 | (catch 'return 9 | (dotimes (_ (abs arg)) 10 | (when (> arg 0) (end-of-line)) 11 | (if-let ((match 12 | (funcall (if (> arg 0) 13 | #'text-property-search-forward 14 | #'text-property-search-backward) 15 | 'face '(shr-h1 shr-h2 shr-h3 shr-h4) 16 | (lambda (tags face) 17 | (cl-loop for x in (if (consp face) face (list face)) 18 | thereis (memq x tags)))))) 19 | (goto-char 20 | (if (> arg 0) (prop-match-beginning match) (prop-match-end match))) 21 | (throw 'return nil)) 22 | (when (< arg 0) (beginning-of-line))) 23 | (beginning-of-line) 24 | (point))) 25 | 26 | (defun shr-heading-previous (&optional arg) 27 | "Move backward by ARG headings (any h1-h4). 28 | If ARG is negative move forwards instead, ARG defaults to 1." 29 | (interactive "p") 30 | (shr-heading-next (- (or arg 1)))) 31 | 32 | (defun shr-heading--line-at-point () 33 | "Return the current line." 34 | (buffer-substring (line-beginning-position) (line-end-position))) 35 | 36 | (defun shr-heading-setup-imenu () 37 | "Setup imenu for h1-h4 headings in eww buffer. 38 | Add this function to appropriate major mode hooks such as 39 | `eww-mode-hook' or `elfeed-show-mode-hook'." 40 | (setq-local 41 | imenu-prev-index-position-function #'shr-heading-previous 42 | imenu-extract-index-name-function #'shr-heading--line-at-point)) 43 | 44 | (defvar shr-heading-repeat-map 45 | (let ((map (make-sparse-keymap))) 46 | (define-key map "n" #'shr-heading-next) 47 | (define-key map "\C-n" #'shr-heading-next) 48 | (define-key map "p" #'shr-heading-previous) 49 | (define-key map "\C-p" #'shr-heading-previous) 50 | map) 51 | "Keymap used to repeat shr heading key sequences. Used in `repeat-mode'.") 52 | 53 | (put 'shr-heading-next 'repeat-map 'shr-heading-repeat-map) 54 | (put 'shr-heading-previous 'repeat-map 'shr-heading-repeat-map) 55 | 56 | (provide 'shr-heading) 57 | -------------------------------------------------------------------------------- /my-lisp/text-extras.el: -------------------------------------------------------------------------------- 1 | ;;; text-extras.el --- Miscellaneous text editing commands -*- lexical-binding: t; -*- 2 | 3 | ;;; the most miscellaneous commands of all 4 | 5 | (defun unfill-paragraph () 6 | "Join a paragraph into a single line." 7 | (interactive) 8 | (let ((fill-column (point-max)) 9 | (emacs-lisp-docstring-fill-column t)) 10 | (fill-paragraph nil t))) 11 | 12 | (defun copy-word-from-above (arg) 13 | "Copy ARG words from the nonblank line above. With a negative 14 | argument, copy the rest of the line." 15 | (interactive "p") 16 | (let ((p (point)) 17 | (c (current-column))) 18 | (beginning-of-line) 19 | (backward-char 1) 20 | (skip-chars-backward " \t\n\r") 21 | (move-to-column c) 22 | (let* ((beg (point)) 23 | (lim (line-end-position)) 24 | (end (if (< arg 0) lim (forward-word arg) (point)))) 25 | (goto-char p) 26 | (insert (buffer-substring beg (min end lim)))))) 27 | 28 | (defun duplicate-line-kill-word () 29 | "Duplicate the current line and kill the word at point in the duplicate." 30 | (interactive) 31 | (let ((column (current-column))) 32 | (forward-line) 33 | (open-line 1) 34 | (copy-from-above-command) 35 | (beginning-of-line) 36 | (forward-char column) 37 | (unless (looking-at "\\<") 38 | (backward-word)) 39 | (kill-word 1))) 40 | 41 | (defun goto-random-line () 42 | "Goto a random line in the buffer." 43 | (interactive) 44 | (push-mark) 45 | (goto-char (point-min)) 46 | (forward-line (random (count-lines (point-min) (point-max))))) 47 | 48 | (defvar-keymap random-line-map 49 | :doc "Repeat map for `goto-random-line'" 50 | "r" #'goto-random-line) 51 | 52 | (put 'goto-random-line 'repeat-map 'random-line-map) 53 | 54 | (defun pipe-region (start end command) 55 | "Pipe region through shell command. If the mark is inactive, 56 | pipe whole buffer." 57 | (interactive (append 58 | (if (use-region-p) 59 | (list (region-beginning) (region-end)) 60 | (list (point-min) (point-max))) 61 | (list (read-shell-command "Pipe through: ")))) 62 | (let ((exit-status (call-shell-region start end command t t))) 63 | (unless (equal 0 exit-status) 64 | (let ((error-msg (string-trim-right (buffer-substring (mark) (point))))) 65 | (undo) 66 | (cond 67 | ((null exit-status) 68 | (message "Unknown error")) 69 | ((stringp exit-status) 70 | (message "Signal %s" exit-status)) 71 | (t 72 | (message "[%d] %s" exit-status error-msg))))))) 73 | 74 | (defun forward-to-whitespace (arg) 75 | "Move forward to the end of the next sequence of non-whitespace 76 | characters. With argument, do this that many times." 77 | (interactive "^p") 78 | (re-search-forward 79 | (if (> arg 0) 80 | "[^[:blank:]\n]\\(?:[[:blank:]\n]\\|\\'\\)" 81 | "\\(?:[[:blank:]\n]\\|\\`\\)[^[:blank:]\n]") 82 | nil t arg) 83 | (unless (= (point) (if (> arg 0) (point-max) (point-min))) 84 | (forward-char (if (> arg 0) -1 1)))) 85 | 86 | (defun backward-to-whitespace (arg) 87 | "Move backward to the beginning of the previous sequence of 88 | non-whitespace characters. With argument, do this that many 89 | times." 90 | (interactive "^p") 91 | (forward-to-whitespace (- arg))) 92 | 93 | ;;; marking things 94 | 95 | (defmacro define-thing-marker (fn-name things forward-thing &rest extra) 96 | `(defun ,fn-name (&optional arg allow-extend) 97 | ,(format "Mark ARG %s starting with the current one. If ARG is negative, 98 | mark -ARG %s ending with the current one. 99 | 100 | Interactively (or if ALLOW-EXTEND is non-nil), if this command is 101 | repeated or (in Transient Mark mode) if the mark is active, it 102 | marks the next ARG %s after the ones already marked." things things things) 103 | (interactive "p\np") 104 | (unless arg (setq arg 1)) 105 | (if (and allow-extend 106 | (or (and (eq last-command this-command) (mark t)) 107 | (and transient-mark-mode mark-active))) 108 | (set-mark 109 | (save-excursion 110 | (goto-char (mark)) 111 | (,forward-thing arg) 112 | (point))) 113 | ,(plist-get extra :pre) 114 | (,forward-thing arg) 115 | ,(plist-get extra :post) 116 | (push-mark nil t t) 117 | (,forward-thing (- arg))))) 118 | 119 | (define-thing-marker mark-line "lines" forward-line 120 | :post (unless (= (preceding-char) ?\n) 121 | (setq arg (1- arg)))) 122 | 123 | (define-thing-marker mark-char "characters" forward-char) 124 | 125 | (define-thing-marker mark-my-word "words" forward-word 126 | :pre (when (and (looking-at "\\>") (> arg 0)) 127 | (forward-word -1))) 128 | 129 | (define-thing-marker mark-non-whitespace "vim WORDS" 130 | forward-to-whitespace) 131 | 132 | ;;; case change commands 133 | 134 | (defmacro define-case-changer (case) 135 | "Define a CASE change command that does what I want. 136 | The defined command will change the case of: the region if 137 | active, and of the next prefix argument many words, starting with 138 | the word point is either on or right after (the \"or right 139 | after\" bit is the only difference with the built-in case-dwim 140 | commands)." 141 | (cl-flet ((case-fn (suffix) (intern (format "%s-%s" case suffix)))) 142 | `(defun ,(case-fn 'dwiw) (arg) 143 | ,(format "%s active region or next ARG words. 144 | If called without a prefix argument and no active region with 145 | point at the end of a word, then %s the previous word. This is 146 | the only difference between this command and %s-dwim." 147 | (capitalize (symbol-name case)) case case) 148 | (interactive "*p") 149 | (if (use-region-p) 150 | (,(case-fn 'region) 151 | (region-beginning) (region-end) (region-noncontiguous-p)) 152 | (when (and (eolp) (= arg 1)) 153 | (setq arg -1)) 154 | (,(case-fn 'word) arg))))) 155 | 156 | (define-case-changer upcase) 157 | (define-case-changer downcase) 158 | (define-case-changer capitalize) 159 | 160 | ;;; poor man's paredit 161 | 162 | (defun mark-inside-sexp () 163 | "Mark inside a sexp." 164 | (interactive) 165 | (let (beg end) 166 | (backward-up-list 1 t t) 167 | (setq beg (1+ (point))) 168 | (forward-sexp) 169 | (setq end (1- (point))) 170 | (goto-char beg) 171 | (push-mark) 172 | (goto-char end)) 173 | (activate-mark)) 174 | 175 | (defun kill-inside-sexp () 176 | "Kill inside a sexp." 177 | (interactive) 178 | (mark-inside-sexp) 179 | (kill-region (mark) (point))) 180 | 181 | (defun unwrap-sexp () 182 | "Unwrap a sexp." 183 | (interactive) 184 | (let (end) 185 | (mark-inside-sexp) 186 | (delete-char 1) 187 | (setq end (1- (point))) 188 | (goto-char (mark)) 189 | (delete-char -1) 190 | (set-mark end))) 191 | 192 | (defun unwrap-mark-sexp () 193 | "Unwrap a sexp and mark the contents." 194 | (interactive) 195 | (unwrap-sexp) 196 | (setq deactivate-mark nil)) 197 | 198 | ;;; Continue dabbreving with the greatest of ease 199 | 200 | (defun dabbrev-next (arg) 201 | "Insert the next ARG words from where previous expansion was found." 202 | (interactive "p") 203 | (dotimes (_ arg) 204 | (insert " ") 205 | (dabbrev-expand 1)) 206 | (setq this-command 'dabbrev-expand)) 207 | 208 | (defun dabbrev-complete-next () 209 | "Choose a continuation for the previous expansion with completion." 210 | (interactive) 211 | (insert " ") 212 | (dabbrev-completion)) 213 | 214 | ;;; pop up a buffer for text to send to clipboard 215 | 216 | (defun text-to-clipboard--done () 217 | "Copy buffer contents to clipboard and quit window." 218 | (interactive) 219 | (gui-set-selection 220 | 'CLIPBOARD 221 | (buffer-substring-no-properties (point-min) (point-max))) 222 | (quit-window :kill)) 223 | 224 | (defvar-keymap text-to-clipboard-minor-mode-map 225 | "C-c C-c" #'text-to-clipboard--done) 226 | 227 | (define-minor-mode text-to-clipboard-minor-mode 228 | "Minor mode binding a key to quit window and copy buffer to clipboard.") 229 | 230 | (defun text-to-clipboard () 231 | "Pop up a temporary buffer to collect text to send to the clipboard. 232 | The pop up buffer is in `markdown-mode' and uses the TeX input 233 | method. Use \\\\[text-to-clipboard--done] to send the buffer contents to the clipboard 234 | and quit the window, killing the buffer. 235 | 236 | If the region is active, use the region as the initial contents 237 | for the pop up buffer." 238 | (interactive) 239 | (let ((region (when (use-region-p) 240 | (buffer-substring-no-properties 241 | (region-beginning) (region-end))))) 242 | (pop-to-buffer (generate-new-buffer "*clipboard*")) 243 | (when region (insert region))) 244 | (if (fboundp 'markdown-mode) (markdown-mode) (text-mode)) 245 | (text-to-clipboard-minor-mode)) 246 | 247 | (defun apply-macro-to-rest-of-paragraph () 248 | "Apply last keyboard macro to each line in the rest of the current paragraph." 249 | (interactive) 250 | (when defining-kbd-macro (kmacro-end-macro nil)) 251 | (apply-macro-to-region-lines 252 | (line-beginning-position 2) 253 | (save-excursion (end-of-paragraph-text) (point)))) 254 | 255 | (defun echo-area-tooltips () 256 | "Show tooltips in the echo area automatically for current buffer." 257 | (setq-local help-at-pt-display-when-idle t 258 | help-at-pt-timer-delay 0) 259 | (help-at-pt-cancel-timer) 260 | (help-at-pt-set-timer)) 261 | 262 | (provide 'text-extras) 263 | -------------------------------------------------------------------------------- /my-lisp/tmp-buffer.el: -------------------------------------------------------------------------------- 1 | ;;; tmp-buffer.el --- Temporary buffers in specified major mode -*- lexical-binding: t; -*- 2 | 3 | (defcustom tmp-buffer-mode-alist 4 | '((?o . org-mode) 5 | (?t . text-mode) 6 | (?m . markdown-mode) 7 | (?l . lisp-interaction-mode) 8 | (?x . LaTeX-mode) 9 | (?f . fundamental-mode) 10 | (?= . tmp-buffer-current-mode) 11 | (?? . tmp-buffer-prompt-for-mode)) 12 | "List of major modes for temporary buffers and their hotkeys." 13 | :type '(alist :key-type character :value-type symbol) 14 | :group 'tmp-buffer) 15 | 16 | (defvar tmp-buffer-mode-history nil) 17 | 18 | (defun tmp-buffer-prompt-for-mode () 19 | "Prompt for a major mode and switch to it." 20 | (funcall 21 | (intern 22 | (completing-read 23 | "Mode: " 24 | (cl-loop for m being the symbols 25 | when (and (commandp m) (string-suffix-p "-mode" (symbol-name m))) 26 | collect m) 27 | nil t nil 'tmp-buffer-mode-history)))) 28 | 29 | (defun tmp-buffer (spec) 30 | "Open temporary buffer in specified major mode." 31 | (interactive "c") 32 | (if (eq spec ?\C-h) 33 | (progn 34 | (help-setup-xref (list #'tmp-buffer ?\C-h) 35 | (called-interactively-p 'interactive)) 36 | (with-output-to-temp-buffer (help-buffer) 37 | (princ "Temporary buffers:\n\nKey\tMode\n") 38 | (dolist (km tmp-buffer-mode-alist) 39 | (princ (format " %c\t%s\n" (car km) (cdr km)))))) 40 | (let ((mode (cdr (assoc spec tmp-buffer-mode-alist)))) 41 | (if (not mode) 42 | (user-error "Unknown mode for temporary buffer.") 43 | (when (eq mode 'tmp-buffer-current-mode) 44 | (setq mode major-mode)) 45 | (let ((region (when (use-region-p) 46 | (buffer-substring-no-properties 47 | (region-beginning) (region-end))))) 48 | (pop-to-buffer (generate-new-buffer "*tmp*")) 49 | (when region (insert region))) 50 | (funcall mode))))) 51 | 52 | (provide 'tmp-buffer) 53 | -------------------------------------------------------------------------------- /my-lisp/topaz-paste.el: -------------------------------------------------------------------------------- 1 | ;;; topaz-paste.el --- Generate topaz paste URLs -*- lexical-binding: t; -*- 2 | 3 | ;; https://topaz.github.io/paste/ is a "client side paste service", 4 | ;; which can be used to share snippets of text. All the data is 5 | ;; encoded in the URL as a Base64-encoded LZMA-compressed string. 6 | ;; Since both Base64 and LZMA are pretty standard this means you don't 7 | ;; actually need the website to generate a URL! 8 | 9 | (defun topaz-paste-region (begin end) 10 | "Save topaz paste URL for region contents on the kill-ring." 11 | (interactive "r") 12 | (kill-new 13 | (concat "https://topaz.github.io/paste/#" 14 | (string-replace 15 | "\n" "" 16 | (with-output-to-string 17 | (call-shell-region begin end "xz -zc --format=lzma | base64" 18 | nil standard-output))))) 19 | (message "topaz paste url saved")) 20 | 21 | (defun topaz-paste-buffer () 22 | "Save topaz paste URL for buffer contents on the kill-ring." 23 | (interactive) 24 | (topaz-paste-region (point-min) (point-max))) 25 | 26 | (provide 'topaz-paste) 27 | -------------------------------------------------------------------------------- /my-lisp/vc-extras.el: -------------------------------------------------------------------------------- 1 | ;;; vc-extras --- Miscellaneous commands to use with VC -*- lexical-binding: t; -*- 2 | 3 | (require 'log-view) 4 | (require 'log-edit) 5 | (require 'vc-git) 6 | 7 | (defun clear-log-edit-buffer (&optional _) 8 | "Clear the buffer if it is in `log-edit-mode'. 9 | Intended to be used as advice for `consult-history'." 10 | (when (derived-mode-p 'log-edit-mode) 11 | (erase-buffer))) 12 | 13 | (defun log-view-save-commit-hash () 14 | "Save commit hash of log entry at point to `kill-ring'." 15 | ;; This is Protesilaos' prot-vc-log-kill-hash function 16 | (interactive) 17 | (let ((commit (cadr (log-view-current-entry (point) t)))) 18 | (kill-new (format "%s" commit)) 19 | (message "Copied: %s" commit))) 20 | 21 | (defun vc-git-commit (message) 22 | "Run git commit -m MESSAGE. 23 | Interactively MESSAGE is just \"Merge resolving conflicts\", but 24 | with a prefix argument you are prompted for a message." 25 | (interactive 26 | (list (if current-prefix-arg 27 | (read-from-minibuffer "Commit message: " "Merge") 28 | "Merge resolving conflicts"))) 29 | (vc-git-command nil 0 nil "commit" "-m" message) 30 | (message "Commited with message: %s" message) 31 | (when (derived-mode-p 'vc-dir-mode) 32 | (revert-buffer))) 33 | 34 | (provide 'vc-extras) 35 | -------------------------------------------------------------------------------- /my-lisp/vimgolf.el: -------------------------------------------------------------------------------- 1 | ;;; vimgolf.el --- Play VimGolf in Emacs -*- lexical-binding: t; -*- 2 | 3 | (require 'json) 4 | 5 | (defun vimgolf () 6 | "Setup buffers for vimgolf challenge." 7 | (interactive) 8 | (goto-char (point-min)) 9 | (let ((json (json-read))) 10 | (switch-to-buffer "*VimGolf B*") 11 | (insert (cdr (assoc 'data (cdr (assoc 'out json))))) 12 | (goto-char (point-min)) 13 | (split-window-horizontally) 14 | (switch-to-buffer "*VimGolf A*") 15 | (insert (cdr (assoc 'data (cdr (assoc 'in json))))) 16 | (goto-char (point-min)))) 17 | 18 | (provide 'vimgolf) 19 | -------------------------------------------------------------------------------- /my-lisp/visiting-buffer.el: -------------------------------------------------------------------------------- 1 | ;;; visiting-buffer.el --- When deleting or renaming files deal with buffer too -*- lexical-binding: t; -*- 2 | 3 | (defun visiting-buffer-rename (file newname &optional _ok-if-already-exists) 4 | "Rename buffer visiting FILE to NEWNAME. 5 | Intended as :after advice for `rename-file'." 6 | (when (called-interactively-p 'any) 7 | (when-let ((old (get-file-buffer file))) 8 | (with-current-buffer old 9 | (set-visited-file-name newname nil t))) 10 | (when-let ((new (get-file-buffer newname))) 11 | (with-current-buffer new 12 | (when (derived-mode-p 'emacs-lisp-mode) 13 | (save-excursion 14 | (let* ((base (file-name-nondirectory file)) 15 | (sans (file-name-sans-extension base)) 16 | (newbase (file-name-nondirectory newname)) 17 | (newsans (file-name-sans-extension newbase))) 18 | (goto-char (point-min)) 19 | (while (search-forward-regexp (format "^;;; %s" base) nil t) 20 | (replace-match (concat ";;; " newbase))) 21 | (goto-char (point-max)) 22 | (when 23 | (search-backward-regexp (format "^(provide '%s)" sans) nil t) 24 | (replace-match (format "(provide '%s)" newsans)))))))))) 25 | 26 | (advice-add 'rename-file :after 'visiting-buffer-rename) 27 | (advice-add 'vc-rename-file :after 'visiting-buffer-rename) 28 | 29 | (defun visiting-buffer-kill (file &optional _trash) 30 | "Kill buffer visiting FILE. 31 | Intended as :after advice for `delete-file'." 32 | (when (called-interactively-p 'any) 33 | (when-let ((buffer (get-file-buffer file))) 34 | (kill-buffer buffer)))) 35 | 36 | (advice-add 'delete-file :after 'visiting-buffer-kill) 37 | (advice-add 'vc-delete-file :after 'visiting-buffer-kill) 38 | 39 | (provide 'visiting-buffer) 40 | -------------------------------------------------------------------------------- /my-lisp/webjump-extras.el: -------------------------------------------------------------------------------- 1 | ;;; webjump-extras.el --- webjump-sites = eww-bookmarks + searches -*- lexical-binding: t; -*- 2 | 3 | (require 'webjump) 4 | (require 'eww) 5 | 6 | (defcustom webjump-search-engines 7 | '(("DuckDuckGo" . "https://html.duckduckgo.com/html/?q=") 8 | ("Google" . "https://google.com/?q=") 9 | ("Wikipedia" . "https://en.wikipedia.org/wiki/") 10 | ("DLE RAE" . "https://dle.rae.es/") 11 | ("IMDb" . "https://www.imdb.com/find?q=") 12 | ("YouTube" . "https://www.youtube.com/results?search_query=") 13 | ("Merriam-Webster" . "https://www.merriam-webster.com/dictionary/") 14 | ("Collins" . "https://www.collinsdictionary.com/dictionary/english/") 15 | ("arXiv" . "https://arxiv.org/search/?searchtype=all&query=") 16 | ("math.??" . "https://arxiv.org/list/math. /recent")) 17 | "Search engines to add to `webjump-sites' as `simply-query' sites." 18 | :group 'webjump 19 | :type '(alist :key-type (string :tag "Name") 20 | :value-type (string :tag "URL"))) 21 | 22 | (defun webjump-reload () 23 | "Reload `webjump-sites' from eww bookmarks and `webjump-search-engines'." 24 | (interactive) 25 | (setq webjump-sites 26 | (append 27 | (progn 28 | (eww-read-bookmarks) 29 | (mapcar (lambda (bm) 30 | (cons (plist-get bm :title) (plist-get bm :url))) 31 | eww-bookmarks)) 32 | (mapcar (pcase-lambda (`(,name . ,url)) 33 | (setq url (split-string url " ")) 34 | `(,name . [simple-query 35 | ,(when (string-match "^.*://[^/]+" (car url)) 36 | (match-string 0 (car url))) 37 | ,(car url) ,(or (cadr url) "")])) 38 | webjump-search-engines)))) 39 | 40 | (defun webjump-in-eww (fn &rest args) 41 | (let ((browse-url-browser-function 42 | (if current-prefix-arg #'browse-url-default-browser #'eww))) 43 | (apply fn args))) 44 | 45 | (advice-add 'webjump :around #'webjump-in-eww) 46 | 47 | (provide 'webjump-extras) 48 | -------------------------------------------------------------------------------- /my-lisp/window-extras.el: -------------------------------------------------------------------------------- 1 | ;;; window-extras.el --- Miscellaneous window commands -*- lexical-binding: t; -*- 2 | 3 | (defun toggle-window-split () 4 | "Toggle window split from vertical to horizontal." 5 | (interactive) 6 | (if (> (length (window-list)) 2) 7 | (error "Can't toggle with more than 2 windows.") 8 | (let ((was-full-height (window-full-height-p))) 9 | (delete-other-windows) 10 | (if was-full-height 11 | (split-window-vertically) 12 | (split-window-horizontally)) 13 | (save-selected-window 14 | (other-window 1) 15 | (switch-to-buffer (other-buffer)))))) 16 | 17 | (defun transpose-windows () 18 | "Swap the buffers shown in current and next window." 19 | (interactive) 20 | (let ((this-buffer (window-buffer)) 21 | (next-window (next-window nil :no-minibuf nil))) 22 | (set-window-buffer nil (window-buffer next-window)) 23 | (set-window-buffer next-window this-buffer) 24 | (select-window next-window))) 25 | 26 | ;; Modified slightly from alphapapa's original at: 27 | ;; https://www.reddit.com/r/emacs/comments/idz35e/emacs_27_can_take_svg_screenshots_of_itself/g2c2c6y 28 | (defun screenshot (arg) 29 | "Save a screenshot of the current frame to ~/Downloads. 30 | By default save a PNG, but with a prefix argument, save a an SVG." 31 | (interactive "P") 32 | (let* ((type (if arg 'svg 'png)) 33 | (filename (make-temp-file 34 | "~/Downloads/Emacs" nil (format ".%s" type) 35 | (x-export-frames nil type)))) 36 | (kill-new filename) 37 | (message "Saved %s" filename))) 38 | 39 | (defun toggle-mode-line () 40 | "Toggle visibility of the mode line." 41 | (interactive) 42 | (if mode-line-format 43 | (setq-local mode-line-format nil) 44 | (kill-local-variable 'mode-line-format) 45 | (force-mode-line-update))) 46 | 47 | (provide 'window-extras) 48 | -------------------------------------------------------------------------------- /viper: -------------------------------------------------------------------------------- 1 | (setq viper-inhibit-startup-message 't) 2 | (setq viper-expert-level '5) 3 | (setq viper-want-ctl-h-help t) 4 | (setq viper-ex-style-motion nil) 5 | (define-key viper-minibuffer-map (kbd "RET") nil) 6 | (viper-buffer-search-enable) 7 | --------------------------------------------------------------------------------