pax_global_header00006660000000000000000000000064124406377700014524gustar00rootroot0000000000000052 comment=82bb75ceb2ddc272d6618d94874b7fc13181a409 paredit-el-24/000077500000000000000000000000001244063777000133415ustar00rootroot00000000000000paredit-el-24/COPYING000066400000000000000000001045131244063777000144000ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . paredit-el-24/CREDITS000066400000000000000000000020251244063777000143600ustar00rootroot00000000000000The original inspirations for paredit were Interlisp-D's structure editor `SEdit' -- a real structure editor, not a cheesy imitation like paredit -- and Guillaume Germain's sedit.el for GNU Emacs. The following people have contributed to paredit in some way -- by submitting patches, by discussing design, by testing paredit and finding broken corner cases, &c. It is not complete: most of those who contributed via IRC are missing, for example, and I started this list long after I ought to have. If your name is not listed here and you think it should be, or if your name is listed here and you would rather that it not be, then let me know. Marco Baringer Eli Barzilay Edwin O'Connor John Cowan Iain Dalton Peter Danenberg Elly Jones Daniel Gackle Guillaume Germain Tobias Gerdin Phil Hagelberg Michael Heerdegen Thomas Hermann David Hilton Trey Jackson `Joe' Richard M Kreuter Leo Liu Brian Mastenbrook `megane' Eitan Postavsky Tobias C. Rittweiler Andreas Roehler Jorgen Schäfer Michael Weber Goran Weinholt Norman Werner John Wiegley paredit-el-24/NEWS000066400000000000000000000742741244063777000140560ustar00rootroot00000000000000* Paredit Release Notes -*- outline -*- This file contains release notes for paredit, whose latest released version is available at , and whose latest beta version is available at . The latest version of this file is available at . ** Version 24 *** Slurp/barf now support prefix arguments with the obvious semantics. *** HTML quick reference is a little prettier now, perhaps. *** paredit.el no longer defines `backward-down-list'. *** Slurp `(|) foo' now yields `(|foo)', not the frustrating `(| foo)'. *** C-M-f/C-M-b (paredit-forward/paredit-backward) now move out of strings. *** Changed M-" (paredit-meta-doublequote) to not break line, like M-). *** New command: paredit-meta-doublequote-and-newline has old behaviour. *** Several commands preserve indentation and point column better. *** Motion commands support shift selection in GNU Emacs 24 and later. *** `backward-delete-char' changes in GNU Emacs 24 no longer botch paredit. *** Various bug fixes. ** Version 23 Paredit no longer runs in GNU Emacs 20. It now requires 21 or later. Paredit now has a small set of automatic tests. *** New key: `M-?' is bound to `paredit-convolute-sexp' *** New variable: paredit-override-check-parens-function *** New command: paredit-delete-region *** New command: paredit-kill-region *** Renamed command: paredit-recentre-on-sexp -> paredit-recenter-on-sexp *** Various bug fixes. ** Version 22 The copying terms of paredit are now the GPLv3+, rather than the 3-clause BSD licence. *** Style and Bugs - paredit.el now has a header and footer conforming to the elisp guidelines, so that it can be used with package.el. - `paredit-mode' now has an autoload cookie. - Miscellaneous bugs have been fixed, mostly to make paredit behave the way it should when before it would simply signal an error, or to make paredit signal an error when before it would do something bogus. *** Altered Behaviour - `paredit-raise-sexp' (M-r) now works when inside strings and characters. - `paredit-comment-dwim' (M-;) behaves slightly differently in the following case: (foo bar |baz quux) Before: (foo bar baz ;| quux) After: (foo bar ;; | baz quux) - `paredit-raise-sexp', `paredit-splice-sexps', and `paredit-comment-dwim' reindent a little more selectively. - `paredit-newline' tries to keep invalid structure inside comments. - `paredit-kill' now works in any string-like object, not just those with double-quotes. E.g., Common Lisp's |...| notation for symbols. - After `...)', inserting a double-quote will insert a space too. See also `paredit-space-for-delimiter-predicates', below. *** New Commands - `paredit-copy-as-kill' is to `paredit-kill' as `copy-region-as-kill' is to `kill-region'. That is, `paredit-copy-as-kill' copies the region that would be killed with `paredit-kill', but does not kill the region. - `paredit-semicolon' is back, with better behaviour. - `paredit-{for,back}ward-{up,down}' move up and down the list structure, like `{backward-,}{up,down}-list', but also enter and exit strings when appropriate. C-M-u, C-M-d, C-M-p, and C-M-u are now bound to these commands. - `paredit-kill-ring-save' and `paredit-kill-region' are an experiment for killing regions without destroying structure -- specifically. Later, I hope to implement `paredit-yank', although it is much trickier to implement. *** New Variables - `paredit-space-for-delimiter-predicates' is a list controlling when inserting a delimiter causes insertion of a space too. See the documentation string for details. Example use, to make paredit less frustrating with Common Lisp's #P, #A, #-, and #+ notations: (defvar common-lisp-octothorpe-quotation-characters '(?P)) (defvar common-lisp-octothorpe-parameter-parenthesis-characters '(?A)) (defvar common-lisp-octothorpe-parenthesis-characters '(?+ ?- ?C)) (defun paredit-space-for-delimiter-predicate-common-lisp (endp delimiter) (or endp (let ((case-fold-search t) (look (lambda (prefix characters n) (looking-back (concat prefix (regexp-opt (mapcar 'string characters))) (min n (point)))))) (let ((oq common-lisp-octothorpe-quotation-characters) (op common-lisp-octothorpe-parenthesis-characters) (opp common-lisp-octothorpe-parameter-parenthesis-characters)) (cond ((eq (char-syntax delimiter) ?\() (and (not (funcall look "#" op 2)) (not (funcall look "#[0-9]*" opp 20)))) ((eq (char-syntax delimiter) ?\") (not (funcall look "#" oq 2))) (else t)))))) (add-hook 'common-lisp-mode-hook (defun common-lisp-mode-hook-paredit () (make-local-variable 'paredit-space-for-delimiter-predicates) (add-to-list 'paredit-space-for-delimiter-predicates 'paredit-space-for-delimiter-predicate-common-lisp))) ** Version 21 Paredit 21 no longer has a message releasing it into the public domain. Instead it is copyright (C) 2008, Taylor R. Campbell, and available under the terms of the 3-clause BSD licence. See the paredit.el file for details. *** Style and Bugs - Use of the terms `parenthesis', `bracket', and `delimiter' has been clarified throughout the file: . /Parentheses/ are nestable objects indicated by matching delimiter pairs. For example, the text `(foo (bar baz) quux)' has two parentheses, `(foo ... quux)' and `(bar baz)'; each is delimited by matching round delimiters. . /Delimiters/ are the actual atomic objects that delimit parentheses. They may be shaped differently; for example, `[' is an opening square delimiter, and `{' is an opening curly delimiter. . The terms `bracket', `brace', and `brocket' (the latter a misspelling of `broket', for `broken bracket') are eschewed. The names `paredit-open-parenthesis' &c. have been changed to `paredit-open-round' &c., and likewise with `bracket' -> `square', `brace' -> `curly', and `brocket' -> `angled'. The old names with the morphemes `parenthesis' and `bracket' have been preserved so that users need not update initialization files immediately, but will instead be confused when a subsequent minor update breaks all their initialization files for no good reason. - Some commands now check their context more strictly, so that they won't try to do anything in comments or strings. Whether this is the right thing, I don't know. - Several small elements of style were adjusted. This should make no functional difference on the code. (If it does, then I have made a mistake.) - paredit.el no longer defines `kill-region-new'; the function is instead called `paredit-hack-kill-region', to reflect its hackish nature and to avoid name clashes. I believe that the only name remaining defined by paredit.el without `paredit-' prefixed is `backward-down-list', whose definition is so obvious that it would be silly to define it any other way, so that any name conflict does not worry me. (I suppose `kill-region-new' is obvious, too -- it is to `kill-region' as `kill-new' is to `kill'. But the omission from a pattern isn't as clear as with `backward-down-list'.) - `paredit-comment-dwim' should work in GNU Emacs 21 now, whose newcomment.el defines no `comment-or-uncomment-region'. More and more ugly compatibility hacks are accruing in paredit.el as a consequence, to my dismay. Oh well. - The release notes are now formatted more legibly, paginated nicely, and organized with Outline Mode. The gross discrepancy in writing style over the years has not changed. - The introductory comments in the file are more clearly written now. - Fixed a bug in S-expression slurpage with mixed delimiters. *** Altered Behaviour - The bindings for `)' and `M-)' have been exchanged: `)' is bound to `paredit-close-round' and `M-)' to `paredit-close-round-and-newline', so that you can now without any glaring exceptions type new code using the same keystrokes with and without Paredit Mode. You can also now paste into Emacs through a terminal without leaving spurious blank lines in the buffer. You are, of course, free to revert to the old behaviour in your .emacs file. - `paredit-semicolon' is no more. Now you may insert semicolons as you please without the infuriating escape of the following text on the line. - `paredit-move-past-close-and-newline' will now leave comments on the same line only if they are margin comments and no S-expressions are intervening between the point and the comment; that is, only if it looks like the margin comment really pertains to the expression being closed will paredit leave it on the same line. - `paredit-backward-delete', `paredit-forward-delete', and `paredit-kill' formerly accepted prefix arguments to run the basic `backward-delete-char', `delete-char', and `kill-line' commands instead, without regard for the value of the prefix argument. Now `C-u' will run the basic commands, but . `paredit-kill' will pass a numeric argument on to `kill-line', and . `paredit-backward-delete' and `paredit-forward-delete' will both delete N characters if given a numeric prefix argument N. (`paredit-kill' should probably do the same, but repeating the command N times is probably not what you want -- what you probably want is to kill at most N *lines*, but `paredit-kill' N times might kill many more lines than that. I don't know what the right thing is here, but I welcome feedback from users who want to do something like this.) - With a `C-u' prefix argument, `paredit-wrap-sexp' now wraps all S-expressions following the point until the end of the buffer or a closing delimiter. - `paredit-splice-sexp' with a `C-u' prefix argument (also known as `paredit-splice-sexp-killing-backward') will now kill from the point, rather than from the beginning of the next S-expression (or, with `C-u C-u', from the end of the previous S-expression). This means that it is more likely to do what you mean with (let ((a b) (c d)) |;; I want to preserve this comment after `C-u M-s'. (foo bar baz)) - `paredit-splice-sexp' now splices strings, by removing backslash escapes, or signals an error if doing so would destroy the structure of the buffer. - I have finally introduced the first bit of code to try to deal sensibly with broken buffers. It will probably go only downhill from here, and continue in an interminable series of kludges to handle every possible way in which the buffer can go *wrong* (it's bad enough how many ways it can be *right*). If you try type a closing delimiter from within a partial S-expression that has an opening delimiter but no closing delimiter, then it will honk at you and insert the closing delimiter -- or if what you typed doesn't match the opening delimiter, it will just honk and refuse to do anything. Also, `DEL' and `C-d' will delete spurious (but not [yet] mismatched) opening and closing delimiters, respectively. (Thanks to John Wiegley for inspiring me to do these dreary deeds.) *** New Commands - New command `paredit-yank-pop' cooperates with `paredit-wrap-sexp' by behaving either like `yank' or like `yank-pop' if the previous command was `paredit-wrap-sexp' or `paredit-yank-pop', and with the added bonus of reindenting the newly wrapped form. It is in need of a key to be bound to it; since it is different from both `yank' and `yank-pop', I decided not to override `C-y' or `M-y', and I considered `C-c C-y', but I imagine that many major modes want to take that. - New command `paredit-focus-on-defun' moves display to the top of the definition at the point. - New command `paredit-reindent-defun', which `M-q' is bound to in Paredit Mode, indents the definition the point is on, or, if the point is in a string or comment, fills the paragraph instead. (Thanks to John Wiegley for the idea.) - New variations on slurpage, barfage, and joinage. I'm still looking for keys to bind to these commands. Find them with the strings `add-to-{previous,next}-list' and `join-with-{previous,next}-list' in their names. (Again, thanks to John Wiegley for the idea.) - New command `paredit-convolute-sexp' performs the combined function of `paredit-splice-sexp-killing-backward', `paredit-wrap-sexp', and `yank'. Example: (let ((foo bar)) (let ((baz quux)) |(zot mumble) (frotz)) (gargle mumph)) -> (let ((baz quux)) (let ((foo bar)) (zot mumble) (frotz) (gargle mumph))) This would otherwise have been done with the key sequence `C-u M-s C-M-u M-( C-y C-M-u C-M-q'. `C-u M-s' could be `M-up', and `C-y C-M-u C-M-q' could be `C-c C-y' if that key is chosen for `paredit-yank-pop', making the sequence `M-up C-M-u M-( C-c C-y'. If there is a good key for `paredit-convolute-sexp', it could be a nice improvement over even that terser sequence. (Once again, this was inspired by John Wiegley's idea (and name).) [Observe, though, that the form (FROTZ) stuck with (ZOT MUMBLE) the whole time, and was not carried along as the `end' of the (LET ((BAZ QUUX)) ...) form. Hence this is *not* the idea mentioned below by the name `paredit-interchange-sexps', but a simpler approximation of the idea.] - `define-paredit-pair' now defines commands `paredit-wrap-...' for wrapping S-expressions with different delimiters, like `paredit-wrap-sexp'. The function `paredit-wrap-sexp' now accepts optional arguments for the delimiters to insert; the new commands are defined in terms of the modified `paredit-wrap-sexp'. `M-[' is now bound to `paredit-wrap-square'. ** Version 20 *** Preliminary Support for XEmacs This version introduces preliminary support for XEmacs. The changes needed were: - `check-parens' is called by the `paredit-mode' initialization only if it is fbound. - The forward and backward deletion keys are specified differently in GNU Emacs and XEmacs. - `paredit-forward' and `paredit-backward' use a "_" interactive specification in XEmacs in order to preserve the mark. - `paredit-region-active-p' is just `region-active-p' in XEmacs. - Some hair was needed to handle S-expression parse error conditions properly, and versions of XEmacs earlier than 21.5 may have obscure problems as a result. *** Style and Bugs - rxvt-specific terminal escape sequences are no longer bound to the commands that the keys those sequences denote are. Set your environment variables correctly and keep your rxvt.el updated. (Aren't terminals fun?) - HTML output is now properly quoted. Some vestigial indirections in the tables have been removed. - Yet *ANOTHER* `paredit-kill' bug is fixed. I do not know what the bug is or why it happened, but it seems to be gone now. - Improved robustness of `paredit-join-sexps' and `paredit-splice-sexp' with respect to their use in the middle of atoms, and made splicing within a string illegal. - Fixed several bugs in the paredit word killing commands. In the process, however, I encountered what seems to be a bug in Emacs's `parse-partial-sexp', which may mean bugs in other things... - Eliminated dependency on `cl' feature. - Fixed a bug in `paredit-backward-kill-word' that would cause deletion of the first double-quote in `(foo "|")'. - Fixed a bug with `paredit-backward-kill-word' at the end of buffer. - Fixed a bug with `paredit-backward-kill-word' before any words in a buffer. *** Altered Behaviour and New Functionality - `paredit-mode' now accepts a prefix argument to mean that it should not check the parentheses in the buffer first. Supply the prefix argument with care; though it is useful for editing small parts of a buffer in Lisp when the rest is random, it might also screw the whole buffer up in unpredictable ways, because most of paredit doesn't even try to handle invalid structure. - Parenthesis blinking is improved somewhat to better respect user customization of `blink-matching-paren'. - The paredit minor mode no longer exchanges C-j & RET; following the GNU Emacs conventions, it now leaves RET alone and binds C-j to `paredit-newline'. Those of you, such as me, who relied on the old exchange will have to update your .emacs files. - C-left and C-right are now bound to paredit-forward-barf-sexp and paredit-forward-slurp-sexp, instead of M-left and M-right, whose word motion bindings are no longer shadowed. - The behaviour of (, ", M-(, and M-" with respect to active regions and prefix arguments has been regularized: . With neither an active region, i.e. an active mark and transient-mark-mode, ( and " will insert a pair of delimiters at the point, and M-( and M-" will wrap the following expression with delimiters. . With an active region and no prefix argument, if and only if the region contains only balanced expressions, all four commands will wrap the region with the respective delimiters. . With a prefix argument N, all four commands will wrap the next N expressions with the commands' respective delimiters. " and M-", of course, escape any characters that need escaping first if they are used to wrap regions. - Implemented slurpage into strings. - Made `M-- M-s' equivalent to `M-- M-1 M-s'. - Changed `paredit-insert-pair' so that it will not skip whitespace before inserting the pair. - `paredit-splice-sexp' with a prefix argument and friends (i.e. `M-s', `M-', and `M-') now always create a new entry on the kill ring. ** Version 19 This version introduces support for delimiters other than round brackets. Previously, if the major mode's syntax table considered square brackets (and curly braces, &c.) to be delimiters, since no paredit commands would insert them balanced, deleting them would be tricky: paredit's DEL & C-d bindings would refuse to delete them because they would be imbalanced unless you manually type the closing delimiter. Now commands are defined for the opening and closing of parentheses (round), brackets (square), braces (curly), and brockets (angled), named `paredit-open-', `paredit-close--and-newline', and `paredit-close-'; paredit-mode binds the opening and closing square bracket keys to be `paredit-open-bracket' and `paredit-close- bracket', respectively. The rest you can bind yourself; this minimal pair of bindings will, I think, account for accidental insertion, elisp vectors, and (bletch) the equation of square and round brackets as parentheses in some non-standard Scheme extensions. Also now supported in this version is insertion of delimiter pairs around active regions in transient-mark-mode. If you mark a region with transient-mark-mode enabled, you can use any of the delimiter pair insertion keys (like opening round bracket, double-quote, &c.) to insert a pair of delimiters around the region. There are now two ways to parenthesize lists of expressions with visual feedback: using M-( (paredit-wrap-sexp) followed by C-) (paredit-forward-slurp-sexp) until satisfied, and now C-M-SPC (mark-sexp) until you have marked the desired expressions and then any of the delimiter pair insertion keys to insert the delimiters. ** Version 18 *** Style and Bugs - Corrected terminal arrow key sequences *again*. M-left and M-right were backwards. - Put the save-excursion back in paredit-recentre-on-sexp. I don't remember why it was taken out in version 13. - Fixed HTML output to stop producing spurious tags. - Corrected a number of paredit command examples. - Aesthetic changes to the code: . Regularized some aspects of code style. . Repaginated so that all pages are at most 80 lines long, and most are at least 40 lines. . Formatted headings for an outline regexp to match so that outline-minor-mode works nicely on paredit.el. *** Altered Behaviour and New Functionality - Implemented paredit-forward-kill-word & paredit-backward-kill-word, or M-d & M-DEL, which are like kill-word & backward-kill-word, but they will not kill parenthesis, string, or comment delimiters; they will preserve the structure of S-expressions, while the built-in Emacs word killing commands would destroy it. - M-" is now bound to paredit-meta-doublequote, which has the old behaviour of paredit-close-string-and-newline if within a string, but which wraps the following S-expression (or N S-expressions) in double-quotes if without a string; paredit-doublequote does the same, but the default argument is 0, not 1. - M-S (paredit-split-sexp) no longer deletes horizontal space in strings before splitting them into two. The rationale, as suggested by Zbigniew Szadkowski, is that whitespace is usually significant in strings, while not in lists, and you can type M-\ M-S if you really do want the horizontal space deleted anyway. - Reintroduced paredit-join-sexps as M-J. The implementation is now more robust: it ensures that the two S-expressions to join match -- i.e. they are both lists, or they are both strings, or they are both symbols --, and it correctly handles the atom case now as well. - Extended paredit command examples to allow multiple steps in succession of a single example. ** Version 17 *** Style and Bugs - Rewrote all documentation strings to be in the imperative mood, per GNU Emacs's guidelines. Some documentation strings may have been corrected, too, but I can't be bothered to grovel through the diff to pick out all changes to all documentation strings. - Forced parenthesis flashing even with show-paren-mode enabled. - Fixed bug in forward deletion within strings so that the empty string can be deleted. - Simplified determination of whether the point is in a comment. *** Altered Behaviour and New Functionality - Eliminated paredit-terminal-mode. All key bindings it had are now incorporated into paredit-mode's keymap. I may have changed some keybindings, too, but I don't remember what they were if I did. I think I fixed some of the keybindings in the terminal. - Added examples to documentation of all paredit commands, as well as code to generate an HTML file containing the examples in nicely formatted tables (sorry, web purists). - Made paredit-mode refuse to be enabled in a buffer with imbalanced parentheses. - Updated documentary heading. It now explains how to customize keys while still autoloading and how to make paredit's RET work nicely with SLIME's REPL. - Improved semicolon insertion: (a) to accept a numeric prefix argument dictating a number of semicolons to insert, instead of a prefix argument that forces the insertion of a semicolon without a trailing newline, which can be effected with C-q anyway; and (b) to allow insertion of semicolons before existing comments without inserting a superfluous leading newline. To comment out code, you must still use M-; or M-x comment-region. ** Version 16 - Introduced M-S (paredit-split-sexp) to split lists or strings from the middle. - Fixed the definition of M-; to use (kbd "M-;") to correctly identify the key sequence meta-semicolon, not "M-;" for M hyphen semicolon. ** Version 15 - Rewrote comment insertion code. - Implemented M-; (paredit-comment-dwim), which is like comment-dwim but inserts comments more appropriately with respect to paredit. ** Version 14 Version 14 introduced fancy comment handling: - paredit-close-list-and-newline now refuses to move a margin comment to another line; instead it will help to preserve the column of the comment. - The semicolon key is now bound to a command that will automatically move any code following the point onto the next line, so that you do not inadvertently comment out half expressions. You can still use M-; (comment-dwim) to comment out specific regions that are not meant to be code (e.g., old comments that were accidentally uncommented) or whole S-expressions, usually in conjunction with C-M-SPC (mark-sexp). ** Version 13 - Removed M-\ (paredit-join-sexps), whose key binding conflicts with delete-horizontal-space and whose implementation was inadequate and led to general uselessness of the command. - Improved RET (paredit-newline) so that it does not do anything fancy within strings and first tests whether the point is in a comment before checking whether it is in a character. - Changed paredit-skip-whitespace from skip-syntax-{forward,backward} to skip-chars-{forward,backward}, because in the Lisp syntax table newlines are not considered whitespace -- rather, they are comment ends --, but we want to skip them nevertheless. - Corrected paredit-kill in a way I don't understand. - Minor code improvements, including: . Changed call to previous-line to use forward-line instead. . Removed unnecessary save-excursion in paredit-recentre-on-sexp. . IF indentation changes. ** Version 12 - Implemented M-r (paredit-raise-sexp), which raises a single S-expression up the tree, deleting all of its siblings and its enclosing list. - Rearranged some arrow key bindings again. - Made paredit-forward-delete and paredit-backward-delete check for buffer bounds and also matching of the delimiters of empty lists. - Added a buffer bounds check to paredit-kill. - Made backward barfing signal an error, not just a message. ** Version 11 - Changed the key for paredit-splice-sexp from M-/, which is by default the popular dabbrev-expand, to M-s, which I was surprised to find no existing binding for. - Added a prefix argument to paredit-splice-sexp; see the command's documentation for details. M-up is now equivalent to C-u M-s; M-down, to C-u C-u M-s. - Fixed a lot of the terminal key sequences for the arrow key bindings in paredit-terminal-mode. - Modified the behaviour of paredit-forward and paredit-backward to change only one level of nesting depth, not to repeat until there is a sibling S-expression to move across. - Changed a lot of code to use character syntax, instead of exact character comparisons, for better generality. - Rewrote much of paredit-kill, again. ** Version 10 - Introduced paredit-forward and paredit-backward, which are like forward-sexp and backward-sexp but which will also go up a nesting level if at the end of a list. - Introduced C-c C-M-l (paredit-recentre-on-sexp), whose name is self-explanatory. - Added support for numeric prefix arguments to paredit-open-list. - Fixed paredit-kill so that it would correctly kill whitespace between parentheses, as in ( ). - Changed suggestion of what to put in your .emacs from require to autoload. ** Version 9 - Introduced enable-paredit-mode and disable-paredit-mode to better choose which one to enable. - Forced blinkage of matching parentheses in paredit-close-list and paredit-close-list-and-newline, even if show-paren-mode is enabled. ** Version 8 - Added paredit-terminal-mode, which is like paredit-mode but which provides key bindings that work in terminals, while paredit-mode contains many (such as controlled brackets) that do not work in terminals. Thanks to Jorgen Schaefer for suggesting many of the terminal key bindings. - Exchanged RET and C-j: RET now inserts the fancy newline with auto- indentation, while C-j inserts a literal line feed. While this defies convention, and some people prefer not to do this, I have found that it is more convenient for RET to have the behaviour of the common case, where auto-indentation is desired, and for C-j to insert the uncommon exact, literal line feed. You can always customize the key bindings yourself, of course. - Rearranged arrow key bindings. - Implemented paredit-close-list-and-newline, which is like paredit-close-list followed by RET (paredit-newline); and M-" (paredit-close-string-and-newline), which is similar but for strings. The closing round bracket character now inserts the newline, while the meta modifier inhibits this. - Overhauled paredit-kill. - Extended slurpage and barfage to permit their operation across arbitrary depth changes. - Fixed bizarre bug with backward barfage. I apologize for the alliteration. - Fixed a number of other small bugs. - Prefixed `paredit-' to the remaining functions defined in the file that did not already have the prefix. - Defined backward-down-list, which for some reason is not provided by lisp.el, although up-list, down-list, & backward-up-list all are. (This is the sole exception to the above item. It deserves no prefix because it ought to be defined in lisp.el with this name anyway.) ** Version 7 - Changed paredit-open-list and paredit-close-list so that they will refuse to insert parentheses inside character literals. - Substituted new code for GNU Emacs's move-past-close-and-reindent. I do not remember why now, and I no longer understand either code well enough to discern differences, other than that Emacs's m-p-c-a-r is probably faster because it incrementally parses each step of the way. All I can see is that paredit.el's code now inserts indentation before the closing parenthesis while Emacs's m-p-c-a-r does not. - Fixed bugs in deletion within character literals and strings. ** Versions 2--6 (I lost versions 2, 3, 4, & 5.) - Flushed M-" (paredit-insert-doublequote), which was rather useless and which " (paredit-doublequote) now subsumes the functionality of. - Added instrumented forward deletion as well as backward deletion, which now behave well inside strings. - Flushed unnecessary individual round bracket insertion keys; use C-q instead. - Added C-left & C-right: backward-sexp & forward-sexp, respectively. - Fixed the test of whether the point is in a character literal. - Modified " (paredit-doublequote) to insert escaped double-quote if in the middle of the string, rather than to jump past the string's closing delimiter. - Introduced bogus backslash escaping mechanism. - Introduced new command for breaking the line and indenting, and bound C-j, rather than RET, to it, according to convention. - Improved C-k (paredit-kill), particularly in strings where it will no longer kill the closing delimiter of the string. - Changed the splicage, joinage, slurpage, and barfage commands so that they will reindent only the modified list, not the whole definition. paredit-el-24/check.sh000077500000000000000000000002261244063777000147550ustar00rootroot00000000000000#!/bin/sh set -eu : ${EMACS:=emacs} exec ${EMACS} --batch --eval '(progn (byte-compile-file "paredit.el" t) (byte-compile-file "test.el" t) )' paredit-el-24/genhtml.sh000066400000000000000000000002551244063777000153350ustar00rootroot00000000000000#!/bin/sh set -Ceu : ${EMACS:=emacs} exec ${EMACS} --batch --load paredit.el --eval ' (with-temp-buffer (paredit-insert-html-examples) (write-file "paredit.html")) ' paredit-el-24/paredit.el000066400000000000000000003552311244063777000153240ustar00rootroot00000000000000;;; paredit.el --- minor mode for editing parentheses -*- Mode: Emacs-Lisp -*- ;; Copyright (C) 2005--2014 Taylor R. Campbell ;; Author: Taylor R. Campbell ;; Version: 24 ;; Created: 2005-07-31 ;; Keywords: lisp ;; Paredit is free software: you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; ;; Paredit is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with paredit. If not, see . ;;; The currently released version of paredit is available at ;;; . ;;; ;;; The latest beta version of paredit is available at ;;; . ;;; ;;; The Git repository for paredit is available at ;;; ;;; ;;; Release notes are available at ;;; . ;;; Install paredit by placing `paredit.el' in `/path/to/elisp', a ;;; directory of your choice, and adding to your .emacs file: ;;; ;;; (add-to-list 'load-path "/path/to/elisp") ;;; (autoload 'enable-paredit-mode "paredit" ;;; "Turn on pseudo-structural editing of Lisp code." ;;; t) ;;; ;;; Start Paredit Mode on the fly with `M-x enable-paredit-mode RET', ;;; or always enable it in a major mode `M' (e.g., `lisp') with: ;;; ;;; (add-hook M-mode-hook 'enable-paredit-mode) ;;; ;;; Customize paredit using `eval-after-load': ;;; ;;; (eval-after-load 'paredit ;;; '(progn ;;; (define-key paredit-mode-map (kbd "ESC M-A-C-s-)") ;;; 'paredit-dwim))) ;;; ;;; Send questions, bug reports, comments, feature suggestions, &c., ;;; via email to the author's surname at mumble.net. ;;; ;;; Paredit should run in GNU Emacs 21 or later and XEmacs 21.5.28 or ;;; later. ;;; The paredit minor mode, Paredit Mode, binds common character keys, ;;; such as `(', `)', `"', and `\', to commands that carefully insert ;;; S-expression structures in the buffer: ;;; ;;; ( inserts `()', leaving the point in the middle; ;;; ) moves the point over the next closing delimiter; ;;; " inserts `""' if outside a string, or inserts an escaped ;;; double-quote if in the middle of a string, or moves over the ;;; closing double-quote if at the end of a string; and ;;; \ prompts for the character to escape, to avoid inserting lone ;;; backslashes that may break structure. ;;; ;;; In comments, these keys insert themselves. If necessary, you can ;;; insert these characters literally outside comments by pressing ;;; `C-q' before these keys, in case a mistake has broken the ;;; structure. ;;; ;;; These key bindings are designed so that when typing new code in ;;; Paredit Mode, you can generally type exactly the same sequence of ;;; keys you would have typed without Paredit Mode. ;;; ;;; Paredit Mode also binds common editing keys, such as `DEL', `C-d', ;;; and `C-k', to commands that respect S-expression structures in the ;;; buffer: ;;; ;;; DEL deletes the previous character, unless it is a delimiter: DEL ;;; will move the point backward over a closing delimiter, and ;;; will delete a delimiter pair together if between an open and ;;; closing delimiter; ;;; ;;; C-d deletes the next character in much the same manner; and ;;; ;;; C-k kills all S-expressions that begin anywhere between the point ;;; and the end of the line or the closing delimiter of the ;;; enclosing list, whichever is first. ;;; ;;; If necessary, you can delete a character, kill a line, &c., ;;; irrespective of S-expression structure, by pressing `C-u' before ;;; these keys, in case a mistake has broken the structure. ;;; ;;; Finally, Paredit Mode binds some keys to complex S-expression ;;; editing operations. For example, `C-' makes the enclosing ;;; list slurp up an S-expression to its right (here `|' denotes the ;;; point): ;;; ;;; (foo (bar | baz) quux) C- (foo (bar | baz quux)) ;;; ;;; Some paredit commands automatically reindent code. When they do, ;;; they try to indent as locally as possible, to avoid interfering ;;; with any indentation you might have manually written. Only the ;;; advanced S-expression manipulation commands automatically reindent, ;;; and only the forms that they immediately operated upon (and their ;;; subforms). ;;; ;;; This code is written for clarity, not efficiency. It frequently ;;; walks over S-expressions redundantly. If you have problems with ;;; the time it takes to execute some of the commands, let me know. ;;; This assumes Unix-style LF line endings. (defconst paredit-version 24) (defconst paredit-beta-p nil) (eval-and-compile (defun paredit-xemacs-p () ;; No idea where I got this definition from. Edward O'Connor ;; (hober in #emacs) suggested the current definition. ;; (and (boundp 'running-xemacs) ;; running-xemacs) (featurep 'xemacs)) (defun paredit-gnu-emacs-p () ;++ This could probably be improved. (not (paredit-xemacs-p))) (defmacro xcond (&rest clauses) "Exhaustive COND. Signal an error if no clause matches." `(cond ,@clauses (t (error "XCOND lost.")))) (defalias 'paredit-warn (if (fboundp 'warn) 'warn 'message)) (defvar paredit-sexp-error-type (with-temp-buffer (insert "(") (condition-case condition (backward-sexp) (error (if (eq (car condition) 'error) (paredit-warn "%s%s%s%s%s" "Paredit is unable to discriminate" " S-expression parse errors from" " other errors. " " This may cause obscure problems. " " Please upgrade Emacs.")) (car condition))))) (defmacro paredit-handle-sexp-errors (body &rest handler) `(condition-case () ,body (,paredit-sexp-error-type ,@handler))) (put 'paredit-handle-sexp-errors 'lisp-indent-function 1) (defmacro paredit-ignore-sexp-errors (&rest body) `(paredit-handle-sexp-errors (progn ,@body) nil)) (put 'paredit-ignore-sexp-errors 'lisp-indent-function 0) (defmacro paredit-preserving-column (&rest body) "Evaluate BODY and restore point to former column, relative to code. Assumes BODY will change only indentation. If point was on code, it moves with the code. If point was on indentation, it stays in indentation." (let ((column (make-symbol "column")) (indentation (make-symbol "indentation"))) `(let ((,column (current-column)) (,indentation (paredit-current-indentation))) (let ((value (progn ,@body))) (paredit-restore-column ,column ,indentation) value)))) (put 'paredit-preserving-column 'lisp-indent-function 0) nil) ;;;; Minor Mode Definition (defvar paredit-mode-map (make-sparse-keymap) "Keymap for the paredit minor mode.") (defvar paredit-override-check-parens-function (lambda (condition) condition nil) "Function to tell whether unbalanced text should inhibit Paredit Mode.") ;;;###autoload (define-minor-mode paredit-mode "Minor mode for pseudo-structurally editing Lisp code. With a prefix argument, enable Paredit Mode even if there are unbalanced parentheses in the buffer. Paredit behaves badly if parentheses are unbalanced, so exercise caution when forcing Paredit Mode to be enabled, and consider fixing unbalanced parentheses instead. \\" :lighter " Paredit" ;; Setting `paredit-mode' to false here aborts enabling Paredit Mode. (if (and paredit-mode (not current-prefix-arg)) (condition-case condition (check-parens) (error (if (not (funcall paredit-override-check-parens-function condition)) (progn (setq paredit-mode nil) (signal (car condition) (cdr condition)))))))) (defun paredit-override-check-parens-interactively (condition) (y-or-n-p (format "Enable Paredit Mode despite condition %S? " condition))) ;;;###autoload (defun enable-paredit-mode () "Turn on pseudo-structural editing of Lisp code." (interactive) (paredit-mode +1)) (defun disable-paredit-mode () "Turn off pseudo-structural editing of Lisp code." (interactive) (paredit-mode -1)) (defvar paredit-backward-delete-key (xcond ((paredit-xemacs-p) "BS") ((paredit-gnu-emacs-p) "DEL"))) (defvar paredit-forward-delete-keys (xcond ((paredit-xemacs-p) '("DEL")) ((paredit-gnu-emacs-p) '("" "")))) ;;;; Paredit Keys ;;; Separating the definition and initialization of this variable ;;; simplifies the development of paredit, since re-evaluating DEFVAR ;;; forms doesn't actually do anything. (defvar paredit-commands nil "List of paredit commands with their keys and examples.") ;;; Each specifier is of the form: ;;; (key[s] function (example-input example-output) ...) ;;; where key[s] is either a single string suitable for passing to KBD ;;; or a list of such strings. Entries in this list may also just be ;;; strings, in which case they are headings for the next entries. (progn (setq paredit-commands `( "Basic Insertion Commands" ("(" paredit-open-round ("(a b |c d)" "(a b (|) c d)") ("(foo \"bar |baz\" quux)" "(foo \"bar (|baz\" quux)")) (")" paredit-close-round ("(a b |c )" "(a b c)|") ("; Hello,| world!" "; Hello,)| world!")) ("M-)" paredit-close-round-and-newline ("(defun f (x| ))" "(defun f (x)\n |)") ("; (Foo.|" "; (Foo.)|")) ("[" paredit-open-square ("(a b |c d)" "(a b [|] c d)") ("(foo \"bar |baz\" quux)" "(foo \"bar [|baz\" quux)")) ("]" paredit-close-square ("(define-key keymap [frob| ] 'frobnicate)" "(define-key keymap [frob]| 'frobnicate)") ("; [Bar.|" "; [Bar.]|")) ("\"" paredit-doublequote ("(frob grovel |full lexical)" "(frob grovel \"|\" full lexical)" "(frob grovel \"\"| full lexical)") ("(foo \"bar |baz\" quux)" "(foo \"bar \\\"|baz\" quux)") ("(frob grovel) ; full |lexical" "(frob grovel) ; full \"|lexical")) ("M-\"" paredit-meta-doublequote ("(foo \"bar |baz\" quux)" "(foo \"bar baz\"| quux)") ("(foo |(bar #\\x \"baz \\\\ quux\") zot)" ,(concat "(foo \"|(bar #\\\\x \\\"baz \\\\" "\\\\ quux\\\")\" zot)"))) ("\\" paredit-backslash ("(string #|)\n ; Character to escape: x" "(string #\\x|)") ("\"foo|bar\"\n ; Character to escape: \"" "\"foo\\\"|bar\"")) (";" paredit-semicolon ("|(frob grovel)" ";|(frob grovel)") ("(frob |grovel)" "(frob ;|grovel\n )") ("(frob |grovel (bloit\n zargh))" "(frob ;|grovel\n (bloit\n zargh))") ("(frob grovel) |" "(frob grovel) ;|")) ("M-;" paredit-comment-dwim ("(foo |bar) ; baz" "(foo bar) ; |baz") ("(frob grovel)|" "(frob grovel) ;|") ("(zot (foo bar)\n|\n (baz quux))" "(zot (foo bar)\n ;; |\n (baz quux))") ("(zot (foo bar) |(baz quux))" "(zot (foo bar)\n ;; |\n (baz quux))") ("|(defun hello-world ...)" ";;; |\n(defun hello-world ...)")) ("C-j" paredit-newline ("(let ((n (frobbotz))) |(display (+ n 1)\nport))" ,(concat "(let ((n (frobbotz)))" "\n |(display (+ n 1)" "\n port))"))) "Deleting & Killing" (("C-d" ,@paredit-forward-delete-keys) paredit-forward-delete ("(quu|x \"zot\")" "(quu| \"zot\")") ("(quux |\"zot\")" "(quux \"|zot\")" "(quux \"|ot\")") ("(foo (|) bar)" "(foo | bar)") ("|(foo bar)" "(|foo bar)")) (,paredit-backward-delete-key paredit-backward-delete ("(\"zot\" q|uux)" "(\"zot\" |uux)") ("(\"zot\"| quux)" "(\"zot|\" quux)" "(\"zo|\" quux)") ("(foo (|) bar)" "(foo | bar)") ("(foo bar)|" "(foo bar|)")) ("C-k" paredit-kill ("(foo bar)| ; Useless comment!" "(foo bar)|") ("(|foo bar) ; Useful comment!" "(|) ; Useful comment!") ("|(foo bar) ; Useless line!" "|") ("(foo \"|bar baz\"\n quux)" "(foo \"|\"\n quux)")) ("M-d" paredit-forward-kill-word ("|(foo bar) ; baz" "(| bar) ; baz" "(|) ; baz" "() ;|") (";;;| Frobnicate\n(defun frobnicate ...)" ";;;|\n(defun frobnicate ...)" ";;;\n(| frobnicate ...)")) (,(concat "M-" paredit-backward-delete-key) paredit-backward-kill-word ("(foo bar) ; baz\n(quux)|" "(foo bar) ; baz\n(|)" "(foo bar) ; |\n()" "(foo |) ; \n()" "(|) ; \n()")) "Movement & Navigation" ("C-M-f" paredit-forward ("(foo |(bar baz) quux)" "(foo (bar baz)| quux)") ("(foo (bar)|)" "(foo (bar))|")) ("C-M-b" paredit-backward ("(foo (bar baz)| quux)" "(foo |(bar baz) quux)") ("(|(foo) bar)" "|((foo) bar)")) ("C-M-u" paredit-backward-up) ("C-M-d" paredit-forward-down) ("C-M-p" paredit-backward-down) ; Built-in, these are FORWARD- ("C-M-n" paredit-forward-up) ; & BACKWARD-LIST, which have ; no need given C-M-f & C-M-b. "Depth-Changing Commands" ("M-(" paredit-wrap-round ("(foo |bar baz)" "(foo (|bar) baz)")) ("M-s" paredit-splice-sexp ("(foo (bar| baz) quux)" "(foo bar| baz quux)")) (("M-" "ESC ") paredit-splice-sexp-killing-backward ("(foo (let ((x 5)) |(sqrt n)) bar)" "(foo |(sqrt n) bar)")) (("M-" "ESC ") paredit-splice-sexp-killing-forward ("(a (b c| d e) f)" "(a b c| f)")) ("M-r" paredit-raise-sexp ("(dynamic-wind in (lambda () |body) out)" "(dynamic-wind in |body out)" "|body")) ("M-?" paredit-convolute-sexp ("(let ((x 5) (y 3)) (frob |(zwonk)) (wibblethwop))" "(frob |(let ((x 5) (y 3)) (zwonk) (wibblethwop)))")) "Barfage & Slurpage" (("C-)" "C-") paredit-forward-slurp-sexp ("(foo (bar |baz) quux zot)" "(foo (bar |baz quux) zot)") ("(a b ((c| d)) e f)" "(a b ((c| d) e) f)")) (("C-}" "C-") paredit-forward-barf-sexp ("(foo (bar |baz quux) zot)" "(foo (bar |baz) quux zot)")) (("C-(" "C-M-" "ESC C-") paredit-backward-slurp-sexp ("(foo bar (baz| quux) zot)" "(foo (bar baz| quux) zot)") ("(a b ((c| d)) e f)" "(a (b (c| d)) e f)")) (("C-{" "C-M-" "ESC C-") paredit-backward-barf-sexp ("(foo (bar baz |quux) zot)" "(foo bar (baz |quux) zot)")) "Miscellaneous Commands" ("M-S" paredit-split-sexp ("(hello| world)" "(hello)| (world)") ("\"Hello, |world!\"" "\"Hello, \"| \"world!\"")) ("M-J" paredit-join-sexps ("(hello)| (world)" "(hello| world)") ("\"Hello, \"| \"world!\"" "\"Hello, |world!\"") ("hello-\n| world" "hello-|world")) ("C-c C-M-l" paredit-recenter-on-sexp) ("M-q" paredit-reindent-defun) )) nil) ; end of PROGN ;;;;; Command Examples (eval-and-compile (defmacro paredit-do-commands (vars string-case &rest body) (let ((spec (nth 0 vars)) (keys (nth 1 vars)) (fn (nth 2 vars)) (examples (nth 3 vars))) `(dolist (,spec paredit-commands) (if (stringp ,spec) ,string-case (let ((,keys (let ((k (car ,spec))) (cond ((stringp k) (list k)) ((listp k) k) (t (error "Invalid paredit command %s." ,spec))))) (,fn (cadr ,spec)) (,examples (cddr ,spec))) ,@body))))) (put 'paredit-do-commands 'lisp-indent-function 2)) (defun paredit-define-keys () (paredit-do-commands (spec keys fn examples) nil ; string case (dolist (key keys) (define-key paredit-mode-map (read-kbd-macro key) fn)))) (defun paredit-function-documentation (fn) (let ((original-doc (get fn 'paredit-original-documentation)) (doc (documentation fn 'function-documentation))) (or original-doc (progn (put fn 'paredit-original-documentation doc) doc)))) (defun paredit-annotate-mode-with-examples () (let ((contents (list (paredit-function-documentation 'paredit-mode)))) (paredit-do-commands (spec keys fn examples) (push (concat "\n \n" spec "\n") contents) (let ((name (symbol-name fn))) (if (string-match (symbol-name 'paredit-) name) (push (concat "\n\n\\[" name "]\t" name (if examples (mapconcat (lambda (example) (concat "\n" (mapconcat 'identity example "\n --->\n") "\n")) examples "") "\n (no examples)\n")) contents)))) (put 'paredit-mode 'function-documentation (apply 'concat (reverse contents)))) ;; PUT returns the huge string we just constructed, which we don't ;; want it to return. nil) (defun paredit-annotate-functions-with-examples () (paredit-do-commands (spec keys fn examples) nil ; string case (put fn 'function-documentation (concat (paredit-function-documentation fn) "\n\n\\\\[" (symbol-name fn) "]\n" (mapconcat (lambda (example) (concat "\n" (mapconcat 'identity example "\n ->\n") "\n")) examples ""))))) ;;;;; HTML Examples (defun paredit-insert-html-examples () "Insert HTML for a paredit quick reference table." (interactive) (let ((insert-lines (lambda (&rest lines) (dolist (line lines) (insert line) (newline)))) (initp nil)) (paredit-do-commands (spec keys fn examples) (progn (if initp (funcall insert-lines "") (setq initp t)) (funcall insert-lines (concat "

" spec "

")) (funcall insert-lines "")) (let ((name (symbol-name fn)) (keys (mapconcat (lambda (key) (concat "" (paredit-html-quote key) "")) keys ", "))) (funcall insert-lines "") (funcall insert-lines (concat " ")) (funcall insert-lines (concat " ")) (funcall insert-lines "") (funcall insert-lines ""))) (funcall insert-lines "
" keys "" name "
") (dolist (example examples) (let ((prefix "")) (funcall insert-lines (concat prefix examples suffix)))) (funcall insert-lines "
" "" "
")
                (examples
                 (mapconcat 'paredit-html-quote
                            example
                            (concat "
")))
                (suffix "
"))) (defun paredit-html-quote (string) (with-temp-buffer (dotimes (i (length string)) (insert (let ((c (elt string i))) (cond ((eq c ?\<) "<") ((eq c ?\>) ">") ((eq c ?\&) "&") ((eq c ?\') "'") ((eq c ?\") """) (t c))))) (buffer-string))) ;;;; Delimiter Insertion (eval-and-compile (defun paredit-conc-name (&rest strings) (intern (apply 'concat strings))) (defmacro define-paredit-pair (open close name) `(progn (defun ,(paredit-conc-name "paredit-open-" name) (&optional n) ,(concat "Insert a balanced " name " pair. With a prefix argument N, put the closing " name " after N S-expressions forward. If the region is active, `transient-mark-mode' is enabled, and the region's start and end fall in the same parenthesis depth, insert a " name " pair around the region. If in a string or a comment, insert a single " name ". If in a character literal, do nothing. This prevents changing what was in the character literal to a meaningful delimiter unintentionally.") (interactive "P") (cond ((or (paredit-in-string-p) (paredit-in-comment-p)) (insert ,open)) ((not (paredit-in-char-p)) (paredit-insert-pair n ,open ,close 'goto-char) (save-excursion (backward-up-list) (indent-sexp))))) (defun ,(paredit-conc-name "paredit-close-" name) () ,(concat "Move past one closing delimiter and reindent. \(Agnostic to the specific closing delimiter.) If in a string or comment, insert a single closing " name ". If in a character literal, do nothing. This prevents changing what was in the character literal to a meaningful delimiter unintentionally.") (interactive) (paredit-move-past-close ,close)) (defun ,(paredit-conc-name "paredit-close-" name "-and-newline") () ,(concat "Move past one closing delimiter, add a newline," " and reindent. If there was a margin comment after the closing delimiter, preserve it on the same line.") (interactive) (paredit-move-past-close-and-newline ,close)) (defun ,(paredit-conc-name "paredit-wrap-" name) (&optional argument) ,(concat "Wrap the following S-expression. See `paredit-wrap-sexp' for more details.") (interactive "P") (paredit-wrap-sexp argument ,open ,close)) (add-to-list 'paredit-wrap-commands ',(paredit-conc-name "paredit-wrap-" name))))) (defvar paredit-wrap-commands '(paredit-wrap-sexp) "List of paredit commands that wrap S-expressions. Used by `paredit-yank-pop'; for internal paredit use only.") (define-paredit-pair ?\( ?\) "round") (define-paredit-pair ?\[ ?\] "square") (define-paredit-pair ?\{ ?\} "curly") (define-paredit-pair ?\< ?\> "angled") ;;; Aliases for the old names. (defalias 'paredit-open-parenthesis 'paredit-open-round) (defalias 'paredit-close-parenthesis 'paredit-close-round) (defalias 'paredit-close-parenthesis-and-newline 'paredit-close-round-and-newline) (defalias 'paredit-open-bracket 'paredit-open-square) (defalias 'paredit-close-bracket 'paredit-close-square) (defalias 'paredit-close-bracket-and-newline 'paredit-close-square-and-newline) (defun paredit-move-past-close (close) (paredit-move-past-close-and close (lambda () (paredit-blink-paren-match nil)))) (defun paredit-move-past-close-and-newline (close) (paredit-move-past-close-and close (lambda () (let ((comment.point (paredit-find-comment-on-line))) (newline) (if comment.point (save-excursion (forward-line -1) (end-of-line) (indent-to (cdr comment.point)) (insert (car comment.point))))) (lisp-indent-line) (paredit-ignore-sexp-errors (indent-sexp)) (paredit-blink-paren-match t)))) (defun paredit-move-past-close-and (close if-moved) (if (or (paredit-in-string-p) (paredit-in-comment-p)) (insert close) (if (paredit-in-char-p) (forward-char)) (paredit-move-past-close-and-reindent close) (funcall if-moved))) (defun paredit-find-comment-on-line () "Find a margin comment on the current line. Return nil if there is no such comment or if there is anything but whitespace until such a comment. If such a comment exists, delete the comment (including all leading whitespace) and return a cons whose car is the comment as a string and whose cdr is the point of the comment's initial semicolon, relative to the start of the line." (save-excursion (paredit-skip-whitespace t (point-at-eol)) (and (eq ?\; (char-after)) (not (eq ?\; (char-after (1+ (point))))) (not (or (paredit-in-string-p) (paredit-in-char-p))) (let* ((start ;Move to before the semicolon. (progn (backward-char) (point))) (comment (buffer-substring start (point-at-eol)))) (paredit-skip-whitespace nil (point-at-bol)) (delete-region (point) (point-at-eol)) (cons comment (- start (point-at-bol))))))) (defun paredit-insert-pair (n open close forward) (let* ((regionp (and (paredit-region-active-p) (paredit-region-safe-for-insert-p))) (end (and regionp (not n) (prog1 (region-end) (goto-char (region-beginning)))))) (let ((spacep (paredit-space-for-delimiter-p nil open))) (if spacep (insert " ")) (insert open) (save-excursion ;; Move past the desired region. (cond (n (funcall forward (paredit-scan-sexps-hack (point) (prefix-numeric-value n)))) (regionp (funcall forward (+ end (if spacep 2 1))))) ;; The string case can happen if we are inserting string ;; delimiters. The comment case may happen by moving to the ;; end of a buffer that has a comment with no trailing newline. (if (and (not (paredit-in-string-p)) (paredit-in-comment-p)) (newline)) (insert close) (if (paredit-space-for-delimiter-p t close) (insert " ")))))) ;++ This needs a better name... (defun paredit-scan-sexps-hack (point n) (save-excursion (goto-char point) (let ((direction (if (< 0 n) +1 -1)) (magnitude (abs n)) (count 0)) (catch 'exit (while (< count magnitude) (let ((p (paredit-handle-sexp-errors (scan-sexps (point) direction) nil))) (if (not p) (throw 'exit nil)) (goto-char p)) (setq count (+ count 1))))) (point))) (defun paredit-region-safe-for-insert-p () (save-excursion (let ((beginning (region-beginning)) (end (region-end))) (goto-char beginning) (let* ((beginning-state (paredit-current-parse-state)) (end-state (parse-partial-sexp beginning end nil nil beginning-state))) (and (= (nth 0 beginning-state) ; 0. depth in parens (nth 0 end-state)) (eq (nth 3 beginning-state) ; 3. non-nil if inside a (nth 3 end-state)) ; string (eq (nth 4 beginning-state) ; 4. comment status, yada (nth 4 end-state)) (eq (nth 5 beginning-state) ; 5. t if following char (nth 5 end-state))))))) ; quote (defvar paredit-space-for-delimiter-predicates nil "List of predicates for whether to put space by delimiter at point. Each predicate is a function that is is applied to two arguments, ENDP and DELIMITER, and that returns a boolean saying whether to put a space next to the delimiter -- before the delimiter if ENDP is false, after the delimiter if ENDP is true. If any predicate returns false, no space is inserted: every predicate has veto power. Each predicate may assume that the point is not at the beginning of the buffer, if ENDP is false, or at the end of the buffer, if ENDP is true; and that the point is not preceded, if ENDP is false, or followed, if ENDP is true, by a word or symbol constituent, a quote, or the delimiter matching DELIMITER. Each predicate should examine only text before the point, if ENDP is false, or only text after the point, if ENDP is true.") (defun paredit-space-for-delimiter-p (endp delimiter) ;; If at the buffer limit, don't insert a space. If there is a word, ;; symbol, other quote, or non-matching parenthesis delimiter (i.e. a ;; close when want an open the string or an open when we want to ;; close the string), do insert a space. (and (not (if endp (eobp) (bobp))) (memq (char-syntax (if endp (char-after) (char-before))) (list ?w ?_ ?\" (let ((matching (matching-paren delimiter))) (and matching (char-syntax matching))) (and (not endp) (eq ?\" (char-syntax delimiter)) ?\) ))) (catch 'exit (dolist (predicate paredit-space-for-delimiter-predicates) (if (not (funcall predicate endp delimiter)) (throw 'exit nil))) t))) (defun paredit-move-past-close-and-reindent (close) (let ((open (paredit-missing-close))) (if open (if (eq close (matching-paren open)) (save-excursion (message "Missing closing delimiter: %c" close) (insert close)) (error "Mismatched missing closing delimiter: %c ... %c" open close)))) (up-list) (if (catch 'return ; This CATCH returns T if it (while t ; should delete leading spaces (save-excursion ; and NIL if not. (let ((before-paren (1- (point)))) (back-to-indentation) (cond ((not (eq (point) before-paren)) ;; Can't call PAREDIT-DELETE-LEADING-WHITESPACE ;; here -- we must return from SAVE-EXCURSION ;; first. (throw 'return t)) ((save-excursion (forward-line -1) (end-of-line) (paredit-in-comment-p)) ;; Moving the closing delimiter any further ;; would put it into a comment, so we just ;; indent the closing delimiter where it is and ;; abort the loop, telling its continuation that ;; no leading whitespace should be deleted. (lisp-indent-line) (throw 'return nil)) (t (delete-indentation))))))) (paredit-delete-leading-whitespace))) (defun paredit-missing-close () (save-excursion (paredit-handle-sexp-errors (backward-up-list) (error "Not inside a list.")) (let ((open (char-after))) (paredit-handle-sexp-errors (progn (forward-sexp) nil) open)))) (defun paredit-delete-leading-whitespace () ;; This assumes that we're on the closing delimiter already. (save-excursion (backward-char) (while (let ((syn (char-syntax (char-before)))) (and (or (eq syn ?\ ) (eq syn ?-)) ; whitespace syntax ;; The above line is a perfect example of why the ;; following test is necessary. (not (paredit-in-char-p (1- (point)))))) (delete-char -1)))) (defun paredit-blink-paren-match (another-line-p) (if (and blink-matching-paren (or (not show-paren-mode) another-line-p)) (paredit-ignore-sexp-errors (save-excursion (backward-sexp) (forward-sexp) ;; SHOW-PAREN-MODE inhibits any blinking, so we disable it ;; locally here. (let ((show-paren-mode nil)) (blink-matching-open)))))) (defun paredit-doublequote (&optional n) "Insert a pair of double-quotes. With a prefix argument N, wrap the following N S-expressions in double-quotes, escaping intermediate characters if necessary. If the region is active, `transient-mark-mode' is enabled, and the region's start and end fall in the same parenthesis depth, insert a pair of double-quotes around the region, again escaping intermediate characters if necessary. Inside a comment, insert a literal double-quote. At the end of a string, move past the closing double-quote. In the middle of a string, insert a backslash-escaped double-quote. If in a character literal, do nothing. This prevents accidentally changing a what was in the character literal to become a meaningful delimiter unintentionally." (interactive "P") (cond ((paredit-in-string-p) (if (eq (point) (- (paredit-enclosing-string-end) 1)) (forward-char) ; Just move past the closing quote. ;; Don't split a \x into an escaped backslash and a string end. (if (paredit-in-string-escape-p) (forward-char)) (insert ?\\ ?\" ))) ((paredit-in-comment-p) (insert ?\" )) ((not (paredit-in-char-p)) (paredit-insert-pair n ?\" ?\" 'paredit-forward-for-quote)))) (defun paredit-meta-doublequote (&optional n) "Move to the end of the string. If not in a string, act as `paredit-doublequote'; if not prefix argument is specified and the region is not active or `transient-mark-mode' is disabled, the default is to wrap one S-expression, however, not zero." (interactive "P") (if (not (paredit-in-string-p)) (paredit-doublequote (or n (and (not (paredit-region-active-p)) 1))) (goto-char (paredit-enclosing-string-end)))) (defun paredit-meta-doublequote-and-newline (&optional n) "Move to the end of the string, insert a newline, and indent. If not in a string, act as `paredit-doublequote'; if not prefix argument is specified and the region is not active or `transient-mark-mode' is disabled, the default is to wrap one S-expression, however, not zero." (interactive "P") (if (not (paredit-in-string-p)) (paredit-doublequote (or n (and (not (paredit-region-active-p)) 1))) (progn (goto-char (paredit-enclosing-string-end)) (newline) (lisp-indent-line) (paredit-ignore-sexp-errors (indent-sexp))))) (defun paredit-forward-for-quote (end) (let ((state (paredit-current-parse-state))) (while (< (point) end) (let ((new-state (parse-partial-sexp (point) (1+ (point)) nil nil state))) (if (paredit-in-string-p new-state) (if (not (paredit-in-string-escape-p)) (setq state new-state) ;; Escape character: turn it into an escaped escape ;; character by appending another backslash. (insert ?\\ ) ;; Now the point is after both escapes, and we want to ;; rescan from before the first one to after the second ;; one. (setq state (parse-partial-sexp (- (point) 2) (point) nil nil state)) ;; Advance the end point, since we just inserted a new ;; character. (setq end (1+ end))) ;; String: escape by inserting a backslash before the quote. (backward-char) (insert ?\\ ) ;; The point is now between the escape and the quote, and we ;; want to rescan from before the escape to after the quote. (setq state (parse-partial-sexp (1- (point)) (1+ (point)) nil nil state)) ;; Advance the end point for the same reason as above. (setq end (1+ end))))))) ;;;; Escape Insertion (defun paredit-backslash () "Insert a backslash followed by a character to escape." (interactive) (cond ((paredit-in-string-p) (paredit-backslash-interactive)) ((paredit-in-comment-p) (insert ?\\)) ((paredit-in-char-p) (forward-char) (paredit-backslash-interactive)) (t (paredit-backslash-interactive)))) (defun paredit-backslash-interactive () (insert ?\\ ) ;; Read a character to insert after the backslash. If anything ;; goes wrong -- the user hits delete (entering the rubout ;; `character'), aborts with C-g, or enters non-character input ;; -- then delete the backslash to avoid a dangling escape. (let ((delete-p t)) (unwind-protect (let ((char (read-char "Character to escape: "))) (if (not (eq char ?\^?)) (progn (message "Character to escape: %c" char) (insert char) (setq delete-p nil)))) (if delete-p (progn (message "Deleting escape.") (delete-char -1)))))) (defun paredit-newline () "Insert a newline and indent it. This is like `newline-and-indent', but it not only indents the line that the point is on but also the S-expression following the point, if there is one. Move forward one character first if on an escaped character. If in a string, just insert a literal newline. If in a comment and if followed by invalid structure, call `indent-new-comment-line' to keep the invalid structure in a comment." (interactive) (cond ((paredit-in-string-p) (newline)) ((paredit-in-comment-p) (if (paredit-region-ok-p (point) (point-at-eol)) (progn (newline-and-indent) (paredit-ignore-sexp-errors (indent-sexp))) (indent-new-comment-line))) (t (if (paredit-in-char-p) (forward-char)) (newline-and-indent) ;; Indent the following S-expression, but don't signal an ;; error if there's only a closing delimiter after the point. (paredit-ignore-sexp-errors (indent-sexp))))) (defun paredit-reindent-defun (&optional argument) "Reindent the definition that the point is on. If the point is in a string or a comment, fill the paragraph instead, and with a prefix argument, justify as well." (interactive "P") (if (or (paredit-in-string-p) (paredit-in-comment-p)) (lisp-fill-paragraph argument) (paredit-preserving-column (save-excursion (end-of-defun) (beginning-of-defun) (indent-sexp))))) ;;;; Comment Insertion (defun paredit-semicolon (&optional n) "Insert a semicolon. With a prefix argument N, insert N semicolons. If in a string, do just that and nothing else. If in a character literal, move to the beginning of the character literal before inserting the semicolon. If the enclosing list ends on the line after the point, break the line after the last S-expression following the point. If a list begins on the line after the point but ends on a different line, break the line after the last S-expression following the point before the list." (interactive "p") (if (or (paredit-in-string-p) (paredit-in-comment-p)) (insert (make-string (or n 1) ?\; )) (if (paredit-in-char-p) (backward-char 2)) (let ((line-break-point (paredit-semicolon-find-line-break-point))) (if line-break-point (paredit-semicolon-with-line-break line-break-point (or n 1)) (insert (make-string (or n 1) ?\; )))))) (defun paredit-semicolon-find-line-break-point () (and (not (eolp)) ;Implies (not (eobp)). (let ((eol (point-at-eol))) (save-excursion (catch 'exit (while t (let ((line-break-point (point))) (cond ((paredit-handle-sexp-errors (progn (forward-sexp) t) nil) ;; Successfully advanced by an S-expression. ;; If that S-expression started on this line ;; and ended on another one, break here. (cond ((not (eq eol (point-at-eol))) (throw 'exit (and (save-excursion (backward-sexp) (eq eol (point-at-eol))) line-break-point))) ((eobp) (throw 'exit nil)))) ((save-excursion (paredit-skip-whitespace t (point-at-eol)) (or (eolp) (eobp) (eq (char-after) ?\;))) ;; Can't move further, but there's no closing ;; delimiter we're about to clobber -- either ;; it's on the next line or we're at the end of ;; the buffer. Don't break the line. (throw 'exit nil)) (t ;; Can't move because we hit a delimiter at the ;; end of this line. Break here. (throw 'exit line-break-point)))))))))) (defun paredit-semicolon-with-line-break (line-break-point n) (let ((line-break-marker (make-marker))) (set-marker line-break-marker line-break-point) (set-marker-insertion-type line-break-marker t) (insert (make-string (or n 1) ?\; )) (save-excursion (goto-char line-break-marker) (set-marker line-break-marker nil) (newline) (lisp-indent-line) ;; This step is redundant if we are inside a list, but even if we ;; are at the top level, we want at least to indent whatever we ;; bumped off the line. (paredit-ignore-sexp-errors (indent-sexp)) (paredit-indent-sexps)))) ;;; This is all a horrible, horrible hack, primarily for GNU Emacs 21, ;;; in which there is no `comment-or-uncomment-region'. (autoload 'comment-forward "newcomment") (autoload 'comment-normalize-vars "newcomment") (autoload 'comment-region "newcomment") (autoload 'comment-search-forward "newcomment") (autoload 'uncomment-region "newcomment") (defun paredit-initialize-comment-dwim () (require 'newcomment) (if (not (fboundp 'comment-or-uncomment-region)) (defalias 'comment-or-uncomment-region (lambda (beginning end &optional argument) (interactive "*r\nP") (if (save-excursion (goto-char beginning) (comment-forward (point-max)) (<= end (point))) (uncomment-region beginning end argument) (comment-region beginning end argument))))) (defalias 'paredit-initialize-comment-dwim 'comment-normalize-vars) (comment-normalize-vars)) (defun paredit-comment-dwim (&optional argument) "Call the Lisp comment command you want (Do What I Mean). This is like `comment-dwim', but it is specialized for Lisp editing. If transient mark mode is enabled and the mark is active, comment or uncomment the selected region, depending on whether it was entirely commented not not already. If there is already a comment on the current line, with no prefix argument, indent to that comment; with a prefix argument, kill that comment. Otherwise, insert a comment appropriate for the context and ensure that any code following the comment is moved to the next line. At the top level, where indentation is calculated to be at column 0, insert a triple-semicolon comment; within code, where the indentation is calculated to be non-zero, and on the line there is either no code at all or code after the point, insert a double-semicolon comment; and if the point is after all code on the line, insert a single- semicolon margin comment at `comment-column'." (interactive "*P") (paredit-initialize-comment-dwim) (cond ((paredit-region-active-p) (comment-or-uncomment-region (region-beginning) (region-end) argument)) ((paredit-comment-on-line-p) (if argument (comment-kill (if (integerp argument) argument nil)) (comment-indent))) (t (paredit-insert-comment)))) (defun paredit-comment-on-line-p () "True if there is a comment on the line following point. This is expected to be called only in `paredit-comment-dwim'; do not call it elsewhere." (save-excursion (beginning-of-line) (let ((comment-p nil)) ;; Search forward for a comment beginning. If there is one, set ;; COMMENT-P to true; if not, it will be nil. (while (progn (setq comment-p ;t -> no error (comment-search-forward (point-at-eol) t)) (and comment-p (or (paredit-in-string-p) (paredit-in-char-p (1- (point)))))) (forward-char)) comment-p))) (defun paredit-insert-comment () (let ((code-after-p (save-excursion (paredit-skip-whitespace t (point-at-eol)) (not (eolp)))) (code-before-p (save-excursion (paredit-skip-whitespace nil (point-at-bol)) (not (bolp))))) (cond ((and (bolp) (let ((indent (let ((indent (calculate-lisp-indent))) (if (consp indent) (car indent) indent)))) (and indent (zerop indent)))) ;; Top-level comment (if code-after-p (save-excursion (newline))) (insert ";;; ")) ((or code-after-p (not code-before-p)) ;; Code comment (if code-before-p (newline-and-indent) (lisp-indent-line)) (insert ";; ") (if code-after-p (save-excursion (newline) (lisp-indent-line) (paredit-indent-sexps)))) (t ;; Margin comment (indent-to comment-column 1) ; 1 -> force one leading space (insert ?\; ))))) ;;;; Character Deletion (defun paredit-forward-delete (&optional argument) "Delete a character forward or move forward over a delimiter. If on an opening S-expression delimiter, move forward into the S-expression. If on a closing S-expression delimiter, refuse to delete unless the S-expression is empty, in which case delete the whole S-expression. With a numeric prefix argument N, delete N characters forward. With a `C-u' prefix argument, simply delete a character forward, without regard for delimiter balancing." (interactive "P") (cond ((or (consp argument) (eobp)) (delete-char +1)) ((integerp argument) (if (< argument 0) (paredit-backward-delete argument) (while (> argument 0) (paredit-forward-delete) (setq argument (- argument 1))))) ((paredit-in-string-p) (paredit-forward-delete-in-string)) ((paredit-in-comment-p) (paredit-forward-delete-in-comment)) ((paredit-in-char-p) ; Escape -- delete both chars. (delete-char -1) (delete-char +1)) ((eq (char-after) ?\\ ) ; ditto (delete-char +2)) ((let ((syn (char-syntax (char-after)))) (or (eq syn ?\( ) (eq syn ?\" ))) (if (save-excursion (paredit-handle-sexp-errors (progn (forward-sexp) t) nil)) (forward-char) (message "Deleting spurious opening delimiter.") (delete-char +1))) ((and (not (paredit-in-char-p (1- (point)))) (eq (char-syntax (char-after)) ?\) ) (eq (char-before) (matching-paren (char-after)))) (delete-char -1) ; Empty list -- delete both (delete-char +1)) ; delimiters. ((eq ?\; (char-after)) (paredit-forward-delete-comment-start)) ((eq (char-syntax (char-after)) ?\) ) (if (paredit-handle-sexp-errors (save-excursion (forward-char) (backward-sexp) t) nil) (message "End of list!") (progn (message "Deleting spurious closing delimiter.") (delete-char +1)))) ;; Just delete a single character, if it's not a closing ;; delimiter. (The character literal case is already handled ;; by now.) (t (delete-char +1)))) (defun paredit-forward-delete-in-string () (let ((start+end (paredit-string-start+end-points))) (cond ((not (eq (point) (cdr start+end))) ;; If it's not the close-quote, it's safe to delete. But ;; first handle the case that we're in a string escape. (cond ((paredit-in-string-escape-p) ;; We're right after the backslash, so backward ;; delete it before deleting the escaped character. (delete-char -1)) ((eq (char-after) ?\\ ) ;; If we're not in a string escape, but we are on a ;; backslash, it must start the escape for the next ;; character, so delete the backslash before deleting ;; the next character. (delete-char +1))) (delete-char +1)) ((eq (1- (point)) (car start+end)) ;; If it is the close-quote, delete only if we're also right ;; past the open-quote (i.e. it's empty), and then delete ;; both quotes. Otherwise we refuse to delete it. (delete-char -1) (delete-char +1))))) (defun paredit-check-forward-delete-in-comment () ;; Point is in a comment, possibly at eol. We are about to delete ;; some characters forward; if we are at eol, we are about to delete ;; the line break. Refuse to do so if if moving the next line into ;; the comment would break structure. (if (eolp) (let ((next-line-start (point-at-bol 2)) (next-line-end (point-at-eol 2))) (paredit-check-region next-line-start next-line-end)))) (defun paredit-forward-delete-in-comment () (paredit-check-forward-delete-in-comment) (delete-char +1)) (defun paredit-forward-delete-comment-start () ;; Point precedes a comment start (not at eol). Refuse to delete a ;; comment start if the comment contains unbalanced junk. (paredit-check-region (+ (point) 1) (point-at-eol)) (delete-char +1)) (defun paredit-backward-delete (&optional argument) "Delete a character backward or move backward over a delimiter. If on a closing S-expression delimiter, move backward into the S-expression. If on an opening S-expression delimiter, refuse to delete unless the S-expression is empty, in which case delete the whole S-expression. With a numeric prefix argument N, delete N characters backward. With a `C-u' prefix argument, simply delete a character backward, without regard for delimiter balancing." (interactive "P") (cond ((or (consp argument) (bobp)) ;++ Should this untabify? (delete-char -1)) ((integerp argument) (if (< argument 0) (paredit-forward-delete (- 0 argument)) (while (> argument 0) (paredit-backward-delete) (setq argument (- argument 1))))) ((paredit-in-string-p) (paredit-backward-delete-in-string)) ((paredit-in-comment-p) (paredit-backward-delete-in-comment)) ((paredit-in-char-p) ; Escape -- delete both chars. (delete-char -1) (delete-char +1)) ((paredit-in-char-p (1- (point))) (delete-char -2)) ; ditto ((let ((syn (char-syntax (char-before)))) (or (eq syn ?\) ) (eq syn ?\" ))) (if (save-excursion (paredit-handle-sexp-errors (progn (backward-sexp) t) nil)) (backward-char) (message "Deleting spurious closing delimiter.") (delete-char -1))) ((and (eq (char-syntax (char-before)) ?\( ) (eq (char-after) (matching-paren (char-before)))) (delete-char -1) ; Empty list -- delete both (delete-char +1)) ; delimiters. ((bolp) (paredit-backward-delete-maybe-comment-end)) ((eq (char-syntax (char-before)) ?\( ) (if (paredit-handle-sexp-errors (save-excursion (backward-char) (forward-sexp) t) nil) (message "Beginning of list!") (progn (message "Deleting spurious closing delimiter.") (delete-char -1)))) ;; Delete it, unless it's an opening delimiter. The case of ;; character literals is already handled by now. (t ;; Turn off the @#&*&!^&(%^ botch in GNU Emacs 24 that changed ;; `backward-delete-char' and `backward-delete-char-untabify' ;; semantically so that they delete the region in transient ;; mark mode. (let ((delete-active-region nil)) (backward-delete-char-untabify +1))))) (defun paredit-backward-delete-in-string () (let ((start+end (paredit-string-start+end-points))) (cond ((not (eq (1- (point)) (car start+end))) ;; If it's not the open-quote, it's safe to delete. (if (paredit-in-string-escape-p) ;; If we're on a string escape, since we're about to ;; delete the backslash, we must first delete the ;; escaped char. (delete-char +1)) (delete-char -1) (if (paredit-in-string-escape-p) ;; If, after deleting a character, we find ourselves in ;; a string escape, we must have deleted the escaped ;; character, and the backslash is behind the point, so ;; backward delete it. (delete-char -1))) ((eq (point) (cdr start+end)) ;; If it is the open-quote, delete only if we're also right ;; past the close-quote (i.e. it's empty), and then delete ;; both quotes. Otherwise we refuse to delete it. (delete-char -1) (delete-char +1))))) (defun paredit-backward-delete-in-comment () ;; Point is in a comment, possibly just after the comment start. ;; Refuse to delete a comment start if the comment contains ;; unbalanced junk. (if (save-excursion (backward-char) ;; Must call `paredit-in-string-p' before ;; `paredit-in-comment-p'. (not (or (paredit-in-string-p) (paredit-in-comment-p)))) (paredit-check-region (point) (point-at-eol))) (backward-delete-char-untabify +1)) (defun paredit-backward-delete-maybe-comment-end () ;; Point is at bol, possibly just after a comment end (i.e., the ;; previous line may have had a line comment). Refuse to delete a ;; comment end if moving the current line into the previous line's ;; comment would break structure. (if (save-excursion (backward-char) (and (not (paredit-in-string-p)) (paredit-in-comment-p))) (paredit-check-region (point-at-eol) (point-at-bol))) (delete-char -1)) ;;;; Killing (defun paredit-kill (&optional argument) "Kill a line as if with `kill-line', but respecting delimiters. In a string, act exactly as `kill-line' but do not kill past the closing string delimiter. On a line with no S-expressions on it starting after the point or within a comment, act exactly as `kill-line'. Otherwise, kill all S-expressions that start after the point. With a `C-u' prefix argument, just do the standard `kill-line'. With a numeric prefix argument N, do `kill-line' that many times." (interactive "P") (cond (argument (kill-line (if (integerp argument) argument 1))) ((paredit-in-string-p) (paredit-kill-line-in-string)) ((paredit-in-comment-p) (paredit-kill-line-in-comment)) ((save-excursion (paredit-skip-whitespace t (point-at-eol)) (or (eolp) (eq (char-after) ?\; ))) ;** Be careful about trailing backslashes. (if (paredit-in-char-p) (backward-char)) (kill-line)) (t (paredit-kill-sexps-on-line)))) (defun paredit-kill-line-in-string () (if (save-excursion (paredit-skip-whitespace t (point-at-eol)) (eolp)) (kill-line) (save-excursion ;; Be careful not to split an escape sequence. (if (paredit-in-string-escape-p) (backward-char)) (kill-region (point) (min (point-at-eol) (cdr (paredit-string-start+end-points))))))) (defun paredit-kill-line-in-comment () ;; The variable `kill-whole-line' is not relevant: the point is in a ;; comment, and hence not at the beginning of the line. (paredit-check-forward-delete-in-comment) (kill-line)) (defun paredit-kill-sexps-on-line () (if (paredit-in-char-p) ; Move past the \ and prefix. (backward-char 2)) ; (# in Scheme/CL, ? in elisp) (let ((beginning (point)) (eol (point-at-eol))) (let ((end-of-list-p (paredit-forward-sexps-to-kill beginning eol))) ;; If we got to the end of the list and it's on the same line, ;; move backward past the closing delimiter before killing. (This ;; allows something like killing the whitespace in ( ).) (if end-of-list-p (progn (up-list) (backward-char))) (if kill-whole-line (paredit-kill-sexps-on-whole-line beginning) (kill-region beginning ;; If all of the S-expressions were on one line, ;; i.e. we're still on that line after moving past ;; the last one, kill the whole line, including ;; any comments; otherwise just kill to the end of ;; the last S-expression we found. Be sure, ;; though, not to kill any closing parentheses. (if (and (not end-of-list-p) (eq (point-at-eol) eol)) eol (point))))))) ;;; Please do not try to understand this code unless you have a VERY ;;; good reason to do so. I gave up trying to figure it out well ;;; enough to explain it, long ago. (defun paredit-forward-sexps-to-kill (beginning eol) (let ((end-of-list-p nil) (firstp t)) ;; Move to the end of the last S-expression that started on this ;; line, or to the closing delimiter if the last S-expression in ;; this list is on the line. (catch 'return (while t ;; This and the `kill-whole-line' business below fix a bug that ;; inhibited any S-expression at the very end of the buffer ;; (with no trailing newline) from being deleted. It's a ;; bizarre fix that I ought to document at some point, but I am ;; too busy at the moment to do so. (if (and kill-whole-line (eobp)) (throw 'return nil)) (save-excursion (paredit-handle-sexp-errors (forward-sexp) (up-list) (setq end-of-list-p (eq (point-at-eol) eol)) (throw 'return nil)) (if (or (and (not firstp) (not kill-whole-line) (eobp)) (paredit-handle-sexp-errors (progn (backward-sexp) nil) t) (not (eq (point-at-eol) eol))) (throw 'return nil))) (forward-sexp) (if (and firstp (not kill-whole-line) (eobp)) (throw 'return nil)) (setq firstp nil))) end-of-list-p)) (defun paredit-kill-sexps-on-whole-line (beginning) (kill-region beginning (or (save-excursion ; Delete trailing indentation... (paredit-skip-whitespace t) (and (not (eq (char-after) ?\; )) (point))) ;; ...or just use the point past the newline, if ;; we encounter a comment. (point-at-eol))) (cond ((save-excursion (paredit-skip-whitespace nil (point-at-bol)) (bolp)) ;; Nothing but indentation before the point, so indent it. (lisp-indent-line)) ((eobp) nil) ; Protect the CHAR-SYNTAX below against NIL. ;; Insert a space to avoid invalid joining if necessary. ((let ((syn-before (char-syntax (char-before))) (syn-after (char-syntax (char-after)))) (or (and (eq syn-before ?\) ) ; Separate opposing (eq syn-after ?\( )) ; parentheses, (and (eq syn-before ?\" ) ; string delimiter (eq syn-after ?\" )) ; pairs, (and (memq syn-before '(?_ ?w)) ; or word or symbol (memq syn-after '(?_ ?w))))) ; constituents. (insert " ")))) ;;;;; Killing Words ;;; This is tricky and asymmetrical because backward parsing is ;;; extraordinarily difficult or impossible, so we have to implement ;;; killing in both directions by parsing forward. (defun paredit-forward-kill-word () "Kill a word forward, skipping over intervening delimiters." (interactive) (let ((beginning (point))) (skip-syntax-forward " -") (let* ((parse-state (paredit-current-parse-state)) (state (paredit-kill-word-state parse-state 'char-after))) (while (not (or (eobp) (eq ?w (char-syntax (char-after))))) (setq parse-state (progn (forward-char 1) (paredit-current-parse-state)) ;; (parse-partial-sexp (point) (1+ (point)) ;; nil nil parse-state) ) (let* ((old-state state) (new-state (paredit-kill-word-state parse-state 'char-after))) (cond ((not (eq old-state new-state)) (setq parse-state (paredit-kill-word-hack old-state new-state parse-state)) (setq state (paredit-kill-word-state parse-state 'char-after)) (setq beginning (point))))))) (goto-char beginning) (kill-word 1))) (defun paredit-backward-kill-word () "Kill a word backward, skipping over any intervening delimiters." (interactive) (if (not (or (bobp) (eq (char-syntax (char-before)) ?w))) (let ((end (point))) (backward-word 1) (forward-word 1) (goto-char (min end (point))) (let* ((parse-state (paredit-current-parse-state)) (state (paredit-kill-word-state parse-state 'char-before))) (while (and (< (point) end) (progn (setq parse-state (parse-partial-sexp (point) (1+ (point)) nil nil parse-state)) (or (eq state (paredit-kill-word-state parse-state 'char-before)) (progn (backward-char 1) nil))))) (if (and (eq state 'comment) (eq ?\# (char-after (point))) (eq ?\| (char-before (point)))) (backward-char 1))))) (backward-kill-word 1)) ;;;;;; Word-Killing Auxiliaries (defun paredit-kill-word-state (parse-state adjacent-char-fn) (cond ((paredit-in-comment-p parse-state) 'comment) ((paredit-in-string-p parse-state) 'string) ((memq (char-syntax (funcall adjacent-char-fn)) '(?\( ?\) )) 'delimiter) (t 'other))) ;;; This optionally advances the point past any comment delimiters that ;;; should probably not be touched, based on the last state change and ;;; the characters around the point. It returns a new parse state, ;;; starting from the PARSE-STATE parameter. (defun paredit-kill-word-hack (old-state new-state parse-state) (cond ((and (not (eq old-state 'comment)) (not (eq new-state 'comment)) (not (paredit-in-string-escape-p)) (eq ?\# (char-before)) (eq ?\| (char-after))) (forward-char 1) (paredit-current-parse-state) ;; (parse-partial-sexp (point) (1+ (point)) ;; nil nil parse-state) ) ((and (not (eq old-state 'comment)) (eq new-state 'comment) (eq ?\; (char-before))) (skip-chars-forward ";") (paredit-current-parse-state) ;; (parse-partial-sexp (point) (save-excursion ;; (skip-chars-forward ";")) ;; nil nil parse-state) ) (t parse-state))) (defun paredit-copy-as-kill () "Save in the kill ring the region that `paredit-kill' would kill." (interactive) (cond ((paredit-in-string-p) (paredit-copy-as-kill-in-string)) ((paredit-in-comment-p) (copy-region-as-kill (point) (point-at-eol))) ((save-excursion (paredit-skip-whitespace t (point-at-eol)) (or (eolp) (eq (char-after) ?\; ))) ;** Be careful about trailing backslashes. (save-excursion (if (paredit-in-char-p) (backward-char)) (copy-region-as-kill (point) (point-at-eol)))) (t (paredit-copy-sexps-as-kill)))) (defun paredit-copy-as-kill-in-string () (save-excursion (if (paredit-in-string-escape-p) (backward-char)) (copy-region-as-kill (point) (min (point-at-eol) (cdr (paredit-string-start+end-points)))))) (defun paredit-copy-sexps-as-kill () (save-excursion (if (paredit-in-char-p) (backward-char 2)) (let ((beginning (point)) (eol (point-at-eol))) (let ((end-of-list-p (paredit-forward-sexps-to-kill beginning eol))) (if end-of-list-p (progn (up-list) (backward-char))) (copy-region-as-kill beginning (cond (kill-whole-line (or (save-excursion (paredit-skip-whitespace t) (and (not (eq (char-after) ?\; )) (point))) (point-at-eol))) ((and (not end-of-list-p) (eq (point-at-eol) eol)) eol) (t (point)))))))) ;;;; Deleting Regions (defun paredit-delete-region (start end) "Delete the text between point and mark, like `delete-region'. If that text is unbalanced, signal an error instead. With a prefix argument, skip the balance check." (interactive "r") (if (and start end (not current-prefix-arg)) (paredit-check-region-for-delete start end)) (setq this-command 'delete-region) (delete-region start end)) (defun paredit-kill-region (start end) "Kill the text between point and mark, like `kill-region'. If that text is unbalanced, signal an error instead. With a prefix argument, skip the balance check." (interactive "r") (if (and start end (not current-prefix-arg)) (paredit-check-region-for-delete start end)) (setq this-command 'kill-region) (kill-region start end)) (defun paredit-check-region-for-delete (start end) "Signal an error deleting text between START and END is unsafe." (save-excursion (goto-char start) (let* ((start-state (paredit-current-parse-state)) (end-state (parse-partial-sexp start end nil nil start-state))) (paredit-check-region-for-delete:depth start start-state end end-state) (paredit-check-region-for-delete:string start start-state end end-state) (paredit-check-region-for-delete:comment start start-state end end-state) (paredit-check-region-for-delete:char-quote start start-state end end-state)))) (defun paredit-check-region-for-delete:depth (start start-state end end-state) (let ((start-depth (nth 0 start-state)) (end-depth (nth 0 end-state))) (if (not (= start-depth end-depth)) (error "Mismatched parenthesis depth: %S at start, %S at end." start-depth end-depth)))) (defun paredit-check-region-for-delete:string (start start-state end end-state) (let ((start-string-p (nth 3 start-state)) (end-string-p (nth 3 end-state))) (if (not (eq start-string-p end-string-p)) (error "Mismatched string state: start %sin string, end %sin string." (if start-string-p "" "not ") (if end-string-p "" "not "))))) (defun paredit-check-region-for-delete:comment (start start-state end end-state) (let ((start-comment-state (nth 4 start-state)) (end-comment-state (nth 4 end-state))) (if (not (or (eq start-comment-state end-comment-state) ;; If we are moving text into or out of a line ;; comment, make sure that the text is balanced. (The ;; comment state may be a number, not t or nil at all, ;; for nestable comments, which are not handled by ;; this heuristic (or any of paredit, really).) (and (or (and (eq start-comment-state nil) (eq end-comment-state t)) (and (eq start-comment-state t) (eq end-comment-state nil))) (save-excursion (goto-char end) (paredit-region-ok-p (point) (point-at-eol)))))) (error "Mismatched comment state: %s" (cond ((and (integerp start-comment-state) (integerp end-comment-state)) (format "depth %S at start, depth %S at end." start-comment-state end-comment-state)) ((integerp start-comment-state) "start in nested comment, end otherwise.") ((integerp end-comment-state) "end in nested comment, start otherwise.") (start-comment-state "start in comment, end not in comment.") (end-comment-state "end in comment, start not in comment.") (t (format "start %S, end %S." start-comment-state end-comment-state))))))) (defun paredit-check-region-for-delete:char-quote (start start-state end end-state) (let ((start-char-quote (nth 5 start-state)) (end-char-quote (nth 5 end-state))) (if (not (eq start-char-quote end-char-quote)) (let ((phrase "character quotation")) (error "Mismatched %s: start %sin %s, end %sin %s." phrase (if start-char-quote "" "not ") phrase (if end-char-quote "" "not ") phrase))))) ;;;; Point Motion (eval-and-compile (defmacro defun-motion (name bvl doc &rest body) `(defun ,name ,bvl ,doc ,(xcond ((paredit-xemacs-p) '(interactive "_")) ((paredit-gnu-emacs-p) ;++ Not sure this is sufficient for the `^'. (if (fboundp 'handle-shift-selection) '(interactive "^p") '(interactive "p")))) ,@body))) (defun-motion paredit-forward (&optional arg) "Move forward an S-expression, or up an S-expression forward. If there are no more S-expressions in this one before the closing delimiter, move past that closing delimiter; otherwise, move forward past the S-expression following the point." (let ((n (or arg 1))) (cond ((< 0 n) (dotimes (i n) (paredit-move-forward))) ((< n 0) (dotimes (i (- n)) (paredit-move-backward)))))) (defun-motion paredit-backward (&optional arg) "Move backward an S-expression, or up an S-expression backward. If there are no more S-expressions in this one before the opening delimiter, move past that opening delimiter backward; otherwise, move move backward past the S-expression preceding the point." (let ((n (or arg 1))) (cond ((< 0 n) (dotimes (i n) (paredit-move-backward))) ((< n 0) (dotimes (i (- n)) (paredit-move-forward)))))) (defun paredit-move-forward () (cond ((paredit-in-string-p) (let ((end (paredit-enclosing-string-end))) ;; `forward-sexp' and `up-list' may move into the next string ;; in the buffer. Don't do that; move out of the current one. (if (paredit-handle-sexp-errors (progn (paredit-handle-sexp-errors (forward-sexp) (up-list)) (<= end (point))) t) (goto-char end)))) ((paredit-in-char-p) (forward-char)) (t (paredit-handle-sexp-errors (forward-sexp) (up-list))))) (defun paredit-move-backward () (cond ((paredit-in-string-p) (let ((start (paredit-enclosing-string-start))) (if (paredit-handle-sexp-errors (progn (paredit-handle-sexp-errors (backward-sexp) (backward-up-list)) (<= (point) start)) t) (goto-char start)))) ((paredit-in-char-p) ;++ Corner case: a buffer of `\|x'. What to do? (backward-char 2)) (t (paredit-handle-sexp-errors (backward-sexp) (backward-up-list))))) ;;;; Window Positioning (defalias 'paredit-recentre-on-sexp 'paredit-recenter-on-sexp) (defun paredit-recenter-on-sexp (&optional n) "Recenter the screen on the S-expression following the point. With a prefix argument N, encompass all N S-expressions forward." (interactive "P") (let* ((p (point)) (end-point (progn (forward-sexp n) (point))) (start-point (progn (goto-char end-point) (backward-sexp n) (point)))) ;; Point is at beginning of first S-expression. (let ((p-visible nil) (start-visible nil)) (save-excursion (forward-line (/ (count-lines start-point end-point) 2)) (recenter) (setq p-visible (pos-visible-in-window-p p)) (setq start-visible (pos-visible-in-window-p start-point))) (cond ((not start-visible) ;; Implies (not p-visible). Put the start at the top of ;; the screen. (recenter 0)) (p-visible ;; Go back to p if we can. (goto-char p)))))) (defun paredit-recenter-on-defun () "Recenter the screen on the definition at point." (interactive) (save-excursion (beginning-of-defun) (paredit-recenter-on-sexp))) (defun paredit-focus-on-defun () "Moves display to the top of the definition at point." (interactive) (beginning-of-defun) (recenter 0)) ;;;; Generalized Upward/Downward Motion (defun paredit-up/down (n vertical-direction) (let ((horizontal-direction (if (< 0 n) +1 -1))) (while (/= n 0) (goto-char (paredit-next-up/down-point horizontal-direction vertical-direction)) (setq n (- n horizontal-direction))))) (defun paredit-next-up/down-point (horizontal-direction vertical-direction) (let ((state (paredit-current-parse-state)) (scan-lists (lambda () (scan-lists (point) horizontal-direction vertical-direction)))) (cond ((paredit-in-string-p state) (let ((start+end (paredit-string-start+end-points state))) (if (< 0 vertical-direction) (if (< 0 horizontal-direction) (+ 1 (cdr start+end)) (car start+end)) ;; We could let the user try to descend into lists ;; within the string, but that would be asymmetric ;; with the up case, which rises out of the whole ;; string and not just out of a list within the ;; string, so this case will just be an error. (error "Can't descend further into string.")))) ((< 0 vertical-direction) ;; When moving up, just try to rise up out of the list. (or (funcall scan-lists) (buffer-end horizontal-direction))) ((< vertical-direction 0) ;; When moving down, look for a string closer than a list, ;; and use that if we find it. (let* ((list-start (paredit-handle-sexp-errors (funcall scan-lists) nil)) (string-start (paredit-find-next-string-start horizontal-direction list-start))) (if (and string-start list-start) (if (< 0 horizontal-direction) (min string-start list-start) (max string-start list-start)) (or string-start ;; Scan again: this is a kludgey way to report the ;; error if there really was one. (funcall scan-lists) (buffer-end horizontal-direction))))) (t (error "Vertical direction must be nonzero in `%s'." 'paredit-up/down))))) (defun paredit-find-next-string-start (horizontal-direction limit) (let ((buffer-limit-p (if (< 0 horizontal-direction) 'eobp 'bobp)) (next-char (if (< 0 horizontal-direction) 'char-after 'char-before)) (pastp (if (< 0 horizontal-direction) '> '<))) (paredit-handle-sexp-errors (save-excursion (catch 'exit (while t (if (or (funcall buffer-limit-p) (and limit (funcall pastp (point) limit))) (throw 'exit nil)) (forward-sexp horizontal-direction) (save-excursion (backward-sexp horizontal-direction) (if (eq ?\" (char-syntax (funcall next-char))) (throw 'exit (+ (point) horizontal-direction))))))) nil))) (defun-motion paredit-forward-down (&optional argument) "Move forward down into a list. With a positive argument, move forward down that many levels. With a negative argument, move backward down that many levels." (paredit-up/down (or argument +1) -1)) (defun-motion paredit-backward-up (&optional argument) "Move backward up out of the enclosing list. With a positive argument, move backward up that many levels. With a negative argument, move forward up that many levels. If in a string initially, that counts as one level." (paredit-up/down (- 0 (or argument +1)) +1)) (defun-motion paredit-forward-up (&optional argument) "Move forward up out of the enclosing list. With a positive argument, move forward up that many levels. With a negative argument, move backward up that many levels. If in a string initially, that counts as one level." (paredit-up/down (or argument +1) +1)) (defun-motion paredit-backward-down (&optional argument) "Move backward down into a list. With a positive argument, move backward down that many levels. With a negative argument, move forward down that many levels." (paredit-up/down (- 0 (or argument +1)) -1)) ;;;; Depth-Changing Commands: Wrapping, Splicing, & Raising (defun paredit-wrap-sexp (&optional argument open close) "Wrap the following S-expression. If a `C-u' prefix argument is given, wrap all S-expressions following the point until the end of the buffer or of the enclosing list. If a numeric prefix argument N is given, wrap N S-expressions. Automatically indent the newly wrapped S-expression. As a special case, if the point is at the end of a list, simply insert a parenthesis pair, rather than inserting a lone opening delimiter and then signalling an error, in the interest of preserving structure. By default OPEN and CLOSE are round delimiters." (interactive "P") (paredit-lose-if-not-in-sexp 'paredit-wrap-sexp) (let ((open (or open ?\( )) (close (or close ?\) ))) (paredit-handle-sexp-errors ((lambda (n) (paredit-insert-pair n open close 'goto-char)) (cond ((integerp argument) argument) ((consp argument) (paredit-count-sexps-forward)) ((paredit-region-active-p) nil) (t 1))) (insert close) (backward-char))) (save-excursion (backward-up-list) (indent-sexp))) (defun paredit-yank-pop (&optional argument) "Replace just-yanked text with the next item in the kill ring. If this command follows a `yank', just run `yank-pop'. If this command follows a `paredit-wrap-sexp', or any other paredit wrapping command (see `paredit-wrap-commands'), run `yank' and reindent the enclosing S-expression. If this command is repeated, run `yank-pop' and reindent the enclosing S-expression. The argument is passed on to `yank' or `yank-pop'; see their documentation for details." (interactive "*p") (cond ((eq last-command 'yank) (yank-pop argument)) ((memq last-command paredit-wrap-commands) (yank argument) ;; `yank' futzes with `this-command'. (setq this-command 'paredit-yank-pop) (save-excursion (backward-up-list) (indent-sexp))) ((eq last-command 'paredit-yank-pop) ;; Pretend we just did a `yank', so that we can use ;; `yank-pop' without duplicating its definition. (setq last-command 'yank) (yank-pop argument) ;; Return to our original state. (setq last-command 'paredit-yank-pop) (setq this-command 'paredit-yank-pop) (save-excursion (backward-up-list) (indent-sexp))) (t (error "Last command was not a yank or a wrap: %s" last-command)))) (defun paredit-splice-sexp (&optional argument) "Splice the list that the point is on by removing its delimiters. With a prefix argument as in `C-u', kill all S-expressions backward in the current list before splicing all S-expressions forward into the enclosing list. With two prefix arguments as in `C-u C-u', kill all S-expressions forward in the current list before splicing all S-expressions backward into the enclosing list. With a numerical prefix argument N, kill N S-expressions backward in the current list before splicing the remaining S-expressions into the enclosing list. If N is negative, kill forward. Inside a string, unescape all backslashes, or signal an error if doing so would invalidate the buffer's structure." (interactive "P") (if (paredit-in-string-p) (paredit-splice-string argument) (if (paredit-in-comment-p) (error "Can't splice comment.")) (paredit-handle-sexp-errors (paredit-enclosing-list-start) (error "Can't splice top level.")) (paredit-kill-surrounding-sexps-for-splice argument) (let ((delete-start (paredit-enclosing-list-start)) (delete-end (let ((limit (save-excursion (paredit-ignore-sexp-errors (forward-sexp) (backward-sexp)) (point)))) (save-excursion (backward-up-list) (forward-char +1) (paredit-skip-whitespace t limit) (point))))) (let ((end-marker (make-marker))) (save-excursion (up-list) (delete-char -1) (set-marker end-marker (point))) (delete-region delete-start delete-end) (paredit-splice-reindent delete-start (marker-position end-marker)))))) (defun paredit-splice-reindent (start end) (paredit-preserving-column ;; If we changed the first subform of the enclosing list, we must ;; reindent the whole enclosing list. (if (paredit-handle-sexp-errors (save-excursion (backward-up-list) (down-list) (paredit-ignore-sexp-errors (forward-sexp)) (< start (point))) nil) (save-excursion (backward-up-list) (indent-sexp)) (paredit-indent-region start end)))) (defun paredit-kill-surrounding-sexps-for-splice (argument) (cond ((or (paredit-in-string-p) (paredit-in-comment-p)) (error "Invalid context for splicing S-expressions.")) ((or (not argument) (eq argument 0)) nil) ((or (numberp argument) (eq argument '-)) ;; Kill S-expressions before/after the point by saving the ;; point, moving across them, and killing the region. (let* ((argument (if (eq argument '-) -1 argument)) (saved (paredit-point-at-sexp-boundary (- argument)))) (goto-char saved) (paredit-ignore-sexp-errors (backward-sexp argument)) (paredit-hack-kill-region saved (point)))) ((consp argument) (let ((v (car argument))) (if (= v 4) ;One `C-u'. ;; Move backward until we hit the open paren; then ;; kill that selected region. (let ((end (point))) (paredit-ignore-sexp-errors (while (not (bobp)) (backward-sexp))) (paredit-hack-kill-region (point) end)) ;; Move forward until we hit the close paren; then ;; kill that selected region. (let ((beginning (point))) (paredit-ignore-sexp-errors (while (not (eobp)) (forward-sexp))) (paredit-hack-kill-region beginning (point)))))) (t (error "Bizarre prefix argument `%s'." argument)))) (defun paredit-splice-sexp-killing-backward (&optional n) "Splice the list the point is on by removing its delimiters, and also kill all S-expressions before the point in the current list. With a prefix argument N, kill only the preceding N S-expressions." (interactive "P") (paredit-splice-sexp (if n (prefix-numeric-value n) '(4)))) (defun paredit-splice-sexp-killing-forward (&optional n) "Splice the list the point is on by removing its delimiters, and also kill all S-expressions after the point in the current list. With a prefix argument N, kill only the following N S-expressions." (interactive "P") (paredit-splice-sexp (if n (- (prefix-numeric-value n)) '(16)))) (defun paredit-raise-sexp (&optional argument) "Raise the following S-expression in a tree, deleting its siblings. With a prefix argument N, raise the following N S-expressions. If N is negative, raise the preceding N S-expressions. If the point is on an S-expression, such as a string or a symbol, not between them, that S-expression is considered to follow the point." (interactive "P") (save-excursion (cond ((paredit-in-string-p) (goto-char (car (paredit-string-start+end-points)))) ((paredit-in-char-p) (backward-sexp)) ((paredit-in-comment-p) (error "No S-expression to raise in comment."))) ;; Select the S-expressions we want to raise in a buffer substring. (let* ((n (prefix-numeric-value argument)) (bound (scan-sexps (point) n)) (sexps (if (< n 0) (buffer-substring bound (paredit-point-at-sexp-end)) (buffer-substring (paredit-point-at-sexp-start) bound)))) ;; Move up to the list we're raising those S-expressions out of and ;; delete it. (backward-up-list) (delete-region (point) (scan-sexps (point) 1)) (let* ((indent-start (point)) (indent-end (save-excursion (insert sexps) (point)))) (indent-region indent-start indent-end nil))))) ;;; The effects of convolution on the surrounding whitespace are pretty ;;; random. If you have better suggestions, please let me know. (defun paredit-convolute-sexp (&optional n) "Convolute S-expressions. Save the S-expressions preceding point and delete them. Splice the S-expressions following point. Wrap the enclosing list in a new list prefixed by the saved text. With a prefix argument N, move up N lists before wrapping." (interactive "p") (paredit-lose-if-not-in-sexp 'paredit-convolute-sexp) ;; Make sure we can move up before destroying anything. (save-excursion (backward-up-list n) (backward-up-list)) (let (open close) ;++ Is this a good idea? (let ((prefix (let ((end (point))) (paredit-ignore-sexp-errors (while (not (bobp)) (backward-sexp))) (prog1 (buffer-substring (point) end) (backward-up-list) (save-excursion (forward-sexp) (setq close (char-before)) (delete-char -1)) (setq open (char-after)) (delete-region (point) end) ;; I'm not sure this makes sense... (if (not (eolp)) (just-one-space)))))) (backward-up-list n) (paredit-insert-pair 1 open close 'goto-char) (insert prefix) ;; I'm not sure this makes sense either... (if (not (eolp)) (just-one-space)) (save-excursion (backward-up-list) (paredit-ignore-sexp-errors (indent-sexp)))))) (defun paredit-splice-string (argument) (let ((original-point (point)) (start+end (paredit-string-start+end-points))) (let ((start (car start+end)) (end (cdr start+end))) ;; START and END both lie before the respective quote ;; characters, which we want to delete; thus we increment START ;; by one to extract the string, and we increment END by one to ;; delete the string. (let* ((escaped-string (cond ((not (consp argument)) (buffer-substring (1+ start) end)) ((= 4 (car argument)) (buffer-substring original-point end)) (t (buffer-substring (1+ start) original-point)))) (unescaped-string (paredit-unescape-string escaped-string))) (if (not unescaped-string) (error "Unspliceable string.") (save-excursion (goto-char start) (delete-region start (1+ end)) (insert unescaped-string)) (if (not (and (consp argument) (= 4 (car argument)))) (goto-char (- original-point 1)))))))) (defun paredit-unescape-string (string) (with-temp-buffer (insert string) (goto-char (point-min)) (while (and (not (eobp)) ;; nil -> no bound; t -> no errors. (search-forward "\\" nil t)) (delete-char -1) (forward-char)) (paredit-handle-sexp-errors (progn (scan-sexps (point-min) (point-max)) (buffer-string)) nil))) ;;;; Slurpage & Barfage (defun paredit-forward-slurp-sexp (&optional argument) "Add the S-expression following the current list into that list by moving the closing delimiter. Automatically reindent the newly slurped S-expression with respect to its new enclosing form. If in a string, move the opening double-quote forward by one S-expression and escape any intervening characters as necessary, without altering any indentation or formatting." (interactive "P") (save-excursion (cond ((paredit-in-comment-p) (error "Invalid context for slurping S-expressions.")) ((numberp argument) (if (< argument 0) (paredit-forward-barf-sexp (- 0 argument)) (while (< 0 argument) (paredit-forward-slurp-sexp) (setq argument (- argument 1))))) ((paredit-in-string-p) ;; If there is anything to slurp into the string, take that. ;; Otherwise, try to slurp into the enclosing list. (if (save-excursion (goto-char (paredit-enclosing-string-end)) (paredit-handle-sexp-errors (progn (forward-sexp) nil) t)) (progn (goto-char (paredit-enclosing-string-end)) (paredit-forward-slurp-into-list argument)) (paredit-forward-slurp-into-string argument))) (t (paredit-forward-slurp-into-list argument))))) (defun paredit-forward-slurp-into-list (&optional argument) (let ((nestedp nil)) (save-excursion (up-list) ; Up to the end of the list to (let ((close (char-before))) ; save and delete the closing (delete-char -1) ; delimiter. (let ((start (point))) (catch 'return ; Go to the end of the desired (while t ; S-expression, going up a (paredit-handle-sexp-errors ; list if it's not in this, (progn (forward-sexp) (if argument (paredit-ignore-sexp-errors (while (not (eobp)) (forward-sexp)))) (throw 'return nil)) (setq nestedp t) (up-list) (setq close ; adjusting for mixed (prog1 (char-before) ; delimiters as necessary, (delete-char -1) (insert close)))))) (insert close) ; to insert that delimiter. (indent-region start (point) nil)))) (if (and (not nestedp) (eq (save-excursion (paredit-skip-whitespace nil) (point)) (save-excursion (backward-up-list) (forward-char) (point))) (eq (save-excursion (forward-sexp) (backward-sexp) (point)) (save-excursion (paredit-skip-whitespace t) (point)))) (delete-region (save-excursion (paredit-skip-whitespace nil) (point)) (save-excursion (paredit-skip-whitespace t) (point)))))) (defun paredit-forward-slurp-into-string (&optional argument) (let ((start (paredit-enclosing-string-start)) (end (paredit-enclosing-string-end))) (goto-char end) ;; Signal any errors that we might get first, before mucking with ;; the buffer's contents. (save-excursion (forward-sexp)) (let ((close (char-before))) ;; Skip intervening whitespace if we're slurping into an empty ;; string. XXX What about nonempty strings? (if (and (= (+ start 2) end) (eq (save-excursion (paredit-skip-whitespace t) (point)) (save-excursion (forward-sexp) (backward-sexp) (point)))) (delete-region (- (point) 1) (save-excursion (paredit-skip-whitespace t) (point))) (delete-char -1)) (paredit-forward-for-quote (save-excursion (forward-sexp) (if argument (while (paredit-handle-sexp-errors (progn (forward-sexp) t) nil))) (point))) (insert close)))) (defun paredit-forward-barf-sexp (&optional argument) "Remove the last S-expression in the current list from that list by moving the closing delimiter. Automatically reindent the newly barfed S-expression with respect to its new enclosing form." (interactive "P") (paredit-lose-if-not-in-sexp 'paredit-forward-barf-sexp) (if (and (numberp argument) (< argument 0)) (paredit-forward-slurp-sexp (- 0 argument)) (let ((start (point)) (end nil)) (save-excursion (up-list) ; Up to the end of the list to (let ((close (char-before))) ; save and delete the closing (delete-char -1) ; delimiter. (setq end (point)) (paredit-ignore-sexp-errors ; Go back to where we want to (if (or (not argument) ; insert the delimiter. (numberp argument)) (backward-sexp argument) (while (paredit-handle-sexp-errors (save-excursion (backward-sexp) (<= start (point))) nil) (backward-sexp)))) (paredit-skip-whitespace nil) ; Skip leading whitespace. (cond ((bobp) ;++ We'll have deleted the close, but there's no open. ;++ Is that OK? (error "Barfing all subexpressions with no open-paren?")) ((paredit-in-comment-p) ; Don't put the close-paren in (newline))) ; a comment. (insert close)) ;; Reindent all of the newly barfed S-expressions. Start at the ;; start of the first barfed S-expression, not at the close we ;; just inserted. (forward-sexp) (backward-sexp) (if (or (not argument) (numberp argument)) (paredit-forward-and-indent argument) (indent-region (point) end)))))) (defun paredit-backward-slurp-sexp (&optional argument) "Add the S-expression preceding the current list into that list by moving the closing delimiter. Automatically reindent the whole form into which new S-expression was slurped. If in a string, move the opening double-quote backward by one S-expression and escape any intervening characters as necessary, without altering any indentation or formatting." (interactive "P") (save-excursion (cond ((paredit-in-comment-p) (error "Invalid context for slurping S-expressions.")) ((numberp argument) (if (< argument 0) (paredit-backward-barf-sexp (- 0 argument)) (while (< 0 argument) (paredit-backward-slurp-sexp) (setq argument (- argument 1))))) ((paredit-in-string-p) ;; If there is anything to slurp into the string, take that. ;; Otherwise, try to slurp into the enclosing list. (if (save-excursion (goto-char (paredit-enclosing-string-start)) (paredit-handle-sexp-errors (progn (backward-sexp) nil) t)) (progn (goto-char (paredit-enclosing-string-start)) (paredit-backward-slurp-into-list argument)) (paredit-backward-slurp-into-string argument))) (t (paredit-backward-slurp-into-list argument))))) (defun paredit-backward-slurp-into-list (&optional argument) (let ((nestedp nil)) (save-excursion (backward-up-list) (let ((open (char-after))) (delete-char +1) (catch 'return (while t (paredit-handle-sexp-errors (progn (backward-sexp) (if argument (paredit-ignore-sexp-errors (while (not (bobp)) (backward-sexp)))) (throw 'return nil)) (setq nestedp t) (backward-up-list) (setq open (prog1 (char-after) (save-excursion (insert open) (delete-char +1))))))) (insert open)) ;; Reindent the line at the beginning of wherever we inserted the ;; opening delimiter, and then indent the whole S-expression. (backward-up-list) (lisp-indent-line) (indent-sexp)) ;; If we slurped into an empty list, don't leave dangling space: ;; (foo |). (if (and (not nestedp) (eq (save-excursion (paredit-skip-whitespace nil) (point)) (save-excursion (backward-sexp) (forward-sexp) (point))) (eq (save-excursion (up-list) (backward-char) (point)) (save-excursion (paredit-skip-whitespace t) (point)))) (delete-region (save-excursion (paredit-skip-whitespace nil) (point)) (save-excursion (paredit-skip-whitespace t) (point)))))) (defun paredit-backward-slurp-into-string (&optional argument) (let ((start (paredit-enclosing-string-start)) (end (paredit-enclosing-string-end))) (goto-char start) ;; Signal any errors that we might get first, before mucking with ;; the buffer's contents. (save-excursion (backward-sexp)) (let ((open (char-after)) (target (point))) ;; Skip intervening whitespace if we're slurping into an empty ;; string. XXX What about nonempty strings? (if (and (= (+ start 2) end) (eq (save-excursion (paredit-skip-whitespace nil) (point)) (save-excursion (backward-sexp) (forward-sexp) (point)))) (delete-region (save-excursion (paredit-skip-whitespace nil) (point)) (+ (point) 1)) (delete-char +1)) (backward-sexp) (if argument (paredit-ignore-sexp-errors (while (not (bobp)) (backward-sexp)))) (insert open) (paredit-forward-for-quote target)))) (defun paredit-backward-barf-sexp (&optional argument) "Remove the first S-expression in the current list from that list by moving the closing delimiter. Automatically reindent the barfed S-expression and the form from which it was barfed." (interactive "P") (paredit-lose-if-not-in-sexp 'paredit-backward-barf-sexp) (if (and (numberp argument) (< argument 0)) (paredit-backward-slurp-sexp (- 0 argument)) (let ((end (make-marker))) (set-marker end (point)) (save-excursion (backward-up-list) (let ((open (char-after))) (delete-char +1) (paredit-ignore-sexp-errors (paredit-forward-and-indent (if (or (not argument) (numberp argument)) argument (let ((n 0)) (save-excursion (while (paredit-handle-sexp-errors (save-excursion (forward-sexp) (<= (point) end)) nil) (forward-sexp) (setq n (+ n 1)))) n)))) (while (progn (paredit-skip-whitespace t) (eq (char-after) ?\; )) (forward-line 1)) (if (eobp) ;++ We'll have deleted the close, but there's no open. ;++ Is that OK? (error "Barfing all subexpressions with no close-paren?")) ;** Don't use `insert' here. Consider, e.g., barfing from ;** (foo|) ;** and how `save-excursion' works. (insert-before-markers open)) (backward-up-list) (lisp-indent-line) (indent-sexp))))) ;;;; Splitting & Joining (defun paredit-split-sexp () "Split the list or string the point is on into two." (interactive) (cond ((paredit-in-string-p) (insert "\"") (save-excursion (insert " \""))) ((or (paredit-in-comment-p) (paredit-in-char-p)) (error "Invalid context for splitting S-expression.")) (t (let ((open (save-excursion (backward-up-list) (char-after))) (close (save-excursion (up-list) (char-before)))) (delete-horizontal-space) (insert close) (save-excursion (insert ?\ ) (insert open) (backward-char) (indent-sexp)))))) (defun paredit-join-sexps () "Join the S-expressions adjacent on either side of the point. Both must be lists, strings, or atoms; error if there is a mismatch." (interactive) (cond ((paredit-in-comment-p) (error "Can't join S-expressions in comment.")) ((paredit-in-string-p) (error "Nothing to join in a string.")) ((paredit-in-char-p) (error "Can't join characters."))) (let ((left-point (paredit-point-at-sexp-end)) (right-point (paredit-point-at-sexp-start))) (let ((left-char (char-before left-point)) (right-char (char-after right-point))) (let ((left-syntax (char-syntax left-char)) (right-syntax (char-syntax right-char))) (cond ((< right-point left-point) (error "Can't join a datum with itself.")) ((and (eq left-syntax ?\) ) (eq right-syntax ?\( ) (eq left-char (matching-paren right-char)) (eq right-char (matching-paren left-char))) (paredit-join-lists-internal left-point right-point) (paredit-preserving-column (save-excursion (backward-up-list) (indent-sexp)))) ((and (eq left-syntax ?\" ) (eq right-syntax ?\" )) ;; Delete any intermediate formatting. (delete-region (1- left-point) (1+ right-point))) ((and (memq left-syntax '(?w ?_)) ; Word or symbol (memq right-syntax '(?w ?_))) (delete-region left-point right-point)) (t (error "Mismatched S-expressions to join."))))))) (defun paredit-join-lists-internal (left-point right-point) (save-excursion ;; Leave intermediate formatting alone. (goto-char right-point) (delete-char +1) (goto-char left-point) (delete-char -1) ;; Kludge: Add an extra space in several conditions. (if (or ;; (foo)| ;x\n(bar) => (foo | ;x\nbar), not (foo| ;x\nbar). (and (not (eolp)) (save-excursion (paredit-skip-whitespace t (point-at-eol)) (eq (char-after) ?\;))) ;; (foo)|(bar) => (foo| bar), not (foo|bar). (and (= left-point right-point) (not (or (eq ?\ (char-syntax (char-before))) (eq ?\ (char-syntax (char-after))))))) (insert ?\ )))) ;++ How ought paredit-join to handle comments intervening symbols or strings? ;++ Idea: ;++ ;++ "foo" | ;bar ;++ "baz" ;quux ;++ ;++ => ;++ ;++ "foo|baz" ;bar ;++ ;quux ;++ ;++ The point should stay where it is relative to the comments, and the ;++ the comments' columns should all be preserved, perhaps. Hmmmm... ;++ What about this? ;++ ;++ "foo" ;bar ;++ | ;baz ;++ "quux" ;zot ;++ Should rename: ;++ paredit-point-at-sexp-start -> paredit-start-of-sexp-after-point ;++ paredit-point-at-sexp-end -> paredit-end-of-sexp-before-point ;;;; Variations on the Lurid Theme ;;; I haven't the imagination to concoct clever names for these. (defun paredit-add-to-previous-list () "Add the S-expression following point to the list preceding point." (interactive) (paredit-lose-if-not-in-sexp 'paredit-add-to-previous-list) (save-excursion (down-list -1) ;++ backward-down-list... (paredit-forward-slurp-sexp))) (defun paredit-add-to-next-list () "Add the S-expression preceding point to the list following point. If no S-expression precedes point, move up the tree until one does." (interactive) (paredit-lose-if-not-in-sexp 'paredit-add-to-next-list) (save-excursion (down-list) (paredit-backward-slurp-sexp))) (defun paredit-join-with-previous-list () "Join the list the point is on with the previous list in the buffer." (interactive) (paredit-lose-if-not-in-sexp 'paredit-join-with-previous-list) (save-excursion (while (paredit-handle-sexp-errors (save-excursion (backward-sexp) nil) (backward-up-list) t)) (paredit-join-sexps))) (defun paredit-join-with-next-list () "Join the list the point is on with the next list in the buffer." (interactive) (paredit-lose-if-not-in-sexp 'paredit-join-with-next-list) (save-excursion (while (paredit-handle-sexp-errors (save-excursion (forward-sexp) nil) (up-list) t)) (paredit-join-sexps))) ;;;; Utilities (defun paredit-in-string-escape-p () "True if the point is on a character escape of a string. This is true only if the character is preceded by an odd number of backslashes. This assumes that `paredit-in-string-p' has already returned true." (let ((oddp nil)) (save-excursion (while (eq (char-before) ?\\ ) (setq oddp (not oddp)) (backward-char))) oddp)) (defun paredit-in-char-p (&optional position) "True if point is on a character escape outside a string." (save-excursion (goto-char (or position (point))) (paredit-in-string-escape-p))) (defun paredit-skip-whitespace (trailing-p &optional limit) "Skip past any whitespace, or until the point LIMIT is reached. If TRAILING-P is nil, skip leading whitespace; otherwise, skip trailing whitespace." (funcall (if trailing-p 'skip-chars-forward 'skip-chars-backward) " \t\n " ; This should skip using the syntax table, but LF limit)) ; is a comment end, not newline, in Lisp mode. (defalias 'paredit-region-active-p (xcond ((paredit-xemacs-p) 'region-active-p) ((paredit-gnu-emacs-p) (lambda () (and mark-active transient-mark-mode))))) (defun paredit-hack-kill-region (start end) "Kill the region between START and END. Do not append to any current kill, and do not let the next kill append to this one." (interactive "r") ;Eh, why not? ;; KILL-REGION sets THIS-COMMAND to tell the next kill that the last ;; command was a kill. It also checks LAST-COMMAND to see whether it ;; should append. If we bind these locally, any modifications to ;; THIS-COMMAND will be masked, and it will not see LAST-COMMAND to ;; indicate that it should append. (let ((this-command nil) (last-command nil)) (kill-region start end))) ;;;;; Reindentation utilities ;++ Should `paredit-indent-sexps' and `paredit-forward-and-indent' use ;++ `paredit-indent-region' rather than `indent-region'? (defun paredit-indent-sexps () "If in a list, indent all following S-expressions in the list." (let* ((start (point)) (end (paredit-handle-sexp-errors (progn (up-list) (point)) nil))) (if end (indent-region start end nil)))) (defun paredit-forward-and-indent (&optional n) "Move forward by N S-expressions, indenting them with `indent-region'." (let ((start (point))) (forward-sexp n) (indent-region start (point) nil))) (defun paredit-indent-region (start end) "Indent the region from START to END. Don't reindent the line starting at START, however." (if (not (<= start end)) (error "Incorrectly related points: %S, %S" start end)) (save-excursion (goto-char start) (let ((bol (point-at-bol))) ;; Skip all S-expressions that end on the starting line, but ;; don't go past `end'. (if (and (save-excursion (goto-char end) (not (eq bol (point-at-bol)))) (paredit-handle-sexp-errors (catch 'exit (while t (save-excursion (forward-sexp) (if (not (eq bol (point-at-bol))) (throw 'exit t)) (if (not (< (point) end)) (throw 'exit nil))) (forward-sexp))) nil)) (progn ;; Point is still on the same line, but precedes an ;; S-expression that ends on a different line. (if (not (eq bol (point-at-bol))) (error "Internal error -- we moved forward a line!")) (goto-char (+ 1 (point-at-eol))) (if (not (<= (point) end)) (error "Internal error -- we frobnitzed the garfnut!")) (indent-region (point) end nil)))))) ;;;;; S-expression Parsing Utilities ;++ These routines redundantly traverse S-expressions a great deal. ;++ If performance issues arise, this whole section will probably have ;++ to be refactored to preserve the state longer, like paredit.scm ;++ does, rather than to traverse the definition N times for every key ;++ stroke as it presently does. (defun paredit-current-parse-state () "Return parse state of point from beginning of defun." (let ((point (point))) (beginning-of-defun) ;; Calling PARSE-PARTIAL-SEXP will advance the point to its second ;; argument (unless parsing stops due to an error, but we assume it ;; won't in paredit-mode). (parse-partial-sexp (point) point))) (defun paredit-in-string-p (&optional state) "True if the parse state is within a double-quote-delimited string. If no parse state is supplied, compute one from the beginning of the defun to the point." ;; 3. non-nil if inside a string (the terminator character, really) (and (nth 3 (or state (paredit-current-parse-state))) t)) (defun paredit-string-start+end-points (&optional state) "Return a cons of the points of open and close quotes of the string. The string is determined from the parse state STATE, or the parse state from the beginning of the defun to the point. This assumes that `paredit-in-string-p' has already returned true, i.e. that the point is already within a string." (save-excursion ;; 8. character address of start of comment or string; nil if not ;; in one (let ((start (nth 8 (or state (paredit-current-parse-state))))) (goto-char start) (forward-sexp 1) (cons start (1- (point)))))) (defun paredit-enclosing-string-start () (car (paredit-string-start+end-points))) (defun paredit-enclosing-string-end () (+ 1 (cdr (paredit-string-start+end-points)))) (defun paredit-enclosing-list-start () (save-excursion (backward-up-list) (point))) (defun paredit-enclosing-list-end () (save-excursion (up-list) (point))) (defun paredit-in-comment-p (&optional state) "True if parse state STATE is within a comment. If no parse state is supplied, compute one from the beginning of the defun to the point." ;; 4. nil if outside a comment, t if inside a non-nestable comment, ;; else an integer (the current comment nesting) (and (nth 4 (or state (paredit-current-parse-state))) t)) (defun paredit-prefix-numeric-value (argument) ;++ Kludgerific. (cond ((integerp argument) argument) ((eq argument '-) -1) ((consp argument) (cond ((equal argument '(4)) (paredit-count-sexps-forward)) ;C-u ((equal argument '(16)) (paredit-count-sexps-backward)) ;C-u C-u (t (error "Invalid prefix argument: %S" argument)))) ((paredit-region-active-p) (save-excursion (save-restriction (narrow-to-region (region-beginning) (region-end)) (cond ((= (point) (point-min)) (paredit-count-sexps-forward)) ((= (point) (point-max)) (paredit-count-sexps-backward)) (t (error "Point %S is not start or end of region: %S..%S" (point) (region-beginning) (region-end))))))) (t 1))) (defun paredit-count-sexps-forward () (save-excursion (let ((n 0) (p nil)) ;hurk (paredit-ignore-sexp-errors (while (setq p (scan-sexps (point) +1)) (goto-char p) (setq n (+ n 1)))) n))) (defun paredit-count-sexps-backward () (save-excursion (let ((n 0) (p nil)) ;hurk (paredit-ignore-sexp-errors (while (setq p (scan-sexps (point) -1)) (goto-char p) (setq n (+ n 1)))) n))) (defun paredit-point-at-sexp-boundary (n) (cond ((< n 0) (paredit-point-at-sexp-start)) ((= n 0) (point)) ((> n 0) (paredit-point-at-sexp-end)))) (defun paredit-point-at-sexp-start () (save-excursion (forward-sexp) (backward-sexp) (point))) (defun paredit-point-at-sexp-end () (save-excursion (backward-sexp) (forward-sexp) (point))) (defun paredit-lose-if-not-in-sexp (command) (if (or (paredit-in-string-p) (paredit-in-comment-p) (paredit-in-char-p)) (error "Invalid context for command `%s'." command))) (defun paredit-check-region (start end) "Signal an error if text between `start' and `end' is unbalanced." ;; `narrow-to-region' will move the point, so avoid calling it if we ;; don't need to. We don't want to use `save-excursion' because we ;; want the point to move if `check-parens' reports an error. (if (not (paredit-region-ok-p start end)) (save-restriction (narrow-to-region start end) (check-parens)))) (defun paredit-region-ok-p (start end) "Return true iff the region between `start' and `end' is balanced. This is independent of context -- it doesn't check what state the text at `start' is in." (save-excursion (paredit-handle-sexp-errors (progn (save-restriction (narrow-to-region start end) (scan-sexps (point-min) (point-max))) t) nil))) (defun paredit-current-indentation () (save-excursion (back-to-indentation) (current-column))) (defun paredit-restore-column (column indentation) ;; Preserve the point's position either in the indentation or in the ;; code: if on code, move with the code; if in indentation, leave it ;; in the indentation, either where it was (if still on indentation) ;; or at the end of the indentation (if the code moved far enough ;; left). (let ((indentation* (paredit-current-indentation))) (goto-char (+ (point-at-bol) (cond ((not (< column indentation)) (+ column (- indentation* indentation))) ((<= indentation* column) indentation*) (t column)))))) ;;;; Initialization (paredit-define-keys) (paredit-annotate-mode-with-examples) (paredit-annotate-functions-with-examples) (provide 'paredit) ;;; Local Variables: ;;; outline-regexp: " \n;;;;+" ;;; End: ;;; paredit.el ends here paredit-el-24/test.el000066400000000000000000001435561244063777000146600ustar00rootroot00000000000000;;; -*- Mode: Emacs-Lisp -*- ;;; Rudimentary, kludgey test suite for paredit -- work in progress! ;; Copyright (C) 2005--2014 Taylor R. Campbell ;; This file is part of paredit. ;; ;; Paredit is free software: you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; ;; Paredit is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with paredit. If not, see . (defun paredit-test-failure-default (command before after expected) (error "%S failed test: after %S, got %S but expected %S." command before after expected)) (defvar paredit-test-failure-function 'paredit-test-failure-default "Function to call when `paredit-test' fails. Four arguments: the paredit command, the text of the buffer before, the text of the buffer after, and the expected text of the buffer after.") (defun paredit-test-failed (command before after expected) (funcall paredit-test-failure-function command before after expected)) (defun paredit-test (command examples) (message "Testing %S..." command) (dolist (example examples) (let ((before (car example))) (dolist (expected (cdr example)) (with-temp-buffer (paredit-test-buffer-setup) (insert before) (goto-char (point-min)) (if (search-forward "_" nil t) (progn (delete-char -1) (set-mark (point)))) (goto-char (point-min)) (search-forward "|") (delete-char -1) (if (cond ((eq expected 'error) ;++ Check that there are no more expected states. (condition-case condition (progn (call-interactively command) t) (error nil))) ((stringp expected) (call-interactively command) (insert ?\|) (not (string= expected (buffer-string)))) (t (error "Bad test expectation: %S" expected))) (paredit-test-failed command before (buffer-string) expected))) (setq before expected))))) (defun paredit-test-buffer-setup () (scheme-mode) (set (make-local-variable 'indent-tabs-mode) nil) (set (make-local-variable 'comment-column) 40) (set (make-local-variable 'show-trailing-whitespace) nil)) (paredit-do-commands (spec keys command examples) nil ;string case ;; `paredit-backslash' has a funny example. (if (not (eq command 'paredit-backslash)) (paredit-test command examples))) (defun paredit-test-bracketed (entries examples) (dolist (entry entries) (let ((command (car entry)) (left (car (cdr entry))) (right (car (cdr (cdr entry))))) (paredit-test command (mapcar (lambda (example) (mapcar (lambda (step) (if (stringp step) (replace-regexp-in-string "(" (string left) (replace-regexp-in-string ")" (string right) step)) step)) example)) examples))))) ;++ Test `paredit-open-...' with the region active. (paredit-test-bracketed '((paredit-open-round ?\( ?\)) (paredit-open-square ?\[ ?\]) (paredit-open-curly ?\{ ?\}) ;; (paredit-open-angled ?\< ?\>) ) '(("(foo|bar)" "(foo (|) bar)") ("(foo| bar)" "(foo (|) bar)") ("(foo |bar)" "(foo (|) bar)") ("(foo|\n bar)" "(foo (|)\n bar)") ("(foo |(bar) baz)" "(foo (|) (bar) baz)") ("(foo (bar)| baz)" "(foo (bar) (|) baz)") ("(foo |\"bar\" baz)" "(foo (|) \"bar\" baz)") ("(foo \"bar\"| baz)" "(foo \"bar\" (|) baz)") ("foo|" "foo (|)") ("|foo" "(|) foo") ("\\|(" "\\|("))) (let ((current-prefix-arg 1)) (paredit-test-bracketed '((paredit-open-round ?\( ?\)) (paredit-open-square ?\[ ?\]) (paredit-open-curly ?\{ ?\}) ;; (paredit-open-angled ?\< ?\>) ) '(("(foo |bar baz)" "(foo (|bar) baz)") ("(x |;y\n z\n w)" "(x (| ;y\n z)\n w)")))) (let ((current-prefix-arg '(4))) ;++ Oops -- `C-u (' is like `M-4 (', not like `C-u M-('. (paredit-test-bracketed '((paredit-open-round ?\( ?\)) (paredit-open-square ?\[ ?\]) (paredit-open-curly ?\{ ?\}) ;; (paredit-open-angled ?\< ?\>) ) '(("(foo |bar baz)" "(foo (|bar baz))") ("(x |;y\n z\n w)" "(x (| ;y\n z\n w))") ("foo |bar baz" "foo (|bar baz)") ;++ These tests are kinda bunk. It's not immediately clear to me ;++ which is right: including or excluding the trailing comment. ("foo\n|bar\nbaz\n;quux\n" "foo\n(|bar\n baz)\n;quux\n") ("foo\n|bar\nbaz\n;; quux" "foo\n(|bar\n baz\n ;; quux\n )")))) (paredit-test-bracketed '((paredit-close-round ?\( ?\)) (paredit-close-square ?\[ ?\]) (paredit-close-curly ?\{ ?\}) ;; (paredit-close-angled ?\< ?\>) ) '(("(#\\|x)" "(#\\x)|") ("(#\\|])" "(#\\])|") ("(#\\| )" "(#\\ )|") ("(#\\|\")" "(#\\\")|") ("(\"|\")" "(\")|\")") ("(\"|\")" "(\")|\")"))) (paredit-test-bracketed '((paredit-close-round ?\( ?\)) (paredit-close-square ?\[ ?\]) (paredit-close-curly ?\{ ?\}) ;; (paredit-close-angled ?\< ?\>) ) '(("(|" "()|") ("foo|" error) ("(foo| ;\n )" "(foo ;\n )|") ("(foo| ;\n bar)" "(foo ;\n bar)|") ("(foo| ;\n bar )" "(foo ;\n bar)|"))) (paredit-test-bracketed '((paredit-close-round-and-newline ?\( ?\)) (paredit-close-square-and-newline ?\[ ?\]) (paredit-close-curly-and-newline ?\{ ?\}) ;; (paredit-close-angled-and-newline ?\< ?\>) ) '(("(foo #\\|( )" "(foo #\\()\n|") ("(foo|\n ) ;bar" "(foo) ;bar\n|") ("((foo|\n) (bar))" "((foo)\n |(bar))"))) (paredit-test-bracketed '((paredit-wrap-round ?\( ?\)) (paredit-wrap-square ?\[ ?\]) (paredit-wrap-curly ?\{ ?\}) ;; (paredit-wrap-angled ?\< ?\>) ) '(("|foo" "(|foo)") ("f|oo" "f (|oo)") ("fo|o" "fo (|o)") ("|foo bar" "(|foo) bar") ("f|oo bar" "f (|oo) bar") ("fo|o bar" "fo (|o) bar") ("foo| bar" "foo (| bar)") ("foo |bar" "foo (|bar)") ("foo b|ar" "foo b (|ar)") ("foo ba|r" "foo ba (|r)") ("|foo bar baz" "(|foo) bar baz"))) (let ((transient-mark-mode t)) (paredit-test-bracketed '((paredit-wrap-round ?\( ?\)) (paredit-wrap-square ?\[ ?\]) (paredit-wrap-curly ?\{ ?\}) ;; (paredit-wrap-angled ?\< ?\>) ) '(("|foo bar_" "(|foo bar)") ("|foo bar_ baz" "(|foo bar) baz")))) (let ((transient-mark-mode nil)) (paredit-test-bracketed '((paredit-wrap-round ?\( ?\)) (paredit-wrap-square ?\[ ?\]) (paredit-wrap-curly ?\{ ?\}) ;; (paredit-wrap-angled ?\< ?\>) ) '(("|foo bar_" "(|foo) bar") ("|foo bar_ baz" "(|foo) bar baz")))) (let ((current-prefix-arg '(4))) (paredit-test 'paredit-wrap-sexp '(("(foo |bar baz)" "(foo (|bar baz))")))) (paredit-test 'paredit-newline '(("\"foo|bar\"" "\"foo\n|bar\"") ("(frob grovel ;full |(lexical)\n mumble)" "(frob grovel ;full\n |(lexical)\n mumble)") ("(frob grovel ;full (|lexical)\n mumble)" "(frob grovel ;full (\n ;|lexical)\n mumble)") ("#\\|(" "#\\(\n|"))) (paredit-test 'paredit-reindent-defun ;++ Test filling paragraphs in comments and strings. '(("|(define (square x)\n (* x x))" "|(define (square x)\n (* x x))") ("(define (square x)\n (* x x))|" "(define (square x)\n (* x x))|") ("(define (square x)\n (* x x))|\n(frob\n wotz)" "(define (square x)\n (* x x))|\n(frob\n wotz)") ("(define (square x)\n (* x x))\n|(frob\n wotz)" "(define (square x)\n (* x x))\n|(frob\n wotz)") ("(define (square x)\n | (* x x))" "(define (square x)\n | (* x x))") ("(define (square x)\n | (* x x))" "(define (square x)\n |(* x x))") ("(define (square x)\n (* |x x))" "(define (square x)\n (* |x x))"))) (paredit-test 'paredit-semicolon '(("|" ";|") ("|foo" ";|foo") ("f|oo" "f;|oo") ("fo|o" "fo;|o") ("foo|" "foo;|") ("|(foo bar)" ";|(foo bar)") ("(|foo bar)" "(;|foo bar\n )") ("(f|oo bar)" "(f;|oo bar\n )") ("(fo|o bar)" "(fo;|o bar\n )") ("(foo| bar)" "(foo;| bar\n )") ("(foo |bar)" "(foo ;|bar\n )") ("(foo b|ar)" "(foo b;|ar\n )") ("(foo ba|r)" "(foo ba;|r\n )") ("(foo bar|)" "(foo bar;|\n )") ("(foo bar)|" "(foo bar);|") ("|(foo\n bar)" ";|\n(foo\n bar)") ("(|foo\n bar)" "(;|foo\n bar)") ("(f|oo\n bar)" "(f;|oo\n bar)") ("(fo|o\n bar)" "(fo;|o\n bar)") ("(foo|\n bar)" "(foo;|\n bar)") ("(foo\n| bar)" "(foo\n;| bar\n )") ("(foo\n |bar)" "(foo\n ;|bar\n )") ("(foo\n b|ar)" "(foo\n b;|ar\n )") ("(foo\n ba|r)" "(foo\n ba;|r\n )") ("(foo\n bar|)" "(foo\n bar;|\n )") ("(foo\n bar)|" "(foo\n bar);|") ("#\\|(" ";|#\\("))) (paredit-test 'paredit-comment-dwim '(("\"foo|bar;baz\" ;quux" "\"foobar;baz\" ;|quux") ;; Uh oh! Bug in `comment-indent'... ;; ("\"foo\nbar|baz;quux\" ;zot" ;; "\"foo\nbarbaz;quux\" ;|zot") ;; I think the loop in `paredit-comment-on-line' is bogus. Can you ;; elicit more than one iteration of it? That is, can you cause ;; `comment-search-forward' to wind up inside a character or a ;; string? )) (paredit-test 'paredit-doublequote '(("\"foo \\|x bar\"" "\"foo \\x\\\"| bar\""))) (paredit-test 'paredit-forward-delete '(("f|oo" "f|o") (";f|(oo" ";f|oo") (";|;(foo)" ";|(foo)") ("|;;(foo)" "|;(foo)" "|(foo)") (";foo|\n(bar)\n(baz\n quux)" ";foo|(bar)\n(baz\n quux)") (";foo|\n(bar\n baz)" error) ("|;;foo(" "|;foo(" error) (";foo|\n(bar);baz\n" ";foo|(bar);baz\n") (";foo|\n(bar);baz" ";foo|(bar);baz") (";foo|\n(bar ;baz\n quux)\n" error) (";foo|\n(bar ;baz\n quux)" error) ("|\\\\\\\\" "|\\\\" "|" error) ("\\\\|\\\\" "\\\\|" error) ("(|\\\\\\\\)" "(|\\\\)" "(|)" "|" error) ("(\\\\|\\\\)" "(\\\\|)" "(\\\\|)") ("|(" "|" error) ("|)" "|" error))) (paredit-test 'paredit-backward-delete '(("fo|o" "f|o") (";fo(|o" ";fo|o") (";|;(foo)" "|;(foo)") (";;|(foo)" ";|(foo)" "|(foo)") (";foo\n|(bar)\n(baz\n quux)" ";foo|(bar)\n(baz\n quux)") (";foo\n|(bar\n baz)" error) (";;|foo(" ";|foo(" error) (";foo\n|(bar);baz\n" ";foo|(bar);baz\n") (";foo\n|(bar);baz" ";foo|(bar);baz") (";foo\n|(bar ;baz\n quux)\n" error) (";foo\n|(bar ;baz\n quux)" error) ("\\\\\\\\|" "\\\\|" "|" error) ("\\\\|\\\\" "|\\\\" error) ("(\\\\\\\\|)" "(\\\\|)" "(|)" "|" error) ("(\\\\|\\\\)" "(|\\\\)" "(|\\\\)") ("(|" "|" error) (")|" "|" error))) (dolist (command '(paredit-delete-region paredit-kill-region)) ;++ Need to check whether `paredit-kill-region' updates the kill ring ;++ correctly. (paredit-test command '(("|foo" error) ("|foo_" "|") ("|(foo)_" "|") (";;; f|oo (bar ;_baz\n(zot)\n" ";;; f|baz\n(zot)\n") ("(foo |bar_ baz)\n" "(foo | baz)\n") ("(foo |(bar \"baz\" ; quux\n zot)\n _mumble)" "(foo |mumble)") ("(foo (bar |baz) (quux _zot) mumble)" "(foo (bar |zot) mumble)") ("(foo bar ;baz| quux\n zot_)" error) ("(foo bar ;baz| quux\n _zot\n mumble)" "(foo bar ;baz|zot\n mumble)") ("(foo bar| baz ;quux (_)\n zot)" error) ("(foo bar| baz ;quux ()_\n zot)" "(foo bar|\n zot)")))) ;;; The hairiest paredit command: paredit-kill. ;++ Need to check whether `paredit-kill' updates the kill ring. (paredit-test (defun paredit-test-kill () (interactive) ;; Horrible kludge. Do it once, and then yank to make ;; sure we get back what we expect. Then do it again, ;; to get the effects on the buffer the automatic test ;; framework will check. (save-excursion (let ((kill-ring nil)) (let ((text (buffer-string))) (call-interactively 'paredit-kill) (call-interactively 'yank) (if (not (equal text (buffer-string))) (error "Before kill %S, after yank %S." text (buffer-string)))))) (let ((kill-ring nil)) (call-interactively 'paredit-kill))) '(("|" error) ("| " "|" error) (" |" error) ("| \n " ;; This ought to be an intermediate step, but evidently it is only ;; in recent versions of GNU Emacs with `show-trailing-whitespace' ;; set to thanks to some brain damage in `kill-line'. ;; ;; "|\n " "| " "|" error) (" |\n " " | " " |" error) (" \n| " " \n|" error) ("|( )" "|" error) ("(| )" "(|)" "(|)") ("( |)" "( |)") ("( )|" error) ("|( )" "|" error) ("(| )" "(|)" "(|)") ("( | )" "( |)" "( |)") ("( | )" "( |)" "( |)") ("( | )" "( |)" "( |)") ("( | )" "( |)" "( |)") ("( |)" "( |)" "( |)") ("|(\n)" "|" error) ("(|\n)" "(|)" "(|)") ("(\n|)" "(\n|)") ("(\n)|" error) ("|(\n)\n" "|\n" "|" error) ("(|\n)\n" "(|)\n" "(|)\n") ("(\n|)\n" "(\n|)\n" "(\n|)\n") ("(\n)|\n" "(\n)|" error) ("(\n)\n|" error) ("|\"\n\"" "|" error) ("\"|\n\"" "\"|\"" "\"|\"") ("\"\n|\"" "\"\n|\"") ("\"\n\"|" error) ("|\"\n\"\n" "|\n" "|" error) ("\"|\n\"\n" "\"|\"\n" "\"|\"\n") ("\"\n|\"\n" "\"\n|\"\n" "\"\n|\"\n") ("\"\n\"|\n" "\"\n\"|" error) ("\"\n\"\n|" error) ("|(a (b) (c)\n (d) (e))" "|" error) ("(a| (b) (c)\n (d) (e))" "(a|\n (d) (e))" "(a| (d) (e))" "(a|)" "(a|)") ("(a |(b) (c)\n (d) (e))" "(a |\n (d) (e))" "(a | (d) (e))" "(a |)" "(a |)") ("(a (|b) (c)\n (d) (e))" "(a (|) (c)\n (d) (e))" "(a (|) (c)\n (d) (e))") ("(a (b|) (c)\n (d) (e))" "(a (b|) (c)\n (d) (e))") ("(a (b)| (c)\n (d) (e))" "(a (b)|\n (d) (e))" "(a (b)| (d) (e))" "(a (b)|)" "(a (b)|)") ("(a (b) |(c)\n (d) (e))" "(a (b) |\n (d) (e))" "(a (b) | (d) (e))" "(a (b) |)" "(a (b) |)") ("(a (b) (|c)\n (d) (e))" "(a (b) (|)\n (d) (e))" "(a (b) (|)\n (d) (e))") ("(a (b) (c|)\n (d) (e))" "(a (b) (c|)\n (d) (e))") ("(a (b) (c)|\n (d) (e))" "(a (b) (c)| (d) (e))" "(a (b) (c)|)" "(a (b) (c)|)") ("(a (b) (c)\n| (d) (e))" "(a (b) (c)\n|)" "(a (b) (c)\n|)") ("(a (b) (c)\n | (d) (e))" "(a (b) (c)\n |)" "(a (b) (c)\n |)") ("(a (b) (c)\n | (d) (e))" "(a (b) (c)\n |)" "(a (b) (c)\n |)") ("(a (b) (c)\n |(d) (e))" "(a (b) (c)\n |)" "(a (b) (c)\n |)") ("(a (b) (c)\n (|d) (e))" "(a (b) (c)\n (|) (e))" "(a (b) (c)\n (|) (e))") ("(a (b) (c)\n (d|) (e))" "(a (b) (c)\n (d|) (e))") ("(a (b) (c)\n (d)| (e))" "(a (b) (c)\n (d)|)" "(a (b) (c)\n (d)|)") ("(a (b) (c)\n (d) |(e))" "(a (b) (c)\n (d) |)" "(a (b) (c)\n (d) |)") ("(a (b) (c)\n (d) (|e))" "(a (b) (c)\n (d) (|))" "(a (b) (c)\n (d) (|))") ("(a (b) (c)\n (d) (e|))" "(a (b) (c)\n (d) (e|))") ("(a (b) (c)\n (d) (e)|)" "(a (b) (c)\n (d) (e)|)") ("(a (b) (c)\n (d) (e))|" error) ("|(a ((b) (c)\n (d) (e)) (f))" "|" error) ("(|a ((b) (c)\n (d) (e)) (f))" "(| (f))" "(|)" "(|)") ("(a| ((b) (c)\n (d) (e)) (f))" "(a| (f))" "(a|)" "(a|)") ("(a |((b) (c)\n (d) (e)) (f))" "(a | (f))" "(a |)" "(a |)") ("(a (|(b) (c)\n (d) (e)) (f))" "(a (|\n (d) (e)) (f))" "(a (| (d) (e)) (f))" "(a (|) (f))" "(a (|) (f))") ("(a ((|b) (c)\n (d) (e)) (f))" "(a ((|) (c)\n (d) (e)) (f))" "(a ((|) (c)\n (d) (e)) (f))") ("(a ((b|) (c)\n (d) (e)) (f))" "(a ((b|) (c)\n (d) (e)) (f))") ("(a ((b)| (c)\n (d) (e)) (f))" "(a ((b)|\n (d) (e)) (f))" "(a ((b)| (d) (e)) (f))" "(a ((b)|) (f))" "(a ((b)|) (f))") ("(a ((b) |(c)\n (d) (e)) (f))" "(a ((b) |\n (d) (e)) (f))" "(a ((b) | (d) (e)) (f))" "(a ((b) |) (f))" "(a ((b) |) (f))") ("(a ((b) (|c)\n (d) (e)) (f))" "(a ((b) (|)\n (d) (e)) (f))" "(a ((b) (|)\n (d) (e)) (f))") ("(a ((b) (c|)\n (d) (e)) (f))" "(a ((b) (c|)\n (d) (e)) (f))") ("(a ((b) (c)|\n (d) (e)) (f))" "(a ((b) (c)| (d) (e)) (f))" "(a ((b) (c)|) (f))" "(a ((b) (c)|) (f))") ("(a ((b) (c)\n| (d) (e)) (f))" "(a ((b) (c)\n|) (f))" "(a ((b) (c)\n|) (f))") ("(a ((b) (c)\n | (d) (e)) (f))" "(a ((b) (c)\n |) (f))" "(a ((b) (c)\n |) (f))") ("(a ((b) (c)\n | (d) (e)) (f))" "(a ((b) (c)\n |) (f))" "(a ((b) (c)\n |) (f))") ("(a ((b) (c)\n | (d) (e)) (f))" "(a ((b) (c)\n |) (f))" "(a ((b) (c)\n |) (f))") ("(a ((b) (c)\n |(d) (e)) (f))" "(a ((b) (c)\n |) (f))" "(a ((b) (c)\n |) (f))") ("(a ((b) (c)\n (|d) (e)) (f))" "(a ((b) (c)\n (|) (e)) (f))" "(a ((b) (c)\n (|) (e)) (f))") ("(a ((b) (c)\n (d|) (e)) (f))" "(a ((b) (c)\n (d|) (e)) (f))") ("(a ((b) (c)\n (d)| (e)) (f))" "(a ((b) (c)\n (d)|) (f))" "(a ((b) (c)\n (d)|) (f))") ("(a ((b) (c)\n (d) |(e)) (f))" "(a ((b) (c)\n (d) |) (f))" "(a ((b) (c)\n (d) |) (f))") ("(a ((b) (c)\n (d) (|e)) (f))" "(a ((b) (c)\n (d) (|)) (f))" "(a ((b) (c)\n (d) (|)) (f))") ("(a ((b) (c)\n (d) (e|)) (f))" "(a ((b) (c)\n (d) (e|)) (f))") ("(a ((b) (c)\n (d) (e)|) (f))" "(a ((b) (c)\n (d) (e)|) (f))") ("(a ((b) (c)\n (d) (e))| (f))" "(a ((b) (c)\n (d) (e))|)" "(a ((b) (c)\n (d) (e))|)") ("(a ((b) (c)\n (d) (e)) |(f))" "(a ((b) (c)\n (d) (e)) |)" "(a ((b) (c)\n (d) (e)) |)") ("(a ((b) (c)\n (d) (e)) (|f))" "(a ((b) (c)\n (d) (e)) (|))" "(a ((b) (c)\n (d) (e)) (|))") ("(a ((b) (c)\n (d) (e)) (f|))" "(a ((b) (c)\n (d) (e)) (f|))") ("(a ((b) (c)\n (d) (e)) (f)|)" "(a ((b) (c)\n (d) (e)) (f)|)") ("(a ((b) (c)\n (d) (e)) (f))|" error) ("|(a \"(b) (c)\n ) { ;;;; \n\n\n(d)( (e);\" (f))" "|" error) ("(|a \"(b) (c)\n ) { ;;;; \n\n\n(d)( (e);\" (f))" "(| (f))" "(|)" "(|)") ("(a| \"(b) (c)\n ) { ;;;; \n\n\n(d)( (e);\" (f))" "(a| (f))" "(a|)" "(a|)") ("(a |\"(b) (c)\n ) { ;;;; \n\n\n(d)( (e);\" (f))" "(a | (f))" "(a |)" "(a |)") ("(a \"|(b) (c)\n ) { ;;;; \n\n\n(d)( (e);\" (f))" "(a \"|\n ) { ;;;; \n\n\n(d)( (e);\" (f))" "(a \"| ) { ;;;; \n\n\n(d)( (e);\" (f))" "(a \"|\n\n\n(d)( (e);\" (f))" "(a \"|\n\n(d)( (e);\" (f))" "(a \"|\n(d)( (e);\" (f))" "(a \"|(d)( (e);\" (f))" "(a \"|\" (f))" "(a \"|\" (f))") ("(a \"(b) (c)|\n ) { ;;;; \n\n\n(d)( (e);\" (f))" "(a \"(b) (c)| ) { ;;;; \n\n\n(d)( (e);\" (f))" "(a \"(b) (c)|\n\n\n(d)( (e);\" (f))" "(a \"(b) (c)|\n\n(d)( (e);\" (f))" "(a \"(b) (c)|\n(d)( (e);\" (f))" "(a \"(b) (c)|(d)( (e);\" (f))" "(a \"(b) (c)|\" (f))" "(a \"(b) (c)|\" (f))") ("(a \"(b) (c)\n ) { ;;;; |\n\n\n(d)( (e);\" (f))" "(a \"(b) (c)\n ) { ;;;; |\n\n(d)( (e);\" (f))" "(a \"(b) (c)\n ) { ;;;; |\n(d)( (e);\" (f))" "(a \"(b) (c)\n ) { ;;;; |(d)( (e);\" (f))" "(a \"(b) (c)\n ) { ;;;; |\" (f))" "(a \"(b) (c)\n ) { ;;;; |\" (f))") ("(a \"(b) (c)\n ) { ;;;; \n\n\n|(d)( (e);\" (f))" "(a \"(b) (c)\n ) { ;;;; \n\n\n|\" (f))" "(a \"(b) (c)\n ) { ;;;; \n\n\n|\" (f))") ("|x(\n)(z)" "|(z)" "|" error) ("x|(\n)(z)" "x|(z)" "x|" error) ("x(|\n)(z)" "x(|)(z)" "x(|)(z)") ("x(\n|)(z)" "x(\n|)(z)") ("x(\n)|(z)" "x(\n)|" error) ("x(\n)(|z)" "x(\n)(|)" "x(\n)(|)") ("x(\n)(z|)" "x(\n)(z|)") ("x(\n)(z)|" error) ("|x\"\n\"(z)" "|(z)" "|" error) ("x|\"\n\"(z)" "x|(z)" "x|" error) ("x\"|\n\"(z)" "x\"|\"(z)" "x\"|\"(z)") ("x\"\n|\"(z)" "x\"\n|\"(z)") ("x\"\n\"|(z)" "x\"\n\"|" error) ("x\"\n\"(|z)" "x\"\n\"(|)" "x\"\n\"(|)") ("x\"\n\"(z|)" "x\"\n\"(z|)") ("x\"\n\"(z)|" error) ("|(f ;; b\n z)" "|" error) ("(|f ;; b\n z)" "(|\n z)" "(| z)" "(|)" "(|)") ("(f| ;; b\n z)" "(f|\n z)" "(f| z)" "(f|)" "(f|)") ("(f |;; b\n z)" "(f |\n z)" "(f | z)" "(f |)" "(f |)") ("(f ;|; b\n z)" "(f ;|\n z)" error) ("(f ;;| b\n z)" "(f ;;|\n z)" error) ("(f ;; |b\n z)" "(f ;; |\n z)" error) ("(f ;; b|\n z)" error) ("(f ;; b\n| z)" "(f ;; b\n|)" "(f ;; b\n|)") ("(f ;; b\n |z)" "(f ;; b\n |)" "(f ;; b\n |)") ("(f ;; b\n z|)" "(f ;; b\n z|)" "(f ;; b\n z|)") ("(f ;; b\n z)|" error) ("|(f b) ;z" "|" error) ("(|f b) ;z" "(|) ;z" "(|) ;z") ("(f| b) ;z" "(f|) ;z" "(f|) ;z") ("(f |b) ;z" "(f |) ;z" "(f |) ;z") ("(f b|) ;z" "(f b|) ;z" "(f b|) ;z") ("(f b)| ;z" "(f b)|" error) ("(f b) |;z" "(f b) |" error) ("(f b) ;|z" "(f b) ;|" error) ("(f b) ;z|" error) ("|(f b) ;z\n" "|\n" "|" error) ("(|f b) ;z\n" "(|) ;z\n" "(|) ;z\n") ("(f| b) ;z\n" "(f|) ;z\n" "(f|) ;z\n") ("(f |b) ;z\n" "(f |) ;z\n" "(f |) ;z\n") ("(f b|) ;z\n" "(f b|) ;z\n") ("(f b)| ;z\n" "(f b)|\n" "(f b)|" error) ("(f b) |;z\n" "(f b) |\n" "(f b) |" error) ("(f b) ;|z\n" "(f b) ;|\n" "(f b) ;|" error) ("(f b) ;z|\n" "(f b) ;z|" error) ("(f b) ;z\n|" error) ("|(f\n b) ;z" "| ;z" "|" error) ("(|f\n b) ;z" "(|\n b) ;z" "(| b) ;z" "(|) ;z" "(|) ;z") ("(f|\n b) ;z" "(f| b) ;z" "(f|) ;z" "(f|) ;z") ("(f\n| b) ;z" "(f\n|) ;z" "(f\n|) ;z") ("(f\n |b) ;z" "(f\n |) ;z" "(f\n |) ;z") ("(f\n b|) ;z" "(f\n b|) ;z") ("(f\n b)| ;z" "(f\n b)|" error) ("(f\n b) |;z" "(f\n b) |" error) ("(f\n b) ;|z" "(f\n b) ;|" error) ("(f\n b) ;z|" error) ("|(f\n b) ;z\n" "| ;z\n" "|\n" "|" error) ("(|f\n b) ;z\n" "(|\n b) ;z\n" "(| b) ;z\n" "(|) ;z\n" "(|) ;z\n") ("(f|\n b) ;z\n" "(f| b) ;z\n" "(f|) ;z\n" "(f|) ;z\n") ("(f\n| b) ;z\n" "(f\n|) ;z\n" "(f\n|) ;z\n") ("(f\n |b) ;z\n" "(f\n |) ;z\n" "(f\n |) ;z\n") ("(f\n b|) ;z\n" "(f\n b|) ;z\n") ("(f\n b)| ;z\n" "(f\n b)|\n" "(f\n b)|" error) ("(f\n b) |;z\n" "(f\n b) |\n" "(f\n b) |" error) ("(f\n b) ;|z\n" "(f\n b) ;|\n" "(f\n b) ;|" error) ("(f\n b) ;z|\n" "(f\n b) ;z|" error) ("(f\n b) ;z\n|" error) ("|;f\n(b)\n" "|\n(b)\n" "|(b)\n" "|\n" "|" error) (";|f\n(b)\n" ";|\n(b)\n" ";|(b)\n" ";|\n" ";|" error) (";f|\n(b)\n" ";f|(b)\n" ";f|\n" ";f|" error) (";f\n|(b)\n" ";f\n|\n" ";f\n|" error) (";f\n(|b)\n" ";f\n(|)\n" ";f\n(|)\n") (";f\n(b|)\n" ";f\n(b|)\n") (";f\n(b)|\n" ";f\n(b)|" error) (";f\n(b)\n|" error) ("|;f\n(b\n z)\n" "|\n(b\n z)\n" "|(b\n z)\n" "|\n" "|" error) (";|f\n(b\n z)\n" ";|\n(b\n z)\n" error) (";f|\n(b\n z)\n" error) (";f\n|(b\n z)\n" ";f\n|\n" ";f\n|" error) (";f\n(|b\n z)\n" ";f\n(|\n z)\n" ";f\n(| z)\n" ";f\n(|)\n" ";f\n(|)\n") (";f\n(b|\n z)\n" ";f\n(b| z)\n" ";f\n(b|)\n" ";f\n(b|)\n") (";f\n(b\n| z)\n" ";f\n(b\n|)\n" ";f\n(b\n|)\n") (";f\n(b\n |z)\n" ";f\n(b\n |)\n" ";f\n(b\n |)\n") (";f\n(b\n z|)\n" ";f\n(b\n z|)\n"))) (paredit-test 'paredit-backward-barf-sexp '(("|(fo)" error) ;++ I think these are wrong. There should be a space. ("(|fo)" "|fo()" error) ("(f|o)" "f|o()" error) ("(fo|)" "fo(|)" "fo(|)") ("(fo)|" error) ("|(fo (ba bz qx) zt)" error) ("(|fo (ba bz qx) zt)" "|fo ((ba bz qx) zt)" error) ("(f|o (ba bz qx) zt)" "f|o ((ba bz qx) zt)" error) ("(fo| (ba bz qx) zt)" "fo| ((ba bz qx) zt)" error) ("(fo |(ba bz qx) zt)" "fo (|(ba bz qx) zt)" "fo |(ba bz qx) (zt)" error) ("(fo (|ba bz qx) zt)" "(fo |ba (bz qx) zt)" "fo (|ba (bz qx) zt)" "fo |ba ((bz qx) zt)" error) ("(fo (b|a bz qx) zt)" "(fo b|a (bz qx) zt)" "fo (b|a (bz qx) zt)" "fo b|a ((bz qx) zt)" error) ("(fo (ba| bz qx) zt)" "(fo ba| (bz qx) zt)" "fo (ba| (bz qx) zt)" "fo ba| ((bz qx) zt)" error) ("(fo (ba |bz qx) zt)" "(fo ba (|bz qx) zt)" "(fo ba |bz (qx) zt)" "fo (ba |bz (qx) zt)" "fo ba (|bz (qx) zt)" "fo ba |bz ((qx) zt)" error) ("(fo (ba b|z qx) zt)" "(fo ba (b|z qx) zt)" "(fo ba b|z (qx) zt)" "fo (ba b|z (qx) zt)" "fo ba (b|z (qx) zt)" "fo ba b|z ((qx) zt)" error) ("(fo (ba bz| qx) zt)" "(fo ba (bz| qx) zt)" "(fo ba bz| (qx) zt)" "fo (ba bz| (qx) zt)" "fo ba (bz| (qx) zt)" "fo ba bz| ((qx) zt)" error) ("(fo (ba bz |qx) zt)" "(fo ba (bz |qx) zt)" "(fo ba bz (|qx) zt)" "(fo ba bz |qx() zt)" ;++ Should have a space. "fo (ba bz |qx() zt)" "fo ba (bz |qx() zt)" "fo ba bz (|qx() zt)" "fo ba bz |qx(() zt)" error) ("(fo (ba bz |qx) zt)" "(fo ba (bz |qx) zt)" "(fo ba bz (|qx) zt)" "(fo ba bz |qx() zt)" "fo (ba bz |qx() zt)" "fo ba (bz |qx() zt)" "fo ba bz (|qx() zt)" "fo ba bz |qx(() zt)" error) ("(fo (ba bz q|x) zt)" "(fo ba (bz q|x) zt)" "(fo ba bz (q|x) zt)" "(fo ba bz q|x() zt)" "fo (ba bz q|x() zt)" "fo ba (bz q|x() zt)" "fo ba bz (q|x() zt)" "fo ba bz q|x(() zt)" error) ("(fo (ba bz qx|) zt)" "(fo ba (bz qx|) zt)" "(fo ba bz (qx|) zt)" "(fo ba bz qx(|) zt)" "(fo ba bz qx(|) zt)") ("(fo (ba bz qx)| zt)" "fo ((ba bz qx)| zt)" "fo (ba bz qx)| (zt)" error) ("(fo (ba bz qx) |zt)" "fo ((ba bz qx) |zt)" "fo (ba bz qx) (|zt)" "fo (ba bz qx) |zt()" error) ("(fo (ba bz qx) z|t)" "fo ((ba bz qx) z|t)" "fo (ba bz qx) (z|t)" "fo (ba bz qx) z|t()" error) ("(fo (ba bz qx) zt|)" "fo ((ba bz qx) zt|)" "fo (ba bz qx) (zt|)" "fo (ba bz qx) zt(|)" "fo (ba bz qx) zt(|)") ("(fo (ba bz qx) zt)|" error) ;++ Test interaction with comments... ("\"|\"" error) ("\"|xy\"" error) ;++ Could be done. Why not? ("\"x|y\"" error) ("\"xy|\"" error))) (paredit-test 'paredit-forward '(("|" "|") ("|()" "()|" "()|") ("(|)" "()|" "()|") ("()|" "()|") ("|( )" "( )|" "( )|") ("(| )" "( )|" "( )|") ("( |)" "( )|" "( )|") ("( )|" "( )|") ("|\"\"" "\"\"|" "\"\"|") ("\"|\"" "\"\"|" "\"\"|") ("\"\"|" "\"\"|") ("|\")\"" "\")\"|" "\")\"|") ("\"|)\"" "\")|\"" "\")\"|" "\")\"|") ("\")|\"" "\")\"|" "\")\"|") ("\")\"|" "\")\"|") ("|\"()\"" "\"()\"|" "\"()\"|") ("\"|()\"" "\"()|\"" "\"()\"|" "\"()\"|") ("\"(|)\"" "\"()|\"" "\"()\"|" "\"()\"|") ("\"()\"|" "\"()\"|") ("|(\"x\" \"y\")" "(\"x\" \"y\")|" "(\"x\" \"y\")|") ("(|\"x\" \"y\")" "(\"x\"| \"y\")" "(\"x\" \"y\"|)" "(\"x\" \"y\")|" "(\"x\" \"y\")|") ("(\"|x\" \"y\")" "(\"x|\" \"y\")" "(\"x\"| \"y\")" "(\"x\" \"y\"|)" "(\"x\" \"y\")|" "(\"x\" \"y\")|") ("(\"x|\" \"y\")" "(\"x\"| \"y\")" "(\"x\" \"y\"|)" "(\"x\" \"y\")|" "(\"x\" \"y\")|") ("(\"x\"| \"y\")" "(\"x\" \"y\"|)" "(\"x\" \"y\")|" "(\"x\" \"y\")|") ("(\"x\" |\"y\")" "(\"x\" \"y\"|)" "(\"x\" \"y\")|" "(\"x\" \"y\")|") ("(\"x\" \"|y\")" "(\"x\" \"y|\")" "(\"x\" \"y\"|)" "(\"x\" \"y\")|" "(\"x\" \"y\")|") ("(\"x\" \"y|\")" "(\"x\" \"y\"|)" "(\"x\" \"y\")|" "(\"x\" \"y\")|") ("(\"x\" \"y\"|)" "(\"x\" \"y\")|" "(\"x\" \"y\")|") ("(\"x\" \"y\")|" "(\"x\" \"y\")|") ("|#\\(" "#\\(|" "#\\(|") ("#|\\(" "#\\(|" "#\\(|") ("#\\|(" "#\\(|" "#\\(|") ("#\\(|" "#\\(|") ("|#\\)" "#\\)|" "#\\)|") ("#|\\)" "#\\)|" "#\\)|") ("#\\|)" "#\\)|" "#\\)|") ("#\\)|" "#\\)|") ("|#\\\\" "#\\\\|" "#\\\\|") ("#|\\\\" "#\\\\|" "#\\\\|") ("#\\|\\" "#\\\\|" "#\\\\|") ("#\\\\|" "#\\\\|") ("|#\\;" "#\\;|" "#\\;|") ("#|\\;" "#\\;|" "#\\;|") ("#\\|;" "#\\;|" "#\\;|") ("#\\;|" "#\\;|"))) (paredit-test 'paredit-backward '(("|" "|") ("|()" "|()") ("(|)" "|()" "|()") ("()|" "|()" "|()") ("|( )" "|( )") ("(| )" "|( )" "|( )") ("( |)" "|( )" "|( )") ("( )|" "|( )" "|( )") ("|\"\"" "|\"\"") ("\"|\"" "|\"\"" "|\"\"") ("\"\"|" "|\"\"" "|\"\"") ("|\")\"" "|\")\"") ("\"|)\"" "|\")\"" "|\")\"") ("\")|\"" "|\")\"" "|\")\"") ("\")\"|" "|\")\"" "|\")\"") ("|\"()\"" "|\"()\"") ("\"|()\"" "|\"()\"" "|\"()\"") ("\"(|)\"" "\"|()\"" "|\"()\"" "|\"()\"") ("\"()\"|" "|\"()\"" "|\"()\"") ("|(\"x\" \"y\")" "|(\"x\" \"y\")") ("(|\"x\" \"y\")" "|(\"x\" \"y\")" "|(\"x\" \"y\")") ("(\"|x\" \"y\")" "(|\"x\" \"y\")" "|(\"x\" \"y\")" "|(\"x\" \"y\")") ("(\"x|\" \"y\")" "(\"|x\" \"y\")" "(|\"x\" \"y\")" "|(\"x\" \"y\")" "|(\"x\" \"y\")") ("(\"x\"| \"y\")" "(|\"x\" \"y\")" "|(\"x\" \"y\")" "|(\"x\" \"y\")") ("(\"x\" |\"y\")" "(|\"x\" \"y\")" "|(\"x\" \"y\")" "|(\"x\" \"y\")") ("(\"x\" \"|y\")" "(\"x\" |\"y\")" "(|\"x\" \"y\")" "|(\"x\" \"y\")" "|(\"x\" \"y\")") ("(\"x\" \"y|\")" "(\"x\" \"|y\")" "(\"x\" |\"y\")" "(|\"x\" \"y\")" "|(\"x\" \"y\")" "|(\"x\" \"y\")") ("(\"x\" \"y\"|)" "(\"x\" |\"y\")" "(|\"x\" \"y\")" "|(\"x\" \"y\")" "|(\"x\" \"y\")") ("(\"x\" \"y\")|" "|(\"x\" \"y\")" "|(\"x\" \"y\")") ("|#\\(" "|#\\(") ("#|\\(" "|#\\(" "|#\\(") ("#\\|(" "|#\\(" "|#\\(") ("#\\(|" "|#\\(" "|#\\(") ("|#\\)" "|#\\)") ("#|\\)" "|#\\)" "|#\\)") ("#\\|)" "|#\\)" "|#\\)") ("#\\)|" "|#\\)" "|#\\)") ("|#\\\\" "|#\\\\") ("#|\\\\" "|#\\\\" "|#\\\\") ("#\\|\\" "|#\\\\" "|#\\\\") ("#\\\\|" "|#\\\\" "|#\\\\") ("|#\\;" "|#\\;") ("#|\\;" "|#\\;" "|#\\;") ("#\\|;" "|#\\;" "|#\\;") ("#\\;|" "|#\\;" "|#\\;"))) (paredit-test 'paredit-join-sexps '(("|ab cd" error) ("a|b cd" error) ("ab| cd" "ab|cd" error) ("ab |cd" "ab|cd" error) ("ab c|d" error) ("ab cd|" error) ("|x (y)" error) ("x| (y)" error) ("x |(y)" error) ("x (|y)" error) ("x (y|)" error) ("x (y)|" error) ("|(x ((y)\n (z)))" error) ("(|x ((y)\n (z)))" error) ("(x| ((y)\n (z)))" error) ("(x |((y)\n (z)))" error) ("(x (|(y)\n (z)))" error) ("(x ((|y)\n (z)))" error) ("(x ((y)|\n (z)))" "(x ((y|\n z)))") ("(x ((y)\n| (z)))" "(x ((y\n| z)))") ("(x ((y)\n | (z)))" "(x ((y\n | z)))") ("(x ((y)\n | (z)))" "(x ((y\n | z)))") ("(x ((y)\n | (z)))" "(x ((y\n | z)))") ;++ I don't think this is right. The point shouldn't move right. ("(x ((y)\n |(z)))" "(x ((y\n |z)))") ("(x ((y)\n (|z)))" error) ("(x ((y)\n (z|)))" error) ("(x ((y)\n (z)|))" error) ("(x ((y)\n (z))|)" error) ("(x ((y)\n (z)))|" error) ("|(ab cd) (ef \"gh\")" error) ("(|ab cd) (ef \"gh\")" error) ("(a|b cd) (ef \"gh\")" error) ("(ab| cd) (ef \"gh\")" "(ab|cd) (ef \"gh\")" error) ("(ab |cd) (ef \"gh\")" "(ab|cd) (ef \"gh\")" error) ("(ab c|d) (ef \"gh\")" error) ("(ab cd|) (ef \"gh\")" error) ("(ab cd)| (ef \"gh\")" "(ab cd| ef \"gh\")" "(ab cd|ef \"gh\")" error) ("(ab cd) |(ef \"gh\")" "(ab cd |ef \"gh\")" "(ab cd|ef \"gh\")" error) ("(ab cd) (|ef \"gh\")" error) ("(ab cd) (e|f \"gh\")" error) ("(ab cd) (ef| \"gh\")" error) ("(ab cd) (ef |\"gh\")" error) ("(ab cd) (ef \"g|h\")" error) ("(ab cd) (ef \"gh\"|)" error) ("(ab cd) (ef \"gh\")|" error) ("|() \"\"" error) ("(|) \"\"" error) ("()| \"\"" error) ("() |\"\"" error) ("() \"|\"" error) ("() \"\"|" error) ("|\"\" ()" error) ("\"|\" ()" error) ("\"\"| ()" error) ("\"\" |()" error) ("\"\" (|)" error) ("\"\" (|)|" error) ("|(x y) [z]" error) ("(|x y) [z]" error) ("(x| y) [z]" "(x|y) [z]" error) ("(x |y) [z]" "(x|y) [z]" error) ("(x y|) [z]" error) ("(x y)| [z]" error) ("(x y) |[z]" error) ("(x y) [|z]" error) ("(x y) [z|]" error) ("(x y) [z]|" error) ("|(x)(y)" error) ("(|x)(y)" error) ("(x|)(y)" error) ("(x)|(y)" "(x| y)") ("(x)(|y)" error) ("(x)(y|)" error) ("(x)(y)|" error) ("|\"ab\" \"cd\"" error) ("\"|ab\" \"cd\"" error) ("\"a|b\" \"cd\"" error) ("\"ab\"| \"cd\"" "\"ab|cd\"" error) ("\"ab\" | \"cd\"" "\"ab|cd\"" error) ("\"ab\" |\"cd\"" "\"ab|cd\"" error) ("\"ab\" \"|cd\"" error) ("\"ab\" \"c|d\"" error) ("\"ab\" \"cd|\"" error) ("\"ab\" \"cd\"|" error) ("|(x ((y)\n (z)))" error) ("(|x ((y)\n (z)))" error) ("(x| ((y)\n (z)))" error) ("(x |((y)\n (z)))" error) ("(x ((|y)\n (z)))" error) ("(x ((y)|\n (z)))" "(x ((y|\n z)))" "(x ((y|z)))" error) ("(x ((y)\n| (z)))" "(x ((y\n| z)))" "(x ((y|z)))" error) ("(x ((y)\n | (z)))" "(x ((y\n | z)))" "(x ((y|z)))" error) ("(x ((y)\n | (z)))" "(x ((y\n | z)))" "(x ((y|z)))" error) ("(x ((y)\n | (z)))" "(x ((y\n | z)))" "(x ((y|z)))" error) ("(x ((y)\n |(z)))" "(x ((y\n |z)))" "(x ((y|z)))" error) ("(x ((y)\n (|z)))" error) ("(x ((y)\n (z|)))" error) ("(x ((y)\n (z)|))" error) ("(x ((y)\n (z))|)" error) ("(x ((y)\n (z)))|" error) ;++ What about comments intervening strings/symbols? ("|((x) ;c\n (y))" error) ("(|(x) ;c\n (y))" error) ("((|x) ;c\n (y))" error) ("((x|) ;c\n (y))" error) ("((x)| ;c\n (y))" "((x| ;c\n y))") ("((x) | ;c\n (y))" "((x | ;c\n y))") ("((x) | ;c\n (y))" "((x | ;c\n y))") ("((x) | ;c\n (y))" "((x | ;c\n y))") ("((x) |;c\n (y))" "((x |;c\n y))") ("((x) ;|c\n (y))" error) ("((x) ;c|\n (y))" error) ("((x) ;c\n| (y))" "((x ;c\n| y))") ("((x) ;c\n |(y))" "((x ;c\n |y))") ("((x) ;c\n (|y))" error) ("((x) ;c\n (y|))" error) ("((x) ;c\n (y)|)" error) ("((x) ;c\n (y))|" error))) (paredit-test 'paredit-meta-doublequote '(("|(fo \"ba\\\" bz\" qx)" "\"|(fo \\\"ba\\\\\\\" bz\\\" qx)\"") ("(|fo \"ba\\\" bz\" qx)" "(\"|fo\" \"ba\\\" bz\" qx)") ("(f|o \"ba\\\" bz\" qx)" "(f \"|o\" \"ba\\\" bz\" qx)") ;++ Should the space be left there after the `"'? ("(fo| \"ba\\\" bz\" qx)" "(fo \"| \\\"ba\\\\\\\" bz\\\"\" qx)") ("(fo |\"ba\\\" bz\" qx)" "(fo \"|\\\"ba\\\\\\\" bz\\\"\" qx)") ("(fo \"|ba\\\" bz\" qx)" "(fo \"ba\\\" bz\"| qx)") ("(fo \"b|a\\\" bz\" qx)" "(fo \"ba\\\" bz\"| qx)") ("(fo \"ba|\\\" bz\" qx)" "(fo \"ba\\\" bz\"| qx)") ("(fo \"ba\\|\" bz\" qx)" "(fo \"ba\\\" bz\"| qx)") ("(fo \"ba\\\"| bz\" qx)" "(fo \"ba\\\" bz\"| qx)") ("(fo \"ba\\\" |bz\" qx)" "(fo \"ba\\\" bz\"| qx)") ("(fo \"ba\\\" b|z\" qx)" "(fo \"ba\\\" bz\"| qx)") ("(fo \"ba\\\" bz|\" qx)" "(fo \"ba\\\" bz\"| qx)") ;++ Should the space be left there after the `"'? ("(fo \"ba\\\" bz\"| qx)" "(fo \"ba\\\" bz\" \"| qx\")") ("(fo \"ba\\\" bz\" |qx)" "(fo \"ba\\\" bz\" \"|qx\")") ("(fo \"ba\\\" bz\" q|x)" "(fo \"ba\\\" bz\" q \"|x\")") ("(fo \"ba\\\" bz\" qx|)" "(fo \"ba\\\" bz\" qx \"|\")") ("(fo \"ba\\\" bz\" qx)|" "(fo \"ba\\\" bz\" qx) \"|\"") ;++ Full tests... ("(foo |(bar #\\x \"baz \\\\ quux\") zot)" "(foo \"|(bar #\\\\x \\\"baz \\\\\\\\ quux\\\")\" zot)"))) ;++ Copy tests from `paredit-meta-doublequote'... (paredit-test 'paredit-meta-doublequote-and-newline '(("(foo \"bar |baz\" quux)" "(foo \"bar baz\"\n |quux)") ("(foo |(bar #\\x \"baz \\\\ quux\") zot)" "(foo \"|(bar #\\\\x \\\"baz \\\\\\\\ quux\\\")\" zot)"))) (paredit-test 'paredit-splice-sexp '(("|(f (g\n h))" error) ("(|f (g\n h))" "|f (g\n h)") ("(f| (g\n h))" "f| (g\n h)") ("(f |(g\n h))" "f |(g\n h)") ("(f (|g\n h))" "(f |g\n h)") ("(f (g|\n h))" "(f g|\n h)") ("(f (g\n| h))" "(f g\n| h)") ("(f (g\n | h))" "(f g\n | h)") ("(f (g\n | h))" "(f g\n | h)") ("(f (g\n | h))" "(f g\n |h)") ("(f (g\n |h))" "(f g\n |h)") ("(f (g\n h|))" "(f g\n h|)") ("(f (g\n h)|)" "f (g\n h)|") ("(f (g\n h))|" error) ;; Omit whitespace if appropriate. ("|(f (\n h))" error) ("(|f (\n h))" "|f (\n h)") ("(f| (\n h))" "f| (\n h)") ("(f |(\n h))" "f |(\n h)") ("(f (|\n h))" "(f |h)") ("(f (\n| h))" "(f |h)") ("(f (\n | h))" "(f |h)") ("(f (\n | h))" "(f |h)") ("(f (\n | h))" "(f |h)") ("(f (\n |h))" "(f |h)") ("(f (\n h|))" "(f h|)") ("(f (\n h)|)" "f (\n h)|") ("(f (\n h))|" error) ;; But leave comments intact. ("(f (| ;xy\n h))" "(f |;xy\n h)") ("(f ( | ;xy\n h))" "(f |;xy\n h)") ("(f ( | ;xy\n h))" "(f |;xy\n h)") ("(f ( |;xy\n h))" "(f |;xy\n h)") ("(f ( ;|xy\n h))" error) ("(f ( ;x|y\n h))" error) ("(f ( ;xy|\n h))" error) ("(f ( ;xy\n| h))" "(f ;xy\n| h)") ("(f ( ;xy\n | h))" "(f ;xy\n |h)") ("(f ( ;xy\n | h))" "(f ;xy\n |h)") ("(f ( ;xy\n | h))" "(f ;xy\n |h)") ("(f ( ;xy\n |h))" "(f ;xy\n |h)") ("(f ( ;xy\n h|))" "(f ;xy\n h|)") ;; Don't touch indentation outside a limited scope. ("(foo (|bar)\n baz)" "(foo |bar\n baz)") ("(foo (b|ar)\n baz)" "(foo b|ar\n baz)") ("(foo (ba|r)\n baz)" "(foo ba|r\n baz)") ("(foo (bar|)\n baz)" "(foo bar|\n baz)") (" (foo\n (|bar baz))" " (foo\n |bar baz)") (" (foo\n (b|ar baz))" " (foo\n b|ar baz)") (" (foo\n (ba|r baz))" " (foo\n ba|r baz)") (" (foo\n (bar| baz))" " (foo\n bar| baz)") (" (foo\n (bar |baz))" " (foo\n bar |baz)") (" (foo\n (bar b|az))" " (foo\n bar b|az)") (" (foo\n (bar ba|z))" " (foo\n bar ba|z)") (" (foo\n (bar baz|))" " (foo\n bar baz|)") (" (foo (|(bar\n baz)\n quux)\n zot)" " (foo |(bar\n baz)\n quux\n zot)") (" (foo ((|bar\n baz)\n quux)\n zot)" " (foo (|bar\n baz\n quux)\n zot)") (" (foo ((b|ar\n baz)\n quux)\n zot)" " (foo (b|ar\n baz\n quux)\n zot)") (" (foo ((ba|r\n baz)\n quux)\n zot)" " (foo (ba|r\n baz\n quux)\n zot)") (" (foo ((ba|r\n baz)\n quux)\n zot)" " (foo (ba|r\n baz\n quux)\n zot)") (" (foo ((bar|\n baz)\n quux)\n zot)" " (foo (bar|\n baz\n quux)\n zot)") (" (foo ((bar\n| baz)\n quux)\n zot)" " (foo (bar\n| baz\n quux)\n zot)") (" (foo ((bar\n | baz)\n quux)\n zot)" " (foo (bar\n | baz\n quux)\n zot)") (" (foo ((bar\n | baz)\n quux)\n zot)" " (foo (bar\n | baz\n quux)\n zot)") (" (foo ((bar\n | baz)\n quux)\n zot)" " (foo (bar\n | baz\n quux)\n zot)") (" (foo ((bar\n | baz)\n quux)\n zot)" " (foo (bar\n | baz\n quux)\n zot)") (" (foo ((bar\n | baz)\n quux)\n zot)" " (foo (bar\n | baz\n quux)\n zot)") (" (foo ((bar\n | baz)\n quux)\n zot)" " (foo (bar\n | baz\n quux)\n zot)") (" (foo ((bar\n | baz)\n quux)\n zot)" " (foo (bar\n | baz\n quux)\n zot)") (" (foo ((bar\n | baz)\n quux)\n zot)" " (foo (bar\n |baz\n quux)\n zot)") (" (foo ((bar\n |baz)\n quux)\n zot)" " (foo (bar\n |baz\n quux)\n zot)") (" (foo ((bar\n b|az)\n quux)\n zot)" " (foo (bar\n b|az\n quux)\n zot)") (" (foo ((bar\n ba|z)\n quux)\n zot)" " (foo (bar\n ba|z\n quux)\n zot)") (" (foo ((bar\n baz|)\n quux)\n zot)" " (foo (bar\n baz|\n quux)\n zot)") (" (foo ((bar\n baz)|\n quux)\n zot)" " (foo (bar\n baz)|\n quux\n zot)") (" (foo ((bar\n baz)\n| quux)\n zot)" " (foo (bar\n baz)\n| quux\n zot)") (" (foo ((bar\n baz)\n | quux)\n zot)" " (foo (bar\n baz)\n | quux\n zot)") (" (foo ((bar\n baz)\n | quux)\n zot)" " (foo (bar\n baz)\n | quux\n zot)") (" (foo ((bar\n baz)\n | quux)\n zot)" " (foo (bar\n baz)\n | quux\n zot)") (" (foo ((bar\n baz)\n | quux)\n zot)" " (foo (bar\n baz)\n | quux\n zot)") (" (foo ((bar\n baz)\n | quux)\n zot)" " (foo (bar\n baz)\n | quux\n zot)") (" (foo ((bar\n baz)\n | quux)\n zot)" " (foo (bar\n baz)\n | quux\n zot)") (" (foo ((bar\n baz)\n | quux)\n zot)" " (foo (bar\n baz)\n |quux\n zot)") (" (foo ((bar\n baz)\n |quux)\n zot)" " (foo (bar\n baz)\n |quux\n zot)") (" (foo ((bar\n baz)\n q|uux)\n zot)" " (foo (bar\n baz)\n q|uux\n zot)") (" (foo ((bar\n baz)\n qu|ux)\n zot)" " (foo (bar\n baz)\n qu|ux\n zot)") (" (foo ((bar\n baz)\n quu|x)\n zot)" " (foo (bar\n baz)\n quu|x\n zot)") (" (foo ((bar\n baz)\n quux|)\n zot)" " (foo (bar\n baz)\n quux|\n zot)") ;; But adjust the whole form's indentation if we change the operator. ("((|let) ((a b))\n c)" "(|let ((a b))\n c)") ("((l|et) ((a b))\n c)" "(l|et ((a b))\n c)") ("((le|t) ((a b))\n c)" "(le|t ((a b))\n c)") ("((let|) ((a b))\n c)" "(let| ((a b))\n c)") ;; Uh oh -- you can really lose here. ("\"|foo\\\"bar\"" error) ;++ ("(\"|foo\\\;bar\")" error) )) (paredit-test 'paredit-forward-slurp-sexp '(("|" error) ("|()" error) ;; ("(|)" error) ;++ Urk... ("()|" error) ("|() foo" error) ("(|) foo" "(|foo)") ("()| foo" error) ("() |foo" error) ("() f|oo" error) ("() fo|o" error) ("() foo|" error) ("|(foo) bar" error) ("(|foo) bar" "(|foo bar)") ("(f|oo) bar" "(f|oo bar)") ("(fo|o) bar" "(fo|o bar)") ("(foo|) bar" "(foo| bar)") ("(foo)| bar" error) ("(foo) |bar" error) ("(foo) b|ar" error) ("(foo) ba|r" error) ("(foo) bar|" error) ("|\"\"" error) ;; ("\"|\"" error) ;++ Urk... ("\"\"|" error) ("|\"\" foo" error) ("\"|\" foo" "\"|foo\"") ("\"\"| foo" error) ("\"\" |foo" error) ("\"\" f|oo" error) ("\"\" fo|o" error) ("\"\" foo|" error) ("|\"foo\" bar" error) ("\"|foo\" bar" "\"|foo bar\"") ("\"f|oo\" bar" "\"f|oo bar\"") ("\"fo|o\" bar" "\"fo|o bar\"") ("\"foo|\" bar" "\"foo| bar\"") ("\"foo\"| bar" error) ("\"foo\" |bar" error) ("\"foo\" b|ar" error) ("\"foo\" ba|r" error) ("\"foo\" bar|" error) ("|\"\" \"\"" error) ("\"|\" \"\"" "\"|\\\"\\\"\"") ("\"\"| \"\"" error) ("\"\" |\"\"" error) ;; ("\"\" \"|\"" error) ;++ Urk... ("\"\" \"\"|" error) ("|(#\\x) y" error) ("(|#\\x) y" "(|#\\x y)") ("(#|\\x) y" "(#|\\x y)") ("(#\\|x) y" "(#\\|x y)") ("(#\\x|) y" "(#\\x| y)") ("(#\\x)| y" error) ("(#\\x) |y" error) ("(#\\x) y|" error) ("|(\"x\") y" error) ("(|\"x\") y" "(|\"x\" y)") ("(\"|x\") y" "(\"|x\" y)" "(\"|x y\")") ("(\"x|\") y" "(\"x|\" y)" "(\"x| y\")") ("(\"x\"|) y" "(\"x\"| y)") ("(\"x\")| y" error) ("(\"x\") |y" error) ("(\"x\") y|" error))) (paredit-test 'paredit-backward-slurp-sexp '(("|" error) ("|()" error) ;; ("(|)" error) ;++ Urk... ("()|" error) ("|foo ()" error) ("f|oo ()" error) ("fo|o ()" error) ("foo| ()" error) ("foo |()" error) ("foo (|)" "(foo|)") ("foo ()|" error) ("|foo (bar)" error) ("f|oo (bar)" error) ("fo|o (bar)" error) ("foo| (bar)" error) ("foo |(bar)" error) ("foo (|bar)" "(foo |bar)") ("foo (b|ar)" "(foo b|ar)") ("foo (ba|r)" "(foo ba|r)") ("foo (bar|)" "(foo bar|)") ("foo (bar)|" error) ("|\"\"" error) ;; ("\"|\"" error) ;++ Urk... ("\"\"|" error) ("|foo \"\"" error) ("f|oo \"\"" error) ("fo|o \"\"" error) ("foo| \"\"" error) ("foo |\"\"" error) ("foo \"|\"" "\"foo|\"") ("foo \"\"|" error) ("|foo \"bar\"" error) ("f|oo \"bar\"" error) ("fo|o \"bar\"" error) ("foo| \"bar\"" error) ("foo |\"bar\"" error) ("foo \"|bar\"" "\"foo |bar\"") ("foo \"b|ar\"" "\"foo b|ar\"") ("foo \"ba|r\"" "\"foo ba|r\"") ("foo \"bar|\"" "\"foo bar|\"") ("foo \"bar\"|" error) ("|\"\" \"\"" error) ;; ("\"|\" \"\"" error) ;++ Urk... ("\"\"| \"\"" error) ("\"\" |\"\"" error) ("\"\" \"|\"" "\"\\\"\\\"|\"") ("\"\" \"\"|" error) ("|x (#\\y)" error) ("x| (#\\y)" error) ("x |(#\\y)" error) ("x (|#\\y)" "(x |#\\y)") ("x (#|\\y)" "(x #|\\y)") ("x (#\\|y)" "(x #\\|y)") ("x (#\\y|)" "(x #\\y|)") ("x (#\\y)|" error) ("|x (\"y\")" error) ("x| (\"y\")" error) ("x |(\"y\")" error) ("x (|\"y\")" "(x |\"y\")") ("x (\"|y\")" "(x \"|y\")" "(\"x |y\")") ("x (\"y|\")" "(x \"y|\")" "(\"x y|\")") ("x (\"y\"|)" "(x \"y\"|)") ("x (\"y\")|" error))) (let ((current-prefix-arg 2)) (paredit-test 'paredit-forward-slurp-sexp '(("(foo|) bar baz" "(foo| bar baz)"))) (paredit-test 'paredit-backward-slurp-sexp '(("foo bar (|baz)" "(foo bar |baz)"))) (paredit-test 'paredit-forward-barf-sexp '(("(foo| bar baz)" "(foo|) bar baz") ("(foo |bar baz)" "(foo) |bar baz"))) (paredit-test 'paredit-backward-barf-sexp '(("(foo bar| baz)" "foo bar| (baz)") ("(foo bar |baz)" "foo bar (|baz)")))) (let ((current-prefix-arg -2)) (paredit-test 'paredit-forward-slurp-sexp '(("(foo| bar baz)" "(foo|) bar baz") ("(foo |bar baz)" "(foo) |bar baz"))) (paredit-test 'paredit-backward-slurp-sexp '(("(foo bar| baz)" "foo bar| (baz)") ("(foo bar |baz)" "foo bar (|baz)"))) (paredit-test 'paredit-forward-barf-sexp '(("(foo|) bar baz" "(foo| bar baz)"))) (paredit-test 'paredit-backward-barf-sexp '(("foo bar (|baz)" "(foo bar |baz)")))) (let ((current-prefix-arg '(4))) (paredit-test 'paredit-forward-slurp-sexp '(("(foo|) bar baz" "(foo| bar baz)") ("(foo| bar) baz" "(foo| bar baz)"))) (paredit-test 'paredit-backward-slurp-sexp '(("foo bar (|baz)" "(foo bar |baz)") ("foo (bar |baz)" "(foo bar |baz)"))) (paredit-test 'paredit-forward-barf-sexp '(("(foo| bar baz)" "(foo|) bar baz") ("(foo |bar baz)" "(foo) |bar baz") ("(foo b|ar baz)" "(foo b|ar) baz"))) (paredit-test 'paredit-backward-barf-sexp '(("(foo ba|r baz)" "foo (ba|r baz)") ("(foo bar| baz)" "foo bar| (baz)") ("(foo bar |baz)" "foo bar (|baz)")))) (defun paredit-canary-indent-method (state indent-point normal-indent) (check-parens) nil) (put 'paredit-canary 'scheme-indent-function 'paredit-canary-indent-method) ;;; Check for regressions the indentation behaviour of forward slurping ;;; and barfing. (paredit-test 'paredit-forward-slurp-sexp '(("(paredit-canary|)\n(lose)" "(paredit-canary|\n (lose))"))) (paredit-test 'paredit-forward-barf-sexp '(("(paredit-canary| ;\n (lose))") ("(paredit-canary| ;\n)\n(lose)"))) (paredit-test 'paredit-convolute-sexp '(("(let ((x 5) (y 3)) |(frob (zwonk)) (wibblethwop))" error) ("(let ((x 0)) (progn| x))" "(progn |(let ((x 0)) x))") ;; Should the space be left inside the LET? In both cases? ("(let ((x 0)) (progn| ))" "(progn |(let ((x 0)) ))") ("(let ((x 0)) (progn|))" "(progn |(let ((x 0)) ))") ;; One space should definitely be left between A and B here. ("(let ((x 0)) a(progn|)b)" "(progn |(let ((x 0)) a b))") ("(let ((x 0)) a (progn|) b)" "(progn |(let ((x 0)) a b))") ("(let ((x 0)) a (progn| ) b)" "(progn |(let ((x 0)) a b))") ("(let ((x 0)) a (progn |) b)" "(progn |(let ((x 0)) a b))")))