pax_global_header00006660000000000000000000000064150043043370014511gustar00rootroot0000000000000052 comment=bb291fc416c474ed9adcd88f41ca78e7aaedfa34 org-mode-9.7.29+dfsg/000077500000000000000000000000001500430433700142515ustar00rootroot00000000000000org-mode-9.7.29+dfsg/.dir-locals.el000066400000000000000000000010621500430433700167010ustar00rootroot00000000000000;;; Directory Local Variables -*- no-byte-compile: t; -*- ;;; For more information see (info "(emacs) Directory Variables") ((nil (indent-tabs-mode . t) (tab-width . 8) (fill-column . 70) (sentence-end-double-space . t)) (emacs-lisp-mode (indent-tabs-mode)) (org-mode (indent-tabs-mode) (org-adapt-indentation) (org-edit-src-content-indentation . 0) (org-footnote-auto-adjust . t) (org-footnote-auto-label . t) (org-footnote-define-inline . nil) (org-footnote-section . "Footnotes") (org-hide-emphasis-markers . nil))) org-mode-9.7.29+dfsg/.gitignore000066400000000000000000000020241500430433700162370ustar00rootroot00000000000000# Don't bother tracking a bunch of stuff when building and installing # Org from the master git repository. # ...by ignoring everything created by 'make', 'make doc', `make info' # `make html_manual', `make release' *.aux *.bak *.cp *.cps *.diff *.dvi *.elc *.fn *.fns *.html *.info *.ky *.kys *.log *.patch *.pdf *.pg *.pgs *.ps *.toc *.tp *.vr *.vrs orgcard_letter.tex orgcard.txt org orgguide org-loaddefs.el org-version.el doc/org.texi doc/orgguide.texi doc/org-version.inc doc/org-version.tex org-*.tar* orgplus-*.tar* org-*.zip version.mk manual org_dual_license.texi ORGWEBPAGE/Changes.txt local*.mk .gitattributes mk/x11idle ChangeLog # Files generated during `make packages/org` in a clone of `elpa.git`. /org-pkg.el /org-autoloads.el /lisp/org-autoloads.el # texi2pdf --tidy doc/*.t2d # aspell word and replacement lists .aspell.org.pws .aspell.org.prepl # allow tmp and test directories that will not be tracked test t auto tmp TODO # and collateral damage from Emacs *~ .DS_Store *# .#* # # Local variables: # End: org-mode-9.7.29+dfsg/.gitmodules000066400000000000000000000001371500430433700164270ustar00rootroot00000000000000[submodule "testing/jump"] path = testing/jump url = https://github.com/eschulte/jump.el.git org-mode-9.7.29+dfsg/CONTRIBUTE.org000066400000000000000000000016501500430433700164420ustar00rootroot00000000000000See [[https://orgmode.org/worg/org-contribute.html][the org-contribute page on Worg]] for guidance on how to contribute effectively. We value a nice tone in our discussions: please check and respect the [[https://www.gnu.org/philosophy/kind-communication.en.html][GNU Kind Communications Guidelines]]. * Contribute as an Org user You can contribute by helping others in various channels. See [[https://orgmode.org/worg/org-contribute.html#org99b8f3e][these directions]]. * Contribute as an Emacs Lisp hacker You can contribute with bug reports and patches. See these [[https://orgmode.org/worg/org-contribute.html#org069b83a][directions]]. * As an Org maintainer We encourage you to volunteer to maintain one of the Org files. Just [[mailto:emacs-orgmode@gnu.org][send an email to the list]] explaining which file and your motivations. See what is [[https://orgmode.org/worg/org-maintenance.html][the role of a maintainer]]. org-mode-9.7.29+dfsg/COPYING000066400000000000000000001045171500430433700153140ustar00rootroot00000000000000 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 . org-mode-9.7.29+dfsg/Makefile000066400000000000000000000071701500430433700157160ustar00rootroot00000000000000# Makefile - for the org-mode distribution # GNU make is required # # This file is not part of GNU Emacs # set up environment include mk/default.mk # defaults, customizable via "local.mk" -include local.mk # optional local customization, use default.mk as template # default target is "all" unless overridden in local.mk all:: # Describe valid make targets for org-mode. .PHONY: targets help helpall targets: help help helpall:: $(info ) $(info Getting Help) $(info ============) $(info make help - show brief help) $(info make targets - ditto) $(info make helpall - show extended help) $(info ) $(info Build and Check) $(info ===============) $(info make - build Org Elisp and all documentation) $(info make all - ditto) $(info make compile - build Org Elisp files) $(info make single - build Org Elisp files, single Emacs per source) $(info make native - build Org natively compiled Elisp files) $(info make autoloads - create org-loaddefs.el to load Org in-place) $(info make test - build Org Elisp files and run test suite) $(info make vanilla - run Emacs with this Org-mode and no personal config) helpall:: $(info make test-dirty - check without building first) $(info make compile-dirty - build only stale Org Elisp files) $(info ) $(info Compatibility) $(info =============) $(info make oldorg - what the old make did: compile autoloads info) $(info ) $(info Cleaning) $(info ========) $(info make clean - remove built Org Elisp files and documentation) $(info make cleanall - remove everything that can be built and all remnants) $(info make clean-install - remove previous Org installation) $(info ) $(info Configuration Check) $(info ===================) help helpall:: $(info make config - check main configuration) helpall:: $(info make config-version - check Org version) $(info make config-test - check test configuration) $(info make config-exe - check executables configuration) $(info make config-cmd - check command configuration) $(info make config-all - check all configuration) $(info ) $(info Documentation) $(info =============) help helpall:: $(info make doc - build all documentation) helpall:: $(info make docs - ditto) help helpall:: $(info make info - build Info documentation) helpall:: $(info make html - build HTML documentation) $(info make pdf - build PDF documentation) $(info make card - build reference cards) $(info make refcard - ditto) help helpall:: $(info ) $(info Installation) $(info ============) $(info make install - build and install Org) helpall:: $(info make install-etc - build and install files in /etc) $(info make install-lisp - build and install Org Elisp files) $(info make install-info - build and install Info documentation) $(info ) $(info Convenience) $(info ===========) $(info make up0 - pull from upstream) $(info make up1 - pull from upstream, build and check) $(info make up2 - pull from upstream, build, check and install) $(info make update - pull from upstream and build) $(info make update2 - pull from upstream, build and install) $(info make uncompiled - combine cleanlisp and autoloads) $(info make local.mk - create new local.mk as template for adaptation) help helpall:: $(info ) $(info Full documentation on Worg) $(info ==========================) $(info https://orgmode.org/worg/dev/org-build-system.html) @echo "" include mk/targets.mk # toplevel make machinery org-mode-9.7.29+dfsg/README.org000066400000000000000000000036011500430433700157170ustar00rootroot00000000000000This is a distribution of Org Mode, a major mode for keeping notes, authoring documents, computational notebooks, literate programming, maintaining to-do lists, planning projects, and more — in a fast and effective plain text system. Check the [[https://orgmode.org][Org Mode website]] for more. * Install Org Org is part of GNU Emacs: you probably don't need to install it. To install a more recent version, please use command: =M-x list-packages=, find "org" in the list, click on it, and click "Install" in the popped up window. * Join the GNU Project Org is part of GNU Emacs and GNU Emacs is part of the GNU Operating System, developed by the GNU Project. If you are the author of an awesome program and want to join us in writing Free (libre) Software, please consider making it an official GNU program and become a GNU Maintainer. Instructions on how to do this are here http://www.gnu.org/help/evaluation. Don't have a program to contribute? Look at all the other ways to help: https://www.gnu.org/help/help.html. And to learn more about Free (libre) Software in general, please read and share this page: https://gnu.org/philosophy/free-sw.html * License Org-mode is published under the [[https://www.gnu.org/licenses/gpl-3.0.html][GNU GPLv3 license]] or any later version, the same as GNU Emacs. Org-mode 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. GNU Emacs 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 Org mode. If not, see https://www.gnu.org/licenses/. org-mode-9.7.29+dfsg/etc/000077500000000000000000000000001500430433700150245ustar00rootroot00000000000000org-mode-9.7.29+dfsg/etc/Makefile000066400000000000000000000013411500430433700164630ustar00rootroot00000000000000ETCDIRS = styles schema csl -include local.mk # optional local customization .NOTPARALLEL: # always run this make serially .SUFFIXES: # we don't need default suffix rules ifeq ($(MAKELEVEL), 0) $(error This make needs to be started as a sub-make from the toplevel directory.) endif .PHONY: all install clean cleanall clean-install all: install: $(ETCDIRS) for dir in $? ; do \ if [ ! -d $(DESTDIR)$(datadir)/$${dir} ] ; then \ $(MKDIR) $(DESTDIR)$(datadir)/$${dir} ; \ fi ; \ $(CP) $${dir}/* $(DESTDIR)$(datadir)/$${dir} ; \ done ; clean: cleanall: clean-install: $(ETCDIRS) for dir in $? ; do \ if [ -d $(DESTDIR)$(datadir)/$${dir} ] ; then \ $(RMR) $(DESTDIR)$(datadir)/$${dir} ; \ fi ; \ done ; org-mode-9.7.29+dfsg/etc/ORG-NEWS000066400000000000000000012026601500430433700161570ustar00rootroot00000000000000ORG NEWS -- history of user-visible changes. -*- mode: org; coding: utf-8 -*- #+STARTUP: overview #+LINK: doc https://orgmode.org/worg/doc.html#%s #+LINK: msg https://list.orgmode.org/%s/ #+LINK: git https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=%s Copyright (C) 2012-2025 Free Software Foundation, Inc. See the end of the file for license conditions. Please send Org bug reports to mailto:emacs-orgmode@gnu.org. * Version 9.7 ** Important announcements and breaking changes # Here, we list the *most important* changes and changes that _likely_ # require user action for most Org mode users. # Sorted from most important to least important. *** Arbitrary shell commands may no longer run when turning on Org mode This is for security reasons, to avoid running malicious commands. *** =python-mode.el (MELPA)= support in =ob-python.el= is removed =python-mode.el= support has been removed from =ob-python.el=. The related customization =org-babel-python-mode= has been changed to a constant. If you still want to use python-mode with ob-python, you might consider [[https://gitlab.com/jackkamm/ob-python-mode-mode][ob-python-mode-mode]], where the code to support python-mode has been ported to. *** It is no longer possible to reveal hidden parts of the links during isearch Org 9.6 introduced support for searching hidden parts of the links. Unfortunately, we had to drop this support because its implementation turned out to be unreliable for many users. Proper implementation would require patching =isearch.el= and possibly a number of external libraries implementing isearch equivalents. It cannot be done on Org side alone. *** =ox-latex=: ~org-latex-line-break-safe~ is deprecated ~org-latex-line-break-safe~ constant was previously introduced to deal with edge cases when LaTeX interprets [...] as LaTeX command argument. However, it caused a number of other issues and proved itself not to be as "safe" as it supposed to be. We now use a Pandoc's approach to deal with the same problem, utilizing ={[}= to escape =[...]= instances where needed. *** ~tab-width~ value is now assumed to be 8 Org mode now assumes tab width to be 8 characters, when calculating list and other indentation. ~tab-width~ is also set to 8 when Org major mode is loaded. This is done to improve consistency of the markup for lists, where indentation affects list items. Users with non-default values of ~tab-width~ should avoid overriding the value of 8 set by Org mode. If the custom ~tab-width~ value is _smaller_ than 8, the existing Org documents can be converted to the new standard tab width using the following helper command: #+begin_src emacs-lisp (defun org-compat-adjust-tab-width-in-buffer (old-width) "Adjust visual indentation from `tab-width' equal OLD-WIDTH to 8." (interactive "nOld `tab-width': ") (cl-assert (derived-mode-p 'org-mode)) (unless (= old-width 8) (org-with-wide-buffer (goto-char (point-min)) (let (bound (repl (if (< old-width 8) (make-string old-width ?\s) (concat "\t" (make-string (- old-width 8) ?\s))))) (while (re-search-forward "^ *\t" nil t) (skip-chars-forward " \t") (setq bound (point-marker)) (forward-line 0) (while (search-forward "\t" bound t) (replace-match repl))))))) #+end_src *** ~org-ctags~ is not activated by default any more To follow Emacs [[info:elisp#Coding Conventions][coding conventions]] and to avoid confusion of users who accidentally get ~org-ctags~ autoloaded due to help completion, the library does not modify ~org-open-link-functions~ during loading any more. Run ~org-ctags-enable~ to setup hooks and advices: #+begin_src emacs-lisp (with-eval-after-load "org-ctags" (org-ctags-enable)) #+end_src *** "Priority" used to sort items in agenda is renamed to "urgency" Previously, ~priority-up~ and ~priority-down~ in ~org-agenda-sorting-strategy~ used a composite rank depending on item's priority (=[#A]=, =[#B]=, =[#C]=, etc) and overdue time to order agenda items (see "11.4.3 Sorting of agenda items" section of Org manual). Now, this composite rank is renamed to =urgency= and the relevant sorting strategies are renamed to ~urgency-up~ and ~urgency-down~. ~priority-up~ and ~priority-down~ sort by item's priority only. Users relying on the previous composite ranking should adjust their agenda sorting settings. *** ~org-priority-show~ command no longer adjusts for scheduled/deadline In agenda views, ~org-priority-show~ command previously displayed the composite rank consisting of the item priority and overdue. This is no longer the case. The displayed and returned value only depends on the item priority now. The behavior in Org buffers is unchanged. *** =ox-icalendar.el= line ending fix may affect downstream packages iCalendar export now uses dos-style CRLF ("\r\n") line endings throughout, as required by the iCalendar specification (RFC 5545). Previously, the export used an inconsistent mix of dos and unix line endings. This might cause errors in external packages that parse output from ox-icalendar. In particular, older versions of org-caldav may encounter issues, and users are advised to update to the most recent version of org-caldav. See [[https://github.com/dengste/org-caldav/commit/618bf4cdc9be140ca1993901d017b7f18297f1b8][this org-caldav commit]] for more information. *** Icalendar export of unscheduled TODOs no longer have start time of today For TODOs without a scheduled start time, ox-icalendar no longer forces them to have a scheduled start time of today when exporting. Instead, the new customization ~org-icalendar-todo-unscheduled-start~ controls the exported start date for unscheduled tasks. Its default is ~recurring-deadline-warning~ which will export unscheduled tasks with no start date, unless it has a recurring deadline (in which case the iCalendar spec demands a start date, and ~org-deadline-warning-days~ is used for that). To revert to the old behavior, set ~org-icalendar-todo-unscheduled-start~ to ~current-datetime~. *** Built-in HTML, LaTeX, Man, Markdown, ODT, and Texinfo exporters preserve the link protocol during export Previously, some link types where not exported as =protocol:uri= but as bare =uri=. This is now changed. When a link is known by Org mode and does not have a custom ~:export~ parameter (see A.3 Adding Hyperlink Types section of the manual), the link protocol is now not stripped. For example, if one adds a link type =tel=, but does not define ~:export~ parameter : (org-link-set-parameters "tel") =[[tel:12345][John Doe]]= link will be correctly exported to LaTeX as =\href{tel:12345}{John Doe}=, not =\href{12345}{John Doe}=. However, links like =[[elisp:(+ 1 2)]]= will be exported as =\url{elisp:(+ 1 2)}=, which may be somewhat unexpected. *** =ox-html=: When exporting footnotes with custom non-number names, the names are used as link anchors Previously, link anchors for footnote references and footnote definitions were based on the footnote number: =fn.1=, =fnr.15=, etc. Now, when the footnote has a non-number name, it is used as an anchor: =fn.name=, =fnr.name=. *** =ox-org= disables citation processors by default Previously, when exporting to Org, all the citations and =print_bibliography= keywords, were transformed according to the chosen citation processor. This is no longer the case. All the citation-related markup is now exported as is. The previous behavior can be reverted by setting new custom option ~org-org-with-cite-processors~. *** ODT export no longer opens the exported file in the background ODT exporter used to open the exported file in ~archive-mode~ "for examination". This was not documented, was done in the background, and is not consistent with all other export backends. Now, this feature is removed. *** Inline image width value in =#+attr_org= is preferred over other =#+attr_...= keywords Previously, when ~org-image-actual-width~ is a list or nil, Org used the first =#+attr_...= keyword containing =:width ...= to compute the inline image width. Now, =#+attr_org=, if present, takes precedence. In the following example the image preview has width of 75% while earlier versions pick 33%. : #+attr_html: :width 33% : #+attr_org: :width 0.75 : [[image.png]] *** ~org-latex-to-mathml-convert-command~ and ~org-latex-to-html-convert-command~ may need to be adjusted Previously, =%i= placeholders in the ~org-latex-to-mathml-convert-command~ and ~org-latex-to-html-convert-command~ user options were replaced with raw LaTeX fragment text, potentially triggering shell-expansion and incorrect result. Now, the =%i= placeholders are shell-escaped to prevent shell expansion. If you have single or double quotes around =%i= then update customizations and remove quotes. *** ~org-insert-subheading~ no longer inserts a sub-heading above current when point is at the beginning of line Previously, calling ~org-insert-subheading~ on : * Heading 1 : * Heading 2 yielded : * Heading 1 : ** : * Heading 2 This is no longer the case. The sub-heading is always created below current heading (prefix arguments have the same meaning as in ~org-insert-heading~): : * Heading 1 : * Heading 2 : ** *** It is no longer allowed to tangle into the same file as Org source Previously, =file.org= with the following contents : #+begin_src org :tangle file.org : Text : #+end_src would overwrite itself. Now, an error is thrown. ** New features # We list the most important features, and the features that may # require user action to be used. *** Images and files in clipboard can be pasted Org asks the user what must be done when pasting images and files copied to the clipboard from a file manager using the ~yank-media~ command. The default action can be set by customizing ~org-yank-dnd-method~. The ~yank-media~ command was added in Emacs 29. Images can be saved to a separate directory instead of being attached, customize ~org-yank-image-save-method~. Image filename chosen can be customized by setting ~org-yank-image-file-name-function~ which by default autogenerates a filename based on the current time. Note that ~yank-media~, as of Emacs 30, does not yet support Windows (Emacs bug#71909) and may not be always reliable on Mac (Emacs bug#71731). *** Files and images can be attached by dropping onto Emacs By default, Org asks the user what to do with the dropped file like for pasted files. The same user option ~org-yank-dnd-method~ is respected. Images dropped also respect the value of ~org-yank-image-save-method~ when ~org-yank-dnd-method~ is =attach=. *** Alignment of image previews can be customized Previously, all the image previews were always left-aligned. Now, you can customize image previews to be left-aligned, centered, or right-aligned. The customization can be done globally, via ~org-image-align~, or per image, using =#+attr_...:=. Example: : #+attr_org: :align center : [[/path/to/image/file/png]] : : or : : #+attr_org: :center t : [[/path/to/image/file/png]] When =#+attr_org= is not present, ~:align~ and ~:center~ attributes from other =#+attr_...:= keywords will be used. *** =id:= links support search options; ~org-id-store-link~ adds search option by default Adding search option by ~org-id-store-link~ can be disabled by setting ~org-id-link-use-context~ to ~nil~, or toggled for a single call by passing universal argument. When using this feature, IDs should not include =::=, which is used in links to indicate the start of the search string. For backwards compatibility, existing IDs including =::= will still be matched (but cannot be used together with search option). A new org-lint checker has been added to warn about this. *** Org mode no longer disallows configuring ~display-buffer-alist~ to open Org popups in other frame Previously, Org mode disallowed pop-up frames when displaying dispatch buffers. This is no longer the case. ~display-buffer-alist~ is fully obeyed. ~org-switch-to-buffer-other-window~ and ~org-no-popups~ are now deprecated. *** Asynchronous code evaluatation in ~ob-shell~ Running shell blocks with the ~:session~ header freezes Emacs until execution completes. The new ~:async~ header allows users to continue editing with Emacs while a ~:session~ block executes. *** Add support for repeating tasks in iCalendar export Repeating Scheduled and Deadline timestamps in TODOs are now exported as recurring tasks in iCalendar export. In case the TODO has just a single planning timestamp (Scheduled or Deadline, but not both), its repeater is used as the iCalendar recurrence rule (RRULE). If the TODO has both Scheduled and Deadline planning timestamps, then the following cases are implemented: - If both have the same repeater, then it is used as the RRULE. - Scheduled has repeater but Deadline does not: the Scheduled repeater is used as RRULE, and Deadline is used as UNTIL (the end date for the repeater). This is similar to ~repeated-after-deadline~ in ~org-agenda-skip-scheduled-if-deadline-is-shown~. The following 2 cases are not yet implemented, and the repeater is skipped (with a warning) if the ox-icalendar export encounters them: - Deadline has a repeater but Scheduled does not. - Scheduled and Deadline have different repeaters. Also note that only vanilla repeaters are currently exported; the special repeaters ~++~ and ~.+~ are skipped. *** Babel references =FILE:REFERENCE= now search current buffer when =FILE= does not exist When =FILE= does not exist, the reference is searched in the current file, using the verbatim reference. This way, =:var table=tbl:example= will be searched inside the current buffer. *** Folded lines can now extend their face beyond ellipsis Previously, ~:extend t~ face attribute did not make folded headlines, blocks, and drawers extend their face beyond end of line. Now, the ellipsis and trailing newline use the same face as the last character before the fold. *** iCalendar export now supports multiline =SUMMARY=, =LOCATION=, and =DESCRIPTION= properties Previously, it was not possible to specify multi-line location, summary, or description when exporting to iCalendar. In the following example, =LOCATION= was exported as "Someplace", ignoring the other lines. #+begin_src org ,* heading with multi-line property :PROPERTIES: :LOCATION: Someplace :LOCATION+: Some Street 5 :LOCATION+: 12345 Small Town :END: #+end_src Now, =SUMMARY+=, =LOCATION+=, and =DESCRIPTION+= properties can be used to create multi-line values. In the above example, =LOCATION= is now exported as : Someplace : Some Street 5 : 12345 Small Town *** Org export backends can now disable citation processors A new global export option ~:with-cite-processors~, when set to nil, disables citation processors completely. This option is available to export backends via ~:options-alist~ when defining the backend. The backends disabling citation processors must take care about exporting citation objects and =print_bibliography= keywords via transcoders. Users can disable citations processors by customizing new ~org-export-process-citations~ option. *** Org babel backends are now expected to define an additional API function ~org-babel-session-buffer:~ Org babel now uses session buffer (if it exists) to retrieve ~default-directory~ environment during src block evaluation. By default, buffer named like session is checked. All the backends that create sessions inside buffers named differently should provide a function ~org-babel-session-buffer:~. The function must accept two arguments - session name and info list (as returned by ~org-babel-get-src-block-info~); and return the session buffer name. *** ~org-paste-subtree~ now handles =C-u= and =C-u C-u= prefix arguments specially With =C-u= prefix argument, force inserting a sibling heading below. With =C-u C-u= prefix argument, force inserting a child heading. *** ~org-metaup~ and ~org-metadown~ now act on headings in region When region is active and starts at a heading, ~org-metaup~ and ~org-metadown~ will move all the selected subtrees. *** Many structure editing commands now do not deactivate region Moving, promoting, and demoting of headings and items in region now do not deactivate Transient mark mode. Users can thus conveniently select multiple headings/items and use, for example, =M-=/=M-= repeatedly without losing the selection. *** Capture templates now support ~(here)~ as a target A capture template can target ~(here)~ which is the equivalent of invoking a capture template with a zero prefix. *** =colview= dynamic block supports custom formatting function The =colview= dynamic block understands a new ~:formatter~ parameter, which specifies a user-supplied function to format and insert the data in the dynamic block. A global default formatting function for =colview= dynamic blocks can be set via the new option ~org-columns-dblock-formatter~ which defaults to the new function ~org-columns-dblock-write-default~, that implements the previous (fixed) formatting behavior. Hence, the default behavior is identical to previous versions. The global default function can be overridden for any given =colview= dynamic block individually by specifying a custom formatter function using the new ~:formatter~ parameter on the block's =BEGIN= line. This new feature replicates the ~:formatter~ option already available for =clocktable= dynamic blocks. *** =colview= dynamic block can link to headlines The =colview= dynamic block understands a new ~:link~ parameter, which when non-~nil~ causes =ITEM= headlines in the table to be linked to their origins. *** =ob-tangle.el=: New flag to remove tangle targets before writing When ~org-babel-tangle-remove-file-before-write~ is set to ~t~ the tangle target is removed before writing. This will allow overwriting read-only tangle targets. However, when tangle target is a symlink, this will convert the tangle target into an ordinary file. The default value is ~auto~ -- overwrite tangle targets when they are read-only. *** ~org-bibtex-yank~ accepts a prefix argument When called with a prefix argument, ~org-bibtex-yank~ adds data to the headline of the entry at point instead of creating a new one. *** =ob-plantuml.el=: Support tikz file format output =ob-plantuml.el= now output =tikz= :file format via =-tlatex:nopreamble= option. So that the output tikz file can be an input into the exported latex correctly. For example, exporting the following to LaTeX #+begin_src plantuml :file test.tikz :exports results Bob -> Alice : Hello World! #+end_src will include the generated =.tikz= into the exported LaTeX source. *** =UNNUMBERED= property inheritance is now honored by ~org-num-mode~ When ~org-num-skip-unnumbered~ is non-nil, ~org-num-mode~ now honors ~org-use-property-inheritance~ for =UNNUMBERED= property (see manual section "Property Inheritance"). Previously, only local =UNNUMBERED= property was taken into account. Users can add ="UNNUMBERED"= to ~org-use-property-inheritance~ and set ~org-numb-skip-unnumbered~ to ~t~ to make ~org-num-mode~ skip numbering of all the sub-headings with non-nil =UNNUMBERED= property. *** ~org-insert-todo-heading-respect-content~ now accepts prefix arguments The prefix arguments are passed to ~org-insert-todo-heading~. *** Make ~ob-sqlite~ use in-memory databases by default ~sqlite~ source blocks with no ~:db~ header argument now make SQLite use a temporary in-memory database instead of throwing an error, matching the behavior of the official ~sqlite3~ shell. As a result, ~sqlite~ source blocks are now usable out of the box, that is with no header arguments. *** ~org-return~ now acts on citations at point When ~org-return-follows-link~ is non-nil and cursor is over an org-cite citation, ~org-return~ will call ~org-open-at-point~. *** ~org-tags-view~ supports more property operators It supports inequality operators ~!=~ and ~/=~ in addition to the less common (BASIC? Pascal? SQL?) ~<>~. And it supports starred versions of all relational operators (~<*~, ~=*~, ~!=*~, etc.) that work like the regular, unstarred operators but match a headline only if the tested property is actually present. *** =ob-python.el=: Support for more result types and plotting =ob-python= now converts the following objects to org-mode tables when ":results table" header arg is set: - Dictionaries - Numpy arrays - Pandas DataFrames - Pandas Series When the header argument =:results graphics= is set, =ob-python= will use matplotlib to save graphics. The behavior depends on whether value or output results are used. For value results, the last line should return a matplotlib Figure object to plot. For output results, the current figure (as returned by =pyplot.gcf()=) is cleared before evaluation, and then plotted afterwards. *** =ob-maxima.el=: Support for ~batch~ and ~draw~ =ob-maxima= has two new header arguments: ~:batch~ and ~:graphics-pkg~. The ~:batch~ header argument can be set to one of Maxima's file loaders (~batch~, ~load~ or ~batchload~); the default remains ~batchload~. The ~:graphics-pkg~ header argument can be set to one of Maxima's graphics packages (~draw~ or ~plot~); the default remains ~plot~. The graphics terminal is now determined from the file-ending of the file-name set in the ~:file~ header argument. *** =ob-calc.el=: Support for tables in ~:var~ =ob-calc= now supports tables in ~:var~. They are converted to a matrix or a vector depending on the dimensionality of the table. A table with a single row is converted to a vector, the rest are converted to a matrix. *** ox-texinfo always generates a ~@direntry~ We use defaults based on the file name and title of the document, and place the entry in the ~Misc~ category if ~TEXINFO_DIR_CATEGORY~ is missing. =TEXINFO_DIR_TITLE= is renamed to =TEXINFO_DIR_NAME=. The old name is obsolete. ** New and changed options # Changes dealing with changing default values of customizations, # adding new customizations, or changing the interpretation of the # existing customizations. *** Org mode faces are now consistently combined, with markup faces taking precedence over the containing element faces Previously, fontification of inline source blocks, macros, footnotes, target links, timestamps, radio targets, targets, inline export snippets, verbatim code, and COMMENT keyword in headings replaced the containing element fontification. Now, this is changed - the inner markup faces and the containing element faces are combined, with "inner" faces taking precedence; just as for all other markup. *** Org mode now fontifies whole table lines (including newline) according to ~org-table~ face Previously, leading indentation and trailing newline in table rows were not fontified using ~org-table~ face. ~default~ face was used instead. This made it impossible to scale line height when ~org-table~ face has smaller height than default (Emacs calculates line height using the tallest face). Now, new ~org-table-row~ face is used on the whole table row lines, including indentation and the final newline. This face, by default, inherits from ~org-table~ face. If the new behavior is not desired, ~org-table-row~ face can be changed to inherit from ~default~ face. See "Customizing Faces" section of Emacs manual or "Face Attribute Functions" section of Elisp manual. ~org-table~ takes precedence over ~org-table-row~ for the parts of table rows without indentation and newline. *** ~org-auto-align-tags~ is now respected universally Previously, only a subset of Org editing commands respected ~org-auto-align-tags~ option. Now, it is no longer the case. All the editing commands, including typing (~org-self-insert-command~) and deletion respect the option. ~org-auto-align-tags~ is still enabled by default. For users who customized ~org-auto-align-tags~ to nil, ~org-edit-headline~, ~org-priority~, ~org-set-tags~, ~org-entry-put~, ~org-kill-line~, and typing/deleting in headlines will no longer unconditionally auto-align the tags. *** New export option ~org-export-expand-links~ The new option makes Org expand environment variables in link and INCLUDE paths. The option is on by default. Users who do not want variable expansion can set ~org-export-expand-links~ variable to nil or provide =expand-links:nil= in-file export option. *** New hook ~org-after-note-stored-hook~ This new hook runs when a note has been stored. *** New option controlling how Org mode sorts things ~org-sort-function~ Sorting of agenda items, tables, menus, headlines, etc can now be controlled using a new custom option ~org-sort-function~. By default, Org mode sorts things according to the operating system language. However, language sorting rules may or may not produce good results depending on the use case. For example, multi-language documents may be sorted weirdly when sorting rules for system language are applied on the text written using different language. Also, some operations systems (e.g. MacOS), do not provide accurate string sorting rules. Org mode provides 3 possible values for ~org-sort-function~: 1. (default) Sort using system language rules. 2. Sort using string comparison (~compare-strings~), making use of UTF case conversion. This may work better for mixed-language documents and on MacOS. 3. Custom function, if the above does not fit the needs. *** =ob-latex= now uses a new option ~org-babel-latex-process-alist~ to generate png output Previously, =ob-latex= used ~org-preview-latex-default-process~ from ~org-preview-latex-process-alist~ to produce png output. Now, the process settings are separated into a new dedicated option ~org-babel-latex-process-alist~. The default value is pulled from =dvipng= process type from ~org-preview-latex-process-alist~, preserving the existing behavior. However, the output is now immune to changes in ~org-preview-latex-default-process~ and can be customized independently of the image preview settings. *** New option ~org-babel-lua-multiple-values-separator~ The string that separates the values of multi-valued results returned from Lua code blocks. *** =.avif= images are now recognized in ~org-html-inline-image-rules~ In =ox-html=, =.avif= image links are now inlined by default. *** New option ~org-beamer-frame-environment~ The new option defines name of an alternative environment to be used for fragile beamer frames. This option is needed to work around beamer bug with frame contents containing literal =\end{frame}= string (for example, inside example blocks). See https://github.com/josephwright/beamer/issues/360 The default value is =orgframe=. The option should normally not be changed, except when you need to put =\end{orgframe}= string inside beamer frames. A checker has been added to =M-x org-lint= to detect instances of ~org-beamer-frame-environment~ in Org documents. *** New option ~org-export-process-citations~ The new option controls whether to use citation processors to process citations. *** New option ~org-org-with-cite-processors~ The new option controls whether to use citation processors to process citations when exporting to Org. *** New option ~org-org-with-special-rows~ The new options controls whether to export special table rows in Org-Org (=ox-org=) export. The default value is ~t~. *** New option ~org-babel-comint-fallback-regexp-threshold~ Org babel is often using Emacs's interactive REPL feature to implement :session functionality in code blocks. However, Emacs's REPLs use heuristics to detect which lines in the REPL buffer correspond to output and which lines are user prompts. Normally, Org babel changes the default prompt to something unique. It avoids incorrect detection of code block output. Sometimes, the Org-configured prompt is changed manually by users or when running a sub-REPL (for example, when running ssh/python interpreter inside shell). The new option controls Org mode's heuristics for catching user-changed prompt in interactive Org babel sessions. When Org mode cannot find REPL's prompt for more than ~org-babel-comint-fallback-regexp-threshold~ seconds, imprecise generic prompt is tried to detect whether the code block output has arrived. Users who often work with altering REPL prompts may consider reducing the default 5 second value of the new option. *** ~repeated-after-deadline~ value of ~org-agenda-skip-scheduled-if-deadline-is-shown~ is moved to a new customization A new custom option ~org-agenda-skip-scheduled-repeats-after-deadline~ is introduced in place of ~repeated-after-deadline~ value of ~org-agenda-skip-scheduled-if-deadline-is-shown~. The following example would no longer show in the agenda as scheduled after January 5th with the new customization set to ~t~. : * TODO Do me every day until Jan, 5th (inclusive) : SCHEDULED: <2024-01-03 Wed +1d> DEADLINE: <2024-01-05 Fri> The old customization will continue to work, ensuring backwards compatibility. *** New custom setting ~org-icalendar-ttl~ for the ~ox-icalendar~ backend The option ~org-icalendar-ttl~ allows to advise a subscriber to the exported =.ics= file to reload after the given time interval. This is useful i.e. if a calendar server subscribes to your exported file and that file is updated regularly. See IETF RFC 5545, Section 3.3.6 Duration and https://en.wikipedia.org/wiki/ICalendar#Other_component_types for details. Default for ~org-icalendar-ttl~ is ~nil~. In that case the setting will not be used in the exported ICS file. The option may also be set using the =ICAL-TTL= keyword. *** The default value of ~org-attach-store-link-p~ is now ~attached~ Now, after attaching a file, =[[attach:...]]= link to the attached file is stored. It can later be inserted using =M-x org-insert-link=. *** ~org-link-descriptive~ can now be set per-buffer via =#+STARTUP= options In addition to ~org-link-descriptive~ custom option, link display can now be controlled per-buffer as: : #+STARTUP: literallinks : #+STARTUP: descriptivelinks *** New option ~org-fast-tag-selection-maximum-tags~ You can now limit the total number of tags displayed in the fast tag selection interface. Useful in buffers with huge number of tags. *** New variable ~org-clock-out-removed-last-clock~ The variable is intended to be used by ~org-clock-out-hook~. It is a flag used to signal when the =CLOCK= line has been removed. This can happen when ~org-clock-out-remove-zero-time-clocks~ is customized to be non-nil. *** ~org-info-other-documents~ is now a custom option Users can now extend the value of ~org-info-other-documents~ to specify Urls to third-party (non-Emacs) online info nodes when exporting =info:= links. *** ~org-export-smart-quotes-alist~ is now a custom option Previously, smart quotes rules for different languages where hard-coded. Now, they can be customized by users. *** Commands affected by ~org-fold-catch-invisible-edits~ can now be customized New user option ~org-fold-catch-invisible-edits-commands~ controls which commands trigger checking for invisible edits. The full list of affected commands is: - ~org-self-insert-command~ - ~org-delete-backward-char~ - ~org-delete-char~ - ~org-meta-return~ - ~org-return~ (not checked in earlier Org versions) *** New customization ~org-image-max-width~ limiting the displayed inline image width New custom variable ~org-image-max-width~ limits the maximum inline image width, but only when the inline image width is not explicitly set via ~org-image-actual-width~, =ORG-IMAGE-ACTUAL-WIDTH= property, or =#+ATTR*= keyword. By default, when ~org-image-actual-width~ is set to t, ~org-image-max-width~ takes effect. Its default value is set to ~fill-column~, limiting the image previews to ~fill-column~ number of characters. To fall back to previous defaults, where the inline image width is not constrained, set ~org-image-max-width~ to nil. *** ~org-src-block-faces~ now accepts empty string ~""~ as language name It is now possible to customize face of source blocks without language specifier. : #+begin_src : Source block with no language : #+end_src For example, to set ~highlight~ face, use #+begin_src emacs-lisp (setq org-src-fontify-natively t) (add-to-list 'org-src-block-faces '("" highlight)) #+end_src *** New ~org-cite-natbib-export-bibliography~ option defining fallback bibliography style ~natbib~ citation export processor now uses ~org-cite-natbib-export-bibliography~ (defaults to ~unsrtnat~) as a fallback bibliography style if none is specified by user in =#+cite_export:= keyword. Previously, export would fail without explicitly selected bibliography style. *** New escape in ~org-beamer-environments-extra~ for labels in Beamer export The escape =%l= in ~org-beamer-environments-extra~ inserts the label obtained from ~org-beamer--get-label~. This is added to the default environments =theorem=, =definition=, =example=, and =exampleblock= in ~org-beamer-environments-default~. *** ~org-clock-x11idle-program-name~ now defaults to =xprintidle=, when available When =xprintidle= executable is available at =org-clock= load time, it is used as the default value for ~org-clock-x11idle-program-name~. The old =x11idle= default is used as the fallback. =xprintidle= is available as system package in most Linux distributions, unlike ancient =x11idle= that is distributed via WORG. *** New options for the "csl" citation export processor's LaTeX output The ~org-cite-csl-latex-label-separator~ and ~org-cite-csl-latex-label-width-per-char~ options allow the user to control the indentation of entries for labeled bibliography styles when the "csl" citation processor is used for LaTeX export. The indentation length is computed as the sum of ~org-cite-csl-latex-label-separator~ and the maximal label width, for example: #+begin_example indentation length <-------------------------> max. label width separator <---------------><--------> [Doe22] John Doe. A title... [DoeSmithJones19] John Doe, Jane Smith and... [SmithDoe02] Jane Smith and John Doe... #+end_example The maximal label width, in turn, is calculated as the product of ~org-cite-csl-latex-label-width-per-char~ and the maximal label length measured in characters. The ~org-cite-csl-latex-preamble~ option makes it possible to customize the entire LaTeX fragment that the "csl" citation processor injects into the preamble. *** New ~org-latex-listings-src-omit-language~ option for LaTeX export The ~org-latex-listings-src-omit-language~ option allows omitting the =language= parameter in the exported =lstlisting= environment. This is necessary when the =listings= backend delegates listing generation to another package like =fancyvrb= using the following setup in the document header: #+BEGIN_src org ,#+LATEX_HEADER: \RequirePackage{fancyvrb} ,#+LATEX_HEADER: \DefineVerbatimEnvironment{verbatim}{Verbatim}{...whatever...} ,#+LATEX_HEADER: \DefineVerbatimEnvironment{lstlisting}{Verbatim}{...whatever...} #+END_src *** New face: ~org-agenda-calendar-daterange~ The face ~org-agenda-calendar-daterange~ is used to show entries with a date range in the agenda. It inherits from the default face in order to remain backward-compatible. *** New ~org-babel-clojurescript-backend~ option to choose ClojureScript backend Before, a ClojureScript source block used the same backend as Clojure, configured in ~org-babel-clojure-backend~ and relied on an undocumented ~:target~ parameter. Now, there's ~org-babel-clojurescript-backend~ to determine the backend used for evaluation of ClojureScript. *** Support for Clojure CLI in ~ob-clojure~ ~ob-clojure~ now supports executing babel source blocks with the official [[https://clojure.org/guides/deps_and_cli][Clojure CLI tools]]. The command can be customized with ~ob-clojure-cli-command~. *** New customization options for ~org-export-dispatch~ New custom variables ~org-export-body-only~, ~org-export-visible-only~, and ~org-export-force-publishing~ allow the default settings of "Body only", "Visible only", and "Force publishing" in the ~org-export-dispatch~ UI to be customized, respectively. *** New option ~org-icalendar-todo-unscheduled-start~ to control unscheduled TODOs in ox-icalendar ~org-icalendar-todo-unscheduled-start~ controls how ox-icalendar exports the starting datetime for unscheduled TODOs. Note this option only has an effect when ~org-icalendar-include-todo~ is non-nil. By default, ox-icalendar will not export a start datetime for unscheduled TODOs, except in cases where the iCalendar spec demands a start (specifically, for recurring deadlines, in which case ~org-deadline-warning-days~ is used). Currently implemented options are: - ~recurring-deadline-warning~: The default as described above. - ~deadline-warning~: Use ~org-deadline-warning-days~ to set the start time if the unscheduled task has a deadline (recurring or not). - ~current-datetime~: Revert to old behavior, using the current datetime as the start of unscheduled tasks. - ~nil~: Never add a start time for unscheduled tasks. For repeating tasks this technically violates the iCalendar spec, but some iCalendar programs support this usage. *** Capture template expansion now supports ID links The capture template expansion element =%K= creates links using ~org-store-link~, which respects the values of ~org-id-link-to-use-id~. *** Changes to ~org-babel-python-command~, and new session/nonsession specific options The default Python command used by interactive sessions has been changed to match ~python-shell-interpreter~ and ~python-shell-interpreter-args~ by default. The default Python command for nonsessions has not changed. New options ~org-babel-python-command-nonsession~ and ~org-babel-python-command-session~ control the default Python command for nonsessions and sessions, respectively. By default, ~org-babel-python-command-session~ is ~auto~, which means to use the configuration for ~python-shell-interpreter(-args)~ as default. The old option ~org-babel-python-command~ has been changed to have default value of ~auto~. When not ~auto~, it overrides both ~org-babel-python-command-nonsession~ and ~org-babel-python-command-session~. Therefore, users who had previously set ~org-babel-python-command~ will not experience any changes. Likewise, users who had neither set ~org-babel-python-command~ nor ~python-shell-interpreter(-args)~ will not see any changes -- ~python~ remains the default command. The main change will be for users who did not configure ~org-babel-python-command~, but did configure ~python-shell-interpreter~, e.g. to use IPython. In this case, ~ob-python~ will now start interactive sessions in a more consistent manner with ~run-python~. *** New hook option ~org-indent-post-buffer-init-functions~ This allows to run functions after ~org-indent~ initializes a buffer to enrich its properties. *** New option ~org-agenda-start-with-archives-mode~ This option starts the agenda to automatically include archives, propagating the value for this variable to ~org-agenda-archives-mode~. For acceptable values and their meaning, see the value of that variable. *** New option ~org-id-link-consider-parent-id~ to allow =id:= links to parent headlines For =id:= links, when this option is enabled, ~org-store-link~ will look for ids from parent/ancestor headlines, if the current headline does not have an id. Combined with the new ability for =id:= links to use search options [fn:: when =org-id-link-use-context= is =t=, which is the default], this allows linking to specific headlines without requiring every headline to have an id property, as long as the headline is unique within a subtree that does have an id property. For example, given this org file: #+begin_src org ,* Parent :PROPERTIES: :ID: abc :END: ,** Child 1 ,** Child 2 #+end_src Storing a link with point at "Child 1" will produce a link ==, which precisely links to the "Child 1" headline even though it does not have its own ID. By giving files top-level id properties, links to headlines in the file can also be made more robust by using the file id instead of the file path. *** New option ~latex-default-footnote-command~ to customize the LaTeX footnote command This new option allows you to define the LaTeX command the Org mode footnotes are converted to (for example ~\sidenote{%s%s}~ instead of the default ~\footnote{%s%s}~). The option can be customized either by 1. setting the global variable in the ~org-export-latex~ customization group or 2. by setting the file local keyword =LATEX_FOOTNOTE_COMMAND= *** Options for ~#+cite_export: biblatex~ can use the package's option syntax When using =biblatex= to export bibliographies, you can use the format as specified in the =biblatex= package documentation as =key=val,key=val,...= *** New option ~org-columns-dblock-formatter~ =colview= dynamic blocks now understand a new ~:formatter~ parameter to use a specific function for formatting and inserting the contents of the dynamic block. This new option can be used to set the global default formatting function that will be used for =colview= dynamic blocks that do not specify any ~:formatter~ parameter. Its default value (the new function ~org-columns-dblock-write-default~) yields the previous (fixed) formatting behavior. *** New allowed value of ~org-md-headline-style~ to mix ATX and Setext style headlines Setting ~org-md-headline-style~ to ~'mixed~ will export headline levels one and two as Setext style headlines, and headline levels three through six will be exported as ATX style headlines. *** ~org-footnote-new~ can be configured to create anonymous footnotes When ~org-footnote-auto-label~ is set to ~'anonymous~, create anonymous footnotes automatically with ~org-footnote-new~. The same can be done via startup options: : #+STARTUP: fnanon *** New final hooks for Modifier-Cursor keys Final hooks are added to the following commands: - ~org-metaleft-final-hook~ to ~org-metaleft~ (bound to =M-=). - ~org-metaright-final-hook~ to ~org-metaright~ (bound to =M-=). - ~org-metaup-final-hook~ to ~org-metaup~ (bound to =M-=). - ~org-metadown-final-hook~ to ~org-metadown~ (bound to =M-=). - ~org-shiftmetaleft-final-hook~ to ~org-shiftmetaleft~ (bound to =M-S-=). - ~org-shiftmetaright-final-hook~ to ~org-shiftmetaright~ (bound to =M-S-=). - ~org-shiftmetaup-final-hook~ to ~org-shiftmetaup~ (bound to =M-S-=). - ~org-shiftmetadown-final-hook~ to ~org-shiftmetadown~ (bound to =M-S-=). ** Major changes and additions to Org element API and Org syntax *** Diary type timestamps now support optional time/timerange Previously, diary type timestamps could not specify time. Now, it is allowed to add a time or time range: : <%%(diary-float t 4 2) 22:00-23:00> : <%%(diary-float t 4 2) 10:30> The parsed representation of such timestamps will have ~:hour-start~, ~:minute-start~, ~:hour-end~, ~:minute-end~, and ~:range-type~ properties set appropriately. In addition, a new ~:diary-sexp~ property will store the diary sexp value. For example, : <%%(diary-float t 4 2) 22:00-23:00> will have the following properties #+begin_src emacs-lisp :type: diary :range-type: timerange :raw-value: "<%%(diary-float t 4 2) 22:00-23:00>" :year-start: nil :month-start: nil :day-start: nil :hour-start: 22 :minute-start: 0 :year-end: nil :month-end: nil :day-end: nil :hour-end: 23 :minute-end: 0 :diary-sexp: "(diary-float t 4 2)" #+end_src *** Underline syntax now takes priority over subscript when both are applicable Previously, Org mode interpreted =(_text_)= as subscript. Now, the interpretation is changed to underline. =(_text_)= matches both subscript and underline markup. The interpretation is changed to keep consistency with other emphasis like =(*bold*)=. Most of the users should not be affected by this change - it only applies when character immediately preceding =_= is one of =-=, =(=, ='=, and ={=. *** New term: "syntax node" To reduce confusion with "element" referring to both "syntax element" and "element/object" class, we now prefer using "syntax node" when referring to generic Org syntax elements. "Elements" and "objects" now refer to different syntax node classes of paragraph-like nodes and markup-like nodes. *** New element type ~anonymous~ Secondary strings can now be recognized as ~anonymous~ type to distinguish from non-elements. With a new optional argument, ~org-element-type~ will return ~anonymous~ for secondary strings instead of nil. The new element type can be used in ~org-element-lineage~, ~org-element-map~, and other functions that filter by element type. *** Internal structure of Org parse tree has been changed The code relying upon the previously used =(TYPE PROPERTIES-PLIST CONTENTS-LIST)= structure may no longer work. Please use ~org-element-create~, ~org-element-property~, and other Org element API functions to work with Org syntax trees. Some syntax node properties are no longer stored as property list elements. Instead, they are kept in a special vector value of a new =:standard-properties= property. This is done to improve performance. If there is a need to traverse all the node properties, a new API function ~org-element-properties-map~ can be used. Properties and their values can now be deferred to avoid overheads when parsing. They are calculated lazily, when the value/property is requested by ~org-element-property~ and other getter functions. Using ~plist-get~ to retrieve values of =PROPERTIES-PLIST= is not recommended as deferred properties will not be resolved in such scenario. New special property =:secondary= is used internally to record which properties store secondary objects. New special property =:deferred= is used to keep information how to calculate property names lazily. See the commentary in =lisp/org-element-ast.el= for more details. *** Multiple affiliated keyword values are now stored in the order they appear in buffer Previously, : #+caption: foo : #+caption: bar : Paragraph would have its =:caption= property set to ~(("bar") ("foo"))~ in reverse order. Now, the order is not reversed: ~(("foo") ("bar"))~. *** Some property values may now be calculated lazily and require original Org buffer to be live ~org-element-at-point~, ~org-element-context~, and ~org-element-at-point-no-context~ may now not calculate all the property values at the call time. Instead, the calculation will be deferred until ~org-element-property~ or the equivalent getter function is called. The property names may not all be calculated as well. It may often be necessary to have the original Org buffer open when resolving the deferred values. One can ensure that all the deferred values are resolved using new function ~org-element-resolve-deferred~ and new optional argument for ~org-element-property~. ~org-element-parse-buffer~ and ~org-element-parse-secondary-string~ will resolve all the deferred values by default. No adjustment is needed for their users. *** New API functions and macros **** New property accessors and setters New functions to retrieve and set (via ~setf~) commonly used element properties: - =:begin= :: ~org-element-begin~ - =:end= :: ~org-element-end~ - =:contents-begin= :: ~org-element-contents-begin~ - =:contents-end= :: ~org-element-contents-end~ - =:post-affiliated= :: ~org-element-post-affiliated~ - =:post-blank= :: ~org-element-post-blank~ - =:parent= :: ~org-element-parent~ **** New macro ~org-element-with-enabled-cache~ The macro arranges the element cache to be active during =BODY= execution. When cache is enabled, the macro is identical to ~progn~. When cache is disabled, the macro arranges a new fresh cache that is discarded upon completion of =BODY=. **** New function ~org-element-property-raw~ This function is like ~org-element-property~ but does not try to resolve deferred properties. ~org-element-property-raw~ can be used with ~setf~. **** New function ~org-element-put-property-2~ Like ~org-element-put-property~, but the argument list is changed to have =NODE= as the last argument. Useful with threading macros like ~thread-last~. **** New function ~org-element-properties-resolve~ This function resolves all the deferred values in a =NODE=, modifying the =NODE= for side effect. **** New functions ~org-element-properties-map~ and ~org-element-properties-mapc~ New functions to map over =NODE= properties. **** New function ~org-element-ast-map~ This is a more general equivalent of ~org-element-map~. It allows more precise control over recursion into secondary strings. **** New function ~org-element-lineage-map~ Traverse syntax tree ancestor list, applying arbitrary function to each ancestor. **** New function ~org-element-property-inherited~ Like ~org-element-property~, but can be used to retrieve and combine multiple different properties for a given =NODE= and its parents. *** ~org-element-cache-map~ can now be used even when element cache is disabled *** =org-element= API functions and macros can now accept syntax nodes as =POM= argument The following functions are updated: - ~org-agenda-entry-get-agenda-timestamp~ - ~org-element-at-point~ - ~org-is-habit-p~ - ~org-id-get~ - ~org-with-point-at~ - ~org-entry-properties~ - ~org-entry-get~ - ~org-entry-delete~ - ~org-entry-add-to-multivalued-property~ - ~org-entry-remove-from-multivalued-property~ - ~org-entry-member-in-multivalued-property~ - ~org-entry-put-multivalued-property~ - ~org-entry-get-with-inheritance~ - ~org-entry-put~ - ~org-read-property-value~ - ~org-property-get-allowed-values~ *** ~org-element-map~ now traverses main value in dual keywords before the secondary value The traverse order for dual keywords is reversed. The main value is now traversed first, followed by the secondary value. *** Org parse tree is now non-printable Org parser now assigns a new property =:buffer= that holds non-printable buffer object. This makes syntax tree non-printable. Using ~print~/~read~ is no longer safe. *** Some Org API functions no longer preserve match data ~org-element-at-point~, ~org-element-context~, ~org-get-category~, and ~org-get-tags~ may modify the match data. The relevant function docstrings now explicitly mention that match data may be modified. *** ~org-element-create~ now treats a single ~anonymous~ =CHILDREN= argument as a list of child nodes When =CHILDREN= is a single anonymous node, use its contents as children nodes. This way, : (org-element-create 'section nil (org-element-contents node)) will yield expected results with contents of another node adopted into a newly created one. Previously, one had to use : (apply #'org-element-create 'section nil (org-element-contents node)) *** New property ~:range-type~ for org-element timestamp object ~org-element-timestamp-parser~ now adds =:range-type= property to each timestamp object. Possible values: ~timerange~, ~daterange~, ~nil~. ~org-element-timestamp-interpreter~ takes into account this property and returns an appropriate timestamp string. *** New properties =:repeater-deadline-value= and =:repeater-deadline-unit= for org-element timestamp object ~org-element-timestamp-parser~ now adds =:repeater-deadline-value= and =:repeater-deadline-unit= properties to each timestamp object that has a repeater deadline. For example, in =<2012-03-29 Thu ++1y/2y>=, =2y= is the repeater deadline with a value of =2= and unit of =y=. See "5.3.3 Tracking your habits" section in the manual. Possible values for =:repeater-deadline-value=: ~positive integer~, ~nil~. Possible values for =:repeater-deadline-unit=: ~hour~, ~day~, ~week~, ~month~, ~year~. ~org-element-timestamp-interpreter~ takes into account these properties and returns an appropriate timestamp string. *** =org-link= store functions are passed an ~interactive?~ argument The ~:store:~ functions set for link types using ~org-link-set-parameters~ are now passed an ~interactive?~ argument, indicating whether ~org-store-link~ was called interactively. Existing store functions will continue to work. ** New functions and changes in function arguments # This also includes changes in function behavior from Elisp perspective. *** ~org-babel-lilypond-compile-lilyfile~ ignores optional second argument The =TEST= parameter is better served by Emacs debugging tools. *** ~org-print-speed-command~ is now an internal function The old name is marked obsolete and the new name is ~org--print-speed-command~. This function was always aimed for internal use when building speed command help buffer. Now, it is stated explicitly. *** When ~org-link-file-path-type~ is a function, its argument is now a filename as it is read by ~org-insert-link~; not an absolute path Previously, when ~org-link-file-path-type~ is set to a function, the function argument was the filename from the link expanded via ~expand-file-name~. Now, a bare filename is passed to the function. *** ~org-create-file-search-functions~ can use ~org-list-store-props~ to suggest link description In Org <9.0, ~org-create-file-search-functions~ could set ~description~ variable to suggest link description for the stored link. However, this feature stopped working since Org 9.0 switched to lexical binding. Now, it is again possible for ~org-create-file-search-functions~ to supply link descriptions using ~(org-list-store-props :description "suggested description")~ in the search function body. *** New API functions to store data within ~org-element-cache~ Elisp programs can now store data inside Org element cache. The data will remain stored as long as the Org buffer text associated with the cached elements remains unchanged. Two options are available: - Store the data until any text within element boundaries is changed - Store the data, but ignore any changes inside element contents that do not affect the high-level element structure. For example, changes inside subheadings can be ignored for the data stored inside parent heading element. The new functions are: ~org-element-cache-store-key~ and ~org-element-cache-get-key~. *** New optional argument =UPDATE-HEADING= for ~org-bibtex-yank~ When the new argument is non-nil, add data to the headline of the entry at point. *** ~org-fold-hide-drawer-all~ is now interactive ~org-fold-hide-drawer-all~ is now a command, accepting two optional arguments - region to act on. *** =TYPES= argument in ~org-element-lineage~ can now be a symbol When =TYPES= is symbol, only check syntax nodes of that type. *** New optional argument =KEEP-CONTENTS= for ~org-element-copy~ With the new argument, the contents is copied recursively. *** ~org-element-property~ can now be used with ~setf~ *** New optional arguments for ~org-element-property~ The value of the new optional argument =DFLT= is returned if the property with given name is not present. Same as =DEFAULT= argument for ~alist-get~. New optional argument =FORCE-UNDEFER= modifies the =NODE=, storing the resolved deferred values. See the top comment in =lisp/org-element-ast.el= for more details about the deferred values. *** New optional argument =NO-UNDEFER= in ~org-element-map~ and changed argument conventions New optional argument =NO-UNDEFER=, when non-nil, will make ~org-element-map~ keep deferred secondary string values in their raw form. See the top comment in =lisp/org-element-ast.el= for more details about the deferred values. =TYPES= argument can now be set to ~t~. This will match all the syntax nodes when traversing the tree. ~FUN~ can now be a lisp form that will be evaluated with symbol ~node~ assigned to the current syntax node. ~FUN~ can now throw ~:org-element-skip~ signal to skip recursing into current element children and secondary strings. *** New optional argument =KEEP-DEFERRED= in ~org-element-parse-buffer~ When non-nil, the deferred values and properties will not be resolved. See the top comment in =lisp/org-element-ast.el= for more details about the deferred values. *** New optional argument =ANONYMOUS= for ~org-element-type~ When the new argument is non-nil, return symbol ~anonymous~ for anonymous elements. Previously, ~nil~ would be returned. *** ~org-element-adopt-elements~ is renamed to ~org-element-adopt~ The old name is kept as an alias. The new name creates less confusion as the function can also act on objects. *** ~org-element-extract-element~ is renamed to ~org-element-extract~ The old name is kept as an alias. The new name creates less confusion as the function can also act on objects. *** ~org-element-set-element~ is renamed to ~org-element-set~ The old name is kept as an alias. The new name creates less confusion as the function can also act on objects. *** ~org-export-get-parent~ is renamed to ~org-element-parent~ and moved to =lisp/org-element.el= *** ~org-export-get-parent-element~ is renamed to ~org-element-parent-element~ and moved to =lisp/org-element.el= *** ~org-insert-heading~ optional argument =TOP= is now =LEVEL= A numeric value forces a heading at that level to be inserted. For backwards compatibility, non-numeric non-nil values insert level 1 headings as before. *** New optional argument for ~org-id-get~ New optional argument =INHERIT= means inherited ID properties from parent entries are considered when getting an entry's ID (see ~org-id-link-consider-parent-id~ option). *** New optional argument for ~org-link-search~ If a missing heading is created to match the search string, the new optional argument =NEW-HEADING-CONTAINER= specifies where in the buffer it will be added. If not specified, new headings are created at level 1 at the end of the accessible part of the buffer, as before. ** Miscellaneous *** Add completion for links to man pages Completion is enabled for links to man pages added using ~org-insert-link~: =C-c C-l man RET emacscl TAB= to get =emacsclient=. Of course, the ~ol-man~ library should be loaded first. *** Datetree structure headlines can now be complex TODO state, priority, tags, statistics cookies, and COMMENT keywords are allowed in the tree structure. *** Org links now support ~thing-at-point~ You can now retrieve the destination of a link by calling ~(thing-at-point 'url)~. Requires Emacs 28 or newer. In Emacs 30 or newer, ~forward-thing~ and ~bounds-of-thing-at-point~ is also supported for links. *** Add support for ~logind~ idle time in ~org-user-idle-seconds~ When Emacs is built with =dbus= support and the =org.freedesktop.login1= interface is available, fallback to checking the =IdleSinceHint= property when determining =org-user-idle-seconds= as the penultimate step. *** =colview= dynamic block now writes column width specifications When column format contains width specifications, =colview= dynamic block now writes these specifications as column width in the generated tables and automatically shrinks the columns on display. Example: : * PROYECTO EMACS : :PROPERTIES: : :COLUMNS: %10ITEM(PROJECT) : :END: : : Before : : #+BEGIN: columnview :id local : | PROJECT | : |----------------| : | PROYECTO EMACS | : #+END: : : After : : #+BEGIN: columnview :id local : | <10> | : | PROJECT | : |----------------| : | PROYECTO EMACS | : #+END: *** =ob-lua=: Support all types and multiple values in results Lua code blocks can now return values of any type and can also return multiple values. Previously, values of certain types were incorrectly converted to the empty string =""=, which broke HTML export for inline code blocks, and multiple values were incorrectly concatenated, where ~return 1, 2, 3~ was evaluated as =123=. Multiple values are comma-separated by default, so that they work well with inline code blocks. To change the string used as the separator, customize ~org-babel-lua-multiple-values-separator~. *** ~org-store-link~ now moves an already stored link to front of the ~org-stored-links~ Previously, when the link to be stored were stored already, ~org-store-link~ displayed a message and did nothing. Now, ~org-store-link~ moves the stored link to front of the list of stored links. This way, the link will show up first in the completion and when inserting all the stored links with ~org-insert-all-links~. *** ob-python now sets ~python-shell-buffer-name~ in Org edit buffers When editing a Python src block, the editing buffer is now associated with the Python shell specified by the src block's ~:session~ header, which means users can now send code directly from the edit buffer, e.g., using ~C-c C-c~, to the session specified in the Org buffer. *** ~org-edit-special~ no longer force-starts session in R and Julia source blocks Previously, when R/Julia source block had =:session= header argument set to a session name with "earmuffs" (like =*session-name*=), ~org-edit-special~ always started a session, if it does not exist. Now, ~org-edit-special~ arranges that a new session with correct name is initiated only when user explicitly executes R/Julia-mode commands that trigger session interactions (requires ESS 24.01.0 or newer). The same session will remain available in the context of Org babel. *** ~org-store-link~ behavior storing additional =CUSTOM_ID= links has changed Previously, when storing =id:= link, ~org-store-link~ stored an additional "human readable" link using a node's =CUSTOM_ID= property. This behavior has been expanded to store an additional =CUSTOM_ID= link when storing any type of external link type in an Org file, not just =id:= links. *** =org-habit.el= now optionally inherits ~:STYLE: habit~ properties Currently, the ~STYLE~ property of habits is not inherited when searching for entries. This change allows the property to be inherited optionally by customizing the ~org-use-property-inheritance~ variable. This change aims to provide more flexibility in managing habits, allowing users to dedicate separate subtrees or files to habits without manually setting the ~STYLE~ property for each sub-task. The change is breaking when ~org-use-property-inheritance~ is set to ~t~. *** =ox-org= preserves header arguments in src blocks Previously, all the header arguments where stripped from src blocks during export. Now, header arguments are preserved. *** =ox-org= now exports special table rows by default Previously, when exporting to Org, special table rows (for example, width cookies) were not exported. Now, they are exported by default. You can customize new option ~org-org-with-special-rows~ to fall back to previous behavior. *** ~org-agenda-search-headline-for-time~ now ignores all the timestamp in headings Previously, ~org-agenda-search-headline-for-time~ made Org agenda match anything resembling time inside headings. Even when the time was a part of a timestamp. Now, all the timestamps in headings are ignored when searching the time. *** =org-crypt.el= now applies initial visibility settings to decrypted entries Previously, all the text was unfolded unconditionally, including property drawers. *** Blank lines after removed objects are now retained during export When certain objects in Org document are to be excluded from export, spaces after these objects were previously removed as well. For example, if ~org-export-with-footnotes~ is set to nil, the footnote in : Pellentesque dapibus suscipit ligula.[fn:1] Donec posuere augue in quam. would be removed, leading to the following exported ASCII document : Pellentesque dapibus suscipit ligula.Donec posuere augue in quam. This is because spaces after footnote (and other markup) are considered a part of the preceding AST object in Org. Now, unless there is a whitespace before an object to be removed, spaces are preserved during export: : Pellentesque dapibus suscipit ligula. Donec posuere augue in quam. *** Remove undocumented ~:target~ header parameter in ~ob-clojure~ The ~:target~ header was only used internally to distinguish from Clojure and ClojureScript. This is now handled with an optional function parameter in the respective functions that need this information. *** New org-entity alias: =\P= for =\para= For symmetry with =\S= and =\sect= for the section symbol, =\P= has been added as an another form for the pilcrow symbol currently available as =\para=. *** ~org-table-to-lisp~ no longer clobbers the regexp global state It does no longer use regexps. It is also faster. Large tables can be read quickly. * Version 9.6 ** Important announcements and breaking changes *** =python-mode.el (MELPA)= support in =ob-python.el= is deprecated We no longer aim to support third-party =python-mode.el= implementation of Python REPL. Only the built-in =python.el= will be supported from now on. We still keep the old, partially broken, code in =ob-python.el= for the time being. It will be removed in the next release. See https://orgmode.org/list/87r0yk7bx8.fsf@localhost for more details. *** Element cache is enabled by default and works for headings The old element cache code has been refactored. Emacs does not hang anymore when the cache is enabled. When cache is enabled, ~org-element-at-point~ for headings is guaranteed to return valid =:parent= property. The highest-level headings contain new =org-data= element as their parent. The new =org-data= element provides properties from top-level property drawer, buffer-global category, and =:path= property containing file path for file Org buffers. The new cache still need to be tested extensively. Please, report any warning coming from element cache. If you see warnings regularly, it would be helpful to set ~org-element--cache-self-verify~ to ='backtrace= and provide the backtrace to Org mailing list. *** Element cache persists across Emacs sessions The cache state is saved between Emacs sessions. Enabled by default. The cache persistence can be controlled via ~org-element-cache-persistent~. *** Users experiencing performance issues can use new folding backend The old folding backend used in Org is poorly scalable when the file size increases beyond few Mbs. The symptoms usually include slow cursor motion, especially in long-running Emacs sessions. A new optimized folding backend is now available, and enabled by default. To disable it, put the following to the Emacs config *before* loading Org: #+begin_src emacs-lisp (setq org-fold-core-style 'overlays) #+end_src Even more performance optimization can be enabled by customizing =org-fold-core--optimise-for-huge-buffers=. However, this option may be dangerous. Please, read the variable docstring carefully to understand the possible consequences. When =org-fold-core-style= is set to =text-properties=, several new features will become available and several notable changes will happen to the Org behavior. The new features and changes are listed below. **** Hidden parts of the links can now be searched and revealed during isearch [2024-06-09 Sun] Since Org 9.7, this is no longer working. See changes for Org 9.7. In the past, hidden parts of the links could not be searched using isearch (=C-s=). Now, they are searchable by default. The hidden match is also revealed temporarily during isearch. To restore the old behavior add the following core to your Emacs config: #+begin_src emacs-lisp (defun org-hidden-link-ignore-isearch () "Do not match hidden parts of links during isearch." (org-fold-core-set-folding-spec-property 'org-link :isearch-open nil) (org-fold-core-set-folding-spec-property 'org-link :isearch-ignore t)) (add-hook 'org-mode-hook #'org-hidden-link-ignore-isearch) #+end_src See docstring of =org-fold-core--specs= to see more details about =:isearch-open= and =:isearch-ignore= properties. **** =org-catch-invisible-edits= now works for hidden parts of the links and for emphasis markers In the past, user could edit invisible parts of the links and emphasis markers. Now, the editing is respecting the value of =org-catch-invisible-edits=. Note that hidden parts of sub-/super-scripts are still not handled. **** Breaking structure of folded elements automatically reveals the folded text In the past, the user could be left with unfoldable text after breaking the org structure. For example, if #+begin_src org :DRAWER: like this :END: #+end_src is folded and then edited into #+begin_src org DRAWER: like this :END: #+end_src The hidden text would not be revealed. Now, breaking structure of drawers, blocks, and headings automatically reveals the folded text. **** Folding state of the drawers is now preserved when cycling headline visibility In the past drawers were folded every time a headline is unfolded. Now, it is not the case anymore. The drawer folding state is preserved. The initial folding state of all the drawers in buffer is set according to the startup visibility settings. To restore the old behavior, add the following code to Emacs config: #+begin_src emacs-lisp (add-hook 'org-cycle-hook #'org-cycle-hide-drawers) #+end_src Note that old behavior may cause performance issues when cycling headline visibility in large buffers. **** =outline-*= functions may no longer work correctly in Org mode The new folding backend breaks some of the =outline-*= functions that rely on the details of visibility state implementation in =outline.el=. The old Org folding backend was compatible with the =outline.el= folding, but it is not the case anymore with the new backend. From now on, using =outline-*= functions is strongly discouraged when working with Org files. *** HTML export uses MathJax 3+ instead of MathJax 2 Org now uses MathJax 3 by default instead of MathJax 2. During HTML exports, Org automatically converts all legacy MathJax 2 options to the corresponding MathJax 3+ options, except for the ~path~ option in which now /must/ point to a file containing MathJax version 3 or later. The new Org does /not/ work with the legacy MathJax 2. Further, if you need to use a non-default ~font~ or ~linebreaks~ (now ~overflow~), then the ~path~ must point to MathJax 4 or later. See the updated ~org-html-mathjax-options~ for more details. MathJax 3, a ground-up rewrite of MathJax 2 came out in 2019. The new version brings modularity, better and faster rendering, improved LaTeX support, and more. For more information about new features, see: https://docs.mathjax.org/en/latest/upgrading/whats-new-3.0.html https://docs.mathjax.org/en/latest/upgrading/whats-new-3.1.html https://docs.mathjax.org/en/latest/upgrading/whats-new-3.2.html MathJax 3 comes with useful extensions. For instance, you can typeset calculus with the ~physics~ extension or chemistry with the ~mhchem~ extension, like in LaTeX. Note that the Org manual does not discuss loading of MathJax extensions via ~+HTML_MATHJAX~ anymore. It has never worked anyway. To actually load extensions, consult the official documentation: https://docs.mathjax.org/en/latest/input/tex/extensions.html Lastly, MathJax 3 changed the default JavaScript content delivery network (CDN) provider from CloudFlare to jsDelivr. You can find the new terms of service, including the privacy policy, at https://www.jsdelivr.com/terms. *** List references in source block variable assignments are now proper lists List representation of named lists is now converted to a simple list as promised by the manual section [[info:org#Environment of a Code Block][org#Environment of a Code Block]]. Previously, it was converted to a list of lists. Before: #+begin_src org ,#+NAME: example-list - simple - not - nested - list ,#+BEGIN_SRC emacs-lisp :var x=example-list :results value (format "%S" x) ,#+END_SRC ,#+RESULTS: : (("simple" (unordered ("not") ("nested"))) ("list")) #+end_src After: #+begin_src org ,#+BEGIN_SRC emacs-lisp :var x=example-list :results value (format "%S" x) ,#+END_SRC ,#+RESULTS: : ("simple" "list") #+end_src ** New features *** Column view: new commands to move rows up & down You can move rows up & down in column view with ~org-columns-move-row-up~ and ~org-columns-move-row-down~. Keybindings are the same as ~org-move-subtree-up~ and ~org-move-subtree-down~ =M-= and =M-=. *** Clock table can now produce quarterly reports =:step= clock table parameter can now be set to =quarter=. *** Publishing now supports links to encrypted Org files Links to other published Org files are automatically converted to the corresponding html links. Now, this feature is also available when links point to encrypted Org files, like =[[file:foo.org.gpg::Heading]]=. *** Interactive commands now support escaping text inside comment blocks ~org-edit-special~ and ~org-insert-structure-template~ now handle comment blocks. See [[*New command ~org-edit-comment-block~ to edit comment block at point]]. *** New customization option =org-property-separators= A new alist variable to control how properties are combined. If a property is specified multiple times with a =+=, like #+begin_src org :PROPERTIES: :EXPORT_FILE_NAME: some/path :EXPORT_FILE_NAME+: to/file :END: #+end_src the old behavior was to always combine them with a single space (=some/path to/file=). For the new variable, the car of each item in the alist should be either a list of property names or a regular expression, while the cdr should be the separator to use when combining that property. The default value for the separator is a single space, if none of the provided items in the alist match a given property. For example, in order to combine =EXPORT_FILE_NAME= properties with a forward slash =/=, one can use #+begin_src emacs-lisp (setq org-property-separators '((("EXPORT_FILE_NAME") . "/"))) #+end_src The example above would then produce the property value =some/path/to/file=. *** New library =org-persist.el= implements variable persistence across Emacs sessions The library stores variable data in ~org-persist-directory~ (set to XDG cache dir by default). The entry points are ~org-persist-register~, ~org-persist-unregister~, ~org-persist-read~, and ~org-persist-read-all~. Storing circular structures is supported. Storing references between different variables is also supported (see =:inherit= key in ~org-persist-register~). The library permits storing buffer-local variables. Such variables are linked to the buffer text, file =inode=, and file path. *** New =:options= attribute when exporting tables to LaTeX The =:options= attribute allows adding an optional argument with a list of various table options (between brackets in LaTeX export), since certain tabular environments, such as longtblr of the tabularray LaTeX package, provides this structure. *** New =:compact= attribute when exporting lists to Texinfo The =:compact= attribute allows exporting multiple description list items to one =@item= command and one or more =@itemx= commands. This feature can also be enabled for all description lists in a file using the =compact-itemx= export option, or globally using the ~org-texinfo-compact-itemx~ variable. *** New shorthands recognized when exporting to Texinfo Items in a description list that begin with =Function:=, =Variable:= or certain related prefixes are converted using Texinfo definition commands. *** New =:noweb-prefix= babel header argument =:noweb-prefix= can be set to =no= to prevent the prefix characters from being repeated when expanding a multiline noweb reference. *** New =:noweb= babel header argument value =strip-tangle= =:noweb= can be set to =strip-tangle= to strip the noweb syntax references before tangling. *** New LaTeX source block backend using =engraved-faces-latex= When ~org-latex-src-block-backend~ is set to ~engraved~, =engrave-faces-latex= from [[http://elpa.gnu.org/packages/engrave-faces.html][engrave-faces]] is used to transcode source blocks to LaTeX. This requires the =fvextra=, =float=, and (by default, but not necessarily) =tcolorbox= LaTeX packages be installed. It uses Emacs's font-lock information, and so tends to produce results superior to Minted or Listings. *** Support for =#+include=-ing URLs =#+include: FILE= will now accept URLs as the file. *** Structure templates now respect case used in ~org-structure-template-alist~ The block type in ~org-structure-template-alist~ is not case-sensitive. When the block type starts from the upper case, structure template will now insert =#+BEGIN_TYPE=. Previously, lower-case =#+begin_type= was inserted unconditionally. *** New ox-latex tabbing support for tables. LaTeX tables can now be exported to the latex tabbing environment tabbing environment]]. This is done by adding =#+ATTR_LATEX: :mode tabbing= at the top of the table. The default column width is set to 1/n times the latex textwidth, where n is the number of columns. This behavior can be changed by supplying a =:align= parameter. The tabbing environment can be useful when generating simple tables which can be span multiple pages and when table cells are allowed to overflow. *** Support for =nocite= citations and sub-bibliographies in the "csl" export processor The "csl" citation export processor now supports =nocite= style citations that add items to the printed bibliography without visible references in the text. Using the key =*= in a nocite citation, for instance, #+begin_src org [cite/n:@*] #+end_src includes all available items in the printed bibliography. The "csl" export processor now also supports sub-bibliographies that show only a subset of the references based on some criterion. For example, #+begin_src org #+print_bibliography: :type book :keyword ai #+end_src prints a sub-bibliography containing the book entries with =ai= among their keywords. *** New =:filetitle= option for clock table The =:filetitle= option for clock tables can be set to ~t~ to show org file title (set by =#+title:=) in the File column instead of the file name. For example: #+begin_src org ,#+BEGIN: clocktable :scope agenda :maxlevel 2 :block thisweek :filetitle t #+end_src If a file does not have a title, the table will show the file name instead. *** New =org-md-toplevel-hlevel= variable for Markdown export The =org-md-toplevel-hlevel= customization variable sets the heading level used for top level headings, much like how =org-html-toplevel-hlevel= sets the heading level used for top level headings in HTML export. *** Babel: new syntax to pass the contents of a src block as argument Use the header argument =:var x=code-block[]= or : #+CALL: fn(x=code-block[]) to pass the contents of a named code block as a string argument. *** New property =ORG-IMAGE-ACTUAL-WIDTH= for overriding global ~org-image-actual-width~ The new property =ORG-IMAGE-ACTUAL-WIDTH= can override the global variable ~org-image-actual-width~ value for inline images display width. *** Outline cycling can now include inline image visibility New ~org-cycle-hook~ function ~org-cycle-display-inline-images~ for auto-displaying inline images in the visible parts of the subtree. This behavior is controlled by new custom option ~org-cycle-inline-images-display~. *** New ~org-babel-tangle-finished-hook~ hook run at the very end of ~org-babel-tangle~ This provides a proper counterpart to ~org-babel-pre-tangle-hook~, as ~org-babel-post-tangle-hook~ is run per-tangle-destination. ~org-babel-tangle-finished-hook~ is just run once after the post tangle hooks. *** New =:backend= header argument for clojure code blocks The =:backend= header argument on clojure code blocks can override the value of ~org-babel-clojure-backend~. For example: #+begin_src clojure :backend babashka (range 2) #+end_src *** New =:results discard= header argument Unlike =:results none=, the return value of code blocks called with =:results discard= header argument is always ~nil~. Org does not attempt to analyze the results and simply returns nil. This can be useful when the code block is used for side effects only but generates large outputs that may be slow to analyze for Org. *** Add Capture template hook properties Capture templates can now attach template specific hooks via the following properties: ~:hook~, ~:prepare-finalize~, ~:before-finalize~, ~:after-finalize~. These nullary functions run prior to their global counterparts for the selected template. ** New options *** New option ~org-columns-checkbox-allowed-values~ This would allow to use more than two states ("[ ]", "[X]") in columns with SUMMARY-TYPE that use checkbox ("X", "X/", "X%"). For example you can add an intermediate state ("[-]"). Or empty state ("") to remove checkbox. *** A new option for custom setting ~org-refile-use-outline-path~ to show document title in refile targets Setting ~org-refile-use-outline-path~ to ~'title~ will show title instead of the file name in refile targets. If the document do not have a title, the filename will be used, similar to ~'file~ option. *** A new option for custom setting ~org-agenda-show-outline-path~ to show document title Setting ~org-agenda-show-outline-path~ to ~'title~ will show title instead of the file name at the beginning of the outline. The title of the document can be set by special keyword =#+title:=. *** New custom settings =org-icalendar-scheduled-summary-prefix= and =org-icalendar-deadline-summary-prefix= These settings allow users to define prefixes for exported summary lines in ICS exports. The customization can be used to disable the prefixes completely or make them a little bit more verbose (e.g. "Deadline: " instead of the default "DL: "). The same settings can also be applied via corresponding exporter options: =:icalendar-scheduled-summary-prefix=, =:icalendar-deadline-summary-prefix= *** A new custom setting =org-hide-drawer-startup= to control initial folding state of drawers Previously, all the drawers were always folded when opening an Org file. This only had an effect on the drawers outside folded headlines. The drawers inside folded headlines were re-folded because =org-cycle-hide-drawers= was present inside =org-cycle-hook=. With the new folding backend, running =org-cycle-hide-drawers= is no longer needed if all the drawers are truly folded on startup: [[*Folding state of the drawers is now preserved when cycling headline visibility]]. However, this has an unwanted effect when a user does not want the drawers to be folded (see [[https://orgmode.org/list/m2r14f407q.fsf@ntnu.no][this bug report]]). The new custom setting gives more control over initial folding state of the drawers. When set to =nil= (default is =t=), the drawers are not folded on startup. The folding state can also be controlled on per-file basis using =STARTUP= keyword: : #+startup: hidedrawers : #+startup: nohidedrawers *** New custom setting ~org-icalendar-force-alarm~ The new setting, when set to non-nil, makes Org create alarm at the event time when the alarm time is set to 0. The default value is nil -- do not create alarms at the event time. *** New special value ~'attach~ for src block =:dir= option Passing the symbol ~attach~ or string ="'attach"= (with quotes) to the =:dir= option of a src block is now equivalent to =:dir (org-attach-dir) :mkdir yes= and any file results with a path descended from the attachment directory will use =attachment:= style links instead of the standard =file:= link type. ** New functions and changes in function arguments *** New function ~org-get-title~ to get =#+TITLE:= property from buffers A function to collect the document title from the org-mode buffer. *** ~org-fold-show-entry~ does not fold drawers by default anymore ~org-fold-show-entry~ now accepts an optional argument HIDE-DRAWERS. When the argument is non-nil, the function folds all the drawers inside entry. This was the default previously. Now, ~org-fold-show-entry~ does not fold drawers by default. *** New command ~org-edit-comment-block~ to edit comment block at point As the contents of comments blocks is not parsed as Org markup, the headlines and keywords inside should be escaped, similar to src blocks, example blocks, and export blocks. This in inconvenient to do manually and ~org-edit-special~ is usually advised to edit text in such kind of blocks. Now, comment block editing is also supported via this new function. *** New function ~org-element-cache-map~ for quick mapping across Org elements When element cache is enabled, the new function provides the best possible performance to map across large Org buffers. It is recommended to provide =:next-re= and =:fail-re= parameters for best speed. Diagnostic information about execution speed can be provided according to ~org-element--cache-map-statistics~ and ~org-element--cache-map-statistics-threshold~. ~org-scan-tags~ and tag views in agenda utilize the new function. *** New function ~org-element-at-point-no-context~ This function is like ~org-element-at-point~, but it does not try to update the cache and does not guarantee correct =:parent= properties for =headline= elements. This function is faster than ~org-element-at-point~ when used together with frequent buffer edits. *** Various Org API functions now use cache and accept Org elements as optional arguments ~org-in-archived-heading-p~, ~org-in-commented-heading-p~, ~org-up-heading-safe~, ~org-end-of-subtree~, ~org-goto-first-child~, ~org-back-to-heading~, ~org-entry-get-with-inheritance~, and ~org-narrow-to-subtree~ all accept Org element as an extra optional argument. ~org-get-tags~ now accepts Org element or buffer position as first argument. *** New function ~org-texinfo-kbd-macro~ This function is intended for us in the definition of a ~kbd~ macro in files that are exported to Texinfo. *** =org-at-heading-p= now recognizes optional argument. Its meaning is inverted. =org-at-heading-p= now returns t by default on headings inside folds. Passing optional argument will produce the old behavior. *** =org-babel-execute:plantuml= can output ASCII graphs in the buffer Previously, executing PlantUML src blocks always exported to a file. Now, if :results is set to a value which does not include "file", no file will be exported and an ASCII graph will be inserted below the src block. ** Removed or renamed functions and variables *** =org-plantump-executable-args= is renamed and applies to jar as well The new variable name is =org-plantuml-args=. It now applies to both jar PlantUML file and executable. *** Default values and interpretations of ~org-time-stamp-formats~ and ~org-time-stamp-custom-formats~ are changed Leading =<= and trailing =>= in the default values of ~org-time-stamp-formats~ and ~org-time-stamp-custom-formats~ are stripped. The Org functions that are using these variables also ignore leading and trailing brackets (=<...>= and =[...]=, if present). This change makes the Org code more consistent and also makes the docstring for ~org-time-stamp-custom-formats~ accurate. No changes on the user side are needed if ~org-time-stamp-custom-formats~ was customized. *** ~org-timestamp-format~ is renamed to ~org-format-timestamp~ The old function name is similar to other ~org-time-stamp-format~ function. The new name emphasizes that ~org-format-timestamp~ works on =timestamp= objects. *** Updated argument list in ~org-time-stamp-format~ New =custom= argument in ~org-time-stamp-format~ makes the function use ~org-time-stamp-custom-formats~ instead of ~org-time-stamp-formats~ to determine the format. Optional argument =long= is renamed to =with-time=, emphasizing that it refers to time stamp format with time specification. Optional argument =inactive= can now have a value =no-brackets= to return format string with brackets stripped. ** Miscellaneous *** SQL Babel ~:dbconnection~ parameter can be mixed with other SQL Babel parameters Before you could either specify SQL parameters like ~:dbhost~, ~:dbuser~, ~:database~, etc or a ~:dbconnection~ parameter which looks up all other parameters from the ~sql-connection-alist~ variable. Now it's possible to specify a ~:dbconnection~ and additionally other parameters that will add or overwrite the parameters coming from ~sql-connection-alist~. E.g. if you have a connection in your ~sql-connection-alist~ to a server that has many databases, you don't need an entry for every database but instead can just specify ~:database~ next to your ~:dbconnection~ parameter. *** Post-processing code blocks can return an empty list When the result of a regular code block is nil, then that was already treated as an empty list. Now that is also the case for code blocks that post-process the result of another block. *** Styles are customizable in ~biblatex~ citation processor It is now possible to add new styles or modify old ones in ~biblatex~ citation processor. See ~org-cite-biblatex-styles~ for more information. *** Citation processors can declare styles dynamically When a citation processor is registered, it is now possible to set ~:cite-styles~ key to a function, which will be called whenever the list of styles is required. *** Org also searches for CSL style files in default directory When CSL style file name is relative, Org first looks into default-directory before trying ~org-cite-csl-styles-dir~. *** Users can add checkers to the linting process The function ~org-lint-add-checker~ allows one to add personal checks when calling ~org-lint~. See its docstring for more information. *** New =transparent-image-converter= property for =dvipng= The =dvipng= option in ~org-preview-latex-process-alist~ has a new property =transparent-image-converter= which is used instead of =image-converter= when producing transparent images. *** =:tangle-mode= now accepts more permissions formats Previously =:tangle-mode (identity #o755)= was the only reasonable way to set the file mode. ~org-babel-interpret-file-mode~ has been introduced which will accept three new formats: + Short octals, e.g. =:tangle-mode o755= + ls-style, e.g. =:tangle-mode rwxrw-rw-= + chmod-style, e.g. =:tangle-mode u+x= Chmod-style permissions are based on the new variable ~org-babel-tangle-default-file-mode~. *** A new custom setting =org-agenda-clock-report-header= to add a header to org agenda clock report *** ~org-latex-listings~ has been replaced with ~org-latex-src-block-backend~ ~org-latex-listings~ has been renamed to better reflect the current purpose of the variable. The replacement variable ~org-latex-src-block-backend~ acts in exactly the same way, however it accepts =listings= and =verbatim= in place of =t= and =nil= (which still work, but are no longer listed as valid options). *** ~org-link-parameters~ has a new ~:insert-description~ parameter The value of ~:insert-description~ is used as the initial input when prompting for a link description. It can be a string (used as-is) or a function (called with the same arguments as ~org-make-link-description-function~ to return a string to use). An example of a such function for =info:= links is ~org-info-description-as-command~. To access a manual section outside of Org, description may be pasted to shell prompt or evaluated within Emacs using =M-:= (wrapped into parenthesis). For example, description of the =info:org#Tags= link is =info "(org) Tags"=. To restore earlier behavior add to your Emacs init file the following: #+begin_src elisp :results silent :eval never-export (with-eval-after-load 'ol-info (org-link-set-parameters "info" :insert-description nil)) #+end_src *** New list of languages for LaTeX export: ~org-latex-language-alist~ ~org-latex-language-alist~ unifies into a single list the old language lists for the =babel= and =polyglossia= LaTeX packages: ~org-latex-babel-language-alist~ and ~org-latex-polyglossia-language-alist~, respectively, which are declared obsolete. This new list captures the current state of art regarding language support in LaTeX. The new =babel= syntax for loading languages via =ini= files and the new command =\babelprovide= (see: https://mirrors.ctan.org/macros/latex/required/babel/base/babel.pdf) are also supported. *** Texinfo exports include LaTeX With the new customization option ~org-texinfo-with-latex~ set to (its default value) ~'detect~, if the system runs Texinfo 6.8 (3 July 2021) or newer, Org will export all LaTeX fragments and environments using Texinfo ~@math~ and ~@displaymath~ commands respectively. *** More flexible ~org-attach-id-to-path-function-list~ List entries may return nil if they are unable to handle the passed ID. So, responsibility is passed to the next item in the list. Default entries ~org-attach-id-uuid-folder-format~ and ~org-attach-id-ts-folder-format~ now return nil for too short IDs. Earlier an obscure error has been thrown. After the change, error text suggests adjusting ~org-attach-id-to-path-function-list~ value. The ~org-attach-dir-from-id~ function is adapted to ignore nil values and to take first non-nil value instead of the value returned by first ~org-attach-id-to-path-function-list~ item. New policy allows mixing different ID styles while keeping subfolder layout suited best for each one. For example, one can use the following snippet to allow multiple different ID formats in Org files. #+begin_src emacs-lisp (setq org-attach-id-to-path-function-list '(;; When ID looks like an UUIDs or Org internal ID, use ;; `org-attach-id-uuid-folder-format'. (lambda (id) (and (or (org-uuidgen-p id) (string-match-p "[0-9a-z]\\{12\\}" id)) (org-attach-id-uuid-folder-format id))) ;; When ID looks like a timestamp-based ID. Group by year-month ;; folders. (lambda (id) (and (string-match-p "[0-9]\\{8\\}T[0-9]\\{6\\}\.[0-9]\\{6\\}" id) (org-attach-id-ts-folder-format id))) ;; Any other ID goes into "important" folder. (lambda (id) (format "important/%s/%s" (substring id 0 1) id)) ;; Fallback to detect existing attachments for old defaults. ;; All the above functions, even when return non-nil, would ;; point to non-existing folders. org-attach-id-uuid-folder-format org-attach-id-ts-folder-format)) #+end_src * Version 9.5 ** Important announcements and breaking changes *** The =contrib/= now lives in a separate repository Org's repository has been trimmed from the =contrib/= directory. The old contents of the =contrib/= directory now lives in a separate repository at https://git.sr.ht/~bzg/org-contrib. You can install this repository by cloning it and updating your ~load-path~ accordingly. You can also install =org-contrib= as a [[https://elpa.nongnu.org/nongnu/][NonGNU ELPA]] package. *** Org ELPA and Org archives won't be available for Org > 9.5 [[https://orgmode.org/elpa.html][Org ELPA]] is still available for installing Org 9.5, either with or without contributed packages, but future versions won't be available via Org ELPA, as we are deprecating this installation method. Also, Org 9.5 is available as =tar.gz= and =zip= archives, but this installation method is also deprecated. If you want to install the latest stable versions of Org, please use the GNU ELPA package. If you want to install the contributed files, please use the NonGNU ELPA package. If you want to keep up with the latest unstable Org, please install from the Git repository. See https://orgmode.org/org.html#Installation for the details. *** =ditaa.jar= is not bundled with Org anymore =ditaa.jar= used to be bundled with Org but it is not anymore. See [[https://github.com/stathissideris/ditaa][the ditaa repository]] on how to install it. *** ~org-adapt-indentation~ now defaults to =nil= If you want to automatically indent headlines' metadata, set it to =headline-data=. If you want to automatically indent every line to the headline's current indentation, set it to =t=. Indent added by =RET= and =C-j= also depends on the value of ~electric-indent-mode~. Enabling this mode by default in 9.4 revealed some bugs caused confusing behavior. If you disabled ~electric-indent-mode~ for this reason, it is time to try it again. Hopefully problems have been fixed. See [[https://orgmode.org/worg/org-faq.html#indentation][this FAQ]] for more details. *** ~org-speed-commands-user~ is obsolete, use ~org-speed-commands~ Setting ~org-speed-commands-user~ in your configuration won't have any effect. Please set ~org-speed-commands~ instead, which see. *** Some =ob-*.el= files have been moved to the org-contrib repo These files have been moved to https://git.sr.ht/~bzg/org-contrib: - ob-abc.el - ob-asymptote.el - ob-coq.el - ob-ebnf.el - ob-hledger.el - ob-io.el - ob-J.el - ob-ledger.el - ob-mscgen.el - ob-picolisp.el - ob-shen.el - ob-stan.el - ob-vala.el See the discussion [[msg::87bl9rq29m.fsf@gnu.org][here]]. *** Compatibility with Emacs versions We made it explicit that we aim at keeping the latest stable version of Org compatible with at least Emacs V, V-1 and V-2, where V is the stable major version of Emacs. For example, if the current major version of Emacs is 28.x, then the latest stable version of Org should be compatible with Emacs 28.x, 27.x and 26.x – but not with Emacs 25.x. See [[https://orgmode.org/worg/org-maintenance.html#emacs-compatibility][this note on Worg]] and [[git::519947e508e081e71bf67db99e27b1c171ba4dfe][this commit]]. *** The keybinding for ~org-table-blank-field~ has been removed If you prefer to keep the keybinding, you can add it back to ~org-mode-map~ like so: #+begin_src emacs-lisp (define-key org-mode-map (kbd "C-c SPC") #'org-table-blank-field) #+end_src ** New features *** New citation engine Org 9.5 provides a new library =oc.el= which provides tooling to handle citations in Org, e.g., activate, follow, insert, and export them, respectively called "activate", "follow", "insert" and "export" capabilities. Libraries responsible for providing some, or all, of these capabilities are called "citation processors". The manual contains a few pointers to let you start and you may want to check [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html][this blog post]]. If you need help using this new features, please ask on the mailing list. Thanks to Nicolas Goaziou for implementing this, to Bruce D’Arcus for helping him and to John Kitchin for paving the way with =org-ref.el=. *** Async session evaluation The =:async= header argument can be used for asynchronous evaluation in session blocks for certain languages. Currently, async evaluation is supported in Python. There is also functionality to implement async evaluation in other languages that use comint, but this needs to be done on a per-language basis. By default, async evaluation is disabled unless the =:async= header argument is present. You can also set =:async no= to force it off (for example if you've set =:async= in a property drawer). Async evaluation is disabled during export. *** ~ox-koma-letter.el~ is now part of Org's core ~ox-koma-letter.el~ provides a KOMA scrlttr2 back-end for the Org export engine. It used to be in the =contrib/= directory but it is now part of Org's core. *** Support exporting DOI links Org now supports export for DOI links, through its new =ol-doi.el= library. For backward compatibility, it is loaded by default. *** Add a new ~:refile-targets~ template option When exiting capture mode via ~org-capture-refile~, the variable ~org-refile-targets~ will be temporarily bound to the value of this template option. *** New startup options =#+startup: showlevels= These startup options complement the existing =overview=, =content=, =showall=, =showeverything= with a way to start the document with n levels shown, where n goes from 2 to 5. Example: : #+startup: show3levels *** New =u= table formula flag to enable Calc units simplification mode A new =u= mode flag for Calc formulas in Org tables has been added to enable Calc units simplification mode. *** Support fontification of inline export snippets See [[msg:87im57fh8j.fsf@gmail.com][this thread]]. *** New command =org-refile-reverse= bound to =C-c C-M-w= You can now use =C-c C-M-w= to run ~org-refile-reverse~. It is almost identical to ~org-refile~, except that it temporarily toggles how ~org-reverse-note-order~ applies to the current buffer. So if ~org-refile~ would append the entry as the last entry under the target heading, ~org-refile-reverse~ will prepend it as the first entry, and vice-versa. *** LaTeX attribute ~:float~ now passes through arbitrary values LaTeX users are able to define arbitrary float types, e.g. with the float package. The Org mode LaTeX exporter is now able to process and export arbitrary float types. The user is responsible for ensuring that Org mode configures LaTeX to process any new float type. *** Support verse and quote blocks in LaTeX export The LaTeX export back-end accepts four attributes for verse blocks: =:lines=, =:center=, =:versewidth= and =:latexcode=. The three first require the external LaTeX package =verse.sty=, which is an extension of the standard LaTeX environment. The LaTeX export back-end accepts two attributes for quote blocks: =:environment=, for an arbitrary quoting environment (the default value is that of =org-latex-default-quote-environment=: ="quote"=) and =:options=. *** =org-set-tags-command= selects tags from ~org-global-tags-completion-table~ Let ~org-set-tags-command~ TAB fast tag completion interface complete tags including from both buffer local and user defined persistent global list (~org-tag-alist~ and ~org-tag-persistent-alist~). Now option ~org-complete-tags-always-offer-all-agenda-tags~ is honored. *** Clocktable option =:formula %= now shows the per-file time percentages This change only has an effect when multiple files are contributing to a given clocktable (such as when =:scope agenda= has been specified). The existing behavior is that such tables have an extra 'File' column, and each individual file that contributes has its own summary line with the headline value '*File time*'. Those summary rows also produce a rollup time value for the file in the 'Time' column. Prior to this change, the built-in =%= formula did not produce a calculation for those per-file times in the '%' column (the relevant cells in the '%' column were blank). With this change, the percentage contribution of each individual file time to the total time is shown. The more agenda files you have, the more useful this behavior becomes. *** =ob-python.el= improvements to =:return= header argument The =:return= header argument in =ob-python= now works for session blocks as well as non-session blocks. Also, it now works with the =:epilogue= header argument -- previously, setting the =:return= header would cause the =:epilogue= to be ignored. This change allows more easily moving boilerplate out of the main code block and into the header. For example, for plotting, we need to add boilerplate to save the figure to a file and return the filename. Instead of doing this within the code block, we can now handle it through the header arguments as follows: #+BEGIN_SRC org ,#+header: :var fname="/home/jack/tmp/plot.svg" ,#+header: :epilogue plt.savefig(fname) ,#+header: :return fname ,#+begin_src python :results value file import matplotlib, numpy import matplotlib.pyplot as plt fig=plt.figure(figsize=(4,2)) x=numpy.linspace(-15,15) plt.plot(numpy.sin(x)/x) fig.tight_layout() ,#+end_src ,#+RESULTS: [[file:/home/jack/tmp/plot.svg]] #+END_SRC As another example, we can use =:return= with the external [[https://pypi.org/project/tabulate/][tabulate]] package, to convert pandas Dataframes into orgmode tables: #+begin_src org ,#+header: :prologue from tabulate import tabulate ,#+header: :return tabulate(table, headers=table.columns, tablefmt="orgtbl") ,#+begin_src python :results value raw :session import pandas as pd table = pd.DataFrame({ "a": [1,2,3], "b": [4,5,6] }) ,#+end_src ,#+RESULTS: | | a | b | |---+---+---| | 0 | 1 | 4 | | 1 | 2 | 5 | | 2 | 3 | 6 | #+end_src *** Display images with width proportional to the buffer text width Previously, if you used a =:width= attribute like =#+attr_html: :width 70%= or =#+attr_latex: :width 0.7\linewidth= this would be interpreted as a 70px wide and 0.7px wide width specification respectively. Now, percentages are transformed into floats (i.e. 70% becomes 0.7), and float width specifications between 0.0 and 2.0 are now interpreted as that portion of the text width in the buffer. For instance, the above examples of =70%= and =0.7\linewidth= will result in an image with width equal to the pixel-width of the buffer text multiplied by 0.7. This functionality is implemented in a new function, ~org-display-inline-image--width~ which contains the width determination logic previously in ~org-display-inline-images~ and the new behavior. ** New options *** Option ~org-hidden-keywords~ now also applies to #+SUBTITLE: The option ~org-hidden-keywords~ previously applied to #+TITLE:, #+AUTHOR:, #+DATE:, and #+EMAIL:. Now it can also be used to hide the #+SUBTITLE: keyword. *** New formatting directive ~%L~ for org-capture The new ~%L~ formatting directive contains the bare link target, and may be used to create links with programmatically generated descriptions. *** New option ~org-id-ts-format~ Earlier, IDs generated using =ts= method had a hard-coded format (i.e. =20200923T160237.891616=). The new option allows user to customize the format. Defaults are unchanged. *** New argument for ~file-desc~ babel header It is now possible to provide the =file-desc= header argument for a babel source block but omit the description by passing an empty vector as an argument (i.e., :file-desc []). This can be useful because providing =file-desc= without an argument results in the result of =file= being used in the description. Previously, the only way to omit a file description was to omit the header argument entirely, which made it difficult/impossible to provide a default value for =file-desc=. *** New option to set ~org-link-file-path-type~ to a function ~org-link-file-path-type~ can now be set to a function that takes the full filename as an argument and returns the path to link to. For example, if you use ~project.el~, you can set this function to use relative links within a project as follows: #+begin_src emacs-lisp (setq (org-link-file-path-type (lambda (path) (let* ((proj (project-current)) (root (if proj (project-root proj) default-directory))) (if (string-prefix-p (expand-file-name root) path) (file-relative-name path) (abbreviate-file-name path)))))) #+end_src *** New options and new behavior for babel LaTeX SVG image files Org babel now uses a two-stage process for converting latex source blocks to SVG image files (when the extension of the output file is ~.svg~). The first stage in the process converts the latex block into a PDF file, which is then converted into an SVG file in the second stage. The TeX->PDF part uses the existing infrastructure for ~org-babel-latex-tex-to-pdf~. The PDF->SVG part uses a command specified in a new customization, ~org-babel-latex-pdf-svg-process~. By default, this uses inkscape for conversion, but since it is fully customizable, any other command can be used in its place. For instance, dvisvgm might be used here. This two-part processing replaces the previous use of htlatex to process LaTeX directly to SVG (htlatex is still used for HTML conversion). Conversion to SVG exposes a number of additional customizations that give the user full control over the contents of the latex source block. ~org-babel-latex-preamble~, ~org-babel-latex-begin-env~ and ~org-babel-latex-end-env~ are new customization options added to allow the user to specify the preamble and code that precedes and proceeds the contents of the source block. *** New option ~org-html-meta-tags~ allows for HTML meta tags customization New variable ~org-html-meta-tags~ makes it possible to customize the == tags used in an HTML export. Accepts either a static list of values, or a function that generates such a list (see ~org-html-meta-tags-default~ as an example of the latter). *** Option ~org-agenda-bulk-custom-functions~ now supports collecting bulk arguments When specifying a custom agenda bulk option, you can now also specify a function which collects the arguments to be used with each call to the custom function. *** New faces to improve the contextuality of Org agenda views Four new faces improve certain styles and offer more flexibility for some Org agenda views: ~org-agenda-date-weekend-today~, ~org-imminent-deadline~, ~org-agenda-structure-secondary~, ~org-agenda-structure-filter~. They inherit from existing faces in order to remain backward-compatible. Quoting from [[https://list.orgmode.org/87lf7q7gpq.fsf@protesilaos.com/][this thread]]: #+begin_quote + The 'org-imminent-deadline' is useful to disambiguate generic warnings from deadlines. For example, a warning could be rendered in a yellow colored text and have a bold weight, whereas a deadline might be red and styled with italics. + The 'org-agenda-structure-filter' applies to all tag/term filters in agenda views that search for keywords or patterns. It is designed to inherit from 'org-agenda-structure' in addition to the 'org-warning' face that was present before (and removes the generic 'warning' face from one place). This offers the benefit of consistency, as, say, an increase in font height or a change in font family in 'org-agenda-structure' will propagate to the filter as well. The whole header line thus looks part of a singular design. + The 'org-agenda-structure-secondary' complements the above for those same views where a description follows the header. For instance, the tags view provides information to "Press N r" to filter by a numbered tag. Themes/users may prefer to disambiguate this line from the header above it, such as by using a less intense color or by reducing its height relative to the 'org-agenda-structure'. + The 'org-agenda-date-weekend-today' provides the option to differentiate the current date on a weekend from the current date on weekdays. #+end_quote *** New option ~org-clock-ask-before-exiting~ By default, a function is now added to ~kill-emacs-query-functions~ that asks whether to clock out and save when there's a running clock. Customize ~org-clock-ask-before-exiting~~ to nil to disable this new behavior. *** Option ~org-html-inline-image-rules~ now includes .webp By default ox-html now inlines webp images. *** ~org-html-head-include-scripts~ is now =nil= by default See [[msg:498dbe2e-0cd2-c81e-7960-4a26c566a1f7@memebeam.org][this thread]]. *** New option ~org-html-content-class~ This is the CSS class name to use for the top level content wrapper. *** New option ~org-babel-plantuml-svg-text-to-path~ This option, nil by default, allows to add a SVG-specific post-export step that runs inkscape text-to-path replacement over the output file. *** You can now configure ~org-html-scripts~ and ~org-html-style-default~ ~org-html-scripts~ and ~org-html-style-default~ used to be constants, you can now configure them. *** New option ~org-attach-git-dir~ ~org-attach-git-dir~ will decide whether to use ~org-attach-git-dir~ (the default) or use the attachment directory of the current node, if it is correctly configured as a Git repository. *** New option ~org-attach-sync-delete-empty-dir~ ~org-attach-sync-delete-empty-dir~ controls the deletion of an empty attachment directory at calls of ~org-attach-sync~. There is Never delete, Always delete and Query the user (default). *** ~org-babel-default-header-args~ can now be specified as closures or strings ~org-babel-default-header-args~ now also accepts closures that evaluate to a string. Previously, only direct strings were supported. These closures are evaluated when point is at the source block, which allows them to make use of contextual information at the relevant source block. One example that illustrates the usefulness of this addition (also given in the documentation for ~org-babel-default-header-args~) is: #+begin_src elisp (defun org-src-sha () (let ((elem (org-element-at-point))) (concat (sha1 (org-element-property :value elem)) \".svg\"))) (setq org-babel-default-header-args:latex `((:results . \"file link replace\") (:file . (lambda () (org-src-sha))))) #+end_src This will set the ~:file~ header argument to the sha1 checksum of the contents of the current latex source block. Finally, the closures are only evaluated if they're not overridden for a source block. This improves efficiency in cases where the result of a compute-expensive closure would otherwise be discarded. ** Miscellaneous *** =org-bibtex= includes =doi= and =url= entries when exporting to BiBTeX =doi= and =url= entries have been made optional for some publication types and will be exported if present for those types. *** Missing or empty placeholders in "eval" macros are now =nil= They used to be the empty string. *** =org-goto-first-child= now works before first heading When point is before first heading =org-goto-first-child= will move point to the first child heading, or return nil if no heading exist in buffer. This is in line with the fact that everything before first heading is regarded as outline level 0, i.e. the parent level of all headings in the buffer. Previously =org-goto-first-child= would do nothing before first heading, except return nil. *** Faces of all the heading text elements now conform to the headline face In the past, faces of todo keywords, emphasized text, tags, and priority cookies inherited =default= face. The resulting headline fontification was not always consistent, as discussed in [[msg::87h7sawubl.fsf@protesilaos.com][this bug report]]. Now, the relevant faces adapt to face used to fontify the current headline level. Users who prefer to keep the old behavior should change their face customization explicitly stating that =default= face is inherited. Example of old face customization: #+begin_src emacs-lisp (setq org-todo-keyword-faces '(("TODO" :background "chocolate" :height 0.75))) #+end_src To preserve the old behavior the above customization should be changed to #+begin_src emacs-lisp (setq org-todo-keyword-faces '(("TODO" :inherit default :background "chocolate" :height 0.75))) #+end_src *** Storing ID-links before first heading uses title as description Storing links to files using ~org-store-link~ (==) when ~org-id-link-to-org-use-id~ is not nil will now store the title as description of the link, if available. If no title exists it falls back to the filename as before. *** Change in =org-tags-expand= signature The function does not allow for a third optional parameter anymore. *** LaTeX environment =#+results= are now removed If a babel src block produces a raw LaTeX environment, it will now be recognized as a result, and so replaced when re-evaluated. *** Tag completion now uses =completing-read-multiple= Tag completion now uses =completing-read-multiple= with a simple completion table, which should allow better interoperability with custom completion functions. *** Providing =directory-empty-p= from Emacs 28 as =org-directory-empty-p= *** =org-get-last-sibling= marked as obsolete Use =org-get-previous-sibling= instead. This is just a rename to have a more consistent naming. E.g. recall the pair of funtctions =next-line= / =previous-line=. *** Make org-protocol compatible with =URLSearchParams= JavaScript class Decoder of query part of org-protocol URI recognizes "+" as an encoded space characters now, so it is possible to avoid call to =encodeURIComponent= for each parameter and use more readable expression in bookmarklet: #+begin_example 'org-protocol://store-link?' + new URLSearchParams({ url: location.href, title: document.title}) #+end_example *** Remove obsolete LaTeX packages from ~org-latex-default-packages-alist~ The LaTeX packages =grffile= and =textcomp= are redundant, with their capabilities being merged into =graphicx= and the LaTeX core respectively a while ago. * Version 9.4 ** Incompatible changes *** Possibly broken internal file links: please check and fix A bug has been affecting internal links to headlines, like : [[*Headline][A link to a headline]] Storing a link to a headline may have been broken in your setup and those links may appear as : [[*TODO Headline][A link to a headline]] Following the link above will result in an error: the TODO keyword should not be part of internal file links. You can use the following command to fix links in an Org buffer: #+begin_src emacs-lisp (defun org-fix-links () "Fix ill-formatted internal links. E.g. replace [[*TODO Headline][headline]] by [[*Headline][headline]]. Go through the buffer and ask for the replacement." (interactive) (visible-mode 1) (save-excursion (goto-char (point-min)) (let ((regexp (format "\\[\\[\\*%s\\s-+" (regexp-opt org-todo-keywords-1 t)))) (while (re-search-forward regexp nil t) (when (and (save-excursion (goto-char (match-beginning 0)) (looking-at-p org-link-bracket-re)) (y-or-n-p "Fix link (remove TODO keyword)? ")) (replace-match "[[*"))))) (visible-mode -1)) #+end_src *** Calling conventions changes when opening or exporting custom links This changes affects export back-ends, and libraries providing new link types. Function used in ~:follow~ link parameter is required to accept a second argument. Likewise, function used in ~:export~ parameter needs to accept a fourth argument. See ~org-link-set-parameters~ for details. Eventually, the function ~org-export-custom-protocol-maybe~ is now called with a fourth argument. Even though the 3-arguments definition is still supported, at least for now, we encourage back-end developers to switch to the new signature. *** Python session return values must be top-level expression statements Python blocks with ~:session :results value~ header arguments now only return a value if the last line is a top-level expression statement. Also, when a None value is returned, "None" will be printed under "#+RESULTS:", as it already did with ~:results value~ for non-session blocks. *** In HTML export, change on how outline-container-* is set When the headline has a =CUSTOM_ID=, use this custom id to build the div id. For example, if you have =:CUSTOM_ID: my-headline= then the resulting
will be ~
~. You may want to check whether your HTML files are rendered differently after this change. *** New keybinding == for ~org-force-cycle-archived~ ~org-force-cycle-archived~ used to be associated with == but this keybinding is used in Emacs for navigating tabs in Emacs. The new keybinding is ==. ** New default settings for some options These options now default to =t=: - ~org-loop-over-headlines-in-active-region~ - ~org-fontify-done-headline~ - ~org-src-tab-acts-natively~ You may want to read the docstrings of these options to understand the consequences of this change. Also, ~org-startup-folded~ now defaults to ~showeverything~. ** New features *** =RET= and =C-j= now obey ~electric-indent-mode~ Since Emacs 24.4, ~electric-indent-mode~ is enabled by default. In most major modes, this causes =RET= to reindent the current line and indent the new line, and =C-j= to insert a newline without indenting. Org mode now obeys this minor mode: when ~electric-indent-mode~ is enabled, and point is neither in a table nor on a timestamp or a link: - =RET= (bound to ~org-return~) reindents the current line and indents the new line; - =C-j= (bound to the new command ~org-return-and-maybe-indent~) merely inserts a newline. To get the previous behavior back, disable ~electric-indent-mode~ explicitly: #+begin_src emacs-lisp (add-hook 'org-mode-hook (lambda () (electric-indent-local-mode -1))) #+end_src Alternatively, if you wish to keep =RET= as the "smart-return" key, but dislike Org's default indentation of sections, you may prefer to customize ~org-adapt-indentation~ to either nil or =headline-data=. *** New allowed value for ~org-adapt-indentation~ ~org-adapt-indentation~ now accepts a new value, =headline-data=. When set to this value, Org will only adapt indentation of headline data lines, such as planning/clock lines and property/logbook drawers. Also, with this setting, =org-indent-mode= will keep these data lines correctly aligned with the headline above. *** Looping agenda commands over headlines ~org-agenda-loop-over-headlines-in-active-region~ allows you to loop agenda commands over the active region. When set to =t= (the default), loop over all headlines. When set to ='start-level=, loop over headlines with the same level as the first headline in the region. When set to a string, loop over lines matching this regular expression. *** New minor mode ~org-table-header-line-mode~ Turn on the display of the first data row of the table at point in the window header line when this first row is not visible anymore in the buffer. You can activate this minor mode by default by setting the option ~org-table-header-line-p~ to =t=. You can also change the face for the header line by customizing the ~org-table-header~ face. *** New minor mode ~org-list-checkbox-radio-mode~ When this minor mode is on, checkboxes behave as radio buttons: if a checkbox is turned on, other checkboxes at the same level are turned off. If you want to occasionally toggle a checkbox as a radio button without turning this minor mode on, you can use == to call ~org-toggle-radio-button~. You can also add =#+ATTR_ORG: :radio t= right before the list to tell Org to use radio buttons for this list only. *** Numeric priorities are now allowed (up to 65) You can now set ~org-priority-highest/lowest/default~ to integers to use numeric priorities globally or set, for example #+PRIORITIES: 1 10 5 to define a buffer-local range and default for priorities. Priority commands should work as usual. You cannot use numbers superior to 64 for numeric priorities, as it would clash with priorities like [#A] where the "A" is internally converted to its numeric value of 65. *** Property drawers allowed before first headline Property drawers are now allowed before the first headline. Org mode is moving more towards making things before the first headline behave just as if it was at outline level 0. Inheritance for properties will work also for this level. In other words: defining things in a property drawer before the first headline will make them "inheritable" for all headlines. *** Refinement in window behavior on exiting Org source buffer After editing a source block, Org will restore the window layout when ~org-src-window-setup~ is set to a value that modifies the layout. *** Display remote inline images Org now knows how to display remote images inline. Whether the images are actually displayed is controlled by the new option ~org-display-remote-inline-images~. *** New option to resolve open clock at a provided time ~org-resolve-clocks~ now has a `t' option, which works just like the `k' option, but the user specifies a time of day, not a number of minutes. *** New step value =semimonth= accepted for clock tables *** Allow text rescaling in column view You can now use =C-x C-+= in column view: the columns face size will increase or decrease, together with the column header size. *** New startup option =#+startup: num= When this startup option is set, display headings as numerated. Use =#+startup: nonum= to turn this off. *** New tool for custom links Org provides a new tool ~org-link-open-as-file~, useful when defining new link types similar to "file"-type links. See docstring for details. *** New optional numeric argument for ~org-return~ In situations where ~org-return~ calls ~newline~, multiple newlines can now be inserted with this prefix argument. *** New source code block header argument =:file-mode= Source code block header argument =:file-mode= can set file permissions if =:file= argument is provided. *** =ob-C.el= allows the inclusion of non-system header files In C and C++ blocks, ~:includes~ arguments that do not start with a ~<~ character will now be formatted as double-quoted ~#include~ statements. *** =ob-clojure.el= supports inf-clojure.el and ClojureScript evaluation You can now set ~(setq org-babel-clojure-backend 'inf-clojure)~ and evaluate Clojure source blocks using [[https://github.com/clojure-emacs/inf-clojure][inf-clojure]]. With a header argument like =:alias "alias"= the Clojure REPL will boot with =clojure -Aalias=. Otherwise Clojure will boot with =lein=, =boot= or =tools.deps=, depending on whether the current directory contains a =project.clj=, =build.boot= or =deps.edn=, falling back on ~inf-clojure-generic-cmd~ in case no such file is present. Also, when using [[https://github.com/clojure-emacs/cider][cider]], you can now use =#+begin_src clojurescript= to execute ClojureScript code from Org files. Note that this works only if your Org file is associated with a cider session that knows how to run ClojureScript code. A bare =lein repl= session outside of a directory configured for ClojureScript will /not/ work. *** =ob-java.el= supports Java command line arguments Babel Java blocks recognize header argument =:cmdargs= and pass its value in call to =java=. *** =ob-screen.el= now accepts =:screenrc= header argument Screen blocks now recognize the =:screenrc= header argument and pass its value to the screen command via the "-c" option. The default remains =/dev/null= (i.e. a clean screen session) *** =ob-plantuml=: now supports using PlantUML executable to generate diagrams Set =org-plantuml-exec-mode= to ='plantuml= in order to use the executable instead of JAR. When using an executable it is also possible to configure executable location as well as arguments via: =org-plantuml-executable-path= and =org-plantuml-executable-args=. ** New commands *** ~org-table-header-line-mode~ Turn on a minor mode to display the first data row of the table at point in the header-line when the beginning of the table is invisible. *** ~org-agenda-ctrl-c-ctrl-c~ Hitting == in an agenda view now calls ~org-agenda-set-tags~. *** ~org-hide-entry~ This command is the counterpart of ~org-show-entry~. *** ~org-columns-toggle-or-columns-quit~ == bound to ~org-columns-toggle-or-columns-quit~ replaces the recent ~org-columns-set-tags-or-toggle~. Tag setting is still possible via column view value edit or with ==. *** ~org-datetree-find-month-create~ Find or create a month entry for a date. ** New options and settings *** New option ~org-html-prefer-user-labels~ When non-nil, use =NAME= affiliated keyword, or raw target values, to generate anchor's ID. Otherwise, consistently use internal naming scheme. =CUSTOM_ID= values are still always used, when available. *** New option for using tabs in ~org-agenda-window-setup~ Choosing ~other-tab~ for ~org-agenda-window-setup~ will open the agenda view in a new tab. This will work with versions of Emacs since 27.1 when ~tab-bar-mode~ was introduced. *** New option ~org-table-header-line-p~ Setting this option to =t= will activate ~org-table-header-line-mode~ in org-mode buffers. *** New option ~org-startup-numerated~ When this option is =t=, Org files will start using ~(org-num-mode 1)~ and headings will be visually numerated. You can turn this on/off on a per-file basis with =#+startup: num= or =#+startup: nonum=. *** New option ~org-clock-auto-clockout-timer~ When this option is set to a number and the user configuration contains =(org-clock-auto-clockout-insinuate)=, Org will clock out the currently clocked in task after that number of seconds of idle time. This is useful when you often forget to clock out before being idle and don't want to have to manually set the clocking time to take into account. *** New option to group captured datetime entries by month A new `:tree-type month' option was added to org-capture-templates to group new datetime entries by month. *** New option to show source buffers using "plain" display-buffer There is a new option ~plain~ to ~org-src-window-setup~ to show source buffers using ~display-buffer~. This allows users to control how source buffers are displayed by modifying ~display-buffer-alist~ or ~display-buffer-base-action~. *** New option ~org-archive-subtree-save-file-p~ Archiving a subtree used to always save the target archive buffer. Commit [[git::b186d1d7][b186d1d7]] changed this behavior by always not saving the target buffer, because batch archiving from agenda could take too much time. This new option ~org-archive-subtree-save-file-p~ defaults to the value =from-org= so that archiving a subtree will save the target buffer when done from an org-mode buffer, but not from the agenda. You can also set this option to =t= or to =from-agenda=. *** New option ~org-show-notification-timeout~ This option will add a timeout to notifications. *** New option ~org-latex-to-html-convert-command~ This new option allows you to convert a LaTeX fragment directly into HTML. *** New option ~org-babel-shell-results-defaults-to-output~ By default, source code blocks are executed in "functional mode": it means that the results of executing them are the value of their last statement (see [[https://orgmode.org/manual/Results-of-Evaluation.html][the documentation]].) The value of a shell script's execution is its exit code. But most users expect the results of executing a shell script to be its output, not its exit code. So we introduced this option, that you can set to nil if you want to stick using ~:results value~ as the implicit header. In all Babel libraries, the absence of a ~:results~ header should produce the same result than setting ~:results value~, unless there is an option to explicitly create an exception. See [[msg:CA+A2iZaziAfMeGpBqL6qGrzrWEVvLvC0DUw++T4gCF3NGuW-DQ@mail.gmail.com][this thread]] for more context. *** New option in ~org-attach-store-link-p~ ~org-attach-store-link-p~ has a new option to store a file link to the attachment. *** New option ~org-fontify-todo-headline~ This feature is the same as ~org-fontify-done-headline~, but for TODO headlines instead. This allows you to distinguish TODO headlines from normal headlines. The face can be customized via ~org-headline-todo~. *** New default value for ~org-file-apps~ The new value uses Emacs as the application for opening directory. *** New hook ~org-agenda-filter-hook~ Functions in this hook are run after ~org-agenda-filter~ is called. ** Removed or renamed functions and variables *** Deprecated ~org-flag-drawer~ function Use ~org-hide-drawer-toggle~ instead. *** Deprecated ~org-hide-block-toggle-maybe~ function Use ~org-hide-block-toggle~ instead. *** Deprecated ~org-hide-block-toggle-all~ function This function was not used in the code base, and has no clear use either. It has been marked for future removal. Please contact the mailing list if you use this function. *** Deprecated ~org-return-indent~ function In Elisp code, use ~(org-return t)~ instead. Interactively, =C-j= is now bound to ~org-return-and-maybe-indent~, which indents the new line when ~electric-indent-mode~ is disabled. *** Removed ~org-maybe-keyword-time-regexp~ The variable was not used in the code base. *** Removed ~org-export-special-keywords~ The variable was not used in the code base. *** Renamed ~org-at-property-block-p~ The new name is ~org-at-property-drawer-p~, which is less confusing. *** Renamed ~org-columns-set-tags-or-toggle~ See [[*~org-columns-toggle-or-columns-quit~]]. *** Renamed priority options From ~org-lowest-priority~ to ~org-priority-lowest~. From ~org-default-priority~ to ~org-priority-default~. From ~org-highest-priority~ to ~org-priority-highest~. From ~org-enable-priority-commands~ to ~org-priority-enable-commands~. From ~org-show-priority~ to ~org-priority-show~. ** Miscellaneous *** =ob-screen.el= now respects screen =:session= name Screen babel session are now named based on the =:session= header argument (defaults to ~default~). Previously all session names had ~org-babel-session-~ prepended. *** Forward/backward paragraph functions in line with the rest of Emacs ~org-forward-paragraph~ and ~org-backward-paragraph~, bound to ~~ and ~~ functions mimic more closely behavior of ~forward-paragraph~ and ~backward-paragraph~ functions when available. They also accept an optional argument for multiple calls. See their docstring for details. *** ~org-table-to-lisp~ no longer checks if point is at a table The caller is now responsible for the check. It can use, e.g., ~org-at-table-p~. The function is also much more efficient than it used to be, even on very large tables. *** New function ~org-collect-keywords~ *** Drawers' folding use an API similar to block's Tooling for folding drawers interactively or programmatically is now on par with block folding. In particular, ~org-hide-drawer-toggle~, a new function, is the central place for drawer folding. *** Duration can be read and written in compact form ~org-duration-to-minutes~ understands =1d3h5min= as a duration, whereas ~org-duration-from-minutes~ can output this compact form if the duration format contains the symbol ~compact~. *** C-n, C-p, SPC and DEL in agenda commands dispatch window You can now use ==, ==, == and == key to scroll up and down the agenda and attach dispatch window. *** == in agenda calls ~org-agenda-set-tags~ Both == and == set the tags of the headline in the Org buffer. Both keybindings are now available from the agenda too. *** Allow to use an empty HTML extension Using =(setq org-html-extension "")= or setting the HTML extension in any fashion will produce the expected output, with no trailing period to the resulting HTML file. *** Handle repeated tasks with =.+= type and hours step A task using a =.+= repeater and hours step is repeated starting from now. E.g., #+begin_example ,,** TODO Wash my hands DEADLINE: <2019-04-05 08:00 Sun .+1h> Marking this DONE shifts the date to exactly one hour from now. #+end_example *** The format of equation reference in HTML export can now be specified By default, HTML (via MathJax) and LaTeX export equation references using different commands. LaTeX must use ~\ref{%s}~ because it is used for all labels; however, HTML (via MathJax) uses ~\eqref{%s}~ for equations producing inconsistent output. New option ~org-html-equation-reference-format~ sets the command used in HTML export. *** =ob-haskell.el= supports compilation with =:compile= header argument By default, Haskell blocks are interpreted. By adding =:compile yes= to a Haskell source block, it will be compiled, executed and the results will be displayed. *** Support for ~org-edit-special~ with LaTeX fragments Calling ~org-edit-special~ on an inline LaTeX fragment calls a new function, ~org-edit-latex-fragment~. This functions in a comparable manner to editing inline source blocks, bringing up a minibuffer set to LaTeX mode. The math-mode deliminators are read only. *** ~org-capture-current-plist~ is now accessible during ~org-capture-mode-hook~ *** New =org-refile.el= file Org refile variables and functions have been moved to a new file. *** The end of a 7 years old bug This bug [[https://lists.gnu.org/archive/html/emacs-orgmode/2013-08/msg00072.html][originally reported]] by Matt Lundin and investigated by Andrew Hyatt has been fixed. Thanks to both of them. * Version 9.3 ** Incompatible changes *** Change bracket link escaping syntax Org used to percent-encode sensitive characters in the URI part of the bracket links. Now, escaping mechanism uses the usual backslash character, according to the following rules: 1. All =[= and =]= characters in the URI must be escaped; 2. Every =\= character preceding either =[= or =]= must be escaped; 3. Every =\= character at the end of the URI must be escaped. When in doubt, use the function ~org-link-escape~ in order to turn a link string into its properly escaped form. The following function will help switching your links to the new syntax: #+begin_src emacs-lisp (defun org-update-link-syntax (&optional no-query) "Update syntax for links in current buffer. Query before replacing a link, unless optional argument NO-QUERY is non-nil." (interactive "P") (org-with-point-at 1 (let ((case-fold-search t)) (while (re-search-forward "\\[\\[[^]]*?%\\(?:2[05]\\|5[BD]\\)" nil t) (let ((object (save-match-data (org-element-context)))) (when (and (eq 'link (org-element-type object)) (= (match-beginning 0) (org-element-property :begin object))) (goto-char (org-element-property :end object)) (let* ((uri-start (+ 2 (match-beginning 0))) (uri-end (save-excursion (goto-char uri-start) (re-search-forward "\\][][]" nil t) (match-beginning 0))) (uri (buffer-substring-no-properties uri-start uri-end))) (when (or no-query (y-or-n-p (format "Possibly obsolete URI syntax: %S. Fix? " uri))) (setf (buffer-substring uri-start uri-end) (org-link-escape (org-link-decode uri))))))))))) #+end_src The old ~org-link-escape~ and ~org-link-unescape~ functions have been renamed into ~org-link-encode~ and ~org-link-decode~. *** Change match group number in ~org-link-bracket-re~ Link description, if any, is located in match group 2 instead of match group 3. *** ob-clojure does not auto prepend ~(ns ..)~ statement anymore When tangling, user usually just wants to tangle literally code instead of prepend inserting a ~(ns ..)~ statement before source block code. Now, when you have no ~:ns~ header argument specified, this behavior will not happen automatically. *** Change in behavior on exit from an Org edit buffer Org will no longer attempt to restore the window configuration in the frame to which the user returns after editing a source block with ~org-edit-src-code~. Instead, the window configuration will remain as it is. *** Change default value for ~org-email-link-description-format~ When linking from a mail buffer, Org used to truncate the subject of the message to 30 characters in order to build the description of the link. This behavior was considered as too surprising. As a consequence, Org no longer truncates subjects. You can get the old behavior back with the following: : (setq org-email-link-description-format "Email %c: %.30s") *** ~:file~ header argument no longer assume "file" ~:results~ The "file" ~:results~ value is now mandatory for a code block returning a link to a file. The ~:file~ or ~:file-ext~ header arguments no longer imply a "file" result is expected. *** Plain numbers are hours in Column View mode See [[git:3367ac9457]] for details. *** All LaTeX preview backends use now xcolor The dvipng backend was previously relying on fg and bg parameters to be passed to the CLI. This didn't work when xcolor was directly or indirectly used in the document (e.g. tkiz is a user of xcolor). Since every other backend was already using xcolor to set fg and bg, the CLI alternative was removed and there is no more a :use-xcolor options since now it's implicitly always true. *** Org-Attach Git commit [[*Org-Attach has been refactored and extended][Refactoring of Org-Attach]] affected the Git commit functionality. Not much, but the following changes are required if you still need to auto-commit attachments to git: - Customization of ~org-attach-annex-auto-get~ needs to be renamed to ~org-attach-git-annex-auto-get~. - Customization of ~org-attach-commit~ is no longer needed. Instead one need to require the =org-attach-git= module in the startup. ** New features *** New option to wrap source code lines in HTML export When new option ~html-wrap-src-lines~ (with variable ~org-html-wrap-src-lines~) is non-nil, HTML export wraps source code lines in HTML ~code~ elements. *** New option to handle schedules and deadlines in iCalendar export Export ignore done tasks with a deadline when ~org-icalendar-use-deadline~ contains ~event-if-todo-not-done~. Likewise, scheduled done tasks are also ignored when ~org-icalendar-use-scheduled~ contains the same symbol. *** Add ~split-window-right~ option for src block edit window placement Given the increasing popularity of wide screen monitors, splitting horizontally may make more sense than splitting vertically. An option, ~split-window-right~, to request horizontal splitting has been added to ~org-src-window-setup~. *** Org-Attach has been refactored and extended Org attach has been refactored and the functionality extended. It should now be easier to understand how it works. A few improvements and extra options have been added as well. From the initial comment in org-attach source-code: - Attachments are managed either by using a custom property DIR or by using property ID from org-id. When DIR is defined, a location in the filesystem is directly attached to the outline node. When org-id is used, attachments are stored in a folder named after the ID, in a location defined by ~org-attach-id-dir~. DIR has precedence over ID when both parameters are defined for the current outline node (also when inherited parameters are taken into account). From now on inheritance requires no extra property and will adhere to ~org-attach-use-inheritance~ by default. Inheritance can be customized to always be activated or never be activated in ~org-attach-use-inheritance~. The ATTACH_DIR property is deprecated in favor of the shorter property DIR. Links to folders inside the DIR property can now be declared as relative links. This is not enabled by default, but can be set in ~org-attach-dir-relative~. When adding new attachment to the outline node the preferred way of doing so can be customized. Take a look at ~org-attach-preferred-new-method~. It defaults to using ID since that was the behavior before this change. If both DIR and ID properties are set on the same node, DIR has precedence and will be used. One can now also choose to build attachment-directory-paths in a customized way. This is an advanced topic, but in some case it makes sense to parse an ID in a different way than the default one. Create your own function and add it to the beginning of ~org-attach-id-to-path-function~list~ if you want to customize the ID based folder structure. If you've used ATTACH_DIR properties to manage attachments, use the following code to rename that property to DIR which supports the same functionality. ATTACH_DIR_INHERIT is no longer supported and is removed. #+begin_src emacs-lisp (defun org-update-attach-properties () "Change properties for Org-Attach." (interactive) (org-with-point-at 1 (while (outline-next-heading) (let ((DIR (org--property-local-values "ATTACH_DIR" nil))) (when DIR (org-set-property "DIR" (car DIR)) (org-delete-property "ATTACH_DIR")))) (org-delete-property-globally "ATTACH_DIR_INHERIT"))) #+end_src For those who hate breaking changes, even though the changes are made to clean things up; fear not. ATTACH_DIR will still continue to work. It's just not documented any longer. When you get the chance, run the code above to clean things up anyway! **** New hooks Two hooks are added to org-attach: - org-attach-after-change-hook - org-attach-open-hook They are added mostly for internal restructuring purposes, but can ofc. be used for other things as well. *** New link-type: Attachment Attachment-links are now first-class citizens. They mimic file-links in everything they do but use the existing attachment-folder as a base when expanding the links. Both =DIR= and =ID= properties are used to try to resolve the links, in exactly the same way as Org-Attach uses those properties. *** Handle overlay specification for notes in Beamer export This aligns Beamer notes with slide overlays. *** Add support for lettered lists in Texinfo Using =:enum A= or =:enum a= Texinfo attribute switches an otherwise numbered list to a lettered list. *** Add a dispatcher command to insert dynamic blocks You can add new dynamic blocks with function ~org-dynamic-block-define~. All such dynamic blocks can be used by ~org-dynamic-block-insert-dblock~ command. *** Babel **** ob-emacs-lisp sets ~lexical-binding~ in Org edit buffers When editing an Elisp src block, the editing buffer's ~lexical-binding~ is set according to the src block's =:lexical= parameter. **** Add LaTeX output support in PlantUML *** New minor mode to display headline numbering Use == to get a visual indication of the numbering in the outline. The numbering is also automatically updated upon changes in the buffer. *** New property =HTML_HEADLINE_CLASS= in HTML export The new property =HTML_HEADLINE_CLASS= assigns a class attribute to a headline. *** Allow LaTeX attributes and captions for "table.el" tables Supported LaTeX attributes are ~:float~, ~:center~, ~:font~ and ~:caption~. *** Attach buffer contents to headline With == key from attachment dispatcher (==), it is now possible to write the contents of a buffer to a file in the headline attachment directory. *** iCalendar export respects a =CLASS= property Set the =CLASS= property on an entry to specify a visibility class for that entry only during iCalendar export. The property can be set to anything the calendar server supports. The iCalendar standard defines the values =PUBLIC=, =CONFIDENTIAL=, =PRIVATE=, which can be interpreted as publicly visible, accessible to a specific group, and private respectively. This property can be inherited during iCalendar export, depending on the value of ~org-use-property-inheritance~. *** New parameter for =INCLUDE= keyword Add =:coding CODING-SYSTEM= to include files using a different coding system than the main Org document. For example: #+begin_example ,#+INCLUDE: "myfile.cmd" src cmd :coding cp850-dos #+end_example *** New values in clock tables' step: =month= and =year= *** ODT export handles numbers cookies in lists *** New cell movement functions in tables ~S-~, ~S-~, ~S-~, and ~S-~ now move cells in the corresponding direction by swapping with the adjacent cell. *** New option to natively fontify LaTeX snippets and environments A 'native option was added to org-highlight-latex-and-related. It matches the same structures than 'latex but it calls org-src-font-lock-fontify-block instead, thus bringing about full LaTeX font locking. *** ~org-clone-subtree-with-time-shift~ learned to shift backward in time == (~org-clone-subtree-with-time-shift~) now takes a negative value as a valid repeater to shift time stamps in backward in cloned subtrees. You can give, for example, ‘-3d’ to shift three days in the past. *** Toggle display of all vs. undone scheduled habits conveniently == (~org-habit-toggle-display-in-agenda~) in an agenda toggles the display of all habits to those which are undone and scheduled. This is a function for convenience. *** New parameter for SQL Babel blocks: ~:dbconnection~ The new parameter ~:dbconnection~ allows to specify a connection name in a SQL block header: this name is used to look up connection parameters in ~sql-connection-alist~. *** New =:scale= attribute supported by LaTeX exporters The builtin "latex" exporters now accept and use a =:scale= attribute, which scales an image by a given factor. This attribute is wrapped around the =scale= parameter of LaTeX's =\includegraphics= (bitmap images) or a TiKZ's =\scalebox=. Therefore, its value should be some string palatable to LaTeX as a positive float Its default value is an empty string (i.e. disabled). This attribute overrides the =:width= and =:height= attributes. #+begin_example ,#+name: Beastie ,#+caption: I think I saw this curious horse already, but where ? ,#+LATEX_ATTR: :scale 2 [[https://orgmode.org/img/org-mode-unicorn-logo.png]] #+end_example *** Allow specifying the target for a table of contents The =+TOC= keyword now accepts a =:target:= attribute that specifies the headline to use for making the table of contents. #+begin_example ,* Target :PROPERTIES: :CUSTOM_ID: TargetSection :END: ,** Heading A ,** Heading B ,* Another section ,#+TOC: headlines 1 :target "#TargetSection" #+end_example ** New functions *** ~org-dynamic-block-insert-dblock~ Use default keybinding == to run command ~org-dynamic-block-insert-dblock~. It will prompt user to select dynamic block in ~org-dynamic-block-alist~. *** ~org-table-cell-up~ *** ~org-table-cell-down~ *** ~org-table-cell-left~ *** ~org-table-cell-right~ *** ~org-habit-toggle-display-in-agenda~ ** Removed functions and variables *** Removed Org Drill You can install it back from MELPA. *** ~org-babel-set-current-result-hash~ *** ~org-capture-insert-template-here~ *** ~org-attach-directory~ It has been deprecated in favor of ~org-attach-id-dir~ which is less ambiguous given the restructured org-attach. *** ~org-enable-fixed-width-editor~ This variable was not used through the code base. ** Miscellaneous *** Change signature for ~org-list-to-subtree~ The function now accepts the level of the subtree as an optional argument. It no longer deduces it from the current level. *** LaTeX preview is simplified Function ~org-latex-preview~, formerly known as ~org-toggle-latex-fragment~, has a hopefully simpler and more predictable behavior. See its docstring for details. *** ~org-table-copy-down~ supports patterns When ~org-table-copy-increment~ is non-nil, it is now possible to increment fields like =A1=, or =0A=, i.e., any string prefixed or suffixed with a whole number. *** No more special indentation for description items Descriptions items are indented like regular ones, i.e., text starts after the bullet. Special indentation used to introduce bugs when inserting sub-items in a description list. *** New hook: ~org-todo-repeat-hook~ This hook was actually introduced in Org 9.2.1, but wasn't advertised. *** Org Table reads numbers starting with 0 as strings *** Disable fast tag selection interface via prefix arg A call of ~org-set-tags-command~ with prefix argument C-u C-u avoids the fast tag selection interface and instead offers the plain interface. *** ~:mkdirp~ now supports create directory for ~:dir~ path The ~:mkdirp~ header argument used to only work for ~:tangle~ tangle files. Now ~:mkdirp~ works for ~:dir~ too. This is more convenient for specify default directory and with ~:file~ header argument. *** New variable: ~org-agenda-breadcrumbs-separator~ If breadcrumbs are showed in org-agenda with the help of "%b" format in ~org-agenda-prefix-format~, user can customize breadcrumbs's separator using ~org-agenda-breadcrumbs-separator~. *** New variable ~org-attach-commands~ This variable makes it possible to customize the list of commands for the attachment dispatcher. *** New ID method based on timestamp If one chooses, it is now possible to create ID's based on timestamp (ISO8601) instead of UUID by changing org-id-method to ts. For an improved folder structure when using timestamp as ID, make sure to promote ~org-attach-id-ts-folder-format~ to the first element of ~org-attach-id-to-path-function-list~ in your configuration at the same time. *** New customization: ~org-id-locations-relative~ New customization to make the persisting of org-id-locations between sessions to store links to files as relative instead of absolute. The links will be stored as relative to the path of org-id-locations-file. *** ~org-ctrl-c-tab~ is functional before the first headline I.e. treat the whole file as if it was a subtree. Also fold everything below the chosen level. Former behavior was to leave unfolded subtrees unfolded. *** ~org-kill-note-or-show-branches~ is functional before the first headline I.e. treat the whole file as if it was a subtree. *** Respect narrowing when agenda command is restricted to buffer *** ~org-table-insert-column~ inserts the column at point position Before, the new column was inserted to the right of the column at point position. *** Table column deletion now consistent with row deletion Point stays in the column at deletion, except when deleting the rightmost column. * Version 9.2 ** Incompatible changes *** Removal of OrgStruct mode mode and radio lists OrgStruct minor mode and radio lists mechanism (~org-list-send-list~ and ~org-list-radio-lists-templates~) are removed from the code base. Note that only radio /lists/ have been removed, not radio tables. If you want to manipulate lists like in Org in other modes, we suggest to use =orgalist.el=, which you can install from GNU ELPA. If you want to use Org folding outside of Org buffers, you can have a look at the outshine package in the MELPA repository. *** Change in the structure template expansion Org 9.2 comes with a new template expansion mechanism, combining ~org-insert-structure-template~ bound to ~C-c C-,~. If you customized the ~org-structure-template-alist~ option manually, you probably need to update it, see the docstring for accepted values. If you prefer using previous patterns, e.g. => ,#+END_SRC #+END_SRC should become #+BEGIN_SRC org ,#+NAME: foo ,#+BEGIN_SRC emacs-lisp :noweb-ref bar 1 ,#+END_SRC ,#+BEGIN_SRC emacs-lisp :noweb-ref bar 2 ,#+END_SRC ,#+BEGIN_SRC emacs-lisp :noweb yes <> ,#+END_SRC #+END_SRC *** Default/accepted values of ~org-calendar-to-agenda-key~ The default value and accepted value of ~org-calendar-to-agenda-key~ changed. This is an excerpt of the new docstring: : When set to ‘default’, bind the function to ‘c’, but only if it is : available in the Calendar keymap. This is the default choice because : ‘c’ can then be used to switch back and forth between agenda and calendar. : : When nil, ‘org-calendar-goto-agenda’ is not bound to any key. Check the full docstring for more. *** Change the signature of the ~org-set-effort~ function Here is the new docstring: : (org-set-effort &optional INCREMENT VALUE) : : Set the effort property of the current entry. : If INCREMENT is non-nil, set the property to the next allowed : value. Otherwise, if optional argument VALUE is provided, use : it. Eventually, prompt for the new value if none of the previous : variables is set. *** Placeholders in =(eval ...)= macros are always strings Within =(eval ...)= macros, =$1=-like placeholders are always replaced with a string. As a consequence, they must not be enclosed within quotes. As an illustration, consider the following, now valid, examples: #+begin_example ,#+macro: join (eval (concat $1 $2)) ,#+macro: sum (eval (+ (string-to-number $1) (string-to-number $2))) {{{join(a,b)}}} => ab {{{sum(1,2)}}} => 3 #+end_example However, there is no change in non-eval macros: #+begin_example ,#+macro: disp argument: $1 {{{disp(text)}}} => argument: text #+end_example *** =align= STARTUP value no longer narrow table columns Columns narrowing (or shrinking) is now dynamic. See [[*Dynamically narrow table columns]] for details. In particular, it is decoupled from aligning. If you need to automatically shrink columns upon opening an Org document, use =shrink= value instead, or in addition to align: #+BEGIN_EXAMPLE ,#+STARTUP: align shrink #+END_EXAMPLE *** ~org-get-tags~ meaning change Function ~org-get-tags~ used to return local tags to the current headline. It now returns all the inherited tags in addition to the local tags. In order to get the old behavior back, you can use: : (org-get-tags nil t) *** Alphabetic sorting in tables and lists When sorting alphabetically, ~org-table-sort-lines~ and ~org-sort-list~ now sort according to the locale’s collation rules instead of by code-point. *** Change the name of the :tags clocktable option to :match The =:match= (renamed from =:tags=) option allows to limit clock entries to those matching a todo-tags matcher. The old =:tags= option can be set to =t= to display a headline's tags in a dedicated column. This is consistent with the naming of =org-dblock-write:columnview= options, where =:match= is also used as a headlines filter. ** New features *** Add ~:session~ support of ob-clojure for CIDER You can initialize source block session with Babel default keybinding =[C-c C-v C-z]= to use =sesman= session manager to link current project, directory or buffer with specific Clojure session, or =cider-jack-in= a new CIDER REPL if no CIDER REPLs available. In older CIDER version which has not =sesman= integrated, only has =cider-jack-in= without Clojure project is supported. #+begin_src clojure :session (dissoc Clojure 'JVM) (conj clojurists "stardiviner") #+end_src *** Add ~:results link~ support for Babel With this output format, create a link to the file specified in ~:file~ header argument, without actually writing any result to it: #+begin_example ,#+begin_src shell :dir "data/tmp" :results link :file "crackzor_1.0.c.gz" wget -c "https://ben.akrin.com/crackzor/crackzor_1.0.c.gz" ,#+end_src ,#+results: [[file:data/tmp/crackzor_1.0.c.gz]] #+end_example *** Add ~:session~ support of ob-js for js-comint #+begin_src js :session "*Javascript REPL*" console.log("stardiviner") #+end_src *** Add ~:session~ support of ob-js for Indium #+begin_src js :session "*JS REPL*" console.log("stardiviner") #+end_src *** Add ~:session~ support of ob-js for skewer-mode #+begin_src js :session "*skewer-repl*" console.log("stardiviner") #+end_src *** Add support for links to LaTeX equations in HTML export Use MathJax links when enabled (by ~org-html-with-latex~), otherwise add a label to the rendered equation. *** Org Tempo may used for snippet expansion of structure template. See manual and the commentary section in ~org-tempo.el~ for details. *** Exclude unnumbered headlines from table of contents Set their =UNNUMBERED= property to the special =notoc= value. See manual for details. *** ~org-archive~ functions update status cookies Archiving headers through ~org-archive-subtree~ and ~org-archive-to-archive-sibling~ such as the ones listed below: #+BEGIN_SRC org ,* Top [1/2] ,** DONE Completed ,** TODO Working #+END_SRC Will update the status cookie in the top level header. *** Disable =org-agenda-overriding-header= by setting to empty string The ~org-agenda-overriding-header~ inserted into agenda views can now be disabled by setting it to an empty string. *** Dynamically narrow table columns With ~C-c TAB~, it is now possible to narrow a column to the width specified by a width cookie in the column, or to 1 character if there is no such cookie. The same keybinding expands a narrowed column to its previous state. Editing the column automatically expands the whole column to its full size. *** =org-columns-summary-types= entries can take an optional COLLECT function You can use this to make collection of a property from an entry conditional on another entry. E.g. given this configuration: #+BEGIN_SRC emacs-lisp (defun custom/org-collect-confirmed (property) "Return `PROPERTY' for `CONFIRMED' entries" (let ((prop (org-entry-get nil property)) (confirmed (org-entry-get nil "CONFIRMED"))) (if (and prop (string= "[X]" confirmed)) prop "0"))) (setq org-columns-summary-types '(("X+" org-columns--summary-sum custom/org-collect-confirmed))) #+END_SRC You can have a file =bananas.org= containing: #+BEGIN_SRC org ,#+columns: %ITEM %CONFIRMED %Bananas{+} %Bananas(Confirmed Bananas){X+} ,* All shipments ,** Shipment 1 :PROPERTIES: :CONFIRMED: [X] :Bananas: 4 :END: ,** Shipment 2 :PROPERTIES: :CONFIRMED: [ ] :BANANAS: 7 :END: #+END_SRC ... and when going to the top of that file and entering column view you should expect to see something like: | ITEM | CONFIRMED | Bananas | Confirmed Bananas | |---------------+-----------+---------+-------------------| | All shipments | | 11 | 4 | | Shipment 1 | [X] | 4 | 4 | | Shipment 2 | [ ] | 7 | 7 | #+BEGIN_EXAMPLE ,#+STARTUP: shrink #+END_EXAMPLE *** Allow to filter by tags/property when capturing colview You can now use =:match= to filter entries using a todo/tags/properties matcher. *** Add support for Oracle's database alias in Babel blocks =ob-sql= library already support running SQL blocks against an Oracle database using ~sqlplus~. Now it's possible to use alias names defined in =TNSNAMES= file instead of specifying full connection parameters. See example below. #+BEGIN_SRC org you can use the previous full connection parameters ,#+BEGIN_SRC sql :engine oracle :dbuser me :dbpassword my_insecure_password :database my_db_name :dbhost my_db_host :dbport 1521 select sysdate from dual; ,#+END_SRC or the alias defined in your TNSNAMES file ,#+BEGIN_SRC sql :engine oracle :dbuser me :dbpassword my_insecure_password :database my_tns_alias select sysdate from dual; ,#+END_SRC #+END_SRC *** ~org-agenda-set-restriction-lock~ toggle agenda restriction at point You can set an agenda restriction lock with =C-x C-x <= or with =<= at the beginning of a headline when using Org speed commands. Now, if there is already a restriction at point, hitting =<= again (or =C-x C-x <=) will remove it. *** Headlines can now link to themselves in HTML export When enabling ~org-html-self-link-headlines~ the headlines exported to HTML contain a hyperlink to themselves. ** New commands and functions *** ~org-insert-structure-template~ This function can be used to wrap existing text of Org elements in a #+BEGIN_FOO/#+END_FOO block. Bound to C-c C-x w by default. *** ~org-export-excluded-from-toc-p~ See docstring for details. *** ~org-timestamp-to-time~ *** ~org-timestamp-from-string~ *** ~org-timestamp-from-time~ *** ~org-attach-dired-to-subtree~ See docstring for details. *** ~org-toggle-narrow-to-subtree~ Toggle the narrowing state of the buffer: when in a narrowed state, widen, otherwise call ~org-narrow-to-subtree~ to narrow. This is attached to the "s" speed command, so that hitting "s" twice will go back to the widen state. *** ~org-browse-news~ Browse https://orgmode.org/Changes.html to let users read information about the last major release. There is a new menu entry for this in the "Documentation" menu item. *** ~org-info-find-node~ From an Org file or an agenda switch to a suitable info page depending on the context. The function is bound to =C-c C-x I=. ** Removed commands and functions *** ~org-outline-overlay-data~ Use ~org-save-outline-visibility~ instead. *** ~org-set-outline-overlay-data~ Use ~org-save-outline-visibility~ instead. *** ~org-get-string-indentation~ It was not used throughout the code base. *** ~org-fix-indentation~ It was not used throughout code base. *** ~org-context-p~ Use ~org-element-at-point~ instead. *** ~org-preserve-lc~ It is no longer used in the code base. *** ~org-try-structure-completion~ Org Tempo may be used as a replacement. See details above. ** Removed options *** org-babel-use-quick-and-dirty-noweb-expansion See [[*Change to Noweb expansion][Change to Noweb expansion]] for explanations. ** Miscellaneous *** New default value for ~org-texinfo-table-scientific-notation~ It is now nil, which means numbers in scientific notation are not handled specially by default. *** New default value for ~org-latex-table-scientific-notation~ It is now nil, which means numbers in scientific notation are not handled specially by default. *** New face: ~org-upcoming-distant-deadline~ It is meant to be used as the face for distant deadlines, see ~org-agenda-deadline-faces~ *** ~org-paste-subtree~ no longer breaks sections Unless point is at the beginning of a headline, ~org-paste-subtree~ now pastes the tree before the next visible headline. If you need to break the section, use ~org-yank~ instead. *** ~org-table-insert-column~ inserts a column to the right It used to insert it on the left. With this change, ~org-table-insert-column~ and ~org-table-delete-column~ are reciprocal. *** ~org-publish-resolve-external-link~ accepts a new optional argument. *** ~org-irc.el~ now supports exporting =irc:= links properly Previously, irc links were exported by ~ox-md~ and ~ox-html~ as normal file links, which lead to them being broken in web browsers. Now both of these exporters will properly export to =irc:= links, which will open properly in irc clients from web browsers. *** ~org-comment-dwim~ (bound to =M-;=) now comments headings, if point is on a heading *** Add support for open source block in window below Set option ~org-src-window-setup~ to ~split-window-below~. *** Alphabetic sorting in headings and tags now uses the locale’s sorting rules When sorting alphabetically, ~org-sort-entries~ and ~org-tags-sort-function~ now sort according to the locale’s collation rules instead of by code-point. *** New speed command "k" to kill (cut) the subtree at point * Version 9.1 ** Incompatible changes *** Variables relative to clocksum duration are obsolete ~org-time-clocksum-format~, ~org-time-clocksum-use-fractional~ and ~org-time-clocksum-fractional-format~ are obsolete. If you changed them, consider modifying ~org-duration-format~ instead. Variable ~org-time-clocksum-use-effort-durations~ is also obsolete. Consider setting ~org-duration-units~ instead. *** ~org-at-timestamp-p~ optional argument accepts different values See docstrings for the allowed values. For backward compatibility, ~(org-at-timestamp-p t)~ is still supported, but should be updated accordingly. *** ~org-capture-templates~ no longer accepts S-expressions as file names Since functions are allowed there, a straightforward way to migrate is to turn, e.g., : (file (sexp)) into : (file (lambda () (sexp))) *** Deleted contributed packages =org-ebib.el, =org-bullets.el= and =org-mime.el= have been deleted from the contrib/ directory. You can now find them here : - https://github.com/joostkremers/ebib - https://github.com/sabof/org-bullets - https://github.com/org-mime/org-mime *** Change ~org-texinfo-classes~ value The value cannot support functions to create sectioning commands anymore. Also, the sectioning commands should include commands for appendices. See the docstring for more information. *** Removal of ~:sitemap-sans-extension~ The publishing property is no longer recognized, as a consequence of changes to site-map generation. You can get the same functionality by setting ~:sitemap-format-entry~ to the following #+BEGIN_SRC elisp (lambda (entry style project) (cond ((not (directory-name-p entry)) (format "[[file:%s][%s]]" (file-name-sans-extension entry) (org-publish-find-title entry project))) ((eq style 'tree) (file-name-nondirectory (directory-file-name entry))) (t entry))) #+END_SRC *** Change signature for ~:sitemap-function~ ~:sitemap-function~ now expects to be called with two arguments. See ~org-publish-project-alist~ for details. *** Change signature for some properties in ~org-list-to-generic~ ~:istart~, ~:icount~, ~:iend~ and ~:isep~ now expect the type of the list as their first argument. *** Change signature for ~org-get-repeater~ The optional argument is now a string to extract the repeater from. See docstring for details. *** Change signature for ~org-time-string-to-time~ See docstring for changes. *** Change order of items in ~org-agenda-time-grid~ ~org-agenda-time-grid~ gained an extra item to allow users to customize the string displayed after times in the agenda. See docstring for details. *** ~tags-todo~ custom searches now include DONE keywords Use "/!" markup when filtering TODO keywords to get only not-done TODO keywords. *** ~org-split-string~ returns ~("")~ when called on an empty string It used to return nil. *** Removal of =ob-scala.el= See [[https://github.com/ensime/emacs-scala-mode/issues/114][this github issue]]. You can use =ob-scala.el= as packaged in scala-mode, available from the MELPA repository. ** New features *** iCalendar export uses inheritance for TIMEZONE and LOCATION properties Both these properties can be inherited during iCalendar export, depending on the value of ~org-use-property-inheritance~. *** iCalendar export respects a TIMEZONE property Set the TIMEZONE property on an entry to specify a time zone for that entry only during iCalendar export. The property value should be specified as in "Europe/London". *** ~org-attach~ can move directory contents When setting a new directory for an entry, org-attach offers to move files over from the old directory. Using a prefix arg will reset the directory to old, ID based one. *** New Org duration library This new library implements tools to read and print time durations in various formats (e.g., "H:MM", or "1d 2h 3min"...). See ~org-duration-to-minutes~ and ~org-duration-from-minutes~ docstrings. *** Agenda **** New variable : ~org-agenda-show-future-repeats~ **** New variable : ~org-agenda-prefer-last-repeat~ **** New variable : ~org-deadline-past-days~ See docstring for details. **** Binding C-c C-x < for ~org-agenda-set-restriction-lock-from-agenda~ **** New auto-align default setting for =org-agenda-tags-column= =org-agenda-tags-column= can now be set to =auto=, which will automatically align tags to the right edge of the window. This is now the default setting. *** New value for ~org-publish-sitemap-sort-folders~ The new ~ignore~ value effectively allows toggling inclusion of directories in published site-maps. *** Babel **** Scheme: support for tables **** Scheme: new variable: ~org-babel-scheme-null-to~ This new custom option allows you to use an empty list or null symbol to format the table output, initially assigned to ~hlines~. **** Scheme: new header ~:prologue~ A new block code header has been created for Org Babel that enables developers to prepend code to the scheme block being processed. Multiple ~:prologue~ headers can be added each of them using a string with the content to be added. The scheme blocks are prepared by surrounding the code in the block with a let form. The content of the ~:prologue~ headers are prepended before this let form. **** Support for hledger accounting reports added **** Clojure: new setting ~org-babel-clojure-sync-nrepl-timeout~ Creation of a new setting to specify the Cider timeout. By setting the =org-babel-clojure-sync-nrepl-timeout= setting option. The value is in seconds and if set to nil then no timeout will occur. **** Clojure: new header ~:show-process~ A new block code header has been created for Org Babel that enables developers to output the process of an ongoing process into a new window/buffer. You can tell Org Babel to output the process of a running code block. To show that output you only have to specify the =:show-process= option in the code block's header like this: #+begin_example ,#+BEGIN_SRC clojure :results output :show-process t (dotimes [n 10] (println n ".") (Thread/sleep 500)) ,#+END_SRC #+end_example If =:show-process= is specified that way, then when you will run the code using =C-c C-c= a new window will open in Emacs. Everything that is output by the REPL will immediately be added to that new window. When the processing of the code is finished, then the window and its buffer will be closed and the results will be reported in the =#+RESULTS= section. Note that the =:results= parameter's behavior is *not* changed. If =silent= is specified, then no result will be displayed. If =output= is specified then all the output from the window will appears in the results section. If =value= is specified, then only the last returned value of the code will be displayed in the results section. **** Maxima: new headers ~:prologue~ and ~:epilogue~ Babel options ~:prologue~ and ~:epilogue~ have been implemented for Maxima source blocks which prepend and append, respectively, the given code strings. This can be useful for specifying formatting settings which would add clutter to exported code. For instance, you can use this ~:prologue "fpprintprec: 2; linel: 50;"~ for presenting Maxima results in a beamer presentation. **** PlantUML: add support for header arguments [[https://plantuml.com/][Plantuml]] source blocks now support the [[https://orgmode.org/manual/prologue.html#prologue][~:prologue~]], [[https://orgmode.org/manual/epilogue.html#epilogue][~:epilogue~]] and [[https://orgmode.org/manual/var.html#var][~:var~]] header arguments. **** SQL: new engine added ~sqsh~ A new engine was added to support ~sqsh~ command line utility for use against Microsoft SQL Server or Sybase SQL server. More information on ~sqsh~ can be found here: [[https://sourceforge.net/projects/sqsh/][sourceforge/sqsh]] To use ~sqsh~ in an *sql* =SRC_BLK= set the =:engine= like this: #+begin_example ,#+BEGIN_SRC sql :engine sqsh :dbhost my_host :dbuser master :dbpassword pass :database support Select * From Users Where clue > 0 ,#+END_SRC #+end_example **** SQL: new engine added =vertica= A new engine was added to support vsql command line utility for use against HP Vertica. More information on =vsql= can be found here: [[https://my.vertica.com/docs/7.2.x/HTML/index.htm#Authoring/ConnectingToHPVertica/vsql/UsingVsql.htm][my.vertica.com]] To use =vertica= in an sql =SRC_BLK= set the =:engine= like this: #+BEGIN_EXAMPLE ,#+BEGIN_SRC sql :engine vertica :dbhost my_host :dbuser dbadmin :dbpassword pw :database vmart SELECT * FROM nodes; ,#+END_SRC #+END_EXAMPLE **** C++: New header ~:namespaces~ The new ~:namespaces~ export option can be used to specify namespaces to be used within a C++ org source block. Its usage is similar to ~:includes~, in that it can accept multiple, space-separated namespaces to use. This header is equivalent to adding ~using namespace ;~ in the source block. Here is a "Hello World" in C++ using ~:namespaces~: #+begin_example ,#+BEGIN_SRC C++ :results output :namespaces std :includes cout << "Hello World" << endl; ,#+END_SRC #+end_example **** Support for Vala language [[https://wiki.gnome.org/Projects/Vala][Vala]] language blocks support two special header arguments: - ~:flags~ passes arguments to the compiler - ~:cmdline~ passes commandline arguments to the generated executable Support for [[https://orgmode.org/manual/var.html#var][~:var~]] does not exist yet, also there is no [[https://orgmode.org/manual/session.html#session][~:session~]] support because Vala is a compiled language. The Vala compiler binary can be changed via the ~defcustom~ ~org-babel-vala-compiler~. *** New ~function~ scope argument for the Clock Table Added a nullary function that returns a list of files as a possible argument for the scope of the clock table. *** Export **** Implement vernacular table of contents in Markdown exporter Global table of contents are generated using vanilla Markdown syntax instead of HTML. Also #+TOC keyword, including local table of contents, are now supported. **** Add Slovenian translations **** Implement ~org-export-insert-image-links~ This new function is meant to be used in back-ends supporting images as descriptions of links, a.k.a. image links. See its docstring for details. **** New macro : ~{{{n}}}~ This macro creates and increment multiple counters in a document. See manual for details. **** Add global macros through ~org-export-global-macros~ With this variable, one can define macros available for all documents. **** New keyword ~#+EXPORT_FILE_NAME~ Similarly to ~:EXPORT_FILE_NAME:~ property, this keyword allows the user to specify the name of the output file upon exporting the document. This also has an effect on publishing. **** Horizontal rules are no longer ignored in LaTeX table math mode **** Use ~compilation-mode~ for compilation output **** Plain lists accept a new ~:separator~ attribute in Texinfo The new ~:separator~ attribute splits a tag from a description list item into multiple parts. This allows to have two-column tables with multiple entries in the first column. See manual for more details. **** ~latex-environment~ elements support ~caption~ keywords for LaTeX export *** ~org-edit-special~ can edit LaTeX environments Using ~C-c '~ on a LaTeX environment opens a sub-editing buffer. By default, major mode in that buffer is ~latex-mode~, but it can be changed by configuring ~org-src-lang-modes~. *** ~org-list-to-generic~ includes a new property: ~:ifmt~ ~:ifmt~ is a function to be called on the body of each item. See ~org-list-to-generic~ documentation for details. *** New variable : ~org-bibtex-headline-format-function~ This allow to use a different title than entry title. *** ~org-attach~ supports attaching files from URLs Using ~C-c C-a u~ prompts for a URL pointing to a file to be attached to the document. *** New option for ~org-refile-use-outline-path~ ~org-refile-use-outline-path~ now supports the setting ~buffer-name~, which causes refile targets to be prefixed with the buffer’s name. This is particularly useful when used in conjunction with ~uniquify.el~. *** ~org-file-contents~ now allows the FILE argument to be a URL. This allows ~#+SETUPFILE:~ to accept a URL instead of a local file path. The URL contents are auto-downloaded and saved to a temporary cache ~org--file-cache~. A new optional argument ~NOCACHE~ is added to ~org-file-contents~. *** ~org-mode-restart~ now resets the newly added ~org--file-cache~. Using ~C-c C-c~ on any keyword (like ~#+SETUPFILE~) will reset the that file cache. *** New option : ~org-table-duration-hour-zero-padding~ This variable allow computed durations in tables to be zero-padded. *** New mode switch for table formulas : =U= This mode omits seconds in durations. ** Removed functions *** Org Timeline This feature has been removed. Use a custom agenda view, possibly narrowed to current buffer to achieve a similar functionality. *** ~org-agenda-skip-entry-when-regexp-matches~ is obsolete Use ~org-agenda-skip-if~ instead. *** ~org-agenda-skip-subtree-when-regexp-matches~ is obsolete Use ~org-agenda-skip-if~ instead. *** ~org-agenda-skip-entry-when-regexp-matches-in-subtree~ is obsolete Use ~org-agenda-skip-if~ instead. *** ~org-minutes-to-clocksum-string~ is obsolete Use ~org-duration-from-minutes~ instead. *** ~org-hh:mm-string-to-minutes~ is obsolete Use ~org-duration-to-minutes~ instead. *** ~org-duration-string-to-minutes~ is obsolete Use ~org-duration-to-minutes~ instead. *** ~org-gnus-nnimap-cached-article-number~ is removed. This function relied on ~nnimap-group-overview-filename~, which was removed from Gnus circa September 2010. ** Removed options *** ~org-agenda-repeating-timestamp-show-all~ is removed. For an equivalent to a nil value, set ~org-agenda-show-future-repeats~ to nil and ~org-agenda-prefer-last-repeat~ to =t=. *** ~org-gnus-nnimap-query-article-no-from-file~ is removed. This variable has no effect, as it was relying on a function that was removed from Gnus circa September 2010. *** ~org-usenet-links-prefer-google~ is obsolete. Use ~org-gnus-prefer-web-links~ instead. *** ~org-publish-sitemap-file-entry-format~ is deprecated One can provide new ~:sitemap-format-entry~ property for a function equivalent to the removed format string. *** ~org-enable-table-editor~ is removed. Setting it to a nil value broke some other features (e.g., speed keys). *** ~org-export-use-babel~ cannot be set to ~inline-only~ The variable is now a boolean. *** ~org-texinfo-def-table-markup~ is obsolete Use ~org-texinfo-table-default-markup~ instead. ** New functions *** ~org-publish-find-property~ This function can be used as a tool to format entries in a site-map, in addition to ~org-publish-find-title~ and ~org-publish-find-date~. *** ~org-list-to-org~ It is the reciprocal of ~org-list-to-lisp~, which see. *** ~org-agenda-set-restriction-lock-from-agenda~ Call ~org-agenda-set-restriction-lock~ from the agenda. ** Miscellaneous *** The Library of Babel now on Worg The library-of-babel.org used to be accessible from the =doc/= directory, distributed with Org’s core. It is now accessible from the Worg community-driven documentation [[https://orgmode.org/worg/library-of-babel.html][here]]. If you want to contribute to it, please see [[https://orgmode.org/worg/org-contribute.html][how to contribute]]. *** Allow multiple columns view Columns view is not limited to a single buffer anymore. *** Org Attach obeys ~dired-dwim-target~ When a Dired buffer is opened next to the Org document being edited, the prompt for file to attach can start in the Dired buffer's directory if `dired-dwim-target' in non-nil. *** ~org-fill-paragraph~ can now fill a whole region *** More specific anniversary descriptions Anniversary descriptions (used in the agenda view, for instance) include the point in time, when the anniversary appears. This is, in its most general form, just the date of the anniversary. Or more specific terms, like "today", "tomorrow" or "in n days" are used to describe the time span. This feature allows to automatically change the description of an anniversary, depending on if it occurs in the next few days or far away in the future. *** Computed dates in tables appear as inactive time stamps *** Save point before opening a file with an unknown search option When following a file link with a search option (e.g., =::#custom-id=) that doesn't exist in the target file, save position before raising an error. As a consequence, it is possible to jump back to the original document with ~org-mark-ring-goto~ (default binding =C-c &=). *** ~org-get-heading~ accepts two more optional arguments See docstring for details. *** New option ~org-babel-uppercase-example-markers~ This variable is a ~defcustom~ and replaces the variable ~org-babel-capitalize-example-region-markers~, which is a ~defvar~ and is now obsolete. *** =INCLUDE= keywords in commented trees are now ignored. *** Default value for ~org-texinfo-text-markup-alist~ changed. Now ~=...=~ markup uses ~@samp{}~ instead of ~@verb{}~. You can use ~@verb{}~ again by customizing the variable. *** Texinfo exports example blocks as ~@example~ *** Texinfo exports inline source blocks as ~@code{}~ *** Texinfo default table markup is ~@asis~ It used to be ~@samp~ but ~@asis~ is neutral and, therefore, more suitable as a default value. *** Texinfo default process includes ~--no-split~ option *** New entities : ~\dollar~ and ~\USD~ *** Support for date style URLs in =org-protocol://open-source= URLs like =https://cool-blog.com/2017/05/20/cool-post/= are covered by rewrite rules. *** Add (C) =COMMENT= support to ~org-structure-template-alist~ * Version 9.0 ** Incompatible changes *** Emacs 23 support has been dropped From now on, Org expects at least Emacs 24.3, although Emacs 24.4 or above is suggested. *** XEmacs support has been dropped Incomplete compatibility layer with XEmacs has been removed. If you want to take over maintenance of this compatibility, please contact our mailing list. *** New syntax for export blocks Export blocks are explicitly marked as such at the syntax level to disambiguate their parsing from special blocks. The new syntax is #+BEGIN_SRC org ,#+BEGIN_EXPORT backend ... ,#+END_EXPORT #+END_SRC instead of #+BEGIN_SRC org ,#+BEGIN_backend ... ,#+END_backend #+END_SRC As a consequence, =INCLUDE= keywords syntax is modified, e.g., #+BEGIN_SRC org ,#+INCLUDE: "file.org" HTML #+END_SRC becomes #+BEGIN_SRC org ,#+INCLUDE: "file.org" export html #+END_SRC The following function repairs export blocks and =INCLUDE= keywords using previous syntax: #+BEGIN_SRC emacs-lisp (defun org-repair-export-blocks () "Repair export blocks and INCLUDE keywords in current buffer." (interactive) (when (eq major-mode 'org-mode) (let ((case-fold-search t) (back-end-re (regexp-opt '("HTML" "ASCII" "LATEX" "ODT" "MARKDOWN" "MD" "ORG" "MAN" "BEAMER" "TEXINFO" "GROFF" "KOMA-LETTER") t))) (org-with-wide-buffer (goto-char (point-min)) (let ((block-re (concat "^[ \t]*#\\+BEGIN_" back-end-re))) (save-excursion (while (re-search-forward block-re nil t) (let ((element (save-match-data (org-element-at-point)))) (when (eq (org-element-type element) 'special-block) (save-excursion (goto-char (org-element-property :end element)) (save-match-data (search-backward "_")) (forward-char) (insert "EXPORT") (delete-region (point) (line-end-position))) (replace-match "EXPORT \\1" nil nil nil 1)))))) (let ((include-re (format "^[ \t]*#\\+INCLUDE: .*?%s[ \t]*$" back-end-re))) (while (re-search-forward include-re nil t) (let ((element (save-match-data (org-element-at-point)))) (when (and (eq (org-element-type element) 'keyword) (string= (org-element-property :key element) "INCLUDE")) (replace-match "EXPORT \\1" nil nil nil 1))))))))) #+END_SRC Moreover, ~:export-block~ keyword used in ~org-export-define-backend~ and ~org-export-define-derived-backend~ is no longer used and needs to be removed. *** Footnotes changes **** [1]-like constructs are not valid footnotes Using =[1]= as a footnote was already discouraged in the manual, since it introduced too many false-positives in many Org documents. These constructs are now unsupported. If you used =[N]= in some of your documents, consider turning them into =[fn:N]=. **** /Org Footnote/ library doesn't handle non-Org buffers Commands for footnotes in an Org document no longer try to do something in non-Org ones. If you need to have footnotes there, consider using the =footnote.el= library, shipped with Emacs. In particular, ~org-footnote-tag-for-non-org-mode-files~ no longer exists. *** ~org-file-apps~ no longer accepts S-expressions as commands The variable now accepts functions of two arguments instead of plain S-expressions. Replacing an S-expression with an appropriate function is straightforward. For example : ("pdf" . (foo)) becomes : ("pdf" . (lambda (file link) (foo))) *** The ~{{{modification-time}}}~ macro can get time via =vc= The modification time will be determined via =vc.el= if the second argument is non-nil. See the manual for details. *** Preparation and completion functions in publishing projects change signature Preparation and completion functions are now called with an argument, which is the project property list. It used to be dynamically scoped through the ~project-plist~ variable. *** Old Babel header properties are no longer supported Using header arguments as property names is no longer possible. As such, the following #+BEGIN_EXAMPLE ,* Headline :PROPERTIES: :exports: code :var: a=1 b=2 :var+: c=3 :END: #+END_EXAMPLE should be written instead #+BEGIN_EXAMPLE ,* Headline :PROPERTIES: :header-args: :exports code :header-args+: :var a=1 b=2 :header-args+: :var c=3 :END: #+END_EXAMPLE Please note that, however, old properties were defined at the source block definition. Current ones are defined where the block is called. ** New features *** ~org-eww~ has been moved into core *** New org-protocol key=value syntax Org-protocol can now handle query-style parameters such as: #+begin_example org-protocol://store-link?url=http:%2F%2Flocalhost%2Findex.html&title=The%20title org-protocol://capture?template=x&title=Hello&body=World&url=http:%2F%2Fexample.com #+end_example Old-style links such as : org-protocol://store-link:/http:%2F%2Flocalhost%2Findex.html/The%20title continue to be supported. If you have defined your own handler functions for ~org-protocol-protocol-alist~, change them to accept either a property list (for new-style links) or a string (for old-style links). Use ~org-protocol-parse-parameters~ to convert old-style links into property lists. *** New Org linter library ~org-lint~ can check syntax and report common issues in Org documents. *** New option ~date-tree-last~ for ~org-agenda-insert-diary-strategy~ When ~org-agenda-insert-diary-strategy~ is set to ~date-tree-last~, diary entries are added to last in the date tree. *** New ~vbar~ entity ~\vbar~ or ~\vbar{}~ will be exported unconditionally as a =|=, unlike to existing ~\vert~, which is expanded as ~|~ when using a HTML derived export back-end. *** Export **** New =#+latex_compiler= keyword to set LaTeX compiler. PDFLaTeX, XeLaTeX, and LuaLaTeX are supported. See the manual for details. **** New option ~org-export-with-broken-links~ This option tells the export process how to behave when encountering a broken internal link. See its docstring for more information. **** Attributes support in custom language environments for LaTeX export Custom language environments for LaTeX export can now define the string to be inserted during export, using attributes to indicate the position of the elements. See variable ~org-latex-custom-lang-environments~ for more details. **** New Texinfo ~options~ attribute on special blocks Using ~:options~ as a Texinfo attribute, it is possible to add information to custom environments. See manual for details. **** New HTML ~id~ attributes on special, example and quote blocks If the block has a =#+NAME:= attribute assigned, then the HTML element will have an ~id~ attribute with that name in the HTML export. This enables one to create links to these elements in other places, e.g., ~text~. **** Listings with captions are now numbered in HTML export The class associated to the numbering is "listing-number". If you don't want these blocks to be numbered, as it was the case until now, You may want to add ~.listing-number { display: none; }~ to the CSS used. **** Line Numbering in SRC/EXAMPLE blocks support arbitrary start number The ~-n~ option to ~SRC~ and ~EXAMPLE~ blocks can now take a numeric argument to specify the staring line number for the source or example block. The ~+n~ option can now take a numeric argument that will be added to the last line number from the previous block as the starting point for the SRC/EXAMPLE block. #+BEGIN_SRC org ,#+BEGIN_SRC emacs-lisp -n 20 ;; this will export with line number 20 (message "This is line 21") ,#+END_SRC ,#+BEGIN_SRC emacs-lisp +n 10 ;; This will be listed as line 31 (message "This is line 32") ,#+END_SRC #+END_SRC **** Allow toggling center for images in LaTeX export With the global variable ~org-latex-images-centered~ or the local attribute ~:center~ it is now possible to center an image in LaTeX export. **** Default CSS class ~org-svg~ for SVG images in HTML export SVG images exported in HTML are now by default assigned a CSS class ~org-svg~ if no CSS class is specified with the ~:class~ attribute. By default, the CSS styling of class ~org-svg~ specifies an image width of 90\thinsp{}% of the container the image. **** Markdown footnote export customization Variables ~org-md-footnotes-section~ and ~org-md-footnote-format~ introduced for =ox-md.el=. Both new variables define template strings which can be used to customize the format of the exported footnotes section and individual footnotes, respectively. *** Babel **** Blocks with coderefs labels can now be evaluated The labels are removed prior to evaluating the block. **** Support for Lua language **** Support for SLY in Lisp blocks See ~org-babel-lisp-eval-fn~ to activate it. **** Support for Stan language New ob-stan.el library. Evaluating a Stan block can produce two different results. 1. Dump the source code contents to a file. This file can then be used as a variable in other blocks, which allows interfaces like RStan to use the model. 2. Compile the contents to a model file. This provides access to the CmdStan interface. To use this, set ~org-babel-stan-cmdstan-directory~ and provide a ~:file~ argument that does not end in ".stan". For more information and usage examples, visit https://orgmode.org/worg/org-contrib/babel/languages/ob-doc-stan.html **** Support for Oracle databases via ~sqlplus~ =ob-sql= library supports running SQL blocks against an Oracle database using ~sqlplus~. Use with properties like this (all mandatory): #+BEGIN_EXAMPLE :engine oracle :dbhost :dbport <1521> :dbuser :database :dbpassword #+END_EXAMPLE **** Improved support to Microsoft SQL Server via ~sqlcmd~ =ob-sql= library removes support to the ~msosql~ engine which uses the deprecated ~osql~ command line tool, and replaces it with ~mssql~ engine which uses the ~sqlcmd~ command line tool. Use with properties like this: #+BEGIN_EXAMPLE :engine mssql :dbhost :dbuser :dbpassword :database #+END_EXAMPLE If you want to use the *trusted connection* feature, omit *both* the =dbuser= and =dbpassword= properties and add =cmdline -E= to the properties. If your Emacs is running in a Cygwin environment, the =ob-sql= library can pass the converted path to the =sqlcmd= tool. **** Improved support of header arguments for postgresql The postgresql engine in a sql code block now supports ~:dbport~ and ~:dbpassword~ as header arguments. **** Support for additional plantuml output formats The support for output formats of [[https://plantuml.com/][plantuml]] has been extended to now include: All Diagrams: - png :: - svg :: - eps :: - pdf :: - vdx :: - txt :: ASCII art - utxt :: ASCII art using unicode characters Class Diagrams: - xmi :: - html :: State Diagrams: - scxml :: The output formats are determined by the file extension specified using the :file property, e.g.: #+begin_src plantuml :file diagram.png @startuml Alice -> Bob: Authentication Request Bob --> Alice: Authentication Response Alice -> Bob: Another authentication Request Alice <-- Bob: another authentication Response @enduml #+end_src Please note that *pdf* *does not work out of the box* and needs additional setup in addition to plantuml. See [[https://plantuml.com/pdf.html]] for details and setup information. *** Rewrite of radio lists Radio lists, i.e, Org plain lists in foreign buffers, have been rewritten to be on par with Radio tables. You can use a large set of parameters to control how a given list should be rendered. See manual for details. *** org-bbdb-anniversaries-future Used like ~org-bbdb-anniversaries~, it provides a few days warning for upcoming anniversaries (default: 7 days). *** Clear non-repeated SCHEDULED upon repeating a task If the task is repeated, and therefore done at least one, scheduling information is no longer relevant. It is therefore removed. See [[git:481719fbd5751aaa9c672b762cb43aea8ee986b0][commit message]] for more information. *** Support for ISO week trees ISO week trees are an alternative date tree format that orders entries by ISO week and not by month. For example: : * 2015 : ** 2015-W35 : ** 2015-W36 : *** 2015-08-31 Monday They are supported in org-capture via ~file+weektree~ and ~file+weektree+prompt~ target specifications. *** Accept ~:indent~ parameter when capturing column view When defining a "columnview" dynamic block, it is now possible to add an :indent parameter, much like the one in the clock table. On the other hand, stars no longer appear in an ITEM field. *** Columns view **** ~org-columns~ accepts a prefix argument When called with a prefix argument, ~org-columns~ apply to the whole buffer unconditionally. **** New variable : ~org-agenda-view-columns-initially~ The variable used to be a ~defvar~, it is now a ~defcustom~. **** Allow custom summaries It is now possible to add new summary types, or override those provided by Org by customizing ~org-columns-summary-types~, which see. **** Allow multiple summaries for any property Columns can now summarize the same property using different summary types. *** Preview LaTeX snippets in buffers not visiting files *** New option ~org-attach-commit~ When non-nil, commit attachments with git, assuming the document is in a git repository. *** Allow conditional case-fold searches in ~org-occur~ When set to ~smart~, the new variable ~org-occur-case-fold-search~ allows to mimic =isearch.el=: if the regexp searched contains any upper case character (or character class), the search is case sensitive. Otherwise, it is case insensitive. *** More robust repeated =ox-latex= footnote handling Repeated footnotes are now numbered by referring to a label in the first footnote. *** The ~org-block~ face is inherited by ~src-blocks~ This works also when =org-src-fontify-natively= is non-nil. It is also possible to specify per-languages faces. See =org-src-block-faces= and the manual for details. *** Links are now customizable Links can now have custom colors, tooltips, keymaps, display behavior, etc. Links are now centralized in ~org-link-parameters~. ** New functions *** ~org-next-line-empty-p~ It replaces the deprecated ~next~ argument to ~org-previous-line-empty-p~. *** ~org-show-children~ It is a faster implementation of ~outline-show-children~. ** Removed functions *** ~org-agenda-filter-by-tag-refine~ has been removed. Use ~org-agenda-filter-by-tag~ instead. *** ~org-agenda-todayp~ is deprecated. Use ~org-agenda-today-p~ instead. *** ~org-babel-get-header~ is removed. Use ~org-babel--get-vars~ or ~assq~ instead, as applicable. *** ~org-babel-trim~ is deprecated. Use ~org-trim~ instead. *** ~org-element-remove-indentation~ is deprecated. Use ~org-remove-indentation~ instead. *** ~org-image-file-name-regexp~ is deprecated Use ~image-file-name-regexp~ instead. The never-used-in-core ~extensions~ argument has been dropped. *** ~org-list-parse-list~ is deprecated Use ~org-list-to-lisp~ instead. *** ~org-on-heading-p~ is deprecated A comment to this effect was in the source code since 7.8.03, but now a byte-compiler warning will be generated as well. *** ~org-table-p~ is deprecated Use ~org-at-table-p~ instead. *** ~org-table-recognize-table.el~ is deprecated It was not called by any org code since 2010. *** Various reimplementations of cl-lib functions are deprecated The affected functions are: - ~org-count~ - ~org-remove-if~ - ~org-remove-if-not~ - ~org-reduce~ - ~org-every~ - ~org-some~ Additionally, ~org-sublist~ is deprecated in favor of ~cl-subseq~. Note the differences in indexing conventions: ~org-sublist~ is 1-based and end-inclusive; ~cl-subseq~ is 0-based and end-exclusive. ** Removed options *** Remove all options related to ~ido~ or ~iswitchb~ This includes ~org-completion-use-iswitchb~ and ~org-completion-use-ido~. Instead Org uses regular functions, e.g., ~completion-read~ so as to let those libraries operate. *** Remove ~org-list-empty-line-terminates-plain-lists~ Two consecutive blank lines always terminate all levels of current plain list. *** ~fixltx2e~ is removed from ~org-latex-default-packages-alist~ fixltx2e is obsolete, see LaTeX News 22. ** Miscellaneous *** Add Icelandic smart quotes *** Allow multiple receiver locations in radio tables and lists *** Allow angular links within link descriptions It is now allowed to write, e.g., ~[[http:orgmode.org][]]~ as an equivalent to ~[[http:orgmode.org][file:unicorn.png]]~. The advantage of the former is that spaces are allowed within the path. *** Beamer export back-ends uses ~org-latex-prefer-user-labels~ *** ~:preparation-function~ called earlier during publishing Functions in this list are called before any file is associated to the current project. Thus, they can be used to generate to be published Org files. *** Function ~org-remove-indentation~ changes. The new algorithm doesn't remove TAB characters not used for indentation. *** Secure placeholders in capture templates Placeholders in capture templates are no longer expanded recursively. However, ~%(...)~ constructs are expanded very late, so you can fill the contents of the S-exp with the replacement text of non-interactive placeholders. As before, interactive ones are still expanded as the very last step, so the previous statement doesn't apply to them. Note that only ~%(...)~ placeholders initially present in the template, or introduced using a file placeholder, i.e., ~%[...]~ are expanded. This prevents evaluating potentially malicious code when another placeholder, e.g., ~%i~ expands to a S-exp. *** Links stored by ~org-gnus-store-link~ in nnir groups Since gnus nnir groups are temporary, ~org-gnus-store-link~ now refers to the article's original group. *** ~org-babel-check-confirm-evaluate~ is now a function instead of a macro The calling convention has changed. *** HTML export table row customization changes Variable ~org-html-table-row-tags~ has been split into ~org-html-table-row-open-tag~ and ~org-html-table-row-close-tag~. Both new variables can be either a string or a function which will be called with 6 parameters. *** =ITEM= special property returns headline without stars *** Rename ~org-insert-columns-dblock~ into ~org-columns-insert-dblock~ The previous name is, for the time being, kept as an obsolete alias. *** ~org-trim~ can preserve leading indentation. When setting a new optional argument to a non-nil value, ~org-trim~ preserves leading indentation while removing blank lines at the beginning of the string. The behavior is identical for white space at the end of the string. *** Function ~org-info-export~ changes. HTML links created from certain info links now point to =gnu.org= URL's rather than just to local files. For example info links such as =info:emacs#List Buffers= used to be converted to HTML links like this: : emacs#List Buffers where local file =emacs.html= is referenced. For most folks this file does not exist. Thus the new behavior is to generate this HTML link instead: : emacs#List Buffers All emacs related info links are similarly translated plus few other =gnu.org= manuals. *** Repeaters with a ~++~ interval and a time can be shifted to later today Previously, if a recurring task had a timestamp of ~<2016-01-01 Fri 20:00 ++1d>~ and was completed on =2016-01-02= at =08:00=, the task would skip =2016-01-02= and would be rescheduled for =2016-01-03=. Timestamps with ~++~ cookies and a specific time will now shift to the first possible future occurrence, even if the occurrence is later the same day the task is completed. (Timestamps already in the future are still shifted one time further into the future.) *** ~org-mobile-action-alist~ is now a defconst It used to be a defcustom, with a warning that it shouldn't be modified anyway. *** ~file+emacs~ and ~file+sys~ link types are deprecated They are still supported in Org 9.0 but will eventually be removed in a later release. Use ~file~ link type along with universal arguments to force opening it in either Emacs or with system application. *** New defcustom ~org-babel-J-command~ stores the j command *** New defalias ~org-babel-execute:j~ Allows J source blocks be indicated by letter j. Previously the indication letter was solely J. *** ~org-open-line~ ignores tables at the very beginning of the buffer When ~org-special-ctrl-o~ is non-nil, it is impractical to create a blank line above a table at the beginning of the document. Now, as a special case, ~org-open-line~ behaves normally in this situation. *** ~org-babel-hash-show-time~ is now customizable The experimental variable used to be more or less confidential, as a ~defvar~. *** New ~:format~ property to parsed links It defines the format of the original link. Possible values are: ~plain~, ~bracket~ and ~angle~. * Version 8.3 ** Incompatible changes *** Properties drawers syntax changes Properties drawers are now required to be located right after a headline and its planning line, when applicable. It will break some documents as TODO states changes were sometimes logged before the property drawer. The following function will repair them: #+BEGIN_SRC emacs-lisp (defun org-repair-property-drawers () "Fix properties drawers in current buffer. Ignore non Org buffers." (when (eq major-mode 'org-mode) (org-with-wide-buffer (goto-char (point-min)) (let ((case-fold-search t) (inline-re (and (featurep 'org-inlinetask) (concat (org-inlinetask-outline-regexp) "END[ \t]*$")))) (org-map-entries (lambda () (unless (and inline-re (org-looking-at-p inline-re)) (save-excursion (let ((end (save-excursion (outline-next-heading) (point)))) (forward-line) (when (org-looking-at-p org-planning-line-re) (forward-line)) (when (and (< (point) end) (not (org-looking-at-p org-property-drawer-re)) (save-excursion (and (re-search-forward org-property-drawer-re end t) (eq (org-element-type (save-match-data (org-element-at-point))) 'drawer)))) (insert (delete-and-extract-region (match-beginning 0) (min (1+ (match-end 0)) end))) (unless (bolp) (insert "\n")))))))))))) #+END_SRC *** Using "COMMENT" is now equivalent to commenting with "#" If you used "COMMENT" in headlines to prevent a subtree from being exported, you can still do it but all information within the subtree is now commented out, i.e. no #+OPTIONS line will be parsed or taken into account when exporting. If you want to exclude a headline from export while using its contents for setting options, use =:noexport:= (see =org-export-exclude-tags=.) *** =#+CATEGORY= keywords no longer apply partially to document It was possible to use several such keywords and have them apply to the text below until the next one, but strongly deprecated since Org 5.14 (2008). =#+CATEGORY= keywords are now global to the document. You can use node properties to set category for a subtree, e.g., #+BEGIN_SRC org ,* Headline :PROPERTIES: :CATEGORY: some category :END: #+END_SRC *** New variable to control visibility when revealing a location ~org-show-following-heading~, ~org-show-siblings~, ~org-show-entry-below~ and ~org-show-hierarchy-above~ no longer exist. Instead, visibility is controlled through a single variable: ~org-show-context-detail~, which see. *** Replace disputed keys again when reading a date ~org-replace-disputed-keys~ has been ignored when reading date since version 8.1, but the former behavior is restored again. Keybinding for reading date can be customized with a new variable ~org-read-date-minibuffer-local-map~. *** No default title is provided when =TITLE= keyword is missing Skipping =TITLE= keyword no longer provides the current file name, or buffer name, as the title. Instead, simply ignore the title. *** Default bindings of =C-c C-n= and =C-c C-p= changed The key sequences =C-c C-n= and =C-c C-p= are now bound to ~org-next-visible-heading~ and ~org-previous-visible-heading~ respectively, rather than the =outline-mode= versions of these functions. The Org version of these functions skips over inline tasks (and even-level headlines when ~org-odd-levels-only~ is set). *** ~org-element-context~ no longer return objects in keywords ~org-element-context~ used to return objects on some keywords, i.e., =TITLE=, =DATE= and =AUTHOR=. It now returns only the keyword. *** ~org-timer-default-timer~ type changed from number to string If you have, in your configuration, something like =(setq org-timer-default-timer 10)= replace it with =(setq org-timer-default-timer "10")=. *** Functions signature changes The following functions require an additional argument. See their docstring for more information. - ~org-export-collect-footnote-definitions~ - ~org-html-format-headline-function~ - ~org-html-format-inlinetask-function~ - ~org-latex-format-headline-function~ - ~org-latex-format-inlinetask-function~ - ~org-link-search~ ** New features *** Default lexical evaluation of emacs-lisp source blocks Emacs-lisp source blocks in Babel are now evaluated using lexical scoping. There is a new header to control this behavior. The default results in an eval with lexical scoping. :lexical yes This turns lexical scoping off in the eval (the former behavior). :lexical no This uses the lexical environment with x=42 in the eval. :lexical '((x . 42)) *** Behavior of ~org-return~ changed If point is before or after the headline title, insert a new line without changing the headline. *** Hierarchies of tags The functionality of nesting tags in hierarchies is added to Org mode. This is the generalization of what was previously called "Tag groups" in the manual. That term is now changed to "Tag hierarchy". The following in-buffer definition: #+BEGIN_SRC org ,#+TAGS: [ Group : SubOne SubTwo ] ,#+TAGS: [ SubOne : SubOne1 SubOne2 ] ,#+TAGS: [ SubTwo : SubTwo1 SubTwo2 ] #+END_SRC Should be seen as the following tree of tags: - Group - SubOne - SubOne1 - SubOne2 - SubTwo - SubTwo1 - SubTwo2 Searching for "Group" should return all tags defined above. Filtering on SubOne filters also it's sub-tags. Etc. There is no limit on the depth for the tag hierarchy. *** Additional syntax for non-unique grouptags Additional syntax is defined for grouptags if the tags in the group don't have to be distinct on a heading. Grouptags had to previously be defined with { }. This syntax is already used for exclusive tags and Grouptags need their own, non-exclusive syntax. This behavior is achieved with [ ]. Note: { } can still be used also for Grouptags but then only one of the given tags can be used on the headline at the same time. Example: [ group : sub1 sub2 ] #+BEGIN_SRC org ,* Test :sub1:sub2: #+END_SRC This is a more general case than the already existing syntax for grouptags; { }. *** Define regular expression patterns as tags Tags can be defined as grouptags with regular expressions as "sub-tags". The regular expressions in the group must be marked up within { }. Example use: : #+TAGS: [ Project : {P@.+} ] Searching for the tag Project will now list all tags also including regular expression matches for P@.+. This is good for example for projects tagged with a common identifier, i.e. P@2014_OrgTags. *** Filtering in the agenda on grouptags (Tag hierarchies) Filtering in the agenda on grouptags filters all of the related tags. Except if a filter is applied with a (double) prefix-argument. Filtering in the agenda on subcategories does not filter the "above" levels anymore. If a grouptag contains a regular expression the regular expression is also used as a filter. *** Minor refactoring of ~org-agenda-filter-by-tag~ Now uses the argument ARG and optional argument exclude instead of strip and narrow. ARG because the argument has multiple purposes and makes more sense than strip now. The term "narrowing" is changed to exclude. The main purpose is for the function to make more logical sense when filtering on tags now when tags can be structured in hierarchies. *** Babel: support for sed scripts Thanks to Bjarte Johansen for this feature. *** Babel: support for Processing language New ob-processing.el library. This library implements necessary functions for implementing editing of Processing code blocks, viewing the resulting sketches in an external viewer, and HTML export of the sketches. Check the documentation for more details. Thanks to Jarmo Hurri for this feature. *** New behavior for ~org-toggle-latex-fragment~ The new behavior is the following: - With a double prefix argument or with a single prefix argument when point is before the first headline, toggle overlays in the whole buffer; - With a single prefix argument, toggle overlays in the current subtree; - On latex code, toggle overlay at point; - Otherwise, toggle overlays in the current section. *** Additional markup with =#+INCLUDE= keyword The content of the included file can now be optionally marked up, for instance as HTML. See the documentation for details. *** File links with =#+INCLUDE= keyword Objects can be extracted via =#+INCLUDE= using file links. It is possible to include only the contents of the object. See manual for more information. *** Drawers do not need anymore to be referenced in =#+DRAWERS= One can use a drawer without listing it in the =#+DRAWERS= keyword, which is now obsolete. As a consequence, this change also deprecates ~org-drawers~ variable. *** ~org-edit-special~ can edit export blocks Using C-c ' on an export block now opens a sub-editing buffer. Major mode in that buffer is determined by export backend name (e.g., "latex" \to "latex-mode"). You can define exceptions to this rule by configuring ~org-src-lang-modes~, which see. *** Additional =:hline= processing to ob-shell If the argument =:hlines yes= is present in a babel call, an optional argument =:hlines-string= can be used to define a string to use as a representation for the lisp symbol ='hline= in the shell program. The default is =hline=. *** Markdown export supports switches in source blocks For example, it is now possible to number lines using the =-n= switch in a source block. *** New option in ASCII export Plain lists can have an extra margin by setting ~org-ascii-list-margin~ variable to an appropriate integer. *** New blocks in ASCII export ASCII export now supports =#+BEGIN_JUSTIFYRIGHT= and =#+BEGIN_JUSTIFYLEFT= blocks. See documentation for details. *** More back-end specific publishing options The number of publishing options specific to each back-end has been increased. See manual for details. *** Export inline source blocks Inline source code was used to be removed upon exporting. They are now handled as standard code blocks, i.e., the source code can appear in the output, depending on the parameters. *** Extend ~org-export-first-sibling-p~ and ~org-export-last-sibling-p~ These functions now support any element or object, not only headlines. *** New function: ~org-export-table-row-in-header-p~ *** New function: ~org-export-get-reference~ *** New function: ~org-element-lineage~ This function deprecates ~org-export-get-genealogy~. It also provides more features. See docstring for details. *** New function: ~org-element-copy~ *** New filter: ~org-export-filter-body-functions~ Functions in this filter are applied on the body of the exported document, before wrapping it within the template. *** New :environment parameter when exporting example blocks to LaTeX : #+ATTR_LATEX: :environment myverbatim : #+BEGIN_EXAMPLE : This sentence is false. : #+END_EXAMPLE will be exported using =@samp(myverbatim)= instead of =@samp(verbatim)=. *** Various improvements on radio tables Radio tables feature now relies on Org's export framework ("ox.el"). ~:no-escape~ parameter no longer exists, but additional global parameters are now supported: ~:raw~, ~:backend~. Moreover, there are new parameters specific to some pre-defined translators, e.g., ~:environment~ and ~:booktabs~ for ~orgtbl-to-latex~. See translators docstrings (including ~orgtbl-to-generic~) for details. *** Non-floating minted listings in LaTeX export It is not possible to specify =#+attr_latex: :float nil= in conjunction with source blocks exported by the minted package. *** Field formulas can now create columns as needed Previously, evaluating formulas that referenced out-of-bounds columns would throw an error. A new variable ~org-table-formula-create-columns~ was added to adjust this behavior. It is now possible to silently add new columns, to do so with a warning or to explicitly ask the user each time. *** ASCII plot Ability to plot values in a column through ASCII-art bars. See manual for details. *** New hook: ~org-archive-hook~ This hook is called after successfully archiving a subtree, with point on the original subtree, not yet deleted. *** New option: ~org-attach-archive-delete~ When non-nil, attachments from archived subtrees are removed. *** New option: ~org-latex-caption-above~ This variable generalizes ~org-latex-table-caption-above~, which is now deprecated. In addition to tables, it applies to source blocks, special blocks and images. See docstring for more information. *** New option: ~org-latex-prefer-user-labels~ See the docstring for more information. *** Export unnumbered headlines Headlines, for which the property ~UNNUMBERED~ is non-nil, are now exported without section numbers irrespective of their levels. The property is inherited by children. *** Tables can be sorted with an arbitrary function It is now possible to specify a function, both programmatically, through a new optional argument, and interactively with ~f~ or ~F~ keys, to sort a table. *** Table of contents can be local to a section The ~TOC~ keywords now accepts an optional ~local~ parameter. See manual for details. *** Countdown timers can now be paused ~org-timer-pause-time~ now pauses and restarts both relative and countdown timers. *** New option ~only-window~ for ~org-agenda-window-setup~ When ~org-agenda-window-setup~ is set to ~only-window~, the agenda is displayed as the sole window of the current frame. *** ~{{{date}}}~ macro supports optional formatting argument It is now possible to supply and optional formatting argument to ~{{{date}}}~. See manual for details. *** ~{{{property}}}~ macro supports optional search argument It is now possible to supply an optional search option to ~{{{property}}}~ in order to retrieve remote properties optional. See manual for details. *** New option ~org-export-with-title~ It is possible to suppress the title insertion with ~#+OPTIONS: title:nil~ or globally using the variable ~org-export-with-title~. *** New entities family: "\_ " "\_ " are used to insert up to 20 contiguous spaces in various back-ends. In particular, this family can be used to introduce leading spaces within table cells. *** New MathJax configuration options Org uses the MathJax CDN by default. See the manual and the docstring of ~org-html-mathjax-options~ for details. *** New behavior in `org-export-options-alist' When defining a back-end, it is now possible to specify to give `parse' behavior on a keyword. It is equivalent to call `org-element-parse-secondary-string' on the value. However, parsed =KEYWORD= is automatically associated to an =:EXPORT_KEYWORD:= property, which can be used to override the keyword value during a subtree export. Moreover, macros are expanded in such keywords and properties. *** Viewport support in html export Viewport for mobile-optimized website is now automatically inserted when exporting to html. See ~org-html-viewport~ for details. *** New ~#+SUBTITLE~ export keyword Org can typeset a subtitle in some export backends. See the manual for details. *** Remotely edit a footnote definition Calling ~org-edit-footnote-reference~ (C-c ') on a footnote reference allows to edit its definition, as long as it is not anonymous, in a dedicated buffer. It works even if buffer is currently narrowed. *** New function ~org-delete-indentation~ bound to ~M-^~ Work as ~delete-indentation~ unless at heading, in which case text is added to headline text. *** Support for images in Texinfo export ~Texinfo~ back-end now handles images. See the manual for details. *** Support for captions in Texinfo export Tables and source blocks can now have captions. Additionally, lists of tables and lists of listings can be inserted in the document with =#+TOC= keyword. *** Countdown timer support hh:mm:ss format In addition to setting countdown timers in minutes, they can also be set using the hh:mm:ss format. *** Extend ~org-clone-subtree-with-time-shift~ ~org-clone-subtree-with-time-shift~ now accepts 0 as an argument for the number of clones, which removes the repeater from the original subtree and creates one shifted, repeating clone. *** New time block for clock tables: ~untilnow~ It encompasses all past closed clocks. *** Support for the ~polyglossia~ LaTeX package See the docstring of ~org-latex-classes~ and ~org-latex-guess-polyglossia-language~ for details. *** None-floating tables, graphics and blocks can have captions *** `org-insert-heading' can be forced to insert top-level headline ** Removed functions *** Removed function ~org-translate-time~ Use ~org-timestamp-translate~ instead. *** Removed function ~org-beamer-insert-options-template~ This function inserted a Beamer specific template at point or in current subtree. Use ~org-export-insert-default-template~ instead, as it provides more features and covers all export back-ends. It is also accessible from the export dispatcher. *** Removed function ~org-timer-cancel-timer~ ~org-timer-stop~ now stops both relative and countdown timers. *** Removed function ~org-export-solidify-link-text~ This function, being non-bijective, introduced bug in internal references. Use ~org-export-get-reference~ instead. *** Removed function ~org-end-of-meta-data-and-drawers~ The function is superseded by ~org-end-of-meta-data~, called with an optional argument. *** Removed functions ~org-table-colgroup-line-p~, ~org-table-cookie-line-p~ These functions were left-over from pre 8.0 era. They are not correct anymore. Since they are not needed, they have no replacement. ** Removed options *** ~org-list-empty-line-terminates-plain-lists~ is deprecated It will be kept in code base until next release, for backward compatibility. If you need to separate consecutive lists with blank lines, always use two of them, as if this option was nil (default value). *** ~org-export-with-creator~ is a boolean Special ~comment~ value is no longer allowed. It is possible to use a body filter to add comments about the creator at the end of the document instead. *** Removed option =org-html-use-unicode-chars= Setting this to non-nil was problematic as it converted characters everywhere in the buffer, possibly corrupting URLs. *** Removed option =org-babel-sh-command= This undocumented option defaulted to the value of =shell-file-name= at the time of loading =ob-shell=. The new behavior is to use the value of =shell-file-name= directly when the shell language is =shell=. To chose a different shell, either customize =shell-file-name= or bind this variable locally. *** Removed option =org-babel-sh-var-quote-fmt= This undocumented option was supposed to provide different quoting styles when changing the shell type. Changing the shell type can now be done directly from the source block and the quoting style has to be compatible across all shells, so a customization doesn't make sense anymore. The chosen hard coded quoting style conforms to POSIX. *** Removed option ~org-insert-labeled-timestamps-at-point~ Setting this option to anything else that the default value (nil) would create invalid planning info. This dangerous option is now removed. *** Removed option ~org-koma-letter-use-title~ Use org-export-with-title instead. See also below. *** Removed option ~org-entities-ascii-explanatory~ This variable has no effect since Org 8.0. *** Removed option ~org-table-error-on-row-ref-crossing-hline~ This variable has no effect since August 2009. *** Removed MathML-related options from ~org-html-mathjax-options~ MathJax automatically chooses the best display technology based on the end-users browser. You may force initial usage of MathML via ~org-html-mathjax-template~ or by setting the ~path~ property of ~org-html-mathjax-options~. *** Removed comment-related filters ~org-export-filter-comment-functions~ and ~org-export-filter-comment-block-functions~ variables do not exist anymore. ** Miscellaneous *** Strip all meta data from ITEM special property ITEM special property does not contain TODO, priority or tags anymore. *** File names in links accept are now compatible with URI syntax Absolute file names can now start with =///= in addition to =/=. E.g., =[[file:///home/me/unicorn.jpg]]=. *** Footnotes in included files are now local to the file As a consequence, it is possible to include multiple Org files with footnotes in a master document without being concerned about footnote labels colliding. *** Mailto links now use regular URI syntax This change deprecates old Org syntax for mailto links: =mailto:user@domain::Subject=. *** =QUOTE= keywords do not exist anymore =QUOTE= keywords have been deprecated since Org 8.2. *** Select tests to perform with the build system The build system has been enhanced to allow test selection with a regular expression by defining =BTEST_RE= during the test invocation. This is especially useful during bisection to find just when a particular test failure was introduced. *** Exact heading search for external links ignore spaces and cookies Exact heading search for links now ignore spaces and cookies. This is the case for links of the form ~file:projects.org::*task title~, as well as links of the form ~file:projects.org::some words~ when ~org-link-search-must-match-exact-headline~ is not nil. *** ~org-latex-hyperref-template~, ~org-latex-title-command~ formatting New formatting keys are supported. See the respective docstrings. Note, ~org-latex-hyperref-template~ has a new default value. *** ~float, wasysym, marvosym~ are removed from ~org-latex-default-packages-alist~ If you require any of these package add them to your preamble via ~org-latex-packages-alist~. Org also uses default LaTeX ~\tolerance~ now. *** When exporting, throw an error on unresolved id/fuzzy links and code refs This helps spotting wrong links. * Version 8.2 ** Incompatible changes *** =ob-sh.el= renamed to =ob-shell= This may require two changes in user config. 1. In =org-babel-do-load-languages=, change =(sh . t)= to =(shell . t)=. 2. Edit =local.mk= files to change the value of =BTEST_OB_LANGUAGES= to remove "sh" and include "shell". *** Combine org-mac-message.el and org-mac-link-grabber into org-mac-link.el Please remove calls to =(require 'org-mac-message)= and =(require 'org-mac-link-grabber)= in your =.emacs= initialization file. All you need now is =(require 'org-mac-link)=. Additionally, replace any calls to =ogml-grab-link= to =org-mac-grab-link=. For example, replace this line: : (define-key org-mode-map (kbd "C-c g") 'omgl-grab-link) with this: : (define-key org-mode-map (kbd "C-c g") 'org-mac-grab-link) *** HTML export: Replace =HTML_HTML5_FANCY= by =:html-html5-fancy= (...) Some of the HTML specific export options in Org <8.1 are either nil or t, like =#+HTML_INCLUDE_STYLE=. We replaced these binary options with option keywords like :html-include-style. So you need to replace : #+HTML_INCLUDE_STYLE: t by : #+OPTIONS: :html-include-style t Options affected by this change: =HTML5_FANCY=, =HTML_INCLUDE_SCRIPTS= and =HTML_INCLUDE_STYLE=. *** Add an argument to ~org-export-to-file~ and ~org-export-to-buffer~ ~org-export-to-file~ and ~org-export-to-file~ can run in a different process when provided a non-nil =ASYNC= optional argument, without relying on ~org-export-async-start~ macro. Since =ASYNC= is the first of optional arguments, you have to shift the other optional arguments accordingly. *** Export back-ends are now structures Export back-ends are now structures, and stored as such in the communication channel during an export process. In other words, from now on, ~(plist-get info :back-end)~ will return a structure instead of a symbol. Arguments in hooks and in filters are still symbols, though. ** Important bugfixes *** [[doc:org-insert-heading][org-insert-heading]] has been rewritten and bugs are now fixed *** The replacement of disputed keys is now turned of when reading a date *** Match string for sparse trees can now contain a slash in a property value You can now have searches like SOMEPROP="aaa/bbb". Until now, this would break because the slash would be interpreted as the separator starting a TOTO match string. ** New features *** =C-c ^ x= will now sort checklist items by their checked status See [[doc:org-sort-list][org-sort-list]]: hitting =C-c ^ x= will put checked items at the end of the list. *** Various LaTeX export enhancements - Support SVG images - Support for .pgf files - LaTeX Babel blocks can now be exported as =.tikz= files - Allow =latexmk= as an option for [[doc:org-latex-pdf-process][org-latex-pdf-process]] - When using =\usepackage[AUTO]{babel}=, AUTO will automatically be replaced with a value compatible with ~org-export-default-language~ or ~LANGUAGE~ keyword. - The dependency on the =latexsym= LaTeX package has been removed, we now use =amssymb= symbols by default instead. *** New functions for paragraph motion The commands =C-down= and =C-up= now invoke special commands that use knowledge from the org-elements parser to move the cursor in a paragraph-like way. *** New entities in =org-entities.el= Add support for ell, imath, jmath, varphi, varpi, aleph, gimel, beth, dalet, cdots, S (§), dag, ddag, colon, therefore, because, triangleq, leq, geq, lessgtr, lesseqgtr, ll, lll, gg, ggg, prec, preceq, preccurlyeq, succ, succeq, succurlyeq, setminus, nexist(s), mho, check, frown, diamond. Changes loz, vert, checkmark, smile and tilde. *** Anonymous export back-ends ~org-export-create-backend~ can create anonymous export back-ends, which can then be passed to export functions like ~org-export-to-file~, ~org-export-to-buffer~ or ~org-export-as~. It allows for quick translation of Org syntax without the overhead of registering a new back-end. *** New agenda fortnight view The agenda has not, in addition to day, week, month, and year views, also a fortnight view covering 14 days. ** New options *** New option [[doc:org-bookmark-names-plist][org-bookmark-names-plist]] This allows to specify the names of automatic bookmarks. *** New option [[doc:org-agenda-ignore-drawer-properties][org-agenda-ignore-drawer-properties]] This allows more flexibility when optimizing the agenda generation. See https://orgmode.org/worg/agenda-optimization.html for details. *** New option: [[doc:org-html-link-use-abs-url][org-html-link-use-abs-url]] to force using absolute URLs This is an export/publishing option, and should be used either within the =#+OPTIONS= line(s) or within a [[doc:org-publish-project-alist][org-publish-project-alist]]. Setting this option to =t= is needed when the HTML output does not allow relative URLs. For example, the =contrib/lisp/ox-rss.el= library produces a RSS feed, and RSS feeds need to use absolute URLs, so a combination of =:html-link-home "..." and :html-link-use-abs-url t= is required---see the configuration example in the comment section of =ox-rss.el=. *** New option [[doc:org-babel-ditaa-java-cmd][org-babel-ditaa-java-cmd]] This makes java executable configurable for ditaa blocks. *** New options [[doc:org-babel-latex-htlatex][org-babel-latex-htlatex]] and [[doc:org-babel-latex-htlatex-packages][org-babel-latex-htlatex-packages]] This enables SVG generation from latex code blocks. *** New option: [[doc:org-habit-show-done-always-green][org-habit-show-done-always-green]] See [[https://lists.gnu.org/r/emacs-orgmode/2013-05/msg00214.html][this message]] from Max Mikhanosha. *** New option: [[doc:org-babel-inline-result-wrap][org-babel-inline-result-wrap]] If you set this to the following : (setq org-babel-inline-result-wrap "$%s$") then inline code snippets will be wrapped into the formatting string. *** New option: [[doc:org-special-ctrl-o][org-special-ctrl-o]] This variable can be used to turn off the special behavior of =C-o= in tables. ** New contributed packages - =ox-bibtex.el= by Nicolas Goaziou :: an utility to handle BibTeX export to both LaTeX and HTML exports. It uses the [[https://www.lri.fr/~filliatr/bibtex2html/][bibtex2html]] software. - =org-screenshot.el= by Max Mikhanosha :: an utility to handle screenshots easily from Org, using the external tool [[https://freecode.com/projects/scrot][scrot]]. ** Miscellaneous *** "QUOTE" keywords in headlines are deprecated "QUOTE" keywords are an undocumented feature in Org. When a headline starts with the keyword "QUOTE", its contents are parsed as a ~quote-section~ and treated as an example block. You can achieve the same with example blocks. This feature is deprecated and will be removed in the next Org release. * Version 8.0.1 ** Installation Installation instructions have been updated and simplified. If you have troubles installing or updating Org, focus on these instructions: - when updating via a =.zip/.tar.gz= file, you only need to set the =load-path= in your =.emacs=. Set it before any other Org customization that would call autoloaded Org functions. - when updating by pulling Org's Git repository, make sure to create the correct autoloads. You can do this by running =~$ make autoloads= (to only create the autoloads) or by running =~$ make= (to also compile the Emacs lisp files.) =~$ make help= and =~$ make helpall= gives you detailed explanations. - when updating through ELPA (either from GNU ELPA or from Org ELPA), you have to install Org's ELPA package in a session where no Org function has been called already. When in doubt, run =M-x org-version RET= and see if you have a mixed-up installation. See https://orgmode.org/org.html#Installation for details. ** Incompatible changes Org 8.0 is the most disruptive major version of Org. If you configured export options, you will have to update some of them. If you used =#+ATTR_*= keywords, the syntax of the attributes changed and you will have to update them. Below is a list of changes for which you need to take action. See https://orgmode.org/worg/org-8.0.html for the most recent version of this list and for detailed instructions on how to migrate. **** New export engine Org 8.0 comes with a new export engine written by Nicolas Goaziou. This export engine relies on ~org-element.el~ (Org's syntax parser), which was already in Org's core. This new export engine triggered the rewriting of /all/ export back-ends. The most visible change is the export dispatcher, accessible through the keybinding =C-c C-e=. By default, this menu only shows some of the built-in export formats, but you can add more formats by loading them directly (e.g., =(require 'ox-texinfo)= or by configuring the option [[doc:org-export-backends][org-export-backends]]. More contributed back-ends are available from the =contrib/= directory, the corresponding files start with the =ox-= prefix. If you customized an export back-end (like HTML or LaTeX), you will need to rename some options so that your customization is not lost. Typically, an option starting with =org-export-html-= is now named =org-html-=. See the manual for details and check [[https://orgmode.org/worg/org-8.0.html][this Worg page]] for directions. **** New syntax for #+ATTR_HTML/LaTeX/... options : #+ATTR_HTML width="200px" should now be written : #+ATTR_HTML :width 200px Keywords like =#+ATTR_HTML= and =#+ATTR_LaTeX= are defined in their respective back-ends, and the list of supported parameters depends on each backend. See Org's manual for details. **** ~org-remember.el~ has been removed You cannot use =remember.el= anymore to capture notes. Support for remember templates has been obsoleted since long, it is now fully removed. Use =M-x org-capture-import-remember-templates RET= to import your remember templates into capture templates. **** ~org-jsinfo.el~ has been merged into ~ox-html.el~ If you were requiring ~ox-jsinfo.el~ in your ~.emacs.el~ file, you will have to remove this requirement from your initialization file. **** Note for third-party developers The name of the files for export back-end have changed: we now use the prefix =ox-= for those files (like we use the =ob-= prefix for Babel files.) For example ~org-html.el~ is now ~ox-html.el~. If your code relies on these files, please update the names in your code. **** Packages moved from core to contrib Since packages in Org's core are meant to be part of GNU Emacs, we try to be minimalist when it comes to adding files into core. For 8.0, we moved some contributions into the =contrib/= directory. The rationale for deciding that these files should live in =contrib/= is either because they rely on third-party software that is not included in Emacs, or because they are not targeting a significant user-base. - org-colview-xemacs.el - org-mac-message.el - org-mew.el - org-wl.el - ox-freedmind.el - ox-taskjuggler.el Note that ~ox-freedmind.el~ has been rewritten by Jambunathan, ~org-mew.el~ has been enhanced by Tokuya Kameshima and ~ox-taskjuggler.el~ by Nicolas Goaziou and others. Also, the Taskjuggler exporter now uses TJ3 by default. John Hendy wrote [[https://orgmode.org/worg/org-tutorials/org-taskjuggler3.html][a tutorial on Worg]] for the TJ3 export. ** New packages in core *** ~ob-makefile.el~ by Eric Schulte and Thomas S. Dye =ob-makefile.el= implements Org Babel support for Makefile tangling. *** ~ox-man.el~ by Luis Anaya =ox-man.el= allows you to export Org files to =man= pages. *** ~ox-md.el~ by Nicolas Goaziou =ox-md.el= allows you to export Org files to Markdown files, using the vanilla [[https://daringfireball.net/projects/markdown/][Markdown syntax]]. *** ~ox-texinfo.el~ by Jonathan Leech-Pepin =ox-texinfo.el= allows you to export Org files to [[https://www.gnu.org/software/texinfo/][Texinfo]] files. ** New packages in contrib *** ~ob-julia.el~ by G. Jay Kerns [[https://julialang.org/][Julia]] is a new programming language. =ob-julia.el= provides Org Babel support for evaluating Julia source code. *** ~ob-mathomatic.el~ by Luis Anaya [[https://www.mathomatic.org/][mathomatic]] a portable, command-line, educational CAS and calculator software, written entirely in the C programming language. ~ob-mathomatic.el~ provides Org Babel support for evaluating mathomatic entries. *** ~ob-tcl.el~ by Luis Anaya ~ob-tcl.el~ provides Org Babel support for evaluating [[https://www.tcl.tk/][Tcl]] source code. *** ~org-bullets.el~ by Evgeni Sabof Display bullets instead of stars for headlines. Also see [[https://orgmode.org/worg/org-faq.html#sec-8-12][this updated FAQ]] on how to display another character than "*" for starting headlines. *** ~org-favtable.el~ by Marc-Oliver Ihm ~org-favtable.el~ helps you to create and update a table of favorite locations in org, keeping the most frequently visited lines right at the top. This table is called "favtable". See the documentation on [[https://orgmode.org/worg/org-contrib/org-favtable.html][Worg]]. *** ~ox-confluence.el~ by Sébastien Delafond ~ox-confluence.el~ lets you convert Org files to [[https://confluence.atlassian.com/display/DOC/Confluence%2BWiki%2BMarkup][Confluence Wiki]] files. *** ~ox-deck.el~ and ~ox-s5.el~ by Rick Frankel [[http://imakewebthings.com/deck.js/][deck.js]] is a javascript library for displaying HTML ages as presentations. ~ox-deck.el~ exports Org files to HTML presentations using =deck.js=. [[https://meyerweb.com/eric/tools/s5/][s5]] is a set of scripts which also allows to display HTML pages as presentations. ~ox-s5.el~ exports Org files to HTML presentations using =s5=. *** ~ox-groff.el~ by Luis Anaya and Nicolas Goaziou The [[https://www.gnu.org/software/groff/][groff]] (GNU troff) software is a typesetting package which reads plain text mixed with formatting commands and produces formatted output. Luis Anaya and Nicolas Goaziou implemented ~ox-groff.el~ to allow conversion from Org files to groff. *** ~ox-koma-letter.el~ by Nicolas Goaziou and Alan Schmitt This back-end allow to export Org pages to the =KOMA Scrlttr2= format. *** ~ox-rss.el~ by Bastien This back-end lets you export Org pages to RSS 2.0 feeds. Combined with the HTML publishing feature, this allows you to build a blog entirely with Org. ** New features *** Export **** New export generic options If you use Org exporter, we advise you to re-read [[https://orgmode.org/org.html#Exporting][the manual section about it]]. It has been updated and includes new options. Among the new/updated export options, three are of particular importance: - [[doc:org-export-allow-bind-keywords][org-export-allow-bind-keywords]] :: This option replaces the old option =org-export-allow-BIND= and the default value is =nil=, not =confirm=. You will need to explicitly set this to =t= in your initialization file if you want to allow =#+BIND= keywords. - [[doc:org-export-with-planning][org-export-with-planning]] :: This new option controls the export of =SCHEDULED:, DEADLINE:, CLOSED:= lines, and planning information is now skipped by default during export. This use to be the job of [[doc:org-export-with-timestamps][org-export-with-timestamps]], but this latter option has been given a new role: it controls the export of /standalone time-stamps/. When set to =nil=, Org will not export active and inactive time-stamps standing on a line by themselves or within a paragraph that only contains time-stamps. To check if an option has been introduced or its default value changed in Org 8.0, do =C-h v [option] RET= and check if the documentation says that the variable has been introduced (or changed) in version 24.4 of Emacs. **** Enhanced default stylesheet for the HTML exporter See the new default value of [[doc:org-html-style-default][org-html-style-default]]. **** New tags, classes and ids for the HTML exporter See the new default value of [[doc:org-html-divs][org-html-divs]]. **** Support for tikz pictures in LaTeX export **** ~org-man.el~: New export function for "man" links **** ~org-docview.el~: New export function for docview links *** Structure editing **** =C-u C-u M-RET= inserts a heading at the end of the parent subtree **** Cycling to the =CONTENTS= view keeps inline tasks folded [[doc:org-cycle-hook][org-cycle-hook]] as a new function [[doc:org-cycle-hide-inline-tasks][org-cycle-hide-inline-tasks]] which prevents the display of inline tasks when showing the content of a subtree. **** =C-c -= in a region makes a list item for each line This is the opposite of the previous behavior, where =C-c -= on a region would create one item for the whole region, and where =C-u C-c -= would create an item for each line. Now =C-c -= on the selected region creates an item per line, and =C-u C-c -= creates a single item for the whole region. **** When transposing words, markup characters are now part of the words In Emacs, you can transpose words with =M-t=. Transposing =*these* _words__= will preserve markup. **** New command [[doc:org-set-property-and-value][org-set-property-and-value]] bound to =C-c C-x P= This command allows you to quickly add both the property and its value. It is useful in buffers where there are many properties and where =C-c C-x p= can slow down the flow of editing too much. **** New commands [[doc:org-next-block][org-next-block]] and [[doc:org-previous-block][org-previous-block]] These commands allow you to go to the previous block (=C-c M-b= or the speedy key =B=) or to the next block (=C-c M-f= or the speedy key =F=.) **** New commands [[doc:org-drag-line-forward][org-drag-line-forward]] and [[doc:org-drag-line-backward][org-drag-line-backward]] These commands emulate the old behavior of =M-= and =M-= but are now bound to =S-M-= and =S-M-= respectively, since =M-= and =M-= now drag the whole element at point (a paragraph, a table, etc.) forward and backward. **** When a list item has a checkbox, inserting a new item uses a checkbox too **** When sorting entries/items, only the description of links is considered Now Org will sort this list : - [[https://abc.org][B]] : - [[https://def.org][A]] like this: : - [[https://def.org][A]] : - [[https://abc.org][B]] by comparing the descriptions, not the links. Same when sorting headlines instead of list items. **** New option =orgstruct-heading-prefix-regexp= For example, setting this option to "^;;; " in Emacs lisp files and using =orgstruct-mode= in those files will allow you to cycle through visibility states as if lines starting with ";;; *..." where headlines. In general, you want to set =orgstruct-heading-prefix-regexp= as a file local variable. **** New behavior of [[doc:org-clone-subtree-with-time-shift][org-clone-subtree-with-time-shift]] The default is now to ask for a time-shift only when there is a time-stamp. When called with a universal prefix argument =C-u=, it will not ask for a time-shift even if there is a time-stamp. **** New option [[doc:org-agenda-restriction-lock-highlight-subtree][org-agenda-restriction-lock-highlight-subtree]] This defaults to =t= so that the whole subtree is highlighted when you restrict the agenda view to it with =C-c C-x <= (or the speed command =<=). The default setting helps ensuring that you are not adding tasks after the restricted region. If you find this highlighting too intrusive, set this option to =nil=. **** New option [[doc:org-closed-keep-when-no-todo][org-closed-keep-when-no-todo]] When switching back from a =DONE= keyword to a =TODO= keyword, Org now removes the =CLOSED= planning information, if any. It also removes this information when going back to a non-TODO state (e.g., with =C-c C-t SPC=). If you want to keep the =CLOSED= planning information when removing the TODO keyword, set [[doc:org-closed-keep-when-no-todo][org-closed-keep-when-no-todo]] to =t=. **** New option [[doc:org-image-actual-width][org-image-actual-width]] This option allows you to change the width of in-buffer displayed images. The default is to use the actual width of the image, but you can use a fixed value for all images, or fall back on an attribute like : #+attr_html: :width 300px *** Scheduled/deadline **** Implement "delay" cookies for scheduled items If you want to delay the display of a scheduled task in the agenda, you can now use a delay cookie like this: =SCHEDULED: <2004-12-25 Sat -2d>=. The task is still scheduled on the 25th but will appear in your agenda starting from two days later (i.e. from March 27th.) Imagine for example that your co-workers are not done in due time and tell you "we need two more days". In that case, you may want to delay the display of the task in your agenda by two days, but you still want the task to appear as scheduled on March 25th. In case the task contains a repeater, the delay is considered to affect all occurrences; if you want the delay to only affect the first scheduled occurrence of the task, use =--2d= instead. See [[doc:org-scheduled-delay-days][org-scheduled-delay-days]] and [[doc:org-agenda-skip-scheduled-delay-if-deadline][org-agenda-skip-scheduled-delay-if-deadline]] for details on how to control this globally or per agenda. **** Use =C-u C-u C-c C-s= will insert a delay cookie for scheduled tasks See the previous section for why delay cookies may be useful. **** Use =C-u C-u C-c C-d= will insert a warning delay for deadline tasks =C-u C-u C-c C-d= now inserts a warning delay to deadlines. *** Calendar, diary and appts **** New variable [[doc:org-read-date-minibuffer-local-map][org-read-date-minibuffer-local-map]] By default, this new local map uses "." to go to today's date, like in the normal =M-x calendar RET=. If you want to deactivate this and to reassign the "@" key to =calendar-goto-today=, use this: #+BEGIN_SRC emacs-lisp ;; Unbind "." in Org's calendar: (define-key org-read-date-minibuffer-local-map (kbd ".") nil) ;; Bind "@" to `calendar-goto-today': (define-key org-read-date-minibuffer-local-map (kbd "@") (lambda () (interactive) (org-eval-in-calendar '(calendar-goto-today)))) #+END_SRC **** In Org's calendar, =!= displays diary entries of the date at point This is useful when you want to check if you don't already have an appointment when setting new ones with =C-c .= or =C-c s=. =!= will call =diary-view-entries= and display the diary in a separate buffer. **** [[doc:org-diary][org-diary]]: only keep the descriptions of links [[doc:org-diary][org-diary]] returns diary information from Org files, but it returns it in a diary buffer, not in an Org mode buffer. When links are displayed, only show their description, not the full links. *** Agenda **** New agenda type =agenda*= and entry types =:scheduled* :deadline*= When defining agenda custom commands, you can now use =agenda*=: this will list entries that have both a date and a time. This is useful when you want to build a list of appointments. You can also set [[doc:org-agenda-entry-types][org-agenda-entry-types]] either globally or locally in each agenda custom command and use =:timestamp*= and/or =:deadline*= there. Another place where this is useful is your =.diary= file: : %%(org-diary :scheduled*) ~/org/rdv.org This will list only entries from =~/org/rdv.org= that are scheduled with a time value (i.e. appointments). **** New agenda sorting strategies [[doc:org-agenda-sorting-strategy][org-agenda-sorting-strategy]] allows these new sorting strategies: | Strategy | Explanations | |----------------+------------------------------------------| | timestamp-up | Sort by any timestamp, early first | | timestamp-down | Sort by any timestamp, late first | | scheduled-up | Sort by scheduled timestamp, early first | | scheduled-down | Sort by scheduled timestamp, late first | | deadline-up | Sort by deadline timestamp, early first | | deadline-down | Sort by deadline timestamp, late first | | ts-up | Sort by active timestamp, early first | | ts-down | Sort by active timestamp, late first | | tsia-up | Sort by inactive timestamp, early first | | tsia-down | Sort by inactive timestamp, late first | **** New options to limit the number of agenda entries You can now limit the number of entries in an agenda view. This is different from filters: filters only /hide/ the entries in the agenda, while limits are set while generating the list of agenda entries. These new options are available: - [[doc:org-agenda-max-entries][org-agenda-max-entries]] :: limit by number of entries. - [[doc:org-agenda-max-todos][org-agenda-max-todos]] :: limit by number of TODOs. - [[doc:org-agenda-max-tags][org-agenda-max-tags]] :: limit by number of tagged entries. - [[doc:org-agenda-max-effort][org-agenda-max-effort]] :: limit by effort (minutes). For example, if you locally set [[doc:org-agenda-max-todos][org-agenda-max-todos]] to 3 in an agenda view, the agenda will be limited to the first three todos. Other entries without a TODO keyword or beyond the third TODO headline will be ignored. When setting a limit (e.g. about an effort's sum), the default behavior is to exclude entries that cannot be checked against (e.g. entries that have no effort property.) To include other entries too, you can set the limit to a negative number. For example =(setq org-agenda-max-tags -3)= will not show the fourth tagged headline (and beyond), but it will also show non-tagged headlines. **** =~= in agenda view sets temporary limits You can hit =~= in the agenda to temporarily set limits: this will regenerate the agenda as if the limits were set. This is useful for example when you want to only see a list of =N= tasks, or a list of tasks that take only =N= minutes. **** "=" in agenda view filters by regular expressions You can now filter agenda entries by regular expressions using ~=~. =C-u == will filter entries out. Regexp filters are cumulative. You can set [[doc:org-agenda-regexp-filter-preset][org-agenda-regexp-filter-preset]] to suit your needs in each agenda view. **** =|= in agenda view resets all filters Since it's common to combine tag filters, category filters, and now regexp filters, there is a new command =|= to reset all filters at once. **** Allow writing an agenda to an =.org= file You can now write an agenda view to an =.org= file. It copies the headlines and their content (but not subheadings) into the new file. This is useful when you want to quickly share an agenda containing the full list of notes. **** New commands to drag an agenda line forward (=M-=) or backward (=M-=) It sometimes handy to move agenda lines around, just to quickly reorganize your tasks, or maybe before saving the agenda to a file. Now you can use =M-= and =M-= to move the line forward or backward. This does not persist after a refresh of the agenda, and this does not change the =.org= files who contribute to the agenda. **** Use =%b= for displaying "breadcrumbs" in the agenda view [[doc:org-agenda-prefix-format][org-agenda-prefix-format]] now allows to use a =%b= formatter to tell Org to display "breadcrumbs" in the agenda view. This is useful when you want to display the task hierarchy in your agenda. **** Use =%l= for displaying the headline's level in the agenda view [[doc:org-agenda-prefix-format][org-agenda-prefix-format]] allows to use a =%l= formatter to tell Org to display entries with additional spaces corresponding to their level in the outline tree. **** [[doc:org-agenda-write][org-agenda-write]] will ask before overwriting an existing file =M-x org-agenda-write RET= (or =C-c C-w= from an agenda buffer) used to overwrite preexisting file with the same name without confirmation. It now asks for a confirmation. **** New commands =M-m= and =M-*= to toggle (all) mark(s) for bulk action - [[doc:org-agenda-bulk-toggle][org-agenda-bulk-toggle]] :: this command is bound to =M-m= and toggles the mark of the entry at point. - [[doc:org-agenda-bulk-toggle-all][org-agenda-bulk-toggle-all]] :: this command is bound to =M-*= and toggles all the marks in the current agenda. **** New option [[doc:org-agenda-search-view-max-outline-level][org-agenda-search-view-max-outline-level]] This option sets the maximum outline level to display in search view. E.g. when this is set to 1, the search view will only show headlines of level 1. **** New option [[doc:org-agenda-todo-ignore-time-comparison-use-seconds][org-agenda-todo-ignore-time-comparison-use-seconds]] This allows to compare times using seconds instead of days when honoring options like =org-agenda-todo-ignore-*= in the agenda display. **** New option [[doc:org-agenda-entry-text-leaders][org-agenda-entry-text-leaders]] This allows you to get rid of the ">" character that gets added in front of entries excerpts when hitting =E= in the agenda view. **** New formatting string for past deadlines in [[doc:org-agenda-deadline-leaders][org-agenda-deadline-leaders]] The default formatting for past deadlines is ="%2d d. ago: "=, which makes it explicit that the deadline is in the past. You can configure this via [[doc:org-agenda-deadline-leaders][org-agenda-deadline-leaders]]. Note that the width of the formatting string is important to keep the agenda alignment clean. **** New allowed value =repeated-after-deadline= for [[doc:org-agenda-skip-scheduled-if-deadline-is-shown][org-agenda-skip-scheduled-if-deadline-is-shown]] When [[doc:org-agenda-skip-scheduled-if-deadline-is-shown][org-agenda-skip-scheduled-if-deadline-is-shown]] is set to =repeated-after-deadline=, the agenda will skip scheduled items if they are repeated beyond the current deadline. **** New option for [[doc:org-agenda-skip-deadline-prewarning-if-scheduled][org-agenda-skip-deadline-prewarning-if-scheduled]] This variable may be set to nil, t, the symbol `pre-scheduled', or a number which will then give the number of days before the actual deadline when the prewarnings should resume. The symbol `pre-scheduled' eliminates the deadline prewarning only prior to the scheduled date. Read the full docstring for details. **** [[doc:org-class][org-class]] now supports holiday strings in the skip-weeks parameter For example, this task will now be skipped only on new year's day: : * Task : <%%(org-class 2012 1 1 2013 12 12 2 "New Year's Day")> *** Capture **** Allow =C-1= as a prefix for [[doc:org-agenda-capture][org-agenda-capture]] and [[doc:org-capture][org-capture]] With a =C-1= prefix, the capture mechanism will use the =HH:MM= value at point (if any) or the current =HH:MM= time as the default time for the capture template. **** Expand keywords within %(sexp) placeholder in capture templates If you use a =%:keyword= construct within a =%(sexp)= construct, Org will expand the keywords before expanding the =%(sexp)=. **** Allow to contextualize capture (and agenda) commands by checking the name of the buffer [[doc:org-capture-templates-contexts][org-capture-templates-contexts]] and [[doc:org-agenda-custom-commands-contexts][org-agenda-custom-commands-contexts]] allow you to define what capture templates and what agenda commands should be available in various contexts. It is now possible for the context to check against the name of the buffer. *** Tag groups Using =#+TAGS: { Tag1 : Tag2 Tag3 }= will define =Tag1= as a /group tag/ (note the colon after =Tag1=). If you search for =Tag1=, it will return headlines containing either =Tag1=, =Tag2= or =Tag3= (or any combination of those tags.) You can use group tags for sparse tree in an Org buffer, for creating agenda views, and for filtering. See https://orgmode.org/org.html#Tag-groups for details. *** Links **** =C-u C-u M-x org-store-link RET= will ignore non-core link functions Org knows how to store links from Org buffers, from info files and from other Emacs buffers. Org can be taught how to store links from any buffer through new link protocols (see [[https://orgmode.org/org.html#Adding-hyperlink-types]["Adding hyperlink types"]] in the manual.) Sometimes you want Org to ignore added link protocols and store the link as if the protocol was not known. You can now do this with =C-u C-u M-x org-store-link RET=. **** =C-u C-u C-u M-x org-store-link RET= on an active region will store links for each lines Imagine for example that you want to store a link for every message in a Gnus summary buffer. In that case =C-x h C-u C-u C-u M-x org-store-link RET= will store a link for every line (i.e. message) if the region is active. **** =C-c C-M-l= will add a default description for links which don't have one =C-c C-M-l= inserts all stored links. If a link does not have a description, this command now adds a default one, so that we are not mixing with-description and without-description links when inserting them. **** No curly braces to bracket links within internal links When storing a link to a headline like : * See [[https://orgmode.org][Org website]] [[doc:org-store-link][org-store-link]] used to convert the square brackets into curly brackets. It does not anymore, taking the link description or the link path, when there is no description. *** Table **** Switching between #+TBLFM lines If you have several =#+TBLFM= lines below a table, =C-c C-c= on a line will apply the formulas from this line, and =C-c C-c= on another line will apply those other formulas. **** You now use "nan" for empty fields in Calc formulas If empty fields are of interest, it is recommended to reread the section [[https://orgmode.org/org.html#Formula-syntax-for-Calc][3.5.2 Formula syntax for Calc]] of the manual because the description for the mode strings has been clarified and new examples have been added towards the end. **** Handle localized time-stamps in formulas evaluation If your =LOCALE= is set so that Org time-stamps use another language than english, and if you make time computations in Org's table, it now works by internally converting the time-stamps with a temporary =LOCALE=C= before doing computation. **** New lookup functions There are now three lookup functions: - [[doc:org-lookup-first][org-lookup-first]] - [[doc:org-lookup-last][org-lookup-last]] - [[doc:org-lookup-all][org-lookup-all]] See [[https://orgmode.org/org.html#Lookup-functions][the manual]] for details. *** Startup keywords These new startup keywords are now available: | Startup keyword | Option | |----------------------------------+---------------------------------------------| | =#+STARTUP: logdrawer= | =(setq org-log-into-drawer t)= | | =#+STARTUP: nologdrawer= | =(setq org-log-into-drawer nil)= | |----------------------------------+---------------------------------------------| | =#+STARTUP: logstatesreversed= | =(setq org-log-states-order-reversed t)= | | =#+STARTUP: nologstatesreversed= | =(setq org-log-states-order-reversed nil)= | |----------------------------------+---------------------------------------------| | =#+STARTUP: latexpreview= | =(setq org-startup-with-latex-preview t)= | | =#+STARTUP: nolatexpreview= | =(setq org-startup-with-latex-preview nil)= | *** Clocking **** New option [[doc:org-clock-rounding-minutes][org-clock-rounding-minutes]] E.g. if [[doc:org-clock-rounding-minutes][org-clock-rounding-minutes]] is set to 5, time is 14:47 and you clock in: then the clock starts at 14:45. If you clock out within the next 5 minutes, the clock line will be removed; if you clock out 8 minutes after your clocked in, the clock out time will be 14:50. **** New option [[doc:org-time-clocksum-use-effort-durations][org-time-clocksum-use-effort-durations]] When non-nil, =C-c C-x C-d= uses effort durations. E.g., by default, one day is considered to be a 8 hours effort, so a task that has been clocked for 16 hours will be displayed as during 2 days in the clock display or in the clocktable. See [[doc:org-effort-durations][org-effort-durations]] on how to set effort durations and [[doc:org-time-clocksum-format][org-time-clocksum-format]] for more on time clock formats. **** New option [[doc:org-clock-x11idle-program-name][org-clock-x11idle-program-name]] This allows to set the name of the program which prints X11 idle time in milliseconds. The default is to use =x11idle=. **** New option [[doc:org-use-last-clock-out-time-as-effective-time][org-use-last-clock-out-time-as-effective-time]] When non-nil, use the last clock out time for [[doc:org-todo][org-todo]]. Note that this option has precedence over the combined use of [[doc:org-use-effective-time][org-use-effective-time]] and [[doc:org-extend-today-until][org-extend-today-until]]. **** =S-= on a clocksum column will update the sum by updating the last clock **** =C-u 3 C-S-= will update clock timestamps synchronously by 3 units **** New parameter =:wstart= for clocktables to define the week start day **** New parameter =:mstart= to state the starting day of the month **** Allow relative times in clocktable tstart and tend options **** The clocktable summary is now a caption **** =:tstart= and =:tend= and friends allow relative times like "<-1w>" or "" *** Babel **** You can now use =C-c C-k= for [[doc:org-edit-src-abort][org-edit-src-abort]] This allows you to quickly cancel editing a source block. **** =C-u C-u M-x org-babel-tangle RET= tangles by the target file of the block at point This is handy if you want to tangle all source code blocks that have the same target than the block at point. **** New options for auto-saving the base buffer or the source block editing buffer When [[doc:org-edit-src-turn-on-auto-save][org-edit-src-turn-on-auto-save]] is set to =t=, editing a source block in a new window will turn on =auto-save-mode= and save the code in a new file under the same directory than the base Org file. When [[doc:org-edit-src-auto-save-idle-delay][org-edit-src-auto-save-idle-delay]] is set to a number of minutes =N=, the base Org buffer will be saved after this number of minutes of idle time. **** New =:post= header argument post-processes results This header argument may be used to pass the results of the current code block through another code block for post-processing. See the manual for a usage example. **** Commented out heading are ignored when collecting blocks for tangling If you comment out a heading (with =C-c ;= anywhere on the heading or in the subtree), code blocks from within this heading are now ignored when collecting blocks for tangling. **** New option [[doc:org-babel-hash-show-time][org-babel-hash-show-time]] to show a time-stamp in the result hash **** Do not ask for confirmation if cached value is current Do not run [[doc:org-babel-confirm-evaluate][org-babel-confirm-evaluate]] if source block has a cache and the cache value is current as there is no evaluation involved in this case. **** =ob-sql.el= and =ob-python.el= have been improved. **** New Babel files only need to =(require 'ob)= When writing a new Babel file, you now only need to use =(require 'ob)= instead of requiring each Babel library one by one. *** Faces - Org now fontifies radio link targets by default - In the agenda, use [[doc:org-todo-keyword-faces][org-todo-keyword-faces]] to highlight selected TODO keywords - New face [[doc:org-priority][org-priority]], enhanced fontification of priority cookies in agenda - New face [[doc:org-tag-group][org-tag-group]] for group tags ** Miscellaneous - New speedy key =s= pour [[doc:org-narrow-to-subtree][org-narrow-to-subtree]] - Handling of [[doc:org-html-table-row][org-html-table-row]] has been updated (incompatible change) - [[doc:org-export-html-table-tag][org-export-html-table-tag]] is replaced by [[doc:org-html-table-default-attributes][org-html-table-default-attributes]] - Support using =git-annex= with Org attachments - org-protocol: Pass optional value using query in url to capture from protocol - When the refile history is empty, use the current filename as default - When you cannot change the TODO state of a task, Org displays the blocking task - New option [[doc:org-mobile-allpriorities][org-mobile-allpriorities]] - org-bibtex.el now use =visual-line-mode= instead of the deprecated =longlines-mode= - [[doc:org-format-latex-options][org-format-latex-options]] allows to set the foreground/background colors automatically - New option [[doc:org-archive-file-header-format][org-archive-file-header-format]] - New "neg" entity in [[doc:org-entities][org-entities]] - New function [[doc:org-docview-export][org-docview-export]] to export docview links - New =:eps= header argument for ditaa code blocks - New option [[doc:org-gnus-no-server][org-gnus-no-server]] to start Gnus with =gnus-no-server= - Org is now distributed with =htmlize.el= version 1.43 - ~org-drill.el~ has been updated to version 2.3.7 - ~org-mac-iCal.el~ now supports OS X versions up to 10.8 - Various improvements to ~org-contacts.el~ and =orgpan.el= ** Outside Org *** Spanish translation of the Org guide by David Arroyo Menéndez David (and others) translated the Org compact guide in spanish: You can read the [[https://orgmode.org/worg/orgguide/orgguide.es.pdf][PDF guide]]. *** ~poporg.el~ and ~outorg.el~ Two new libraries (~poporg.el~ by François Pinard and ~outorg.el~ by Thorsten Jolitz) now enable editing of comment-sections from source-code buffers in temporary Org-mode buffers, making the full editing power of Org-mode available. ~outorg.el~ comes together with ~outshine.el~ and ~navi-mode.el~, two more libraries by Thorsten Jolitz with the goal to give source-code buffers the /look & feel/ of Org-mode buffers while greatly improving navigation and structure editing. A detailed description can be found here: https://orgmode.org/worg/org-tutorials/org-outside-org.html Here are two screencasts demonstrating Thorsten's tools: - [[https://youtu.be/nqE6YxlY0rw]["Modern conventions for Emacs Lisp files"]] - [[https://www.youtube.com/watch?v%3DII-xYw5VGFM][Exploring Bernt Hansen's Org-mode tutorial with 'navi-mode']] *** MobileOrg for iOS MobileOrg for iOS back in the App Store The 1.6.0 release was focused on the new Dropbox API and minor bug fixes but also includes a new ability to launch in Capture mode. Track development and contribute [[https://github.com/MobileOrg/mobileorg/issues][on github]]. * Version 7.9.3 ** New option [[doc::org-agenda-use-tag-inheritance][org-agenda-use-tag-inheritance]] [[doc::org-use-tag-inheritance][org-use-tag-inheritance]] controls whether tags are inherited when org-tags-view is called (either in =tags=, =tags-tree= or =tags-todo= agenda views.) When generating other agenda types such as =agenda=, =todo= and =todo-tree=, tags inheritance is not used when selecting the entries to display. Still, you might want to have all tag information correct in the agenda buffer, e.g. for tag filtering. In that case, add the agenda type to this variable. Setting this variable to nil should considerably speeds up the agenda generation. Note that the default was to display inherited tags in the agenda lines even if `org-use-tag-inheritance' was nil. The default is now to *never* display inherited tags in agenda lines, but to /know/ about them when the agenda type is listed in [[doc::org-agenda-use-tag-inheritance][org-agenda-use-tag-inheritance]]. ** New default value =nil= for [[doc::org-agenda-dim-blocked-tasks][org-agenda-dim-blocked-tasks]] Using `nil' as the default value speeds up the agenda generation. You can hit `#' (or `C-u #') in agenda buffers to temporarily dim (or turn invisible) blocked tasks. ** New speedy keys for [[doc::org-speed-commands-default][org-speed-commands-default]] You can now use `:' (instead of `;') for setting tags---this is consistent with using the `:' key in agenda view. You can now use `=' for [[doc::org-columns][org-columns]]. ** =org-float= is now obsolete, use =diary-float= instead ** No GPL manual anymore There used to be a GPL version of the Org manual, but this is not the case anymore, the Free Software Foundation does not permit this. The GNU FDL license is now included in the manual directly. ** Enhanced compatibility with Emacs 22 and XEmacs Thanks to Achim for his work on enhancing Org's compatibility with various Emacsen. Things may not be perfect, but Org should work okay in most environments. * Version 7.9.2 ** New ELPA repository for Org packages You can now add the Org ELPA repository like this: #+BEGIN_SRC emacs-lisp (add-to-list 'package-archives '("org" . "https://orgmode.org/elpa/") t) #+END_SRC It contains both the =org-*.tar= package (the core Org distribution, also available through https://elpa.gnu.org) and the =org-plus*.tar= package (the extended Org distribution, with non-GNU packages from the =contrib/= directory.) See https://orgmode.org/elpa/ ** Overview of the new keybindings | Keybinding | Speedy | Command | |-----------------+--------+-----------------------------| | =C-c C-x C-z= | | [[doc::org-clock-resolve][org-clock-resolve]] | | =C-c C-x C-q= | | [[doc::org-clock-cancel][org-clock-cancel]] | | =C-c C-x C-x= | | [[doc::org-clock-in-last][org-clock-in-last]] | | =M-h= | | [[doc::org-mark-element][org-mark-element]] | | =*= | | [[doc::org-agenda-bulk-mark-all][org-agenda-bulk-mark-all]] | | =C-c C-M-l= | | [[doc::org-insert-all-links][org-insert-all-links]] | | =C-c C-x C-M-v= | | [[doc::org-redisplay-inline-images][org-redisplay-inline-images]] | | =C-c C-x E= | =E= | [[doc::org-inc-effort][org-inc-effort]] | | | =#= | [[doc::org-toggle-comment][org-toggle-comment]] | | | =:= | [[doc::org-columns][org-columns]] | | | =W= | Set =APPT_WARNTIME= | | =k= | | [[doc::org-agenda-capture][org-agenda-capture]] | | C-c , | , | [[doc::org-priority][org-priority]] | ** New package and Babel language *** =org-eshell.el= by Konrad Hinsen is now in Org =org-eshell.el= allows you to create links from [[https://www.gnu.org/software/emacs/manual/html_node/eshell/index.html][Eshell]]. *** Support for execution of Scala code blocks (see ob-scala.el) *** Support for execution of IO code blocks (see ob-io.el) ** Incompatible changes - If your code relies on =org-write-agenda=, please use [[doc::org-agenda-write][org-agenda-write]] from now on. - If your code relies on =org-make-link=, please use =concat= instead. - =org-link-to-org-use-id= has been renamed to =org-id-link-to-org-use-id= and its default value is nil. The previous default was =create-if-interactive-and-no-custom-id=. ** New features and user-visible changes *** Org Element =org-element.el= is a toolbox for parsing and analyzing "elements" in an Org-mode buffer. This has been written by Nicolas Goaziou and has been tested for quite some time. It is now part of Org's core and many core functions rely on this package. Two functions might be particularly handy for users: =org-element-at-point= and =org-element-context=. See the docstrings for more details. Below is a list of editing and navigating commands that now rely on =org-element.el=. **** [[doc::org-fill-paragraph][org-fill-paragraph]] has been completely rewritten The filling mechanisms now rely on org-element, trying to do the right thing on each element in various contexts. E.g. filling in a list item will preserve indentation; filling in message-mode will fall back on the relevant filling functions; etc. **** [[doc::org-metaup][org-metaup]] and [[doc::org-metadown][org-metadown]] will drag the element backward/forward If you want to get the old behavior (i.e. moving a line up and down), you can first select the line as an active region, then =org-metaup= or =org-metadown= to move the region backward or forward. This also works with regions bigger than just one line. **** [[doc::org-up-element][org-up-element]] and [[doc::org-down-element][org-down-element]] (respectively =C-c C-^= and =C-c C-_=) This will move the point up/down in the hierarchy of elements. **** [[doc::org-backward-element][org-backward-element]] and [[doc::org-forward-element][org-forward-element]] (respectively =M-{= and =M-}=) This will move the point backward/forward in the hierarchy of elements. **** [[doc::org-narrow-to-element][org-narrow-to-element]] will narrow to the element at point **** [[doc::org-mark-element][org-mark-element]] will mark the element at point This command is bound to =M-h= and will mark the element at point. If the point is at a paragraph, it will mark the paragraph. If the point is at a list item, it will mark the list item. Etc. Note that if point is at the beginning of a list, it will mark the whole list. To mark a subtree, you can either use =M-h= on the headline (since there is no ambiguity about the element you're at) or [[doc::org-mark-subtree][org-mark-subtree]] (=C-c @=) anywhere in the subtree. Invoking [[doc::org-mark-element][org-mark-element]] repeatedly will try to mark the next element on top of the previous one(s). E.g. hitting =M-h= twice on a headline will mark the current subtree and the next one on the same level. *** Org Agenda **** New option [[doc::org-agenda-sticky][org-agenda-sticky]] There is a new option =org-agenda-sticky= which enables "sticky" agendas. Sticky agendas remain opened in the background so that you don't need to regenerate them each time you hit the corresponding keystroke. This is a big time saver. When [[doc::org-agenda-sticky][org-agenda-sticky]] is =non-nil=, the agenda buffer will be named using the agenda key and its description. In sticky agendas, the =q= key will just bury the agenda buffers and further agenda commands will show existing buffer instead of generating new ones. If [[doc::org-agenda-sticky][org-agenda-sticky]] is set to =nil=, =q= will kill the single agenda buffer. **** New option [[doc::org-agenda-custom-commands-contexts][org-agenda-custom-commands-contexts]] Setting this option allows you to define specific context where agenda commands should be available from. For example, when set to this value #+BEGIN_SRC emacs-lisp (setq org-agenda-custom-commands-contexts '(("p" (in-file . "\\.txt")))) #+END_SRC then the =p= agenda command will only be available from buffers visiting *.txt files. See the docstring and the manual for more details on how to use this. **** Changes in bulk actions The set of commands starting with =k ...= as been deleted and the features have been merged into the "bulk action" feature. After you marked some entries in the agenda, if you call =B s=, the agenda entries will be rescheduled using the date at point if on a date header. If you are on an entry with a timestamp, you will be prompted for a date to reschedule your marked entries to, using the timestamp at point as the default prompt. You can now use =k= to capture the marked entry and use the date at point as an overriding date for the capture template. To bind this behavior to =M-x org-capture RET= (or its keybinding), set the new option [[doc::org-capture-use-agenda-date][org-capture-use-agenda-date]] to =t=. **** =N= and =P= in the agenda will move to the next/previous item **** New command [[doc::org-agenda-bulk-mark-all][org-agenda-bulk-mark-all]] to mark all items This new command is bound to =*= in agenda mode. There is also a new option [[doc::org-agenda-bulk-mark-char][org-agenda-bulk-mark-char]] to set the character to use as a mark for bulk actions. **** New option [[doc::org-agenda-persistent-marks][org-agenda-persistent-marks]] When set to =non-nil=, marks will remain visible after a bulk action. You can temporarily toggle this by pressing =p= when invoking [[doc::org-agenda-bulk-action][org-agenda-bulk-action]]. Marks are deleted if your rebuild the agenda buffer or move to another date/span (e.g. with =f= or =w=). **** New option [[doc::org-agenda-skip-timestamp-if-deadline-is-shown][org-agenda-skip-timestamp-if-deadline-is-shown]] =Non-nil= means skip timestamp line if same entry shows because of deadline. In the agenda of today, an entry can show up multiple times because it has both a plain timestamp and has a nearby deadline. When this variable is t, then only the deadline is shown and the fact that the entry has a timestamp for or including today is not shown. When this variable is =nil=, the entry will be shown several times. **** New =todo-unblocked= and =nottodo-unblocked= skip conditions See the [[https://orgmode.org/cgit.cgi/org-mode.git/commit/?id=f426da][git commit]] for more explanations. **** Allow category filtering in the agenda You can now filter the agenda by category. Pressing "<" will filter by the category of the item on the current line, and pressing "<" again will remove the filter. You can combine tag filters and category filters. You can use =org-agenda-category-filter= in your custom agenda views and =org-agenda-category-filter-preset= in your main configuration. See also the new command [[doc::org-agenda-filter-by-top-category][org-agenda-filter-by-top-category]]: hitting =^= will filter by "Top" category: only show entries that are of the same category than the Top category of the entry at point. *** Org Links **** Inserting links When inserting links through [[doc::org-insert-link][org-insert-link]], the description is now displayed first, followed by the literal link, as the description is often more useful when you look for the link you want to insert. Completion now complete both literal links and description. If you complete a description, the literal link and its description will be inserted directly, whereas when you complete the literal link, you will be prompted for a description (as with Org 7.8.) In the completion buffer, links to the current buffer are now highlighted. **** New templates =%h= and =%(sexp)= for abbreviated links On top of =%s= template, which is replaced by the link tag in abbreviated links, you can now use =%h= (which does the same than =%s= but does not hexify the tag) and =%(sexp)= (which can run a function that takes the tag as its own argument.) **** New link type =help= You can now create links from =help= buffers. For example, if you request help for the command [[doc::org-agenda][org-agenda]] with =C-h f org-agenda RET=, creating a link from this buffer will let you go back to the same buffer. **** New command [[doc::org-insert-all-links][org-insert-all-links]] This will insert all links as list items. With a universal prefix argument, links will not be deleted from the variable =org-stored-links=. This new command is bound to =C-c C-M-l=. **** New option [[doc::org-url-hexify-p][org-url-hexify-p]] When set to =nil=, the =URL= part of a link will not be hexified. **** Org can now open multiple shell links **** New option [[doc::org-doi-server-url][org-doi-server-url]] to specify an alternate DOI server **** RET now follows time stamps links *** Org Editing **** [[doc::org-todo][org-todo]] and =org-archive-*= can now loop in the active region When [[doc::org-loop-over-headlines-in-active-region][org-loop-over-headlines-in-active-region]] is =non-nil=, using [[doc::org-todo][org-todo]] or =org-archive-*= commands in the active region will loop over headlines. This is handy if you want to set the TODO keyword for several items, or archive them quickly. **** You can now set tags for headlines in a region If [[doc::org-loop-over-headlines-in-active-region][org-loop-over-headlines-in-active-region]] is =non-nil=, then selecting the region and hitting =C-c C-q= will set the tags for all headlines in the region. **** New command [[doc::org-insert-drawer][org-insert-drawer]] to insert a drawer interactively **** Comments start with "^[ \t]*# " anywhere on a line Note that the space after the hashtag is mandatory. Comments with "^#+" are not supported anymore. **** New speed key =#= to toggle the COMMENT cookie on a headline **** =indent-region-function= is now set to [[doc::org-indent-region][org-indent-region]] =C-M-\= should now produce useful results. You can unindent the buffer with [[doc::org-unindent-buffer][org-unindent-buffer]]. **** New option [[doc::org-allow-promoting-top-level-subtree][org-allow-promoting-top-level-subtree]] When =non-nil=, =S-M-= will promote level-1 subtrees containing other subtrees. The level-1 headline will be commented out. You can revert to the previous state with =M-x undo RET=. *** Org Clock **** New keybinding =C-c C-x C-z= for [[doc::org-clock-resolve][org-clock-resolve]] **** New keybinding =C-c C-x C-q= for [[doc::org-clock-cancel][org-clock-cancel]] **** New command [[doc::org-clock-in-last][org-clock-in-last]] to clock in the last clocked item This command is bound to =C-c C-x C-x= and will clock in the last clocked entry, if any. **** =C-u M-x= [[doc::org-clock-out][org-clock-out]] =RET= now prompts for a state to switch to **** =S-M-= on a clock timestamps adjusts the previous/next clock **** New option [[doc::org-clock-continuously][org-clock-continuously]] When set to =nil=, clocking in a task will first try to find the last clocked out task and restart from when that task was clocked out. You can temporarily activate continuous clocking with =C-u C-u C-u M-x= [[doc::org-clock-in][org-clock-in]] =RET= (three universal prefix arguments) and =C-u C-u M-x= [[doc::org-clock-in-last][org-clock-in-last]] =RET= (two universal prefix arguments). **** New option [[doc::org-clock-frame-title-format][org-clock-frame-title-format]] This option sets the value of =frame-title-format= when clocking in. **** New options for controlling the clockreport display [[doc::org-clock-file-time-cell-format][org-clock-file-time-cell-format]]: Format string for the file time cells in clockreport. [[doc::org-clock-total-time-cell-format][org-clock-total-time-cell-format]]: Format string for the total time cells in clockreport. **** New options for controlling the clock/timer display [[doc::org-clock-clocked-in-display][org-clock-clocked-in-display]]: control whether the current clock is displayed in the mode line and/or frame title. [[doc::org-timer-display][org-timer-display]]: control whether the current timer is displayed in the mode line and/or frame title. This allows the clock and timer to be displayed in the frame title instead of, or as well as, the mode line. This is useful for people with limited space in the mode line but with ample space in the frame title. *** Org Appearance **** New option [[doc::org-custom-properties][org-custom-properties]] The visibility of properties listed in this options can be turn on/off with [[doc::org-toggle-custom-properties-visibility][org-toggle-custom-properties-visibility]]. This might be useful for properties used by third-part tools or that you don't want to see temporarily. **** New command [[doc::org-redisplay-inline-images][org-redisplay-inline-images]] This will redisplay all images. It is bound to =C-c C-x C-M-v=. **** New entities in =org-entities.el= There are these new entities: : ("tilde" "\\~{}" nil "˜" "~" "~" "~") : ("slash" "/" nil "/" "/" "/" "/") : ("plus" "+" nil "+" "+" "+" "+") : ("under" "\\_" nil "_" "_" "_" "_") : ("equal" "=" nil "=" "=" "=" "=") : ("asciicirc" "\\textasciicircum{}" nil "^" "^" "^" "^") **** New face =org-list-dt= for definition terms **** New face =org-date-selected= for the selected calendar day **** New face value for =org-document-title= The face is back to a normal height. *** Org Columns **** New speed command =:= to activate the column view **** New special property =CLOCKSUM_T= to display today's clocked time You can use =CLOCKSUM_T= the same way you use =CLOCKSUM=. It will display the time spent on tasks for today only. **** Use the =:COLUMNS:= property in columnview dynamic blocks If the =:COLUMNS:= is set in a subtree, the columnview dynamic block will use its value as the column format. **** Consider inline tasks when computing a sum *** Org Dates and Time Stamps **** Enhanced [[doc::org-sparse-tree][org-sparse-tree]] =C-c /= can now check for time ranges. When checking for dates with =C-c /= it is useful to change the type of dates that you are interested in. You can now do this interactively with =c= after =C-c /= and/or by setting [[doc::org-sparse-tree-default-date-type][org-sparse-tree-default-date-type]] to the default value you want. **** Support for hourly repeat cookies You can now use : SCHEDULED: <2012-08-20 lun. 08:00 +1h> if you want to add an hourly repeater to an entry. **** =C-u C-u C-c .= inserts a time-stamp with no prompt **** When (setq [[doc::org-read-date-prefer-future][org-read-date-prefer-future]] 'time), accept days in the prompt "8am Wed" and "Wed 8am" are now acceptable values when entering a date from the prompt. If [[doc::org-read-date-prefer-future][org-read-date-prefer-future]] is set to =time=, this will produce the expected prompt indication. **** New option [[doc::org-datetree-add-timestamp][org-datetree-add-timestamp]] When set to =non-nil=, datetree entries will also have a timestamp. This is useful if you want to see these entries in a sparse tree with =C-c /=. *** Org Capture **** New command [[doc::org-capture-string][org-capture-string]] M-x [[doc::org-capture-string][org-capture-string]] RET will prompt for a string and a capture template. The string will be used as an annotation for the template. This is useful when capturing in batch mode as it lets you define the content of the template without being in Emacs. **** New option [[doc::org-capture-templates-contexts][org-capture-templates-contexts]] Setting this option allows you to define specific context where capture templates should be available from. For example, when set to this value #+BEGIN_SRC emacs-lisp (setq org-capture-templates-contexts '(("c" (in-mode . "message-mode")))) #+END_SRC then the =c= capture template will only be available from =message-mode= buffers. See the docstring and the manual for more details on how to use this. **** New =%l= template to insert the literal link **** New option [[doc::org-capture-bookmark][org-capture-bookmark]] Org used to automatically add a bookmark with capture a note. You can now turn this on by setting [[doc::org-capture-bookmark][org-capture-bookmark]] to =nil=. **** Expand =%= escape sequences into text entered for 'th =%^{PROMPT}= escape See the manual for more explanations. **** More control over empty lines You can use =:empty-lines-before= and =:empty-lines-after= to control the insertion of empty lines. Check the manual for more explanations. **** New hook [[doc::org-capture-prepare-finalize-hook][org-capture-prepare-finalize-hook]] This new hook runs before the finalization process starts. *** Org Export **** New functions =orgtbl-to-table.el= and =orgtbl-to-unicode= =orgtbl-to-table.el= convert the table to a =table.el= table, and =orgtbl-to-unicode= will use =ascii-art-to-unicode.el= (when available) to print beautiful tables. **** [[doc::org-table-export][org-table-export]] now a bit clever about the target format When you specify a file name like =table.csv=, [[doc::org-table-export][org-table-export]] will now suggest =orgtbl-to-csv= the default method for exporting the table. **** New option [[doc::org-export-date-timestamp-format][org-export-date-timestamp-format]] The option allows to set a time string format for Org timestamps in the #+DATE option. **** LaTeX: New options for exporting table rules :tstart, :hline and :tend See [[doc::org-export-latex-tables-hline][org-export-latex-tables-hline]] and [[doc::org-export-latex-tables-tend][org-export-latex-tables-tend]]. **** LaTeX: You can now set =:hfmt= from =#+ATTR_LaTeX= **** Beamer: Add support and keybinding for the =exampleblock= environment Add support for these languages in [[doc::org-export-language-setup][org-export-language-setup]]. More languages are always welcome. **** Beamer: New option [[doc::org-beamer-inherited-properties][org-beamer-inherited-properties]] This option allows Beamer export to inherit some properties. Thanks to Carsten for implementing this. **** ODT: Add support for ODT export in org-bbdb.el **** ODT: Add support for indented tables (see [[https://orgmode.org/cgit.cgi/org-mode.git/commit/?id=e9fd33][this commit]] for details) **** ODT: Improve the conversion from ODT to other formats **** ASCII: Swap the level-1/level-2 characters to underline the headlines **** Support for Chinese, simplified Chinese, Russian, Ukrainian and Japanese **** HTML: New option [[doc::org-export-html-date-format-string][org-export-html-date-format-string]] Format string to format the date and time in HTML export. Thanks to Sébastien Vauban for this patch. *** Org Babel **** New =:results drawer= parameter =:results drawer= replaces =:results wrap=, which is deprecated but still supported. **** =:results org= now put results in a =#+BEGIN_SRC org= block =:results org= used to put results in a =#+BEGIN_ORG= block but it now puts results in a =#+BEGIN_SRC org= block, with comma-escaped lines. =#+BEGIN_ORG= blocks are obsolete. **** Exporting =#+BEGIN_SRC org= blocks exports the code It used to exports the results of the code. *** Miscellaneous **** New menu entry for [[doc::org-refile][org-refile]] **** Allow capturing to encrypted entries If you capture to an encrypted entry, it will be decrypted before inserting the template then re-encrypted after finalizing the capture. **** Inactive timestamps are now handled in tables Calc can do computation on active time-stamps like <2012-09-29 sat.>. Inactive time-stamps in a table's cell are now internally deactivated so that Calc formulas can operate on them. **** [[doc::org-table-number-regexp][org-table-number-regexp]] can now accept comma as decimal mark **** Org allows a new property =APPT_WARNTIME= You can set it with the =W= speedy key or set it manually. When set, exporting to iCalendar and [[doc::org-agenda-to-appt][org-agenda-to-appt]] will use the value of this property as the number of minutes for the warning alarm. **** New command [[doc::org-inc-effort][org-inc-effort]] This will increment the effort value. It is bound to =C-c C-x E= and to =E= as a speedy command. **** Attach: Add support for creating symbolic links =org-attach-method= now supports a new method =lns=, allowing to attach symbolic links. **** Archive: you can now archive to a datetree **** New option [[doc::org-inlinetask-show-first-star][org-inlinetask-show-first-star]] =Non-nil= means display the first star of an inline task as additional marker. When =nil=, the first star is not shown. **** New option [[doc::org-latex-preview-ltxpng-directory][org-latex-preview-ltxpng-directory]] This lets you define the path for the =ltxpng/= directory. **** You can now use imagemagick instead of dvipng to preview LaTeX fragments **** You can now turn off [[doc::orgstruct++-mode][orgstruct++-mode]] safely **** =C-u C-c C-c= on list items to add check boxes =C-u C-c C-c= will add an empty check box on a list item. When hit from the top of the list, it will add check boxes for all top level list items. **** =org-list-ending-method= and =org-list-end-regexp= are now obsolete Fall back on using =org-list-end-re= only, which see. **** org-feed.el now expands =%(sexp)= templates **** New option [[doc::org-protocol-data-separator][org-protocol-data-separator]] **** New option [[doc::org-ditaa-jar-option][org-ditaa-jar-option]] to specify the ditaa jar file **** New possible value for [[doc::org-loop-over-headlines-in-active-region][org-loop-over-headlines-in-active-region]] When [[doc::org-loop-over-headlines-in-active-region][org-loop-over-headlines-in-active-region]] is set to =start-level=, the command will loop over the active region but will only act upon entries that are of the same level than the first headline in the region. **** New option [[doc::org-habit-show-all-today][org-habit-show-all-today]] When set to =t=, show all (even unscheduled) habits on today's agenda. ** Important bug fixes *** M-TAB on options keywords perform completion correctly again If you hit =M-TAB= on keywords like =#+TITLE=, Org will try to perform completion with meaningful values. *** Add licenses to javascript embedded and external code snippets Embedded javascript code produced when exporting an Org file to HTML is now licensed under GPLv3 (or later), and the copyright is owned by the Free Software Foundation, Inc. The javascript code for embedding MathJax in the browser mentions the MathJax copyright and the Apache 2.0 license. The javascript code for embedding =org-injo.js= in the browser mentions the copyright of Sebastian Rose and the GPLv3 (or later) license. =org-export-html-scripts= is now a variable, so that you can adapt the code and the license to your needs. See https://www.gnu.org/philosophy/javascript-trap.html for explanations on why these changes were necessary. * Version 7.8.11 ** Incompatible changes *** Emacs 21 support has been dropped Do not use Org mode 7.xx with Emacs 21, use [[https://orgmode.org/org-6.36c.zip][version 6.36c]] instead. *** XEmacs support requires the XEmacs development version To use Org mode 7.xx with XEmacs, you need to run the developer version of XEmacs. We were about to drop XEmacs support entirely, but Michael Sperber stepped in and made changes to XEmacs that made it easier to keep the support. Thanks to Michael for this last-minute save. *** New keys for TODO sparse trees The key =C-c C-v= is now reserved for Org Babel action. TODO sparse trees can still be made with =C-c / t= (all not-done states) and =C-c / T= (specific states). *** The Agenda =org-agenda-ndays= is now obsolete The variable =org-agenda-ndays= is obsolete - please use =org-agenda-span= instead. Thanks to Julien Danjou for this. *** Changes to the intended use of =org-export-latex-classes= So far this variable has been used to specify the complete header of the LaTeX document, including all the =\usepackage= calls necessary for the document. This setup makes it difficult to maintain the list of packages that Org itself would like to call, for example for the special symbol support it needs. First of all, you can *opt out of this change* in the following way: You can say: /I want to have full control over headers, and I will take responsibility to include the packages Org needs/. If that is what you want, add this to your configuration and skip the rest of this section (except maybe for the description of the =[EXTRA]= place holder): #+begin_src emacs-lisp (setq org-export-latex-default-packages-alist nil org-export-latex-packages-alist nil) #+end_src /Continue to read here if you want to go along with the modified setup./ There are now two variables that should be used to list the LaTeX packages that need to be included in all classes. The header definition in =org-export-latex-classes= should then not contain the corresponding =\usepackage= calls (see below). The two new variables are: 1. =org-export-latex-default-packages-alist= :: This is the variable where Org-mode itself puts the packages it needs. Normally you should not change this variable. The only reason to change it anyway is when one of these packages causes a conflict with another package you want to use. Then you can remove that packages and hope that you are not using Org-mode functionality that needs it. 2. =org-export-latex-packages-alist= :: This is the variable where you can put the packages that you'd like to use across all classes. The sequence how these customizations will show up in the LaTeX document are: 1. Header from =org-export-latex-classes= 2. =org-export-latex-default-packages-alist= 3. =org-export-latex-packages-alist= 4. Buffer-specific things set with =#+LaTeX_HEADER:= If you want more control about which segment is placed where, or if you want, for a specific class, have full control over the header and exclude some of the automatic building blocks, you can put the following macro-like place holders into the header: #+begin_example [DEFAULT-PACKAGES] \usepackage statements for default packages [NO-DEFAULT-PACKAGES] do not include any of the default packages [PACKAGES] \usepackage statements for packages [NO-PACKAGES] do not include the packages [EXTRA] the stuff from #+LaTeX_HEADER [NO-EXTRA] do not include #+LaTeX_HEADER stuff #+end_example If you have currently customized =org-export-latex-classes=, you should revise that customization and remove any package calls that are covered by =org-export-latex-default-packages-alist=. This applies to the following packages: - inputenc - fontenc - fixltx2e - graphicx - longtable - float - wrapfig - soul - t1enc - textcomp - marvosym - wasysym - latexsym - amssymb - hyperref If one of these packages creates a conflict with another package you are using, you can remove it from =org-export-latex-default-packages-alist=. But then you risk that some of the advertised export features of Org will not work properly. You can also consider moving packages that you use in all classes to =org-export-latex-packages-alist=. If necessary, put the place holders so that the packages get loaded in the right sequence. As said above, for backward compatibility, if you omit the place holders, all the variables will dump their content at the end of the header. *** The constant =org-html-entities= is obsolete Its content is now part of the new constant =org-entities=, which is defined in the file org-entities.el. =org-html-entities= was an internal variable, but it is possible that some users did write code using it. *** =org-bbdb-anniversary-format-alist= has changed Please check the docstring and update your settings accordingly. *** Deleted =org-mode-p= This function has been deleted: please update your code. ** Important new features *** New Org to ODT exporter Jambunathan's Org to ODT exporter is now part of Org. To use it, it `C-c C-e o' in an Org file. See the documentation for more information on how to customize it. *** org-capture.el is now the default capture system This replaces the earlier system org-remember. The manual only describes org-capture, but for people who prefer to continue to use org-remember, we keep a static copy of the former manual section [[https://orgmode.org/org-remember.pdf][chapter about remember]]. The new system has a technically cleaner implementation and more possibilities for capturing different types of data. See [[msg:C46F10DC-DE51-43D4-AFFE-F71E440D1E1F@gmail.com][Carsten's announcement]] for more details. To switch over to the new system: 1. Run : M-x org-capture-import-remember-templates RET to get a translated version of your remember templates into the new variable =org-capture-templates=. This will "mostly" work, but maybe not for all cases. At least it will give you a good place to modify your templates. After running this command, enter the customize buffer for this variable with : M-x customize-variable RET org-capture-templates RET and convince yourself that everything is OK. Then save the customization. 2. Bind the command =org-capture= to a key, similar to what you did with org-remember: : (define-key global-map "\C-cc" 'org-capture) If your fingers prefer =C-c r=, you can also use this key once you have decided to move over completely to the new implementation. During a test time, there is nothing wrong with using both system in parallel. ** New libraries *** New Org libraries **** org-eshell.el (Konrad Hinsen) Implement links to eshell buffers. **** org-special-blocks (Carsten Dominik) This package generalizes the #+begin_foo and #+end_foo tokens. To use, put the following in your init file: #+BEGIN_EXAMPLE (require 'org-special-blocks) #+END_EXAMPLE The tokens #+begin_center, #+begin_verse, etc. existed previously. This package generalizes them (at least for the LaTeX and html exporters). When a #+begin_foo token is encountered by the LaTeX exporter, it is expanded into \begin{foo}. The text inside the environment is not protected, as text inside environments generally is. When #+begin_foo is encountered by the html exporter, a div with class foo is inserted into the HTML file. It is up to the user to add this class to his or her stylesheet if this div is to mean anything. **** org-taskjuggler.el (Christian Egli) Christian Egli's /org-taskjuggler.el/ module is now part of Org. He also wrote a [[https://orgmode.org/worg/org-tutorials/org-taskjuggler.php][tutorial]] for it. **** org-ctags.el (Paul Sexton) Targets like =<>= can now be found by Emacs's etag functionality, and Org-mode links can be used to link to etags, also in non-Org-mode files. For details, see the file /org-ctags.el/. This feature uses a new hook =org-open-link-functions= which will call function to do something special with text links. Thanks to Paul Sexton for this contribution. **** org-docview.el (Jan Böcker) This new module allows links to various file types using docview, where Emacs displays images of document pages. Docview link types can point to a specific page in a document, for example to page 131 of the Org-mode manual: : [[docview:~/.elisp/org/doc/org.pdf::131][Org-Mode Manual]] Thanks to Jan Böcker for this contribution. *** New Babel libraries - ob-picolisp.el (Thorsten Jolitz) - ob-fortran.el (Sergey Litvinov) - ob-shen.el (Eric Schulte) - ob-maxima.el (Eric S Fraga) - ob-java.el (Eric Schulte) - ob-lilypond.el (Martyn Jago) - ob-awk.el (Eric Schulte) ** Other new features and various enhancements *** Hyperlinks **** Org-BibTeX -- major improvements Provides support for managing bibtex bibliographical references data in headline properties. Each headline corresponds to a single reference and the relevant bibliographic meta-data is stored in headline properties, leaving the body of the headline free to hold notes and comments. Org-bibtex is aware of all standard bibtex reference types and fields. The key new functions are - org-bibtex-check :: queries the user to flesh out all required (and with prefix argument optional) bibtex fields available for the specific reference =type= of the current headline. - org-bibtex-create :: Create a new entry at the given level, using org-bibtex-check to flesh out the relevant fields. - org-bibtex-yank :: Yank a bibtex entry on the kill ring as a formatted Org-mode headline into the current buffer - org-bibtex-export-to-kill-ring :: Export the current headline to the kill ring as a formatted bibtex entry. **** org-gnus.el now allows link creation from messages You can now create links from messages. This is particularly useful when the user wants to stored messages that he sends, for later check. Thanks to Ulf Stegemann for the patch. **** Modified link escaping David Maus worked on `org-link-escape'. See [[msg:87k4gysacq.wl%dmaus@ictsoc.de][his message]]: : Percent escaping is used in Org mode to escape certain characters : in links that would either break the parser (e.g. square brackets : in link target or description) or are not allowed to appear in : a particular link type (e.g. non-ascii characters in a http: : link). : : With this change in place Org will apply percent escaping and : unescaping more consistently especially for non-ascii characters. : Additionally some of the outstanding bugs or glitches concerning : percent escaped links are solved. Thanks a lot to David for this work. **** Make =org-store-link= point to directory in a dired buffer When, in a dired buffer, the cursor is not in a line listing a file, `org-store-link' will store a link to the directory. Patch by Stephen Eglen. **** Allow regexps in =org-file-apps= to capture link parameters The way extension regexps in =org-file-apps= are handled has changed. Instead of matching against the file name, the regexps are now matched against the whole link, and you can use grouping to extract link parameters which you can then use in a command string to be executed. For example, to allow linking to PDF files using the syntax =file:/doc.pdf::=, you can add the following entry to org-file-apps: #+begin_example Extension: \.pdf::\([0-9]+\)\' Command: evince "%s" -p %1 #+end_example Thanks to Jan Böcker for a patch to this effect. *** Dates and time **** Allow relative time when scheduling/adding a deadline You can now use relative duration strings like "-2d" or "++3w" when calling =org-schedule= or =org-deadline=: it will schedule (or set the deadline for) the item respectively two days before today and three weeks after the current timestamp, if any. You can use this programmatically: =(org-schedule nil "+2d")= will work on the current entry. You can also use this while (bulk-)rescheduling and (bulk-)resetting the deadline of (several) items from the agenda. Thanks to Memnon Anon for a heads up about this! **** American-style dates are now understood by =org-read-date= So when you are prompted for a date, you can now answer like this #+begin_example 2/5/3 --> 2003-02-05 2/5 --> -02-05 #+end_example *** Agenda **** =org-agenda-custom-commands= has a default value This option used to be `nil' by default. This now has a default value, displaying an agenda and all TODOs. See the docstring for details. Thanks to Carsten for this. **** Improved filtering through =org-agenda-to-appt= The new function allows the user to refine the scope of entries to pass to =org-agenda-get-day-entries= and allows to filter out entries using a function. Thanks to Peter Münster for raising a related issue and to Tassilo Horn for this idea. Also thanks to Peter Münster for [[git:68ffb7a7][fixing a small bug]] in the final implementation. **** Allow ap/pm times in agenda time grid Times in the agenda can now be displayed in am/pm format. See the new variable =org-agenda-timegrid-use-ampm=. Thanks to C. A. Webber for a patch to this effect. **** Agenda: Added a bulk "scattering" command =B S= in the agenda buffer will cause tasks to be rescheduled a random number of days into the future, with 7 as the default. This is useful if you've got a ton of tasks scheduled for today, you realize you'll never deal with them all, and you just want them to be distributed across the next N days. When called with a prefix arg, rescheduling will avoid weekend days. Thanks to John Wiegley for this. *** Exporting **** Simplification of org-export-html-preamble/postamble When set to `t', export the preamble/postamble as usual, honoring the =org-export-email/author/creator-info= variables. When set to a formatting string, insert this string. See the docstring of these variable for details about available %-sequences. You can set =:html-preamble= in publishing project in the same way: `t' means to honor =:email/creator/author-info=, and a formatting string will insert a string. **** New exporters to Latin-1 and UTF-8 While Ulf Stegemann was going through the entities list to improve the LaTeX export, he had the great idea to provide representations for many of the entities in Latin-1, and for all of them in UTF-8. This means that we can now export files rich in special symbols to Latin-1 and to UTF-8 files. These new exporters can be reached with the commands =C-c C-e n= and =C-c C-e u=, respectively. When there is no representation for a given symbol in the targeted coding system, you can choose to keep the TeX-macro-like representation, or to get an "explanatory" representation. For example, =\simeq= could be represented as "[approx. equal to]". Please use the variable =org-entities-ascii-explanatory= to state your preference. **** HTML export: Add class to outline containers using property The =HTML_CONTAINER_CLASS= property can now be used to add a class name to the outline container of a node in HTML export. **** Throw an error when creating an image from a LaTeX snippet fails This behavior can be configured with the new option variable =org-format-latex-signal-error=. **** Support for creating BEAMER presentations from Org-mode documents Org-mode documents or subtrees can now be converted directly in to BEAMER presentation. Turning a tree into a simple presentations is straight forward, and there is also quite some support to make richer presentations as well. See the [[https://orgmode.org/manual/Beamer-class-export.html#Beamer-class-export][BEAMER section]] in the manual for more details. Thanks to everyone who has contributed to the discussion about BEAMER support and how it should work. This was a great example for how this community can achieve a much better result than any individual could. *** Refiling **** Refile targets can now be cached You can turn on caching of refile targets by setting the variable =org-refile-use-cache=. This should speed up refiling if you have many eligible targets in many files. If you need to update the cache because Org misses a newly created entry or still offers a deleted one, press =C-0 C-c C-w=. **** New logging support for refiling Whenever you refile an item, a time stamp and even a note can be added to this entry. For details, see the new option =org-log-refile=. Thanks to Charles Cave for this idea. *** Completion **** In-buffer completion is now done using John Wiegley's pcomplete.el Thanks to John Wiegley for much of this code. *** Tables **** New command =org-table-transpose-table-at-point= See the docstring. This hack from Juan Pechiar is now part of Org's core. Thanks to Juan! **** Display field's coordinates when editing it with =C-c `= When editing a field with =C-c `=, the field's coordinate will the displayed in the buffer. Thanks to Michael Brand for a patch to this effect. **** Spreadsheet computation of durations and time values If you want to compute time values use the =T= flag, either in Calc formulas or Elisp formulas: | Task 1 | Task 2 | Total | |--------+--------+---------| | 35:00 | 35:00 | 1:10:00 | #+TBLFM: @2$3=$1+$2;T Values must be of the form =[HH:]MM:SS=, where hours are optional. Thanks to Martin Halder, Eric Schulte and Carsten for code and feedback on this. **** Implement formulas applying to field ranges Carsten implemented this field-ranges formulas. : A frequently requested feature for tables has been to be able to define : row formulas in a way similar to column formulas. The patch below allows : things like : : @3= : @2$2..@5$7= : @I$2..@II$4= : : as the left hand side for table formulas in order to write a formula that : is valid for an entire column or for a rectangular section in a : table. Thanks a lot to Carsten for this. **** Sending radio tables from org buffers is now allowed Org radio tables can no also be sent inside Org buffers. Also, there is a new hook which get called after a table has been sent. Thanks to Seweryn Kokot. *** Lists **** Improved handling of lists Nicolas Goaziou extended and improved the way Org handles lists. 1. Indentation of text determines again end of items in lists. So, some text less indented than the previous item doesn't close the whole list anymore, only all items more indented than it. 2. Alphabetical bullets are implemented, through the use of the variable `org-alphabetical-lists'. This also adds alphabetical counters like [@c] or [@W]. 3. Lists can now safely contain drawers, inline tasks, or various blocks, themselves containing lists. Two variables are controlling this: `org-list-forbidden-blocks', and `org-list-export-context'. 4. Improve `newline-and-indent' (C-j): used in an item, it will keep text from moving at column 0. This allows to split text and make paragraphs and still not break the list. 5. Improve `org-toggle-item' (C-c -): used on a region with standard text, it will change the region into one item. With a prefix argument, it will fallback to the previous behavior and make every line in region an item. It permits to easily integrate paragraphs inside a list. 6. `fill-paragraph' (M-q) now understands lists. It can freely be used inside items, or on text just after a list, even with no blank line around, without breaking list structure. Thanks a lot to Nicolas for all this! *** Inline display of linked images Images can now be displayed inline. The key C-c C-x C-v does toggle the display of such images. Note that only image links that have no description part will be inlined. *** Implement offsets for ordered lists If you want to start an ordered plain list with a number different from 1, you can now do it like this: : 1. [@start:12] will star a lit a number 12 *** Babel: code block body expansion for table and preview In org-babel, code is "expanded" prior to evaluation. I.e. the code that is actually evaluated comprises the code block contents, augmented with the extra code which assigns the referenced data to variables. It is now possible to preview expanded contents, and also to expand code during tangling. This expansion takes into account all header arguments, and variables. A new keybinding `C-c M-b p' bound to `org-babel-expand-src-block' can be used from inside of a source code block to preview its expanded contents (which can be very useful for debugging). tangling The expanded body can now be tangled, this includes variable values which may be the results of other source-code blocks, or stored in headline properties or tables. One possible use for this is to allow those using org-babel for their emacs initialization to store values (e.g. usernames, passwords, etc...) in headline properties or in tables. Org-babel now supports three new header arguments, and new default behavior for handling horizontal lines in tables (hlines), column names, and rownames across all languages. *** Editing Convenience and Appearance **** New command =org-copy-visible= (=C-c C-x v=) This command will copy the visible text in the region into the kill ring. Thanks to Florian Beck for this function and to Carsten for adding it to org.el and documenting it! **** Make it possible to protect hidden subtrees from being killed by =C-k= See the new variable =org-ctrl-k-protect-subtree=. This was a request by Scott Otterson. **** Implement pretty display of entities, sub-, and superscripts. The command =C-c C-x \= toggles the display of Org's special entities like =\alpha= as pretty unicode characters. Also, sub and superscripts are displayed in a pretty way (raised/lower display, in a smaller font). If you want to exclude sub- and superscripts, see the variable =org-pretty-entities-include-sub-superscripts=. Thanks to Eric Schulte and Ulf Stegeman for making this possible. **** New faces for title, date, author and email address lines The keywords in these lines are now dimmed out, and the title is displayed in a larger font, and a special font is also used for author, date, and email information. This is implemented by the following new faces: =org-document-title= =org-document-info= =org-document-info-keyword= In addition, the variable =org-hidden-keywords= can be used to make the corresponding keywords disappear. Thanks to Dan Davison for this feature. **** Simpler way to specify faces for tags and todo keywords The variables =org-todo-keyword-faces=, =org-tag-faces=, and =org-priority-faces= now accept simple color names as specifications. The colors will be used as either foreground or background color for the corresponding keyword. See also the variable =org-faces-easy-properties=, which governs which face property is affected by this setting. This is really a great simplification for setting keyword faces. The change is based on an idea and patch by Ryan Thompson. **** in tables now means fixed width, not maximum width Requested by Michael Brand. **** Better level cycling function =TAB= in an empty headline cycles the level of that headline through likely states. Ryan Thompson implemented an improved version of this function, which does not depend upon when exactly this command is used. Thanks to Ryan for this improvement. **** Adaptive filling For paragraph text, =org-adaptive-fill-function= did not handle the base case of regular text which needed to be filled. This is now fixed. Among other things, it allows email-style ">" comments to be filled correctly. Thanks to Dan Hackney for this patch. **** `org-reveal' (=C-c C-r=) also decrypts encrypted entries (org-crypt.el) Thanks to Richard Riley for triggering this change. **** Better automatic letter selection for TODO keywords When all first letters of keywords have been used, Org now assigns more meaningful characters based on the keywords. Thanks to Mikael Fornius for this patch. *** Clocking **** Clock: Allow synchronous update of timestamps in CLOCK log Using =S-M-= on CLOCK log timestamps will increase/decrease the two timestamps on this line so that duration will keep the same. Note that duration can still be slightly modified in case a timestamp needs some rounding. Thanks to Rainer Stengele for this idea. **** Localized clock tables Clock tables now support a new =:lang= parameter, allowing the user to customize the localization of the table headers. See the variable =org-clock-clocktable-language-setup= which controls available translated strings. **** Show clock overruns in mode line When clocking an item with a planned effort, overrunning the planned time is now made visible in the mode line, for example using the new face =org-mode-line-clock-overrun=, or by adding an extra string given by =org-task-overrun-text=. Thanks to Richard Riley for a patch to this effect. **** Clock reports can now include the running, incomplete clock If you have a clock running, and the entry being clocked falls into the scope when creating a clock table, the time so far spent can be added to the total. This behavior depends on the setting of =org-clock-report-include-clocking-task=. The default is =nil=. Thanks to Bernt Hansen for this useful addition. *** Misc **** Improvements with inline tasks and indentation There is now a configurable way on how to export inline tasks. See the new variable =org-inlinetask-export-templates=. Thanks to Nicolas Goaziou for coding these changes. **** A property value of =nil= now means to unset a property This can be useful in particular with property inheritance, if some upper level has the property, and some grandchild of it would like to have the default settings (i.e. not overruled by a property) back. Thanks to Robert Goldman and Bernt Hansen for suggesting this change. **** New helper functions in org-table.el There are new functions to access and write to a specific table field. This is for hackers, and maybe for the org-babel people. #+begin_example org-table-get org-table-put org-table-current-line org-table-goto-line #+end_example **** Archiving: Allow to reverse order in target node The new option =org-archive-reversed-order= allows to have archived entries inserted in a last-on-top fashion in the target node. This was requested by Tom. **** Org-reveal: Double prefix arg shows the entire subtree of the parent This can help to get out of an inconsistent state produced for example by viewing from the agenda. This was a request by Matt Lundin. * License This file is part of GNU Emacs. GNU Emacs 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. GNU Emacs 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 GNU Emacs. If not, see . org-mode-9.7.29+dfsg/etc/csl/000077500000000000000000000000001500430433700156055ustar00rootroot00000000000000org-mode-9.7.29+dfsg/etc/csl/README000066400000000000000000000005001500430433700164600ustar00rootroot00000000000000These data files are used by Org's oc-csl.el library. LICENSE INFORMATION chicago-author-date.csl locales-en-US.xml Both of these files are part of the Citation Style Language (CSL) project () and are released under the Creative Commons Attribution-ShareAlike 3.0 Unported license. org-mode-9.7.29+dfsg/etc/csl/chicago-author-date.csl000066400000000000000000000532631500430433700221310ustar00rootroot00000000000000 org-mode-9.7.29+dfsg/etc/csl/locales-en-US.xml000066400000000000000000000261501500430433700207020ustar00rootroot00000000000000 Andrew Dunning Sebastian Karcher Rintze M. Zelle This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License 2015-10-10T23:31:02+00:00 accessed and and others anonymous anon. at available at by circa c. cited edition editions ed. et al. forthcoming from ibid. in in press internet interview letter no date n.d. online presented at the reference references ref. refs. retrieved scale version AD BC th st nd rd th th th first second third fourth fifth sixth seventh eighth ninth tenth book books chapter chapters column columns figure figures folio folios number numbers line lines note notes opus opera page pages page pages paragraph paragraphs part parts section sections sub verbo sub verbis verse verses volume volumes bk. bks. chap. chaps. col. cols. fig. figs. fol. fols. no. nos. l. ll. n. nn. op. opp. p. pp. p. pp. para. paras. pt. pts. sec. secs. s.v. s.vv. v. vv. vol. vols. ¶¶ § §§ director directors editor editors editor editors illustrator illustrators translator translators editor & translator editors & translators dir. dirs. ed. eds. ed. eds. ill. ills. tran. trans. ed. & tran. eds. & trans. by directed by edited by edited by illustrated by interview by to by translated by edited & translated by dir. by ed. by ed. by illus. by trans. by ed. & trans. by January February March April May June July August September October November December Jan. Feb. Mar. Apr. May Jun. Jul. Aug. Sep. Oct. Nov. Dec. Spring Summer Autumn Winter org-mode-9.7.29+dfsg/etc/styles/000077500000000000000000000000001500430433700163475ustar00rootroot00000000000000org-mode-9.7.29+dfsg/etc/styles/OrgOdtContentTemplate.xml000066400000000000000000000507461500430433700233320ustar00rootroot00000000000000 org-mode-9.7.29+dfsg/etc/styles/OrgOdtStyles.xml000066400000000000000000002120521500430433700214750ustar00rootroot00000000000000 / / org-mode-9.7.29+dfsg/etc/styles/README000066400000000000000000000025051500430433700172310ustar00rootroot00000000000000The files OrgOdtContentTemplate.xml and OrgOdtStyles.xml have the following copyright information: Copyright (C) 2010-2025 Free Software Foundation, Inc. These files are part of GNU Emacs. GNU Emacs 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. GNU Emacs 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 GNU Emacs. If not, see . Author: Jambunathan K Keywords: outlines, hypermedia, calendar, wp URL: https://orgmode.org Commentary: These files are part of Org-mode's OpenDocument export module. OrgOdtContentTemplate.xml provides a template within which the content of an exported document is enclosed. This file contributes to "content.xml" file within an exported document and acts as a repository of automatic styles. OrgOdtStyles.xml contributes to "styles.xml" file within an exported document and acts as a repository of custom styles. org-mode-9.7.29+dfsg/lisp/000077500000000000000000000000001500430433700152205ustar00rootroot00000000000000org-mode-9.7.29+dfsg/lisp/Makefile000066400000000000000000000046151500430433700166660ustar00rootroot00000000000000.SUFFIXES: # we don't need default suffix rules ifeq ($(MAKELEVEL), 0) $(error This make needs to be started as a sub-make from the toplevel directory.) endif LISPV := org-version.el LISPI := org-loaddefs.el LISPA := $(LISPV) $(LISPI) LISPB := $(LISPA:%el=%elc) org-install.elc LISPF := $(filter-out $(LISPA),$(sort $(wildcard *.el))) LISPC := $(filter-out $(LISPB) $(LISPN:%el=%elc),$(LISPF:%el=%elc)) LISPN := $(filter-out $(LISPB) $(LISPN:%el=%eln),$(LISPF:%el=%eln)) _ORGCM_ := dirall single native source slint1 slint2 -include local.mk .PHONY: all compile compile-dirty \ $(_ORGCM_) $(_ORGCM_:%=compile-%) \ autoloads \ install clean cleanauto cleanall cleanelc clean-install # do not clean here, done in toplevel make all compile compile-dirty:: | autoloads ifeq ($(filter-out $(_ORGCM_),$(ORGCM)),) $(MAKE) compile-$(ORGCM) else $(error ORGCM has illegal value $(ORGCM) (valid: $(_ORGCM_))) endif compile-dirall: dirall compile-single: $(LISPC) | single compile-native: $(LISPN) | native compile-source: | source dirall compile-slint1: | dirall slint1 compile-slint2: | source dirall slint1 # internal dirall: @$(info ==================== $@ ====================) @$(ELCDIR) single: @$(info ==================== $@ ====================) native: @$(info ==================== $@ ====================) source: cleanelc @$(info ==================== $@ ====================) @$(foreach elc,$(LISPC),$(MAKE) $(elc) && $(RM) $(elc);) slint1: @$(info ==================== $@ ====================) @$(foreach elc,$(LISPC),$(RM) $(elc); $(MAKE) $(elc);) %.elc: %.el @$(info Compiling single $(abspath $<)...) -@$(ELC) $< %.eln: %.el @$(info Native compiling single $(abspath $<)...) -@$(ELN) $< autoloads: cleanauto $(LISPI) $(LISPV) $(LISPV): $(LISPF) @echo "org-version: $(ORGVERSION) ($(GITVERSION))" @$(RM) $(@) @$(MAKE_ORG_VERSION) $(LISPI): $(LISPV) $(LISPF) @echo "org-loaddefs: $(ORGVERSION) ($(GITVERSION))" @$(RM) $(@) @$(MAKE_ORG_INSTALL) install: compile $(LISPF) if [ ! -d $(DESTDIR)$(lispdir) ] ; then \ $(MKDIR) $(DESTDIR)$(lispdir) ; \ fi ; $(CP) $(LISPC) $(LISPF) $(LISPA) $(DESTDIR)$(lispdir) cleanauto clean cleanall:: $(RM) $(LISPA) $(LISPB) clean cleanall cleanelc:: $(RM) *.elc clean-install: if [ -d $(DESTDIR)$(lispdir) ] ; then \ $(RM) $(DESTDIR)$(lispdir)/org*.el* $(DESTDIR)$(lispdir)/ob*.el* $(DESTDIR)$(lispdir)/ol*.el* $(DESTDIR)$(lispdir)/ox*.el* ; \ fi ; org-mode-9.7.29+dfsg/lisp/ob-C.el000066400000000000000000000424241500430433700163300ustar00rootroot00000000000000;;; ob-C.el --- Babel Functions for C and Similar Languages -*- lexical-binding: t; -*- ;; Copyright (C) 2010-2025 Free Software Foundation, Inc. ;; Author: Eric Schulte ;; Thierry Banel ;; Maintainer: Thierry Banel ;; Keywords: literate programming, reproducible research ;; URL: https://orgmode.org ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; Org-Babel support for evaluating C, C++, D code. ;; ;; very limited implementation: ;; - currently only support :results output ;; - not much in the way of error feedback ;;; Code: (require 'org-macs) (org-assert-version) (require 'cc-mode) (require 'ob) (require 'org-macs) (declare-function org-entry-get "org" (pom property &optional inherit literal-nil)) (defvar org-babel-tangle-lang-exts) (add-to-list 'org-babel-tangle-lang-exts '("C++" . "cpp")) (add-to-list 'org-babel-tangle-lang-exts '("D" . "d")) (defvar org-babel-default-header-args:C '()) (defconst org-babel-header-args:C '((includes . :any) (defines . :any) (main . :any) (flags . :any) (cmdline . :any) (libs . :any)) "C/C++-specific header arguments.") (defconst org-babel-header-args:C++ (append '((namespaces . :any)) org-babel-header-args:C) "C++-specific header arguments.") (defcustom org-babel-C-compiler "gcc" "Command used to compile a C source code file into an executable. May be either a command in the path, like gcc or an absolute path name, like /usr/local/bin/gcc parameter may be used, like gcc -v" :group 'org-babel :version "24.3" :type 'string) (defcustom org-babel-C++-compiler "g++" "Command used to compile a C++ source code file into an executable. May be either a command in the path, like g++ or an absolute path name, like /usr/local/bin/g++ parameter may be used, like g++ -v" :group 'org-babel :version "24.3" :type 'string) (defcustom org-babel-D-compiler "rdmd" "Command used to compile and execute a D source code file. May be either a command in the path, like rdmd or an absolute path name, like /usr/local/bin/rdmd parameter may be used, like rdmd --chatty" :group 'org-babel :version "24.3" :type 'string) (defvar org-babel-c-variant nil "Internal variable used to hold which type of C (e.g. C or C++ or D) is currently being evaluated.") (defun org-babel-execute:cpp (body params) "Execute BODY according to its header arguments PARAMS. This function calls `org-babel-execute:C++'." (org-babel-execute:C++ body params)) (defun org-babel-expand-body:cpp (body params) "Expand C++ BODY with org-babel according to its header arguments PARAMS." (org-babel-expand-body:C++ body params)) (defun org-babel-execute:C++ (body params) "Execute C++ BODY with org-babel according to its header arguments PARAMS. This function is called by `org-babel-execute-src-block'." (let ((org-babel-c-variant 'cpp)) (org-babel-C-execute body params))) (defun org-babel-expand-body:C++ (body params) "Expand C++ BODY with org-babel according to its header arguments PARAMS." (let ((org-babel-c-variant 'cpp)) (org-babel-C-expand-C++ body params))) (defun org-babel-execute:D (body params) "Execute D BODY with org-babel according to its header arguments PARAMS. This function is called by `org-babel-execute-src-block'." (let ((org-babel-c-variant 'd)) (org-babel-C-execute body params))) (defun org-babel-expand-body:D (body params) "Expand D BODY with org-babel according to its header arguments PARAMS." (let ((org-babel-c-variant 'd)) (org-babel-C-expand-D body params))) (defun org-babel-execute:C (body params) "Execute a C BODY according to its header arguments PARAMS. This function is called by `org-babel-execute-src-block'." (let ((org-babel-c-variant 'c)) (org-babel-C-execute body params))) (defun org-babel-expand-body:C (body params) "Expand C BODY according to its header arguments PARAMS." (let ((org-babel-c-variant 'c)) (org-babel-C-expand-C body params))) (defun org-babel-C-execute (body params) "Execute C/C++/D BODY according to its header arguments PARAMS. This function should only be called by `org-babel-execute:C' or `org-babel-execute:C++' or `org-babel-execute:D'." (let* ((tmp-src-file (org-babel-temp-file "C-src-" (pcase org-babel-c-variant (`c ".c") (`cpp ".cpp") (`d ".d")))) (tmp-bin-file ;not used for D (org-babel-process-file-name (org-babel-temp-file "C-bin-" org-babel-exeext))) (cmdline (cdr (assq :cmdline params))) (cmdline (if cmdline (concat " " cmdline) "")) (flags (cdr (assq :flags params))) (flags (mapconcat 'identity (if (listp flags) flags (list flags)) " ")) (libs (org-babel-read (or (cdr (assq :libs params)) (org-entry-get nil "libs" t)) nil)) (libs (mapconcat #'identity (if (listp libs) libs (list libs)) " ")) (full-body (pcase org-babel-c-variant (`c (org-babel-C-expand-C body params)) (`cpp (org-babel-C-expand-C++ body params)) (`d (org-babel-C-expand-D body params))))) (with-temp-file tmp-src-file (insert full-body)) (pcase org-babel-c-variant ((or `c `cpp) (org-babel-eval (format "%s -o %s %s %s %s" (pcase org-babel-c-variant (`c org-babel-C-compiler) (`cpp org-babel-C++-compiler)) tmp-bin-file flags (org-babel-process-file-name tmp-src-file) libs) "")) (`d nil)) ;; no separate compilation for D (let ((results (org-babel-eval (pcase org-babel-c-variant ((or `c `cpp) (concat tmp-bin-file cmdline)) (`d (format "%s %s %s %s" org-babel-D-compiler flags (org-babel-process-file-name tmp-src-file) cmdline))) ""))) (when results (setq results (org-remove-indentation results)) (org-babel-reassemble-table (org-babel-result-cond (cdr (assq :result-params params)) results (let ((tmp-file (org-babel-temp-file "c-"))) (with-temp-file tmp-file (insert results)) (org-babel-import-elisp-from-file tmp-file))) (org-babel-pick-name (cdr (assq :colname-names params)) (cdr (assq :colnames params))) (org-babel-pick-name (cdr (assq :rowname-names params)) (cdr (assq :rownames params))))) ))) (defun org-babel-C-expand-C++ (body params) "Expand C/C++ BODY with according to its header arguments PARAMS." (org-babel-C-expand-C body params)) (defun org-babel-C-expand-C (body params) "Expand C/C++ BODY according to its header arguments PARAMS." (let ((vars (org-babel--get-vars params)) (colnames (cdr (assq :colname-names params))) (main-p (not (string= (cdr (assq :main params)) "no"))) (includes (org-babel-read (cdr (assq :includes params)) nil)) (defines (org-babel-read (cdr (assq :defines params)) nil)) (namespaces (org-babel-read (cdr (assq :namespaces params)) nil)) (prologue (cdr (assq :prologue params))) (epilogue (cdr (assq :epilogue params)))) (when (stringp includes) (setq includes (split-string includes))) (when (stringp namespaces) (setq namespaces (split-string namespaces))) (when (stringp defines) (let ((y nil) (result (list t))) (dolist (x (split-string defines)) (if (null y) (setq y x) (nconc result (list (concat y " " x))) (setq y nil))) (setq defines (cdr result)))) (setq body (concat (and prologue (concat prologue "\n")) body (and epilogue (concat "\n" epilogue "\n")))) (mapconcat 'identity (list ;; includes (mapconcat (lambda (inc) ;; :includes '( ) gives us a list of ;; symbols; convert those to strings. (when (symbolp inc) (setq inc (symbol-name inc))) (if (string-prefix-p "<" inc) (format "#include %s" inc) (format "#include \"%s\"" inc))) includes "\n") ;; defines (mapconcat (lambda (inc) (format "#define %s" inc)) (if (listp defines) defines (list defines)) "\n") ;; namespaces (mapconcat (lambda (inc) (format "using namespace %s;" inc)) namespaces "\n") ;; variables (mapconcat 'org-babel-C-var-to-C vars "\n") ;; table sizes (mapconcat 'org-babel-C-table-sizes-to-C vars "\n") ;; tables headers utility (when colnames (org-babel-C-utility-header-to-C)) ;; tables headers (mapconcat (lambda (head) (let* ((tblnm (car head)) (tbl (cdr (car (let* ((el vars)) (while (not (or (equal tblnm (caar el)) (not el))) (setq el (cdr el))) el)))) (type (org-babel-C-val-to-base-type tbl))) (org-babel-C-header-to-C head type))) colnames "\n") ;; body (if main-p (org-babel-C-ensure-main-wrap body) body) "\n") "\n"))) (defun org-babel-C-expand-D (body params) "Expand D BODY according to its header arguments PARAMS." (let ((vars (org-babel--get-vars params)) (colnames (cdr (assq :colname-names params))) (main-p (not (string= (cdr (assq :main params)) "no"))) (imports (or (cdr (assq :imports params)) (org-babel-read (org-entry-get nil "imports" t))))) (when (stringp imports) (setq imports (split-string imports))) (setq imports (append imports '("std.stdio" "std.conv"))) (mapconcat 'identity (list "module mmm;" ;; imports (mapconcat (lambda (inc) (format "import %s;" inc)) imports "\n") ;; variables (mapconcat 'org-babel-C-var-to-C vars "\n") ;; table sizes (mapconcat 'org-babel-C-table-sizes-to-C vars "\n") ;; tables headers utility (when colnames (org-babel-C-utility-header-to-C)) ;; tables headers (mapconcat (lambda (head) (let* ((tblnm (car head)) (tbl (cdr (car (let* ((el vars)) (while (not (or (equal tblnm (caar el)) (not el))) (setq el (cdr el))) el)))) (type (org-babel-C-val-to-base-type tbl))) (org-babel-C-header-to-C head type))) colnames "\n") ;; body (if main-p (org-babel-C-ensure-main-wrap body) body) "\n") "\n"))) (defun org-babel-C-ensure-main-wrap (body) "Wrap BODY in a \"main\" function call if none exists." (if (string-match "^[ \t]*[intvod]+[ \t\n\r]*main[ \t]*(.*)" body) body (format "int main() {\n%s\nreturn 0;\n}\n" body))) (defun org-babel-prep-session:C (_session _params) "Throw and error that sessions are not supported. This function does nothing as C is a compiled language with no support for sessions." (error "C is a compiled language -- no support for sessions")) (defun org-babel-load-session:C (_session _body _params) "Throw and error that sessions are not supported. This function does nothing as C is a compiled language with no support for sessions." (error "C is a compiled language -- no support for sessions")) ;; helper functions (defun org-babel-C-format-val (type val) "Handle the FORMAT part of TYPE with the data from VAL." (let ((format-data (cadr type))) (if (stringp format-data) (cons "" (format format-data val)) (funcall format-data val)))) (defun org-babel-C-val-to-C-type (val) "Determine the type of VAL. Return a list (TYPE-NAME FORMAT). TYPE-NAME should be the name of the type. FORMAT can be either a format string or a function which is called with VAL." (let* ((basetype (org-babel-C-val-to-base-type val)) (type (pcase basetype (`integerp '("int" "%d")) (`floatp '("double" "%s")) ;; %f rounds, use %s to print the float literally (`stringp (list (if (eq org-babel-c-variant 'd) "string" "const char*") "\"%s\"")) (_ (error "Unknown type %S" basetype))))) (cond ((integerp val) type) ;; an integer declared in the #+begin_src line ((floatp val) type) ;; a numeric declared in the #+begin_src line ((and (listp val) (listp (car val))) ;; a table `(,(car type) (lambda (val) (cons (pcase org-babel-c-variant ((or `c `cpp) (format "[%d][%d]" (length val) (length (car val)))) (`d (format "[%d][%d]" (length (car val)) (length val)))) (concat (if (eq org-babel-c-variant 'd) "[\n" "{\n") (mapconcat (lambda (v) (concat (if (eq org-babel-c-variant 'd) " [" " {") (mapconcat (lambda (w) (format ,(cadr type) w)) v ",") (if (eq org-babel-c-variant 'd) "]" "}"))) val ",\n") (if (eq org-babel-c-variant 'd) "\n]" "\n}")))))) ((or (listp val) (vectorp val)) ;; a list declared in the #+begin_src line `(,(car type) (lambda (val) (cons (format "[%d]" (length val)) (concat (if (eq org-babel-c-variant 'd) "[" "{") (mapconcat (lambda (v) (format ,(cadr type) v)) val ",") (if (eq org-babel-c-variant 'd) "]" "}")))))) (t ;; treat unknown types as string type)))) (defun org-babel-C-val-to-base-type (val) "Determine the base type of VAL. The type is: - `integerp' if all base values are integers; - `floatp' if all base values are either floating points or integers; - `stringp' otherwise." (cond ((integerp val) 'integerp) ((floatp val) 'floatp) ((or (listp val) (vectorp val)) (let ((type nil)) (mapc (lambda (v) (pcase (org-babel-C-val-to-base-type v) (`stringp (setq type 'stringp)) (`floatp (when (or (not type) (eq type 'integerp)) (setq type 'floatp))) (`integerp (unless type (setq type 'integerp))))) val) type)) (t 'stringp))) (defun org-babel-C-var-to-C (pair) "Convert PAIR of (var . val) C variable assignment." ;; TODO list support (let ((var (car pair)) (val (cdr pair))) (when (symbolp val) (setq val (symbol-name val)) (when (= (length val) 1) (setq val (string-to-char val)))) (let* ((type-data (org-babel-C-val-to-C-type val)) (type (car type-data)) (formatted (org-babel-C-format-val type-data val)) (suffix (car formatted)) (data (cdr formatted))) (pcase org-babel-c-variant ((or `c `cpp) (format "%s %s%s = %s;" type var suffix data)) (`d (format "%s%s %s = %s;" type suffix var data)))))) (defun org-babel-C-table-sizes-to-C (pair) "Create constants of table dimensions, if PAIR is a table." (when (listp (cdr pair)) (cond ((listp (cadr pair)) ;; a table (concat (format "const int %s_rows = %d;" (car pair) (length (cdr pair))) "\n" (format "const int %s_cols = %d;" (car pair) (length (cadr pair))))) (t ;; a list declared in the #+begin_src line (format "const int %s_cols = %d;" (car pair) (length (cdr pair))))))) (defun org-babel-C-utility-header-to-C () "Generate a utility function to convert a column name into a column number." (pcase org-babel-c-variant ((or `c `cpp) (concat " #ifndef _STRING_H #include #endif int get_column_num (int nbcols, const char** header, const char* column) { int c; for (c=0; c ;; Keywords: literate programming, reproducible research, R, statistics ;; URL: https://orgmode.org ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; Org-Babel support for evaluating R code ;;; Code: (require 'org-macs) (org-assert-version) (require 'cl-lib) (require 'ob) (declare-function orgtbl-to-tsv "org-table" (table params)) (declare-function run-ess-r "ext:ess-r-mode" (&optional start-args)) (declare-function inferior-ess-send-input "ext:ess-inf" ()) (declare-function ess-make-buffer-current "ext:ess-inf" ()) (declare-function ess-eval-buffer "ext:ess-inf" (vis)) (declare-function ess-wait-for-process "ext:ess-inf" (&optional proc sec-prompt wait force-redisplay)) (defconst org-babel-header-args:R '((width . :any) (height . :any) (bg . :any) (units . :any) (pointsize . :any) (antialias . :any) (quality . :any) (compression . :any) (res . :any) (type . :any) (family . :any) (title . :any) (fonts . :any) (version . :any) (paper . :any) (encoding . :any) (pagecentre . :any) (colormodel . :any) (useDingbats . :any) (horizontal . :any) (async . ((yes no))) (results . ((file list vector table scalar verbatim) (raw html latex org code pp drawer) (replace silent none append prepend) (output value graphics)))) "R-specific header arguments.") (defconst ob-R-safe-header-args (append org-babel-safe-header-args '(:width :height :bg :units :pointsize :antialias :quality :compression :res :type :family :title :fonts :version :paper :encoding :pagecentre :colormodel :useDingbats :horizontal)) "Header args which are safe for R babel blocks. See `org-babel-safe-header-args' for documentation of the format of this variable.") (defvar org-babel-default-header-args:R '()) (put 'org-babel-default-header-args:R 'safe-local-variable (org-babel-header-args-safe-fn ob-R-safe-header-args)) (defcustom org-babel-R-command "R --slave --no-save" "Name of command to use for executing R code." :group 'org-babel :version "24.1" :type 'string) ;; The usage of utils::read.table() ensures that the command ;; read.table() can be found even in circumstances when the utils ;; package is not in the search path from R. (defconst ob-R-transfer-variable-table-with-header "%s <- local({ con <- textConnection( %S ) res <- utils::read.table( con, header = %s, row.names = %s, sep = \"\\t\", as.is = TRUE ) close(con) res })" "R code used to transfer a table defined as a variable from org to R. This function is used when the table contains a header.") (defconst ob-R-transfer-variable-table-without-header "%s <- local({ con <- textConnection( %S ) res <- utils::read.table( con, header = %s, row.names = %s, sep = \"\\t\", as.is = TRUE, fill = TRUE, col.names = paste(\"V\", seq_len(%d), sep =\"\") ) close(con) res })" "R code used to transfer a table defined as a variable from org to R. This function is used when the table does not contain a header.") (defun org-babel-expand-body:R (body params &optional _graphics-file) "Expand BODY according to PARAMS, return the expanded body." (mapconcat 'identity (append (when (cdr (assq :prologue params)) (list (cdr (assq :prologue params)))) (org-babel-variable-assignments:R params) (list body) (when (cdr (assq :epilogue params)) (list (cdr (assq :epilogue params))))) "\n")) (defun org-babel-execute:R (body params) "Execute a block of R code BODY according to PARAMS. This function is called by `org-babel-execute-src-block'." (save-excursion (let* ((result-params (cdr (assq :result-params params))) (result-type (cdr (assq :result-type params))) (async (org-babel-comint-use-async params)) (session (org-babel-R-initiate-session (cdr (assq :session params)) params)) (graphics-file (and (member "graphics" (assq :result-params params)) (org-babel-graphical-output-file params))) (colnames-p (unless graphics-file (cdr (assq :colnames params)))) (rownames-p (unless graphics-file (cdr (assq :rownames params)))) (full-body (let ((inside (list (org-babel-expand-body:R body params graphics-file)))) (mapconcat 'identity (if graphics-file (append (list (org-babel-R-construct-graphics-device-call graphics-file params)) inside (list "},error=function(e){plot(x=-1:1, y=-1:1, type='n', xlab='', ylab='', axes=FALSE); text(x=0, y=0, labels=e$message, col='red'); paste('ERROR', e$message, sep=' : ')}); dev.off()")) inside) "\n"))) (result (org-babel-R-evaluate session full-body result-type result-params (or (equal "yes" colnames-p) (org-babel-pick-name (cdr (assq :colname-names params)) colnames-p)) (or (equal "yes" rownames-p) (org-babel-pick-name (cdr (assq :rowname-names params)) rownames-p)) async))) (if graphics-file nil result)))) (defun org-babel-prep-session:R (session params) "Prepare SESSION according to the header arguments specified in PARAMS." (let* ((session (org-babel-R-initiate-session session params)) (var-lines (org-babel-variable-assignments:R params))) (org-babel-comint-in-buffer session (mapc (lambda (var) (end-of-line 1) (insert var) (comint-send-input nil t) (org-babel-comint-wait-for-output session)) var-lines)) session)) (defun org-babel-load-session:R (session body params) "Load BODY into SESSION." (save-window-excursion (let ((buffer (org-babel-prep-session:R session params))) (with-current-buffer buffer (goto-char (process-mark (get-buffer-process (current-buffer)))) (insert (org-babel-chomp body))) buffer))) ;; helper functions (defun org-babel-variable-assignments:R (params) "Return list of R statements assigning the block's variables. Retrieve variables from PARAMS." (let ((vars (org-babel--get-vars params))) (mapcar (lambda (pair) (org-babel-R-assign-elisp (car pair) (cdr pair) (equal "yes" (cdr (assq :colnames params))) (equal "yes" (cdr (assq :rownames params))))) (mapcar (lambda (i) (cons (car (nth i vars)) (org-babel-reassemble-table (cdr (nth i vars)) (cdr (nth i (cdr (assq :colname-names params)))) (cdr (nth i (cdr (assq :rowname-names params))))))) (number-sequence 0 (1- (length vars))))))) (defun org-babel-R-quote-tsv-field (s) "Quote field S for export to R." (if (stringp s) (concat "\"" (mapconcat 'identity (split-string s "\"") "\"\"") "\"") (format "%S" s))) (defun org-babel-R-assign-elisp (name value colnames-p rownames-p) "Construct R code assigning the elisp VALUE to a variable named NAME." (if (listp value) (let* ((lengths (mapcar 'length (cl-remove-if-not 'listp value))) (max (if lengths (apply 'max lengths) 0)) (min (if lengths (apply 'min lengths) 0))) ;; Ensure VALUE has an orgtbl structure (depth of at least 2). (unless (listp (car value)) (setq value (mapcar 'list value))) (let ((file (orgtbl-to-tsv value '(:fmt org-babel-R-quote-tsv-field))) (header (if (or (eq (nth 1 value) 'hline) colnames-p) "TRUE" "FALSE")) (row-names (if rownames-p "1" "NULL"))) (if (= max min) (format ob-R-transfer-variable-table-with-header name file header row-names) (format ob-R-transfer-variable-table-without-header name file header row-names max)))) (cond ((integerp value) (format "%s <- %s" name (concat (number-to-string value) "L"))) ((floatp value) (format "%s <- %s" name value)) ((stringp value) (format "%s <- %S" name (org-no-properties value))) (t (format "%s <- %S" name (prin1-to-string value)))))) (defvar ess-current-process-name) ; dynamically scoped (defvar ess-local-process-name) ; dynamically scoped (defvar ess-ask-for-ess-directory) ; dynamically scoped (defvar ess-gen-proc-buffer-name-function) ; defined in ess-inf.el (defun org-babel-R-initiate-session (session params) "Create or return the current R SESSION buffer. Use PARAMS to set default directory when creating a new session." (unless (string= session "none") (let* ((session (or session "*R*")) (ess-ask-for-ess-directory (and (boundp 'ess-ask-for-ess-directory) ess-ask-for-ess-directory (not (cdr (assq :dir params))))) ;; Make ESS name the process buffer as SESSION. (ess-gen-proc-buffer-name-function (lambda (_) session))) (if (org-babel-comint-buffer-livep session) session (save-window-excursion (when (get-buffer session) ;; Session buffer exists, but with dead process (set-buffer session)) (org-require-package 'ess-r-mode "ESS") (set-buffer (run-ess-r)) (let ((R-proc (get-process (or ess-local-process-name ess-current-process-name)))) (while (process-get R-proc 'callbacks) (ess-wait-for-process R-proc))) (current-buffer)))))) (defun org-babel-R-associate-session (session) "Associate R code buffer with an R session. Make SESSION be the inferior ESS process associated with the current code buffer." (when-let* ((process (get-buffer-process session))) (setq ess-local-process-name (process-name process)) (ess-make-buffer-current)) (setq-local ess-gen-proc-buffer-name-function (lambda (_) session))) (defvar org-babel-R-graphics-devices '((:bmp "bmp" "filename") (:jpg "jpeg" "filename") (:jpeg "jpeg" "filename") (:tikz "tikz" "file") (:tiff "tiff" "filename") (:png "png" "filename") (:svg "svg" "file") (:pdf "pdf" "file") (:ps "postscript" "file") (:postscript "postscript" "file")) "An alist mapping graphics file types to R functions. Each member of this list is a list with three members: 1. the file extension of the graphics file, as an elisp :keyword 2. the R graphics device function to call to generate such a file 3. the name of the argument to this function which specifies the file to write to (typically \"file\" or \"filename\")") (defun org-babel-R-construct-graphics-device-call (out-file params) "Construct the call to the graphics device." (let* ((allowed-args '(:width :height :bg :units :pointsize :antialias :quality :compression :res :type :family :title :fonts :version :paper :encoding :pagecentre :colormodel :useDingbats :horizontal)) (device (file-name-extension out-file)) (device-info (or (assq (intern (concat ":" device)) org-babel-R-graphics-devices) (assq :png org-babel-R-graphics-devices))) (extra-args (cdr (assq :R-dev-args params))) filearg args) (setq device (nth 1 device-info)) (setq filearg (nth 2 device-info)) (setq args (mapconcat (lambda (pair) (if (member (car pair) allowed-args) (format ",%s=%S" (substring (symbol-name (car pair)) 1) (cdr pair)) "")) params "")) (format "%s(%s=\"%s\"%s%s%s); tryCatch({" device filearg out-file args (if extra-args "," "") (or extra-args "")))) (defconst org-babel-R-eoe-indicator "'org_babel_R_eoe'") (defconst org-babel-R-eoe-output "[1] \"org_babel_R_eoe\"") (defconst org-babel-R-write-object-command "{ function(object,transfer.file) { object invisible( if ( inherits( try( { tfile<-tempfile() write.table(object, file=tfile, sep=\"\\t\", na=\"\",row.names=%s,col.names=%s, quote=FALSE) file.rename(tfile,transfer.file) }, silent=TRUE), \"try-error\")) { if(!file.exists(transfer.file)) file.create(transfer.file) } ) } }(object=%s,transfer.file=\"%s\")" "Template for an R command to evaluate a block of code and write result to file. Has four %s escapes to be filled in: 1. Row names, \"TRUE\" or \"FALSE\" 2. Column names, \"TRUE\" or \"FALSE\" 3. The code to be run (must be an expression, not a statement) 4. The name of the file to write to") (defun org-babel-R-evaluate (session body result-type result-params column-names-p row-names-p async) "Evaluate R code in BODY." (if session (if async (ob-session-async-org-babel-R-evaluate-session session body result-type column-names-p row-names-p) (org-babel-R-evaluate-session session body result-type result-params column-names-p row-names-p)) (org-babel-R-evaluate-external-process body result-type result-params column-names-p row-names-p))) (defun org-babel-R-evaluate-external-process (body result-type result-params column-names-p row-names-p) "Evaluate BODY in external R process. If RESULT-TYPE equals `output' then return standard output as a string. If RESULT-TYPE equals `value' then return the value of the last statement in BODY, as elisp." (cl-case result-type (value (let ((tmp-file (org-babel-temp-file "R-"))) (org-babel-eval org-babel-R-command (format org-babel-R-write-object-command (if row-names-p "TRUE" "FALSE") (if column-names-p (if row-names-p "NA" "TRUE") "FALSE") (format "{function ()\n{\n%s\n}}()" body) (org-babel-process-file-name tmp-file 'noquote))) (org-babel-R-process-value-result (org-babel-result-cond result-params (with-temp-buffer (insert-file-contents tmp-file) (org-babel-chomp (buffer-string) "\n")) (org-babel-import-elisp-from-file tmp-file '(16))) column-names-p))) (output (org-babel-eval org-babel-R-command body)))) (defvar ess-eval-visibly-p) (defun org-babel-R-evaluate-session (session body result-type result-params column-names-p row-names-p) "Evaluate BODY in SESSION. If RESULT-TYPE equals `output' then return standard output as a string. If RESULT-TYPE equals `value' then return the value of the last statement in BODY, as elisp." (cl-case result-type (value (with-temp-buffer (insert (org-babel-chomp body)) (let ((ess-local-process-name (process-name (get-buffer-process session))) (ess-eval-visibly-p nil)) (ess-eval-buffer nil))) (let ((tmp-file (org-babel-temp-file "R-"))) (org-babel-comint-eval-invisibly-and-wait-for-file session tmp-file (format org-babel-R-write-object-command (if row-names-p "TRUE" "FALSE") (if column-names-p (if row-names-p "NA" "TRUE") "FALSE") ".Last.value" (org-babel-process-file-name tmp-file 'noquote))) (org-babel-R-process-value-result (org-babel-result-cond result-params (with-temp-buffer (insert-file-contents tmp-file) (org-babel-chomp (buffer-string) "\n")) (org-babel-import-elisp-from-file tmp-file '(16))) column-names-p))) (output (mapconcat 'org-babel-chomp (butlast (delq nil (mapcar (lambda (line) (when (> (length line) 0) line)) (mapcar (lambda (line) ;; cleanup extra prompts left in output (if (string-match "^\\([>+.]\\([ ][>.+]\\)*[ ]\\)" (car (split-string line "\n"))) (substring line (match-end 1)) line)) (with-current-buffer session (let ((comint-prompt-regexp (concat "^" comint-prompt-regexp))) (org-babel-comint-with-output (session org-babel-R-eoe-output) (insert (mapconcat 'org-babel-chomp (list body org-babel-R-eoe-indicator) "\n")) (inferior-ess-send-input)))))))) "\n")))) (defun org-babel-R-process-value-result (result column-names-p) "R-specific processing of return value. Insert hline if column names in output have been requested." (if column-names-p (condition-case nil (cons (car result) (cons 'hline (cdr result))) (error "Could not parse R result")) result)) ;;; async evaluation (defconst ob-session-async-R-indicator "'ob_comint_async_R_%s_%s'") (defun ob-session-async-org-babel-R-evaluate-session (session body result-type column-names-p row-names-p) "Asynchronously evaluate BODY in SESSION. Returns a placeholder string for insertion, to later be replaced by `org-babel-comint-async-filter'." (org-babel-comint-async-register session (current-buffer) "^\\(?:[>.+] \\)*\\[1\\] \"ob_comint_async_R_\\(start\\|end\\|file\\)_\\(.+\\)\"$" 'org-babel-chomp 'ob-session-async-R-value-callback 'disable-prompt-filtering) (cl-case result-type (value (let ((tmp-file (org-babel-temp-file "R-"))) (with-temp-buffer (insert (org-babel-chomp body)) (let ((ess-local-process-name (process-name (get-buffer-process session)))) (ess-eval-buffer nil))) (with-temp-buffer (insert (mapconcat 'org-babel-chomp (list (format org-babel-R-write-object-command (if row-names-p "TRUE" "FALSE") (if column-names-p (if row-names-p "NA" "TRUE") "FALSE") ".Last.value" (org-babel-process-file-name tmp-file 'noquote)) (format ob-session-async-R-indicator "file" tmp-file)) "\n")) (let ((ess-local-process-name (process-name (get-buffer-process session)))) (ess-eval-buffer nil))) tmp-file)) (output (let ((uuid (org-id-uuid)) (ess-local-process-name (process-name (get-buffer-process session))) (ess-eval-visibly-p nil)) (with-temp-buffer (insert (format ob-session-async-R-indicator "start" uuid)) (insert "\n") (insert body) (insert "\n") (insert (format ob-session-async-R-indicator "end" uuid)) (ess-eval-buffer nil )) uuid)))) (defun ob-session-async-R-value-callback (params tmp-file) "Callback for async value results. Assigned locally to `org-babel-comint-async-file-callback' in R comint buffers used for asynchronous Babel evaluation." (let* ((graphics-file (and (member "graphics" (assq :result-params params)) (org-babel-graphical-output-file params))) (colnames-p (unless graphics-file (cdr (assq :colnames params))))) (org-babel-R-process-value-result (org-babel-result-cond (assq :result-params params) (with-temp-buffer (insert-file-contents tmp-file) (org-babel-chomp (buffer-string) "\n")) (org-babel-import-elisp-from-file tmp-file '(16))) (or (equal "yes" colnames-p) (org-babel-pick-name (cdr (assq :colname-names params)) colnames-p))))) ;;; ob-session-async-R.el ends here (provide 'ob-R) ;;; ob-R.el ends here org-mode-9.7.29+dfsg/lisp/ob-awk.el000066400000000000000000000103701500430433700167230ustar00rootroot00000000000000;;; ob-awk.el --- Babel Functions for Awk -*- lexical-binding: t; -*- ;; Copyright (C) 2011-2025 Free Software Foundation, Inc. ;; Author: Eric Schulte ;; Maintainer: Tyler Smith ;; Keywords: literate programming, reproducible research ;; URL: https://orgmode.org ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; Babel's awk can use special header argument: ;; ;; - :in-file takes a path to a file of data to be processed by awk ;; ;; - :stdin takes an Org data or code block reference, the value of ;; which will be passed to the awk process through STDIN ;;; Code: (require 'org-macs) (org-assert-version) (require 'ob) (require 'org-compat) (declare-function org-babel-ref-resolve "ob-ref" (ref)) (declare-function orgtbl-to-generic "org-table" (table params)) (defvar org-babel-tangle-lang-exts) (add-to-list 'org-babel-tangle-lang-exts '("awk" . "awk")) (defvar org-babel-awk-command "awk" "Name of the awk executable command.") (defun org-babel-expand-body:awk (body params) "Expand BODY according to PARAMS, return the expanded body." (let ((prologue (cdr (assq :prologue params))) (epilogue (cdr (assq :epilogue params)))) (concat (and prologue (concat prologue "\n")) body (and epilogue (concat "\n" epilogue "\n"))))) (defun org-babel-execute:awk (body params) "Execute a block of Awk code BODY with org-babel. PARAMS is a plist of src block parameters . This function is called by `org-babel-execute-src-block'." (unless noninteractive (message "Executing Awk source code block")) (let* ((result-params (cdr (assq :result-params params))) (cmd-line (cdr (assq :cmd-line params))) (in-file (cdr (assq :in-file params))) (full-body (org-babel-expand-body:awk body params)) (code-file (let ((file (org-babel-temp-file "awk-"))) (with-temp-file file (insert full-body)) file)) (stdin (let ((stdin (cdr (assq :stdin params)))) (when stdin (let ((tmp (org-babel-temp-file "awk-stdin-")) (res (org-babel-ref-resolve stdin))) (with-temp-file tmp (insert (org-babel-awk-var-to-awk res))) tmp)))) (cmd (mapconcat #'identity (append (list org-babel-awk-command "-f" code-file cmd-line) (mapcar (lambda (pair) (format "-v %s='%s'" (car pair) (org-babel-awk-var-to-awk (cdr pair)))) (org-babel--get-vars params)) (list in-file)) " "))) (org-babel-reassemble-table (let ((results (cond (stdin (with-temp-buffer (call-process-shell-command cmd stdin (current-buffer)) (buffer-string))) (t (org-babel-eval cmd ""))))) (when results (org-babel-result-cond result-params results (let ((tmp (org-babel-temp-file "awk-results-"))) (with-temp-file tmp (insert results)) (org-babel-import-elisp-from-file tmp))))) (org-babel-pick-name (cdr (assq :colname-names params)) (cdr (assq :colnames params))) (org-babel-pick-name (cdr (assq :rowname-names params)) (cdr (assq :rownames params)))))) (defun org-babel-awk-var-to-awk (var &optional sep) "Return a printed value of VAR suitable for parsing with awk. SEP, when non-nil is a separator used when converting list values to awk table." (let ((echo-var (lambda (v) (if (stringp v) v (format "%S" v))))) (cond ((and (listp var) (listp (car var))) (orgtbl-to-generic var (list :sep (or sep "\t") :fmt echo-var))) ((listp var) (mapconcat echo-var var "\n")) (t (funcall echo-var var))))) (provide 'ob-awk) ;;; ob-awk.el ends here org-mode-9.7.29+dfsg/lisp/ob-calc.el000066400000000000000000000112721500430433700170450ustar00rootroot00000000000000;;; ob-calc.el --- Babel Functions for Calc -*- lexical-binding: t; -*- ;; Copyright (C) 2010-2025 Free Software Foundation, Inc. ;; Author: Eric Schulte ;; Maintainer: Tom Gillespie ;; Keywords: literate programming, reproducible research ;; URL: https://orgmode.org ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; Org-Babel support for evaluating calc code ;;; Code: (require 'org-macs) (org-assert-version) (require 'ob) (require 'org-macs) (require 'calc) (require 'calc-trail) (require 'calc-store) (declare-function calc-store-into "calc-store" (&optional var)) (declare-function calc-recall "calc-store" (&optional var)) (declare-function math-evaluate-expr "calc-ext" (x)) (defvar org-babel-default-header-args:calc nil "Default arguments for evaluating a calc source block.") (defun org-babel-expand-body:calc (body params) "Expand BODY according to PARAMS, return the expanded body." (let ((prologue (cdr (assq :prologue params))) (epilogue (cdr (assq :epilogue params)))) (concat (and prologue (concat prologue "\n")) body (and epilogue (concat "\n" epilogue "\n"))))) (defvar org--var-syms) ; Dynamically scoped from org-babel-execute:calc (defun org-babel-execute:calc (body params) "Execute BODY of calc code with Babel using PARAMS." (unless (get-buffer "*Calculator*") (save-window-excursion (calc) (calc-quit))) (let* ((vars (org-babel--get-vars params)) (org--var-syms (mapcar #'car vars)) (var-names (mapcar #'symbol-name org--var-syms))) (mapc (lambda (pair) (let ((val (cdr pair))) (calc-push-list (list (cond ;; For a vector, Calc follows the format (vec 1 2 3 ...) so ;; a matrix becomes (vec (vec 1 2 3) (vec 4 5 6) ...). See ;; the comments in "Arithmetic routines." section of ;; calc.el. ((listp val) (cons 'vec (if (null (cdr val)) (car val) (mapcar (lambda (x) (if (listp x) (cons 'vec x) x)) val)))) ((numberp val) (math-read-number (number-to-string val))) (t val))))) (calc-store-into (car pair))) vars) (mapc (lambda (line) (when (> (length line) 0) (cond ;; simple variable name ((member line var-names) (calc-recall (intern line))) ;; stack operation ((string= "'" (substring line 0 1)) (funcall (lookup-key calc-mode-map (substring line 1)) nil)) ;; complex expression (t (calc-push-list (list (let ((res (calc-eval line))) (cond ((numberp res) res) ((math-read-number res) (math-read-number res)) ((listp res) (error "Calc error \"%s\" on input \"%s\"" (cadr res) line)) (t (replace-regexp-in-string "'" "" (calc-eval (math-evaluate-expr ;; resolve user variables, calc built in ;; variables are handled automatically ;; upstream by calc (mapcar #'org-babel-calc-maybe-resolve-var ;; parse line into calc objects (car (math-read-exprs line))))))))) )))))) (mapcar #'org-trim (split-string (org-babel-expand-body:calc body params) "[\n\r]")))) (save-excursion (with-current-buffer "*Calculator*" (prog1 (calc-eval (calc-top 1)) (calc-pop 1))))) (defun org-babel-calc-maybe-resolve-var (el) "Resolve user variables in EL. EL is taken from the output of `math-read-exprs'." (if (consp el) (if (and (eq 'var (car el)) (member (cadr el) org--var-syms)) (progn (calc-recall (cadr el)) (prog1 (calc-top 1) (calc-pop 1))) (mapcar #'org-babel-calc-maybe-resolve-var el)) el)) (provide 'ob-calc) ;;; ob-calc.el ends here org-mode-9.7.29+dfsg/lisp/ob-clojure.el000066400000000000000000000331661500430433700176140ustar00rootroot00000000000000;;; ob-clojure.el --- Babel Functions for Clojure -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2025 Free Software Foundation, Inc. ;; Author: Joel Boehland, Eric Schulte, Oleh Krehel, Frederick Giasson ;; Maintainer: Daniel Kraus ;; ;; Keywords: literate programming, reproducible research ;; URL: https://orgmode.org ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; Support for evaluating Clojure / ClojureScript code. ;; Requirements: ;; - Clojure (at least 1.2.0) ;; - clojure-mode ;; - babashka, nbb, Clojure CLI tools, Cider, inf-clojure or SLIME ;; For clojure-mode, see https://github.com/clojure-emacs/clojure-mode ;; For babashka, see https://github.com/babashka/babashka ;; For nbb, see https://github.com/babashka/nbb ;; For Clojure CLI tools, see https://clojure.org/guides/deps_and_cli ;; For Cider, see https://github.com/clojure-emacs/cider ;; For inf-clojure, see https://github.com/clojure-emacs/inf-clojure ;; For SLIME, see https://slime.common-lisp.dev ;; For SLIME, the best way to install its components is by following ;; the directions as set out by Phil Hagelberg (Technomancy) on the ;; web page: https://technomancy.us/126 ;;; Code: (require 'org-macs) (org-assert-version) (require 'ob) (declare-function cider-current-connection "ext:cider-client" (&optional type)) (declare-function cider-current-ns "ext:cider-client" ()) (declare-function inf-clojure "ext:inf-clojure" (cmd)) (declare-function inf-clojure-cmd "ext:inf-clojure" (project-type)) (declare-function inf-clojure-eval-string "ext:inf-clojure" (code)) (declare-function inf-clojure-project-type "ext:inf-clojure" ()) (declare-function nrepl-dict-get "ext:nrepl-client" (dict key)) (declare-function nrepl-sync-request:eval "ext:nrepl-client" (input connection &optional ns tooling)) (declare-function sesman-start-session "ext:sesman" (system)) (declare-function slime-eval "ext:slime" (sexp &optional package)) (defvar cider-buffer-ns) (defvar org-babel-tangle-lang-exts) (add-to-list 'org-babel-tangle-lang-exts '("clojure" . "clj")) (add-to-list 'org-babel-tangle-lang-exts '("clojurescript" . "cljs")) (defvar org-babel-default-header-args:clojure '()) (defvar org-babel-header-args:clojure '((ns . :any) (package . :any) (backend . ((inf-clojure cider slime babashka nbb))))) (defvar org-babel-default-header-args:clojurescript '()) (defvar org-babel-header-args:clojurescript '((package . :any))) (defcustom org-babel-clojure-backend (cond ((executable-find "bb") 'babashka) ((executable-find "clojure") 'clojure-cli) ((featurep 'cider) 'cider) ((featurep 'inf-clojure) 'inf-clojure) ((featurep 'slime) 'slime) (t nil)) "Backend used to evaluate Clojure code blocks." :group 'org-babel :package-version '(Org . "9.7") :type '(choice (const :tag "babashka" babashka) (const :tag "clojure-cli" clojure-cli) (const :tag "cider" cider) (const :tag "inf-clojure" inf-clojure) (const :tag "slime" slime) (const :tag "Not configured yet" nil))) (defcustom org-babel-clojurescript-backend (cond ((or (executable-find "nbb") (executable-find "npx")) 'nbb) ((featurep 'cider) 'cider) (t nil)) "Backend used to evaluate ClojureScript code blocks." :group 'org-babel :package-version '(Org . "9.7") :type '(choice (const :tag "nbb" nbb) (const :tag "cider" cider) (const :tag "Not configured yet" nil))) (defcustom org-babel-clojure-default-ns "user" "Default Clojure namespace for source block when finding ns failed." :type 'string :group 'org-babel) (defcustom ob-clojure-babashka-command (executable-find "bb") "Babashka command used by the Clojure `babashka' backend." :type '(choice file (const nil)) :group 'org-babel :package-version '(Org . "9.6")) (defcustom ob-clojure-nbb-command (or (executable-find "nbb") (when-let* ((npx (executable-find "npx"))) (concat npx " nbb"))) "Nbb command used by the ClojureScript `nbb' backend." :type '(choice string (const nil)) :group 'org-babel :package-version '(Org . "9.7")) (defcustom ob-clojure-cli-command (when-let* ((cmd (executable-find "clojure"))) (concat cmd " -M")) "Clojure CLI command used by the Clojure `clojure-cli' backend." :type '(choice string (const nil)) :group 'org-babel :package-version '(Org . "9.7")) (defun org-babel-expand-body:clojure (body params &optional cljs-p) "Expand BODY according to PARAMS, return the expanded body. When CLJS-P is non-nil, expand in a cljs context instead of clj." (let* ((vars (org-babel--get-vars params)) (backend-override (cdr (assq :backend params))) (org-babel-clojure-backend (cond (backend-override (intern backend-override)) (org-babel-clojure-backend org-babel-clojure-backend) (t (user-error "You need to customize `org-babel-clojure-backend' or set the `:backend' header argument")))) (ns (or (cdr (assq :ns params)) (if (eq org-babel-clojure-backend 'cider) (or cider-buffer-ns (let ((repl-buf (cider-current-connection))) (and repl-buf (buffer-local-value 'cider-buffer-ns repl-buf)))) org-babel-clojure-default-ns))) (result-params (cdr (assq :result-params params))) (print-level nil) (print-length nil) ;; Remove comments, they break (let [...] ...) bindings (body (replace-regexp-in-string "^[ ]*;+.*$" "" body)) (body (org-trim (concat ;; Source block specified namespace :ns. (and (cdr (assq :ns params)) (format "(ns %s)\n" ns)) ;; Variables binding. (if (null vars) (org-trim body) (format "(let [%s]\n%s)" (mapconcat (lambda (var) (format "%S '%S" (car var) (cdr var))) vars "\n ") body)))))) ;; If the result param is set to "output" we don't have to do ;; anything special and just let the backend handle everything (if (member "output" result-params) body ;; If the result is not "output" (i.e. it's "value"), disable ;; stdout output and print the last returned value. Use pprint ;; instead of prn when results param is "pp" or "code". (concat (if (or (member "code" result-params) (member "pp" result-params)) (concat (if cljs-p "(require '[cljs.pprint :refer [pprint]])" "(require '[clojure.pprint :refer [pprint]])") " (pprint ") "(prn ") (if cljs-p "(binding [cljs.core/*print-fn* (constantly nil)]" "(binding [*out* (java.io.StringWriter.)]") body "))")))) (defvar ob-clojure-inf-clojure-filter-out) (defvar ob-clojure-inf-clojure-tmp-output) (defun ob-clojure-inf-clojure-output (s) "Store a trimmed version of S in a variable and return S." (let ((s0 (org-trim (replace-regexp-in-string ob-clojure-inf-clojure-filter-out "" s)))) (push s0 ob-clojure-inf-clojure-tmp-output)) s) (defmacro ob-clojure-with-temp-expanded (expanded params &rest body) "Run BODY on EXPANDED code block with PARAMS." (declare (debug (body)) (indent 2)) `(with-temp-buffer (insert ,expanded) (goto-char (point-min)) (while (not (looking-at "\\s-*\\'")) (let* ((beg (point)) (end (progn (forward-sexp) (point))) (exp (org-babel-expand-body:clojure (buffer-substring beg end) ,params))) (sit-for .1) ,@body)))) (defsubst ob-clojure-string-or-list (l) "Convert list L into a string or a list of list." (if (and (listp l) (= (length l) 1)) (car l) (mapcar #'list l))) (defvar inf-clojure-buffer) (defvar comint-prompt-regexp) (defvar inf-clojure-comint-prompt-regexp) (defun ob-clojure-eval-with-inf-clojure (expanded params) "Evaluate EXPANDED code block with PARAMS using inf-clojure." (org-require-package 'inf-clojure) ;; Maybe initiate the inf-clojure session (unless (and inf-clojure-buffer (buffer-live-p (get-buffer inf-clojure-buffer))) (save-window-excursion (let* ((alias (cdr (assq :alias params))) (cmd0 (inf-clojure-cmd (inf-clojure-project-type))) (cmd (if alias (replace-regexp-in-string "clojure" (format "clojure -A%s" alias) cmd0) cmd0))) (setq org-babel-comint-prompt-regexp-old comint-prompt-regexp comint-prompt-regexp inf-clojure-comint-prompt-regexp) (funcall-interactively #'inf-clojure cmd) (goto-char (point-max)))) (sit-for 1)) ;; Now evaluate the code (setq ob-clojure-inf-clojure-filter-out (concat "^nil\\|nil$\\|\\s-*" (or (cdr (assq :ns params)) org-babel-clojure-default-ns) "=>\\s-*")) (add-hook 'comint-preoutput-filter-functions #'ob-clojure-inf-clojure-output) (setq ob-clojure-inf-clojure-tmp-output nil) (ob-clojure-with-temp-expanded expanded nil (inf-clojure-eval-string exp)) (sit-for .5) (remove-hook 'comint-preoutput-filter-functions #'ob-clojure-inf-clojure-output) ;; And return the result (ob-clojure-string-or-list (delete nil (mapcar (lambda (s) (unless (or (equal "" s) (string-match-p "^Clojure" s)) s)) (reverse ob-clojure-inf-clojure-tmp-output))))) (defun ob-clojure-eval-with-cider (expanded _params &optional cljs-p) "Evaluate EXPANDED code block using cider. When CLJS-P is non-nil, use a cljs connection instead of clj. The PARAMS from Babel are not used in this function." (org-require-package 'cider "Cider") (let ((connection (cider-current-connection (if cljs-p "cljs" "clj")))) (unless connection (sesman-start-session 'CIDER)) (if (not connection) ;; Display in the result instead of using `user-error' "Please reevaluate when nREPL is connected" (let ((response (nrepl-sync-request:eval expanded connection))) (or (nrepl-dict-get response "root-ex") (nrepl-dict-get response "ex") (nrepl-dict-get response "out")))))) (defun ob-clojure-eval-with-slime (expanded params) "Evaluate EXPANDED code block with PARAMS using slime." (org-require-package 'slime "SLIME") (with-temp-buffer (insert expanded) (slime-eval `(swank:eval-and-grab-output ,(buffer-substring-no-properties (point-min) (point-max))) (cdr (assq :package params))))) (defun ob-clojure-eval-with-cmd (cmd expanded) "Evaluate EXPANDED code block using CMD (babashka, clojure or nbb)." (let ((script-file (org-babel-temp-file "clojure-cmd-script-" ".clj"))) (with-temp-file script-file (insert expanded)) (org-babel-eval (format "%s %s" cmd (org-babel-process-file-name script-file)) ""))) (defun org-babel-execute:clojure (body params &optional cljs-p) "Execute the BODY block of Clojure code with PARAMS using Babel. When CLJS-P is non-nil, execute with a ClojureScript backend instead of Clojure." (let* ((backend-override (cdr (assq :backend params))) (org-babel-clojure-backend (cond (backend-override (intern backend-override)) (org-babel-clojure-backend (if cljs-p org-babel-clojurescript-backend org-babel-clojure-backend)) (t (user-error "You need to customize `%S' or set the `:backend' header argument" (if cljs-p org-babel-clojurescript-backend org-babel-clojure-backend))))) ;; We allow a Clojure source block to be evaluated with the ;; nbb backend and therefore have to expand the body with ;; ClojureScript syntax when we either evaluate a ;; ClojureScript source block or use the nbb backend. (cljs-p (or cljs-p (eq org-babel-clojure-backend 'nbb)))) (let* ((expanded (org-babel-expand-body:clojure body params cljs-p)) (result-params (cdr (assq :result-params params))) result) (setq result (cond ((eq org-babel-clojure-backend 'inf-clojure) (ob-clojure-eval-with-inf-clojure expanded params)) ((eq org-babel-clojure-backend 'clojure-cli) (ob-clojure-eval-with-cmd ob-clojure-cli-command expanded)) ((eq org-babel-clojure-backend 'babashka) (ob-clojure-eval-with-cmd ob-clojure-babashka-command expanded)) ((eq org-babel-clojure-backend 'nbb) (ob-clojure-eval-with-cmd ob-clojure-nbb-command expanded)) ((eq org-babel-clojure-backend 'cider) (ob-clojure-eval-with-cider expanded params cljs-p)) ((eq org-babel-clojure-backend 'slime) (ob-clojure-eval-with-slime expanded params)) (t (user-error "Invalid backend")))) (org-babel-result-cond result-params result (condition-case nil (org-babel-script-escape result) (error result)))))) (defun org-babel-execute:clojurescript (body params) "Evaluate BODY with PARAMS as ClojureScript code." (org-babel-execute:clojure body params t)) (provide 'ob-clojure) ;;; ob-clojure.el ends here org-mode-9.7.29+dfsg/lisp/ob-comint.el000066400000000000000000000415431500430433700174400ustar00rootroot00000000000000;;; ob-comint.el --- Babel Functions for Interaction with Comint Buffers -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2025 Free Software Foundation, Inc. ;; Author: Eric Schulte ;; Keywords: literate programming, reproducible research, comint ;; URL: https://orgmode.org ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; These functions build on comint to ease the sending and receiving ;; of commands and results from comint buffers. ;; Note that the buffers in this file are analogous to sessions in ;; org-babel at large. ;;; Code: (require 'org-macs) (org-assert-version) (require 'ob-core) (require 'org-compat) (require 'comint) (defun org-babel-comint-buffer-livep (buffer) "Check if BUFFER is a comint buffer with a live process." (let ((buffer (when buffer (get-buffer buffer)))) (and buffer (buffer-live-p buffer) (get-buffer-process buffer) buffer))) (defmacro org-babel-comint-in-buffer (buffer &rest body) "Check BUFFER and execute BODY. BUFFER is checked with `org-babel-comint-buffer-livep'. BODY is executed inside the protection of `save-excursion' and `save-match-data'." (declare (indent 1) (debug t)) `(progn (unless (org-babel-comint-buffer-livep ,buffer) (error "Buffer %s does not exist or has no process" ,buffer)) (save-match-data (with-current-buffer ,buffer (save-excursion (let ((comint-input-filter (lambda (_input) nil))) ,@body)))))) (defvar-local org-babel-comint-prompt-regexp-old nil "Fallback regexp used to detect prompt.") (defcustom org-babel-comint-fallback-regexp-threshold 5.0 "Waiting time until trying to use fallback regexp to detect prompt. This is useful when prompt unexpectedly changes." :type 'float :group 'org-babel :package-version '(Org . "9.7")) (defun org-babel-comint--set-fallback-prompt () "Swap `comint-prompt-regexp' and `org-babel-comint-prompt-regexp-old'." (when org-babel-comint-prompt-regexp-old (let ((tmp comint-prompt-regexp)) (setq comint-prompt-regexp org-babel-comint-prompt-regexp-old org-babel-comint-prompt-regexp-old tmp)))) (defun org-babel-comint--prompt-filter (string &optional prompt-regexp) "Remove PROMPT-REGEXP from STRING. PROMPT-REGEXP defaults to `comint-prompt-regexp'." (let* ((prompt-regexp (or prompt-regexp comint-prompt-regexp)) ;; We need newline in case if we do progressive replacement ;; of agglomerated comint prompts with `comint-prompt-regexp' ;; containing ^. (separator "org-babel-comint--prompt-filter-separator\n")) (while (string-match-p prompt-regexp string) (setq string (replace-regexp-in-string (format "\\(?:%s\\)?\\(?:%s\\)[ \t]*" separator prompt-regexp) separator string))) (delete "" (split-string string separator)))) (defun org-babel-comint--echo-filter (string &optional echo) "Remove ECHO from STRING." (and echo string (string-match (replace-regexp-in-string "\n" "[\r\n]+" (regexp-quote echo)) string) (setq string (substring string (match-end 0)))) string) (defmacro org-babel-comint-with-output (meta &rest body) "Evaluate BODY in BUFFER and return process output. Will wait until EOE-INDICATOR appears in the output, then return all process output. If REMOVE-ECHO and FULL-BODY are present and non-nil, then strip echo'd body from the returned output. META should be a list containing the following where the last two elements are optional. (BUFFER EOE-INDICATOR REMOVE-ECHO FULL-BODY) This macro ensures that the filter is removed in case of an error or user `keyboard-quit' during execution of body." (declare (indent 1) (debug (sexp body))) (let ((buffer (nth 0 meta)) (eoe-indicator (nth 1 meta)) (remove-echo (nth 2 meta)) (full-body (nth 3 meta))) `(org-babel-comint-in-buffer ,buffer (let* ((string-buffer "") (comint-output-filter-functions (cons (lambda (text) (setq string-buffer (concat string-buffer text))) comint-output-filter-functions)) dangling-text) ;; got located, and save dangling text (goto-char (process-mark (get-buffer-process (current-buffer)))) (let ((start (point)) (end (point-max))) (setq dangling-text (buffer-substring start end)) (delete-region start end)) ;; pass FULL-BODY to process ,@body ;; wait for end-of-evaluation indicator (let ((start-time (current-time))) (while (progn (goto-char comint-last-input-end) (not (save-excursion (and (re-search-forward (regexp-quote ,eoe-indicator) nil t) (re-search-forward comint-prompt-regexp nil t))))) (accept-process-output (get-buffer-process (current-buffer)) org-babel-comint-fallback-regexp-threshold) (when (and org-babel-comint-prompt-regexp-old (> (float-time (time-since start-time)) org-babel-comint-fallback-regexp-threshold) (progn (goto-char comint-last-input-end) (save-excursion (and (re-search-forward (regexp-quote ,eoe-indicator) nil t) (re-search-forward org-babel-comint-prompt-regexp-old nil t))))) (org-babel-comint--set-fallback-prompt)))) ;; replace cut dangling text (goto-char (process-mark (get-buffer-process (current-buffer)))) (insert dangling-text) ;; remove echo'd FULL-BODY from input (and ,remove-echo ,full-body (setq string-buffer (org-babel-comint--echo-filter string-buffer ,full-body))) ;; Filter out prompts. (org-babel-comint--prompt-filter string-buffer))))) (defun org-babel-comint-input-command (buffer cmd) "Pass CMD to BUFFER. The input will not be echoed." (org-babel-comint-in-buffer buffer (goto-char (process-mark (get-buffer-process buffer))) (insert cmd) (comint-send-input) (org-babel-comint-wait-for-output buffer))) (defun org-babel-comint-wait-for-output (buffer) "Wait until output arrives from BUFFER. Note: this is only safe when waiting for the result of a single statement (not large blocks of code)." (org-babel-comint-in-buffer buffer (let ((start-time (current-time))) (while (progn (goto-char comint-last-input-end) (not (and (re-search-forward comint-prompt-regexp nil t) (goto-char (match-beginning 0))))) (accept-process-output (get-buffer-process buffer) org-babel-comint-fallback-regexp-threshold) (when (and org-babel-comint-prompt-regexp-old (> (float-time (time-since start-time)) org-babel-comint-fallback-regexp-threshold) (progn (goto-char comint-last-input-end) (save-excursion (re-search-forward org-babel-comint-prompt-regexp-old nil t)))) (org-babel-comint--set-fallback-prompt)))))) (defun org-babel-comint-eval-invisibly-and-wait-for-file (buffer file string &optional period) "Evaluate STRING in BUFFER invisibly. Don't return until FILE exists. Code in STRING must ensure that FILE exists at end of evaluation." (unless (org-babel-comint-buffer-livep buffer) (error "Buffer %s does not exist or has no process" buffer)) (when (file-exists-p file) (delete-file file)) (process-send-string (get-buffer-process buffer) (if (= (aref string (1- (length string))) ?\n) string (concat string "\n"))) (while (not (file-exists-p file)) (sit-for (or period 0.25)))) ;;; Async evaluation (defvar-local org-babel-comint-async-indicator nil "Regular expression that `org-babel-comint-async-filter' scans for. It should have 2 parenthesized expressions, e.g. \"org_babel_async_\\(start\\|end\\|file\\)_\\(.*\\)\". The first parenthesized expression determines whether the token is delimiting a result block, or whether the result is in a file. If delimiting a block, the second expression gives a UUID for the location to insert the result. Otherwise, the result is in a tmp file, and the second expression gives the file name.") (defvar-local org-babel-comint-async-buffers nil "List of Org mode buffers to check for Babel async output results.") (defvar-local org-babel-comint-async-file-callback nil "Callback to clean and insert Babel async results from a temp file. The callback function takes two arguments: the alist of params of the Babel source block, and the name of the temp file.") (defvar-local org-babel-comint-async-chunk-callback nil "Callback function to clean Babel async output results before insertion. Its single argument is a string consisting of output from the comint process. It should return a string that will be passed to `org-babel-insert-result'.") (defvar-local org-babel-comint-async-remove-prompts-p t "Whether prompts should be detected and removed from async output.") (defvar-local org-babel-comint-async-dangling nil "Dangling piece of the last process output, as a string. Used when `org-babel-comint-async-indicator' is spread across multiple comint outputs due to buffering.") (defun org-babel-comint-use-async (params) "Determine whether to use session async evaluation. PARAMS are the header arguments as passed to `org-babel-execute:lang'." (let ((async (assq :async params)) (session (assq :session params))) (and async (not org-babel-exp-reference-buffer) (not (equal (cdr async) "no")) (not (equal (cdr session) "none"))))) (defun org-babel-comint-async-filter (string) "Captures Babel async output from comint buffer back to Org mode buffers. This function is added as a hook to `comint-output-filter-functions'. STRING contains the output originally inserted into the comint buffer." ;; Remove outdated Org mode buffers (setq org-babel-comint-async-buffers (cl-loop for buf in org-babel-comint-async-buffers if (buffer-live-p buf) collect buf)) (let* ((indicator org-babel-comint-async-indicator) (org-buffers org-babel-comint-async-buffers) (file-callback org-babel-comint-async-file-callback) (combined-string (concat org-babel-comint-async-dangling string)) (new-dangling combined-string) ;; Assumes comint filter called with session buffer current (session-dir default-directory) ;; list of UUID's matched by `org-babel-comint-async-indicator' uuid-list) (with-temp-buffer (insert combined-string) (goto-char (point-min)) (while (re-search-forward indicator nil t) ;; update dangling (setq new-dangling (buffer-substring (point) (point-max))) (cond ((equal (match-string 1) "end") ;; save UUID for insertion later (push (match-string 2) uuid-list)) ((equal (match-string 1) "file") ;; insert results from tmp-file (let ((tmp-file (match-string 2))) (cl-loop for buf in org-buffers until (with-current-buffer buf (save-excursion (goto-char (point-min)) (when (search-forward tmp-file nil t) (org-babel-previous-src-block) (let* ((info (org-babel-get-src-block-info)) (params (nth 2 info)) (result-params (cdr (assq :result-params params))) (default-directory session-dir)) (org-babel-insert-result (funcall file-callback (nth 2 (org-babel-get-src-block-info)) tmp-file) result-params info)) t)))))))) ;; Truncate dangling to only the most recent output (when (> (length new-dangling) (length string)) (setq new-dangling string))) (setq-local org-babel-comint-async-dangling new-dangling) (when uuid-list ;; Search for results in the comint buffer (save-excursion (goto-char (point-max)) (while uuid-list (re-search-backward indicator) (when (equal (match-string 1) "end") (let* ((uuid (match-string-no-properties 2)) (res-str-raw (buffer-substring ;; move point to beginning of indicator (match-beginning 0) ;; find the matching start indicator (cl-loop do (re-search-backward indicator) until (and (equal (match-string 1) "start") (equal (match-string 2) uuid)) finally return (+ 1 (match-end 0))))) ;; Apply user callback (res-str (funcall org-babel-comint-async-chunk-callback (if org-babel-comint-async-remove-prompts-p (org-trim (string-join (mapcar #'org-trim (org-babel-comint--prompt-filter res-str-raw)) "\n") t) res-str-raw)))) ;; Search for uuid in associated org-buffers to insert results (cl-loop for buf in org-buffers until (with-current-buffer buf (save-excursion (goto-char (point-min)) (when (search-forward uuid nil t) (org-babel-previous-src-block) (let* ((info (org-babel-get-src-block-info)) (params (nth 2 info)) (result-params (cdr (assq :result-params params))) (default-directory session-dir)) (org-babel-insert-result res-str result-params info)) t)))) ;; Remove uuid from the list to search for (setq uuid-list (delete uuid uuid-list))))))))) (defun org-babel-comint-async-register (session-buffer org-buffer indicator-regexp chunk-callback file-callback &optional prompt-handling) "Set local org-babel-comint-async variables in SESSION-BUFFER. ORG-BUFFER is added to `org-babel-comint-async-buffers' if not present. `org-babel-comint-async-indicator', `org-babel-comint-async-chunk-callback', and `org-babel-comint-async-file-callback' are set to INDICATOR-REGEXP, CHUNK-CALLBACK, and FILE-CALLBACK respectively. PROMPT-HANDLING may be either of the symbols `filter-prompts', in which case prompts matching `comint-prompt-regexp' are filtered from output before it is passed to CHUNK-CALLBACK, or `disable-prompt-filtering', in which case this behavior is disabled. For backward-compatibility, the default value of `nil' is equivalent to `filter-prompts'." (org-babel-comint-in-buffer session-buffer (setq org-babel-comint-async-indicator indicator-regexp org-babel-comint-async-chunk-callback chunk-callback org-babel-comint-async-file-callback file-callback) (setq org-babel-comint-async-remove-prompts-p (cond ((eq prompt-handling 'disable-prompt-filtering) nil) ((eq prompt-handling 'filter-prompts) t) ((eq prompt-handling nil) t) (t (error (format "Unrecognized prompt handling behavior %s" prompt-handling))))) (unless (memq org-buffer org-babel-comint-async-buffers) (setq org-babel-comint-async-buffers (cons org-buffer org-babel-comint-async-buffers))) (add-hook 'comint-output-filter-functions 'org-babel-comint-async-filter nil t))) (defmacro org-babel-comint-async-delete-dangling-and-eval (session-buffer &rest body) "Remove dangling text in SESSION-BUFFER and evaluate BODY. This is analogous to `org-babel-comint-with-output', but meant for asynchronous output, and much shorter because inserting the result is delegated to `org-babel-comint-async-filter'." (declare (indent 1) (debug t)) `(org-babel-comint-in-buffer ,session-buffer (goto-char (process-mark (get-buffer-process (current-buffer)))) (delete-region (point) (point-max)) ,@body)) (provide 'ob-comint) ;;; ob-comint.el ends here org-mode-9.7.29+dfsg/lisp/ob-core.el000066400000000000000000004365121500430433700171030ustar00rootroot00000000000000;;; ob-core.el --- Working with Code Blocks -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2025 Free Software Foundation, Inc. ;; Authors: Eric Schulte ;; Dan Davison ;; Keywords: literate programming, reproducible research ;; URL: https://orgmode.org ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;;; Code: (require 'org-macs) (org-assert-version) (require 'cl-lib) (require 'ob-eval) (require 'org-macs) (require 'org-fold) (require 'org-compat) (require 'org-cycle) (defconst org-babel-exeext (if (memq system-type '(windows-nt cygwin)) ".exe" nil)) (defvar org-babel-library-of-babel) (defvar org-edit-src-content-indentation) (defvar org-link-file-path-type) (defvar org-src-lang-modes) (defvar org-babel-tangle-uncomment-comments) (declare-function org-attach-dir "org-attach" (&optional create-if-not-exists-p no-fs-check)) (declare-function org-at-item-p "org-list" ()) (declare-function org-at-table-p "org" (&optional table-type)) (declare-function org-babel-lob-execute-maybe "ob-lob" ()) (declare-function org-babel-ref-goto-headline-id "ob-ref" (id)) (declare-function org-babel-ref-headline-body "ob-ref" ()) (declare-function org-babel-ref-parse "ob-ref" (assignment)) (declare-function org-babel-ref-resolve "ob-ref" (ref)) (declare-function org-babel-ref-split-args "ob-ref" (arg-string)) (declare-function org-babel-tangle-comment-links "ob-tangle" (&optional info)) (declare-function org-current-level "org" ()) (declare-function org-cycle "org-cycle" (&optional arg)) (declare-function org-edit-src-code "org-src" (&optional code edit-buffer-name)) (declare-function org-edit-src-exit "org-src" ()) (declare-function org-src-preserve-indentation-p "org-src" (node)) (declare-function org-element-at-point "org-element" (&optional pom cached-only)) (declare-function org-element-at-point-no-context "org-element" (&optional pom)) (declare-function org-element-context "org-element" (&optional element)) (declare-function org-element-normalize-string "org-element" (s)) (declare-function org-element-property "org-element-ast" (property node)) (declare-function org-element-begin "org-element" (node)) (declare-function org-element-end "org-element" (node)) (declare-function org-element-post-affiliated "org-element" (node)) (declare-function org-element-contents-begin "org-element" (node)) (declare-function org-element-contents-end "org-element" (node)) (declare-function org-element-parent "org-element-ast" (node)) (declare-function org-element-type "org-element-ast" (node &optional anonymous)) (declare-function org-element-type-p "org-element-ast" (node &optional types)) (declare-function org-element-interpret-data "org-element" (data)) (declare-function org-entry-get "org" (pom property &optional inherit literal-nil)) (declare-function org-escape-code-in-region "org-src" (beg end)) (declare-function org-forward-heading-same-level "org" (arg &optional invisible-ok)) (declare-function org-in-commented-heading-p "org" (&optional no-inheritance)) (declare-function org-indent-block "org" ()) (declare-function org-indent-line "org" ()) (declare-function org-list-get-list-end "org-list" (item struct prevs)) (declare-function org-list-prevs-alist "org-list" (struct)) (declare-function org-list-struct "org-list" ()) (declare-function org-list-to-generic "org-list" (LIST PARAMS)) (declare-function org-list-to-lisp "org-list" (&optional delete)) (declare-function org-list-to-org "org-list" (list &optional params)) (declare-function org-macro-escape-arguments "org-macro" (&rest args)) (declare-function org-mark-ring-push "org" (&optional pos buffer)) (declare-function org-narrow-to-subtree "org" (&optional element)) (declare-function org-next-block "org" (arg &optional backward block-regexp)) (declare-function org-open-at-point "org" (&optional in-emacs reference-buffer)) (declare-function org-previous-block "org" (arg &optional block-regexp)) (declare-function org-fold-show-context "org-fold" (&optional key)) (declare-function org-src-coderef-format "org-src" (&optional element)) (declare-function org-src-coderef-regexp "org-src" (fmt &optional label)) (declare-function org-src-get-lang-mode "org-src" (lang)) (declare-function org-table-align "org-table" ()) (declare-function org-table-convert-region "org-table" (beg0 end0 &optional separator)) (declare-function org-table-end "org-table" (&optional table-type)) (declare-function org-table-import "org-table" (file arg)) (declare-function org-table-to-lisp "org-table" (&optional txt)) (declare-function org-unescape-code-in-string "org-src" (s)) (declare-function orgtbl-to-generic "org-table" (table params)) (declare-function orgtbl-to-orgtbl "org-table" (table params)) (declare-function tramp-compat-make-temp-file "tramp-compat" (filename &optional dir-flag)) (defgroup org-babel nil "Code block evaluation and management in `org-mode' documents." :tag "Babel" :group 'org) (defcustom org-confirm-babel-evaluate t "Confirm before evaluation. \\\ Require confirmation before interactively evaluating code blocks in Org buffers. The default value of this variable is t, meaning confirmation is required for any code block evaluation. This variable can be set to nil to inhibit any future confirmation requests. This variable can also be set to a function which takes two arguments the language of the code block and the body of the code block. Such a function should then return a non-nil value if the user should be prompted for execution or nil if no prompt is required. Warning: Disabling confirmation may result in accidental evaluation of potentially harmful code. It may be advisable remove code block execution from `\\[org-ctrl-c-ctrl-c]' \ as further protection against accidental code block evaluation. The `org-babel-no-eval-on-ctrl-c-ctrl-c' variable can be used to remove code block execution from the `\\[org-ctrl-c-ctrl-c]' keybinding." :group 'org-babel :version "24.1" :type '(choice boolean function)) ;; don't allow this variable to be changed through file settings (put 'org-confirm-babel-evaluate 'safe-local-variable (lambda (x) (eq x t))) (defcustom org-babel-no-eval-on-ctrl-c-ctrl-c nil "\\\ Remove code block evaluation from the `\\[org-ctrl-c-ctrl-c]' key binding." :group 'org-babel :version "24.1" :type 'boolean) (defcustom org-babel-results-keyword "RESULTS" "Keyword used to name results generated by code blocks. It should be \"RESULTS\". However any capitalization may be used." :group 'org-babel :version "24.4" :package-version '(Org . "8.0") :type 'string :safe (lambda (v) (and (stringp v) (org-string-equal-ignore-case "RESULTS" v)))) (defcustom org-babel-noweb-wrap-start "<<" "String used to begin a noweb reference in a code block. See also `org-babel-noweb-wrap-end'." :group 'org-babel :type 'string) (defcustom org-babel-noweb-wrap-end ">>" "String used to end a noweb reference in a code block. See also `org-babel-noweb-wrap-start'." :group 'org-babel :type 'string) (defcustom org-babel-inline-result-wrap "=%s=" "Format string used to wrap inline results. This string must include a \"%s\" which will be replaced by the results." :group 'org-babel :type 'string) (put 'org-babel-inline-result-wrap 'safe-local-variable (lambda (value) (and (stringp value) (string-match-p "%s" value)))) (defcustom org-babel-hash-show-time nil "Non-nil means show the time the code block was evaluated in the result hash." :group 'org-babel :type 'boolean :package-version '(Org . "9.0") :safe #'booleanp) (defcustom org-babel-uppercase-example-markers nil "When non-nil, begin/end example markers will be inserted in upper case." :group 'org-babel :type 'boolean :version "26.1" :package-version '(Org . "9.1") :safe #'booleanp) (defun org-babel-noweb-wrap (&optional regexp) "Return regexp matching a Noweb reference. Match any reference, or only those matching REGEXP, if non-nil. When matching, reference is stored in match group 1." (concat (regexp-quote org-babel-noweb-wrap-start) (or regexp "\\([^ \t\n]\\(?:.*?[^ \t\n]\\)?\\)") (regexp-quote org-babel-noweb-wrap-end))) (defvar org-babel-src-name-regexp "^[ \t]*#\\+name:[ \t]*" "Regular expression used to match a source name line.") (defvar org-babel-multi-line-header-regexp "^[ \t]*#\\+headers?:[ \t]*\\([^\n]*\\)$" "Regular expression used to match multi-line header arguments.") (defvar org-babel-src-block-regexp (concat ;; (1) indentation (2) lang "^\\([ \t]*\\)#\\+begin_src[ \t]+\\([^ \f\t\n\r\v]+\\)[ \t]*" ;; (3) switches "\\([^\":\n]*\"[^\"\n*]*\"[^\":\n]*\\|[^\":\n]*\\)" ;; (4) header arguments "\\([^\n]*\\)\n" ;; (5) body "\\(\\(?:.\\|\n\\)*?\n\\)??[ \t]*#\\+end_src") "Regexp used to identify code blocks.") (defun org-babel--get-vars (params) "Return the babel variable assignments in PARAMS. PARAMS is a quasi-alist of header args, which may contain multiple entries for the key `:var'. This function returns a list of the cdr of all the `:var' entries." (mapcar #'cdr (cl-remove-if-not (lambda (x) (eq (car x) :var)) params))) (defvar org-babel-exp-reference-buffer nil "Buffer containing original contents of the exported buffer. This is used by Babel to resolve references in source blocks. Its value is dynamically bound during export.") (defun org-babel-check-confirm-evaluate (info) "Check whether INFO allows code block evaluation. Returns nil if evaluation is disallowed, t if it is unconditionally allowed, and the symbol `query' if the user should be asked whether to allow evaluation." (let* ((headers (nth 2 info)) (eval (or (cdr (assq :eval headers)) (when (assq :noeval headers) "no"))) (eval-no (member eval '("no" "never"))) (export org-babel-exp-reference-buffer) (eval-no-export (and export (member eval '("no-export" "never-export")))) (noeval (or eval-no eval-no-export)) (query (or (equal eval "query") (and export (equal eval "query-export")) (if (functionp org-confirm-babel-evaluate) (funcall org-confirm-babel-evaluate ;; Language, code block body. (nth 0 info) (org-babel--expand-body info)) org-confirm-babel-evaluate)))) (cond (noeval nil) (query 'query) (t t)))) (defun org-babel-check-evaluate (info) "Check if code block INFO should be evaluated. Do not query the user, but do display an informative message if evaluation is blocked. Returns non-nil if evaluation is not blocked." (let ((confirmed (org-babel-check-confirm-evaluate info))) (unless confirmed (message "Evaluation of this %s code block%sis disabled." (nth 0 info) (let ((name (nth 4 info))) (if name (format " (%s) " name) " ")))) confirmed)) ;; Dynamically scoped for asynchronous export. (defvar org-babel-confirm-evaluate-answer-no) (defun org-babel-confirm-evaluate (info) "Confirm evaluation of the code block INFO. This query can also be suppressed by setting the value of `org-confirm-babel-evaluate' to nil, in which case all future interactive code block evaluations will proceed without any confirmation from the user. Note disabling confirmation may result in accidental evaluation of potentially harmful code. The variable `org-babel-confirm-evaluate-answer-no' is used by the async export process, which requires a non-interactive environment, to override this check." (let* ((evalp (org-babel-check-confirm-evaluate info)) (lang (nth 0 info)) (name (nth 4 info)) (name-string (if name (format " (%s) " name) " "))) (pcase evalp (`nil nil) (`t t) (`query (or (and (not (bound-and-true-p org-babel-confirm-evaluate-answer-no)) (yes-or-no-p (format "Evaluate this %s code block%son your system? " lang name-string))) (progn (message "Evaluation of this %s code block%sis aborted." lang name-string) nil))) (x (error "Unexpected value `%s' from `org-babel-check-confirm-evaluate'" x))))) ;;;###autoload (defun org-babel-execute-safely-maybe () "Maybe `org-babel-execute-maybe'. This function does nothing unless `org-babel-no-eval-on-ctrl-c-ctrl-c' is non-nil." (unless org-babel-no-eval-on-ctrl-c-ctrl-c (org-babel-execute-maybe))) ;;;###autoload (defun org-babel-execute-maybe () "Execute src block or babel call at point." (interactive) (or (org-babel-execute-src-block-maybe) (org-babel-lob-execute-maybe))) (defmacro org-babel-when-in-src-block (&rest body) "Execute BODY if point is in a source block and return t. Otherwise do nothing and return nil." `(if (org-element-type-p (org-element-context) '(inline-src-block src-block)) (progn ,@body t) nil)) (defun org-babel-execute-src-block-maybe () "Conditionally execute a source block. Detect if this is context for a Babel src-block and if so then run `org-babel-execute-src-block'." (interactive) (org-babel-when-in-src-block (org-babel-eval-wipe-error-buffer) (org-babel-execute-src-block current-prefix-arg))) ;;;###autoload (defun org-babel-view-src-block-info () "Display information on the current source block. This includes header arguments, language and name, and is largely a window into the `org-babel-get-src-block-info' function." (interactive) (let ((info (org-babel-get-src-block-info 'no-eval)) (full (lambda (it) (> (length it) 0))) (printf (lambda (fmt &rest args) (princ (apply #'format fmt args))))) (when info (let* ((name (nth 4 info)) (language (nth 0 info)) (switches (nth 3 info)) (header-args (nth 2 info)) (property-header-args (org-entry-get (point) "header-args" t)) (property-header-args-language (org-entry-get (point) (concat "header-args:" language) t))) (with-help-window (help-buffer) (when name (funcall printf "Name: %s\n" name)) (when language (funcall printf "Language: %s\n" language)) ;; Show header arguments that have been set through ;; properties (i.e. in property drawers or through ;; #+PROPERTY) (funcall printf "Properties:\n") (funcall printf "\t:header-args \t%s\n" property-header-args) (funcall printf "\t:header-args:%s \t%s\n" language property-header-args-language) ;; Show switches (when (funcall full switches) (funcall printf "Switches: %s\n" switches)) ;; Show default header arguments and header arguments that ;; have been explicitly set in the current code block. (funcall printf "Header Arguments:\n") (dolist (pair (sort header-args (lambda (a b) (string< (symbol-name (car a)) (symbol-name (car b)))))) (when (funcall full (format "%s" (cdr pair))) (funcall printf "\t%S%s\t%s\n" (car pair) (if (> (length (format "%S" (car pair))) 7) "" "\t") (cdr pair))))))))) ;;;###autoload (defun org-babel-expand-src-block-maybe () "Conditionally expand a source block. Detect if this is context for an org-babel src-block and if so then run `org-babel-expand-src-block'." (interactive) (org-babel-when-in-src-block (org-babel-expand-src-block current-prefix-arg))) ;;;###autoload (defun org-babel-load-in-session-maybe () "Conditionally load a source block in a session. Detect if this is context for an org-babel src-block and if so then run `org-babel-load-in-session'." (interactive) (org-babel-when-in-src-block (org-babel-load-in-session current-prefix-arg))) (add-hook 'org-metaup-hook 'org-babel-load-in-session-maybe) ;;;###autoload (defun org-babel-pop-to-session-maybe () "Conditionally pop to a session. Detect if this is context for an org-babel src-block and if so then run `org-babel-switch-to-session'." (interactive) (org-babel-when-in-src-block (org-babel-switch-to-session current-prefix-arg))) (add-hook 'org-metadown-hook 'org-babel-pop-to-session-maybe) (defconst org-babel-common-header-args-w-values '((cache . ((no yes))) (cmdline . :any) (colnames . ((nil no yes))) (comments . ((no link yes org both noweb))) (dir . :any) (eval . ((yes no no-export strip-export never-export eval never query))) (exports . ((code results both none))) (epilogue . :any) (file . :any) (file-desc . :any) (file-ext . :any) (file-mode . ((#o755 #o555 #o444 :any))) (hlines . ((no yes))) (mkdirp . ((yes no))) (no-expand) (noeval) (noweb . ((yes no tangle strip-tangle no-export strip-export))) (noweb-ref . :any) (noweb-sep . :any) (noweb-prefix . ((no yes))) (output-dir . :any) (padline . ((yes no))) (post . :any) (prologue . :any) (results . ((file list vector table scalar verbatim) (raw html latex org code pp drawer link graphics) (replace silent none discard append prepend) (output value))) (rownames . ((no yes))) (sep . :any) (session . :any) (shebang . :any) (tangle . ((tangle yes no :any))) (tangle-mode . ((#o755 #o555 #o444 :any))) (var . :any) (wrap . :any)) "Alist defining common header args and their allowed values. Keys of the alist are header arg symbols. Values of the alist are either a symbol `:any' or a list of allowed values as symbols: (header-name . :any) (header-name . ((value1 value2 value3 ...)) (header-name . ((value1 value2 value3 ... :any)) When Org considers header-arg property inheritance, the innermost value from the list is considered. Symbol `:any' in the value list implies that any value is allowed. Yet the explicitly listed values from the list will be offered as completion candidates. FIXME: This is currently just supported for `results' and `exports'. Values in the alist can also be a list of lists. The inner lists define exclusive groups of values that can be set at the same time for a given header argument. (results . ((file list ...) (raw html ...)) The above example allows multi-component header arguments like #+begin_src bash :results file raw <:results will combine the two values \"file raw\".> #+begin_src bash :results file list <:results will only use the last value \"list\".> #+property: header-args :results file html ... #+begin_src bash :results list <:results will inherit with partial override \"list html\".> See info node `(org)Results of evaluation' for more details.") (defconst org-babel-header-arg-names (mapcar #'car org-babel-common-header-args-w-values) "Common header arguments used by org-babel. Note that individual languages may define their own language specific header arguments as well.") (defconst org-babel-safe-header-args '(:cache :colnames :comments :exports :epilogue :hlines :noeval :noweb :noweb-ref :noweb-sep :noweb-prefix :padline :prologue :rownames :sep :session :tangle :wrap (:eval . ("never" "query")) (:results . (lambda (str) (not (string-match "file" str))))) "A list of safe header arguments for babel source blocks. The list can have entries of the following forms: - :ARG -> :ARG is always a safe header arg - (:ARG . (VAL1 VAL2 ...)) -> :ARG is safe as a header arg if it is `equal' to one of the VALs. - (:ARG . FN) -> :ARG is safe as a header arg if the function FN returns non-nil. FN is passed one argument, the value of the header arg (as a string).") (defmacro org-babel-header-args-safe-fn (safe-list) "Return a function that determines whether a list of header args are safe. Intended usage is: \(put \\='org-babel-default-header-args \\='safe-local-variable (org-babel-header-args-safe-p org-babel-safe-header-args) This allows org-babel languages to extend the list of safe values for their `org-babel-default-header-args:foo' variable. For the format of SAFE-LIST, see `org-babel-safe-header-args'." `(lambda (value) (and (listp value) (cl-every (lambda (pair) (and (consp pair) (org-babel-one-header-arg-safe-p pair ,safe-list))) value)))) (defvar org-babel-default-header-args '((:session . "none") (:results . "replace") (:exports . "code") (:cache . "no") (:noweb . "no") (:hlines . "no") (:tangle . "no")) "Default arguments to use when evaluating a source block. This is a list in which each element is an alist. Each key corresponds to a header argument, and each value to that header's value. The value can either be a string or a closure that evaluates to a string. A closure is evaluated when the source block is being evaluated (e.g. during execution or export), with point at the source block. It is not possible to use an arbitrary function symbol (e.g. `some-func'), since org uses lexical binding. To achieve the same functionality, call the function within a closure (e.g. (lambda () (some-func))). To understand how closures can be used as default header arguments, imagine you'd like to set the file name output of a latex source block to a sha1 of its contents. We could achieve this with: (defun org-src-sha () (let ((elem (org-element-at-point))) (concat (sha1 (org-element-property :value elem)) \".svg\"))) (setq org-babel-default-header-args:latex `((:results . \"file link replace\") (:file . (lambda () (org-src-sha))))) Because the closure is evaluated with point at the source block, the call to `org-element-at-point' above will always retrieve information about the current source block. Some header arguments can be provided multiple times for a source block. An example of such a header argument is :var. This functionality is also supported for default header arguments by providing the header argument multiple times in the alist. For example: ((:var . \"foo=\\\"bar\\\"\") (:var . \"bar=\\\"foo\\\"\"))") (put 'org-babel-default-header-args 'safe-local-variable (org-babel-header-args-safe-fn org-babel-safe-header-args)) (defvar org-babel-default-inline-header-args '((:session . "none") (:results . "replace") (:exports . "results") (:hlines . "yes")) "Default arguments to use when evaluating an inline source block.") (put 'org-babel-default-inline-header-args 'safe-local-variable (org-babel-header-args-safe-fn org-babel-safe-header-args)) (defconst org-babel-name-regexp (format "^[ \t]*#\\+%s:[ \t]*" ;; FIXME: TBLNAME is for backward compatibility. (regexp-opt '("NAME" "TBLNAME"))) "Regexp matching a NAME keyword.") (defconst org-babel-result-regexp (rx (seq bol (zero-or-more (any "\t ")) "#+results" (opt "[" ;; Time stamp part. (opt "(" (= 4 digit) (= 2 "-" (= 2 digit)) " " (= 2 digit) (= 2 ":" (= 2 digit)) ") ") ;; SHA1 hash. (group (one-or-more hex-digit)) "]") ":" (zero-or-more (any "\t ")))) "Regular expression used to match result lines. If the results are associated with a hash key then the hash will be saved in match group 1.") (defconst org-babel-result-w-name-regexp (concat org-babel-result-regexp "\\(?9:[^ \t\n\r\v\f]+\\)") "Regexp matching a RESULTS keyword with a name. Name is saved in match group 9.") (defvar org-babel-min-lines-for-block-output 10 "The minimum number of lines for block output. If number of lines of output is equal to or exceeds this value, the output is placed in a #+begin_example...#+end_example block. Otherwise the output is marked as literal by inserting colons at the starts of the lines. This variable only takes effect if the :results output option is in effect.") (defvar org-babel-noweb-error-all-langs nil "Raise errors when noweb references don't resolve. Also see `org-babel-noweb-error-langs' to control noweb errors on a language by language bases.") (defvar org-babel-noweb-error-langs nil "Languages for which Babel will raise literate programming errors. List of languages for which errors should be raised when the source code block satisfying a noweb reference in this language can not be resolved. Also see `org-babel-noweb-error-all-langs' to raise errors for all languages.") (defvar org-babel-hash-show 4 "Number of initial characters to show of a hidden results hash.") (defvar org-babel-after-execute-hook nil "Hook for functions to be called after `org-babel-execute-src-block'.") (defun org-babel-named-src-block-regexp-for-name (&optional name) "Generate a regexp used to match a source block named NAME. If NAME is nil, match any name. Matched name is then put in match group 9. Other match groups are defined in `org-babel-src-block-regexp'." (concat org-babel-src-name-regexp (concat (if name (regexp-quote name) "\\(?9:.*?\\)") "[ \t]*" ) "\\(?:\n[ \t]*#\\+\\S-+:.*\\)*?" "\n" (substring org-babel-src-block-regexp 1))) (defun org-babel-named-data-regexp-for-name (name) "Generate a regexp used to match data named NAME." (concat org-babel-name-regexp (regexp-quote name) "[ \t]*$")) (defun org-babel--normalize-body (datum) "Normalize body for element or object DATUM. DATUM is a source block element or an inline source block object. Remove final newline character and spurious indentation." (let* ((value (org-element-property :value datum)) (body (if (string-suffix-p "\n" value) (substring value 0 -1) value))) (cond ((org-element-type-p datum 'inline-src-block) ;; Newline characters and indentation in an inline ;; src-block are not meaningful, since they could come from ;; some paragraph filling. Treat them as a white space. (replace-regexp-in-string "\n[ \t]*" " " body)) ((org-src-preserve-indentation-p datum) body) (t (org-remove-indentation body))))) ;;; functions (defvar org-babel-current-src-block-location nil "Marker pointing to the source block currently being executed. This may also point to a call line or an inline code block. If multiple blocks are being executed (e.g., in chained execution through use of the :var header argument) this marker points to the outer-most code block.") (defun org-babel-eval-headers (headers) "Compute header list set with HEADERS. Evaluate all header arguments set to functions prior to returning the list of header arguments." (let ((lst nil)) (dolist (elem headers) (if (and (cdr elem) (functionp (cdr elem))) (push `(,(car elem) . ,(funcall (cdr elem))) lst) (push elem lst))) (reverse lst))) (defun org-babel-get-src-block-info (&optional no-eval datum) "Extract information from a source block or inline source block. When optional argument NO-EVAL is non-nil, Babel does not resolve remote variable references; a process which could likely result in the execution of other code blocks, and do not evaluate Lisp values in parameters. By default, consider the block at point. However, when optional argument DATUM is provided, extract information from that parsed object instead. Return nil if point is not on a source block (blank lines after a source block are considered a part of that source block). Otherwise, return a list with the following pattern: (language body arguments switches name start coderef)" (let* ((datum (or datum (org-element-context))) (type (org-element-type datum)) (inline (eq type 'inline-src-block))) (when (memq type '(inline-src-block src-block)) (let* ((lang (org-element-property :language datum)) (lang-headers (intern (concat "org-babel-default-header-args:" lang))) (name (org-element-property :name datum)) (info (list lang (org-babel--normalize-body datum) (apply #'org-babel-merge-params ;; Use `copy-tree' to avoid creating shared structure ;; with the `org-babel-default-header-args-*' variables: ;; modifications by `org-babel-generate-file-param' ;; below would modify the shared structure, thereby ;; modifying the variables. (copy-tree (if inline org-babel-default-inline-header-args org-babel-default-header-args) t) (and (boundp lang-headers) (copy-tree (eval lang-headers t) t)) (append ;; If DATUM is provided, make sure we get node ;; properties applicable to its location within ;; the document. (org-with-point-at (org-element-begin datum) (org-babel-params-from-properties lang no-eval)) (mapcar (lambda (h) (org-babel-parse-header-arguments h no-eval)) (cons (org-element-property :parameters datum) (org-element-property :header datum))))) (or (org-element-property :switches datum) "") name (org-element-property (if inline :begin :post-affiliated) datum) (and (not inline) (org-src-coderef-format datum))))) (unless no-eval (setf (nth 2 info) (org-babel-process-params (nth 2 info)))) (setf (nth 2 info) (org-babel-generate-file-param name (nth 2 info))) info)))) (defun org-babel--expand-body (info) "Expand noweb references in src block and remove any coderefs. The src block is defined by its INFO, as returned by `org-babel-get-src-block-info'." (let ((coderef (nth 6 info)) (expand (if (org-babel-noweb-p (nth 2 info) :eval) (org-babel-expand-noweb-references info) (nth 1 info)))) (if (not coderef) expand (replace-regexp-in-string (org-src-coderef-regexp coderef) "" expand nil nil 1)))) (defun org-babel--file-desc (params result) "Retrieve description for file link result of evaluation. PARAMS is header argument values. RESULT is the file link as returned by the code block. When `:file-desc' header argument is provided use its value or duplicate RESULT in the description. When `:file-desc' is missing, return nil." (pcase (assq :file-desc params) (`nil nil) (`(:file-desc) result) (`(:file-desc . ,(and (pred stringp) val)) val))) (defvar *this*) ;; Dynamically bound in `org-babel-execute-src-block' ;; and `org-babel-read' (defun org-babel-session-buffer (&optional info) "Return buffer name for session associated with current code block. Return nil when no such live buffer with process exists. When INFO is non-nil, it should be a list returned by `org-babel-get-src-block-info'. This function uses org-babel-session-buffer: function to retrieve backend-specific session buffer name." (declare-function org-babel-comint-buffer-livep "ob-comint" (buffer)) (when-let* ((info (or info (org-babel-get-src-block-info 'no-eval))) (lang (nth 0 info)) (session (cdr (assq :session (nth 2 info)))) (cmd (intern (concat "org-babel-session-buffer:" lang))) (buffer-name (if (fboundp cmd) (funcall cmd session info) ;; Use session name as buffer name by default. session))) (require 'ob-comint) (when (org-babel-comint-buffer-livep buffer-name) buffer-name))) ;;;###autoload (defun org-babel-execute-src-block (&optional arg info params executor-type) "Execute the current source code block and return the result. Insert the results of execution into the buffer. Source code execution and the collection and formatting of results can be controlled through a variety of header arguments. With prefix argument ARG, force re-execution even if an existing result cached in the buffer would otherwise have been returned. Optionally supply a value for INFO in the form returned by `org-babel-get-src-block-info'. Optionally supply a value for PARAMS which will be merged with the header arguments specified at the front of the source code block. EXECUTOR-TYPE is the type of the org element responsible for the execution of the source block. If not provided then informed guess will be made." (interactive) (let* ((org-babel-current-src-block-location (or org-babel-current-src-block-location (nth 5 info) (org-babel-where-is-src-block-head))) (info (if info (copy-tree info) (org-babel-get-src-block-info))) (executor-type (or executor-type ;; If `executor-type' is unset, then we will make an ;; informed guess. (pcase (and ;; When executing virtual src block, no location ;; is known. org-babel-current-src-block-location (char-after org-babel-current-src-block-location)) (?s 'inline-src-block) (?c 'inline-babel-call) (?# (pcase (char-after (+ 2 org-babel-current-src-block-location)) (?b 'src-block) (?c 'call-block) (_ 'unknown))) (_ 'unknown))))) ;; Merge PARAMS with INFO before considering source block ;; evaluation since both could disagree. (cl-callf org-babel-merge-params (nth 2 info) params) (when (org-babel-check-evaluate info) (cl-callf org-babel-process-params (nth 2 info)) (let* ((params (nth 2 info)) (cache (let ((c (cdr (assq :cache params)))) (and (not arg) c (string= "yes" c)))) (new-hash (and cache (org-babel-sha1-hash info :eval))) (old-hash (and cache (org-babel-current-result-hash))) (current-cache (and new-hash (equal new-hash old-hash)))) (cond (current-cache (save-excursion ;Return cached result. (goto-char (org-babel-where-is-src-block-result nil info)) (forward-line) (skip-chars-forward " \t") (let ((result (org-babel-read-result))) (unless noninteractive (message (format "Cached: %s" (replace-regexp-in-string "%" "%%" (format "%S" result))))) result))) ((org-babel-confirm-evaluate info) (let* ((lang (nth 0 info)) (result-params (cdr (assq :result-params params))) (body (org-babel--expand-body info)) (dir (cdr (assq :dir params))) (mkdirp (cdr (assq :mkdirp params))) (default-directory (cond ((not dir) default-directory) ((when-let* ((session (org-babel-session-buffer info))) (buffer-local-value 'default-directory (get-buffer session)))) ((member mkdirp '("no" "nil" nil)) (file-name-as-directory (expand-file-name dir))) (t (let ((d (file-name-as-directory (expand-file-name dir)))) (make-directory d 'parents) d)))) (cmd (intern (concat "org-babel-execute:" lang))) result exec-start-time) (unless (fboundp cmd) (error "No org-babel-execute function for %s!" lang)) (unless noninteractive (message "Executing %s %s %s..." (capitalize lang) (pcase executor-type ('src-block "code block") ('inline-src-block "inline code block") ('babel-call "call") ('inline-babel-call "inline call") (e (symbol-name e))) (let ((name (nth 4 info))) (if name (format "(%s)" name) (format "at position %S" (nth 5 info)))))) (setq exec-start-time (current-time) result (let ((r ;; Code block may move point in the buffer. ;; Make sure that the point remains on the ;; code block. (save-excursion (funcall cmd body params)))) (if (and (eq (cdr (assq :result-type params)) 'value) (or (member "vector" result-params) (member "table" result-params)) (not (listp r))) (list (list r)) r))) (let ((file (and (member "file" result-params) (cdr (assq :file params))))) ;; If non-empty result and :file then write to :file. (when file ;; If `:results' are special types like `link' or ;; `graphics', don't write result to `:file'. Only ;; insert a link to `:file'. (when (and result (not (or (member "link" result-params) (member "graphics" result-params)))) (with-temp-file file (insert (org-babel-format-result result (cdr (assq :sep params))))) ;; Set file permissions if header argument ;; `:file-mode' is provided. (when (assq :file-mode params) (set-file-modes file (cdr (assq :file-mode params))))) (setq result file)) ;; Possibly perform post process provided its ;; appropriate. Dynamically bind "*this*" to the ;; actual results of the block. (let ((post (cdr (assq :post params)))) (when post (let ((*this* (if (not file) result (org-babel-result-to-file file (org-babel--file-desc params result) 'attachment)))) (setq result (org-babel-ref-resolve post)) (when file (setq result-params (remove "file" result-params)))))) (unless (member "none" result-params) (org-babel-insert-result result result-params info ;; append/prepend cannot handle hash as we accumulate ;; multiple outputs together. (when (member "replace" result-params) new-hash) lang (time-subtract (current-time) exec-start-time)))) (run-hooks 'org-babel-after-execute-hook) result))))))) (defun org-babel-expand-body:generic (body params &optional var-lines) "Expand BODY with PARAMS. Expand a block of code with org-babel according to its header arguments. This generic implementation of body expansion is called for languages which have not defined their own specific org-babel-expand-body:lang function. VAR-LINES is a list of lines that define variable environment. These lines will be added after `:prologue' parameter and before BODY." (let ((pro (cdr (assq :prologue params))) (epi (cdr (assq :epilogue params)))) (mapconcat #'identity (append (when pro (list pro)) var-lines (list body) (when epi (list epi))) "\n"))) ;;;###autoload (defun org-babel-expand-src-block (&optional _arg info params) "Expand the current source code block or block specified by INFO. INFO is the output of `org-babel-get-src-block-info'. PARAMS defines inherited header arguments. Expand according to the source code block's header arguments and pop open the results in a preview buffer." (interactive) (let* ((info (or info (org-babel-get-src-block-info))) (lang (nth 0 info)) (params (setf (nth 2 info) (sort (org-babel-merge-params (nth 2 info) params) (lambda (el1 el2) (string< (symbol-name (car el1)) (symbol-name (car el2))))))) (body (setf (nth 1 info) (if (org-babel-noweb-p params :eval) (org-babel-expand-noweb-references info) (nth 1 info)))) (expand-cmd (intern (concat "org-babel-expand-body:" lang))) (assignments-cmd (intern (concat "org-babel-variable-assignments:" lang))) (expanded (if (fboundp expand-cmd) (funcall expand-cmd body params) (org-babel-expand-body:generic body params (and (fboundp assignments-cmd) (funcall assignments-cmd params)))))) (if (called-interactively-p 'any) (org-edit-src-code expanded (concat "*Org-Babel Preview " (buffer-name) "[ " lang " ]*")) expanded))) (defun org-babel-combine-header-arg-lists (original &rest others) "Combine ORIGINAL and OTHERS lists of header argument names and arguments." (let ((results (copy-sequence original))) (dolist (new-list others) (dolist (arg-pair new-list) (let ((header (car arg-pair))) (setq results (cons arg-pair (cl-remove-if (lambda (pair) (equal header (car pair))) results)))))) results)) ;;;###autoload (defun org-babel-check-src-block () "Check for misspelled header arguments in the current code block." (interactive) ;; TODO: report malformed code block ;; TODO: report incompatible combinations of header arguments ;; TODO: report uninitialized variables (let ((too-close 2) ;; <- control closeness to report potential match (names (mapcar #'symbol-name org-babel-header-arg-names))) (dolist (header (mapcar (lambda (arg) (substring (symbol-name (car arg)) 1)) (and (org-babel-where-is-src-block-head) (org-babel-parse-header-arguments (org-no-properties (match-string 4)))))) (dolist (name names) (when (and (not (string= header name)) (<= (org-string-distance header name) too-close) (not (member header names))) (error "Supplied header \"%S\" is suspiciously close to \"%S\"" header name)))) (message "No suspicious header arguments found."))) ;;;###autoload (defun org-babel-insert-header-arg (&optional header-arg value) "Insert a header argument and its value. HEADER-ARG and VALUE, when provided, are the header argument name and its value. When HEADER-ARG or VALUE are nil, offer interactive completion from lists of common args and values." (interactive) (let* ((info (org-babel-get-src-block-info 'no-eval)) (lang (car info)) (begin (nth 5 info)) (lang-headers (intern (concat "org-babel-header-args:" lang))) (headers (org-babel-combine-header-arg-lists org-babel-common-header-args-w-values (when (boundp lang-headers) (eval lang-headers t)))) (header-arg (or header-arg (completing-read "Header Arg: " (mapcar (lambda (header-spec) (symbol-name (car header-spec))) headers)))) (vals (cdr (assoc (intern header-arg) headers))) (value (or value (cond ((eq vals :any) (read-from-minibuffer "value: ")) ((listp vals) (mapconcat (lambda (group) (let ((arg (completing-read "Value: " (cons "default" (mapcar #'symbol-name group))))) (if (and arg (not (string= "default" arg))) (concat arg " ") ""))) vals "")))))) (save-excursion (goto-char begin) (goto-char (line-end-position)) (unless (= (char-before (point)) ?\ ) (insert " ")) (insert ":" header-arg) (when value (insert " " value))))) ;; Add support for completing-read insertion of header arguments after ":" (defun org-babel-header-arg-expand () "Call `org-babel-enter-header-arg-w-completion' in appropriate contexts." (when (and (equal (char-before) ?\:) (org-babel-where-is-src-block-head)) (org-babel-enter-header-arg-w-completion (match-string 2)))) (defun org-babel-enter-header-arg-w-completion (&optional lang) "Insert header argument appropriate for LANG with completion." (let* ((lang-headers-var (intern (concat "org-babel-header-args:" lang))) (lang-headers (when (boundp lang-headers-var) (eval lang-headers-var t))) (headers-w-values (org-babel-combine-header-arg-lists org-babel-common-header-args-w-values lang-headers)) (headers (mapcar #'symbol-name (mapcar #'car headers-w-values))) (header (org-completing-read "Header Arg: " headers)) (args (cdr (assoc (intern header) headers-w-values))) (arg (when (and args (listp args)) (org-completing-read (format "%s: " header) (mapcar #'symbol-name (apply #'append args)))))) (insert (concat header " " (or arg ""))) (cons header arg))) (add-hook 'org-cycle-tab-first-hook 'org-babel-header-arg-expand) ;;;###autoload (defun org-babel-load-in-session (&optional _arg info) "Load the body of the current source-code block. When optional argument INFO is non-nil, use source block defined in INFO, as returned by `org-babel-get-src-block-info'. Evaluate the header arguments for the source block before entering the session. After loading the body this pops open the session." (interactive) (let* ((info (or info (org-babel-get-src-block-info))) (lang (nth 0 info)) (params (nth 2 info)) (body (if (not info) (user-error "No src code block at point") (setf (nth 1 info) (if (org-babel-noweb-p params :eval) (org-babel-expand-noweb-references info) (nth 1 info))))) (session (cdr (assq :session params))) (dir (cdr (assq :dir params))) (default-directory (or (and dir (file-name-as-directory dir)) default-directory)) (cmd (intern (concat "org-babel-load-session:" lang)))) (unless (fboundp cmd) (error "No org-babel-load-session function for %s!" lang)) (pop-to-buffer (funcall cmd session body params)) (end-of-line 1))) ;;;###autoload (defun org-babel-initiate-session (&optional arg info) "Initiate session for current code block or the block defined by INFO. If called with a prefix argument ARG, then resolve any variable references in the header arguments and assign these variables in the session. Copy the body of the code block to the kill ring." (interactive "P") (let* ((info (or info (org-babel-get-src-block-info (not arg)))) (lang (nth 0 info)) (body (nth 1 info)) (params (nth 2 info)) (session (cdr (assq :session params))) (dir (cdr (assq :dir params))) (default-directory (or (and dir (file-name-as-directory dir)) default-directory)) (init-cmd (intern (format "org-babel-%s-initiate-session" lang))) (prep-cmd (intern (concat "org-babel-prep-session:" lang)))) (when (and (stringp session) (string= session "none")) (error "This block is not using a session!")) (unless (fboundp init-cmd) (error "No org-babel-initiate-session function for %s!" lang)) (with-temp-buffer (insert (org-trim body)) (copy-region-as-kill (point-min) (point-max))) (when arg (unless (fboundp prep-cmd) (error "No org-babel-prep-session function for %s!" lang)) (funcall prep-cmd session params)) (funcall init-cmd session params))) ;;;###autoload (defun org-babel-switch-to-session (&optional arg info) "Switch to the session of the current code block or block defined by INFO. Uses `org-babel-initiate-session' to start the session. If called with a prefix argument ARG, then this is passed on to `org-babel-initiate-session'." (interactive "P") (pop-to-buffer (org-babel-initiate-session arg info)) (end-of-line 1)) (defalias 'org-babel-pop-to-session 'org-babel-switch-to-session) (defvar org-src-window-setup) ;;;###autoload (defun org-babel-switch-to-session-with-code (&optional arg _info) "Switch to code buffer and display session. Prefix argument ARG is passed to `org-babel-switch-to-session'." (interactive "P") (let ((swap-windows (lambda () (let ((other-window-buffer (window-buffer (next-window)))) (set-window-buffer (next-window) (current-buffer)) (set-window-buffer (selected-window) other-window-buffer)) (other-window 1))) (info (org-babel-get-src-block-info)) (org-src-window-setup 'reorganize-frame)) (save-excursion (org-babel-switch-to-session arg info)) (org-edit-src-code) (funcall swap-windows))) ;;;###autoload (defmacro org-babel-do-in-edit-buffer (&rest body) "Evaluate BODY in edit buffer if there is a code block at point. Return t if a code block was found at point, nil otherwise." (declare (debug (body))) `(let* ((element (org-element-at-point)) ;; This function is not supposed to move point. However, ;; `org-edit-src-code' always moves point back into the ;; source block. It is problematic if the point was before ;; the code, e.g., on block's opening line. In this case, ;; we want to restore this location after executing BODY. (outside-position (and (<= (line-beginning-position) (org-element-post-affiliated element)) (point-marker))) (org-src-window-setup 'switch-invisibly)) (when (and (org-babel-where-is-src-block-head element) (condition-case nil (org-edit-src-code) (t (org-edit-src-exit) (when outside-position (goto-char outside-position)) nil))) (unwind-protect (progn ,@body) (org-edit-src-exit) (when outside-position (goto-char outside-position))) t))) (defun org-babel-do-key-sequence-in-edit-buffer (key) "Read key sequence KEY and execute the command in edit buffer. Enter a key sequence to be executed in the language major-mode edit buffer. For example, TAB will alter the contents of the Org code block according to the effect of TAB in the language major mode buffer. For languages that support interactive sessions, this can be used to send code from the Org buffer to the session for evaluation using the native major mode evaluation mechanisms." (interactive "kEnter key-sequence to execute in edit buffer: ") (org-babel-do-in-edit-buffer (call-interactively (key-binding (or key (read-key-sequence nil)))))) (defvar org-link-bracket-re) (defun org-babel-active-location-p () "Return non-nil, when at executable element." (org-element-type-p (save-match-data (org-element-context)) '(babel-call inline-babel-call inline-src-block src-block))) ;;;###autoload (defun org-babel-open-src-block-result (&optional re-run) "Open results of source block at point. If `point' is on a source block then open the results of the source code block, otherwise return nil. With optional prefix argument RE-RUN the source-code block is evaluated even if results already exist." (interactive "P") (pcase (org-babel-get-src-block-info 'no-eval) (`(,_ ,_ ,arguments ,_ ,_ ,start ,_) (save-excursion ;; Go to the results, if there aren't any then run the block. (goto-char start) (goto-char (or (and (not re-run) (org-babel-where-is-src-block-result)) (progn (org-babel-execute-src-block) (org-babel-where-is-src-block-result)))) (end-of-line) (skip-chars-forward " \r\t\n") ;; Open the results. (if (looking-at org-link-bracket-re) (org-open-at-point) (let ((r (org-babel-format-result (org-babel-read-result) (cdr (assq :sep arguments))))) (pop-to-buffer (get-buffer-create "*Org Babel Results*")) (erase-buffer) (insert r))) t)) (_ nil))) ;;;###autoload (defmacro org-babel-map-src-blocks (file &rest body) "Evaluate BODY forms on each source-block in FILE. If FILE is nil evaluate BODY forms on source blocks in current buffer. During evaluation of BODY the following local variables are set relative to the currently matched code block. full-block ------- string holding the entirety of the code block beg-block -------- point at the beginning of the code block end-block -------- point at the end of the matched code block lang ------------- string holding the language of the code block beg-lang --------- point at the beginning of the lang end-lang --------- point at the end of the lang switches --------- string holding the switches beg-switches ----- point at the beginning of the switches end-switches ----- point at the end of the switches header-args ------ string holding the header-args beg-header-args -- point at the beginning of the header-args end-header-args -- point at the end of the header-args body ------------- string holding the body of the code block beg-body --------- point at the beginning of the body end-body --------- point at the end of the body" (declare (indent 1) (debug t)) (let ((tempvar (make-symbol "file"))) `(let* ((case-fold-search t) (,tempvar ,file) (visited-p (or (null ,tempvar) (get-file-buffer (expand-file-name ,tempvar)))) (point (point)) to-be-removed) (save-window-excursion (when ,tempvar (find-file ,tempvar)) (setq to-be-removed (current-buffer)) (goto-char (point-min)) (while (re-search-forward org-babel-src-block-regexp nil t) (when (org-babel-active-location-p) (goto-char (match-beginning 0)) (let ((full-block (match-string 0)) (beg-block (match-beginning 0)) (end-block (match-end 0)) (lang (match-string 2)) (beg-lang (match-beginning 2)) (end-lang (match-end 2)) (switches (match-string 3)) (beg-switches (match-beginning 3)) (end-switches (match-end 3)) (header-args (match-string 4)) (beg-header-args (match-beginning 4)) (end-header-args (match-end 4)) (body (match-string 5)) (beg-body (match-beginning 5)) (end-body (match-end 5))) ;; Silence byte-compiler in case `body' doesn't use all ;; those variables. (ignore full-block beg-block end-block lang beg-lang end-lang switches beg-switches end-switches header-args beg-header-args end-header-args body beg-body end-body) ,@body (goto-char end-block))))) (unless visited-p (kill-buffer to-be-removed)) (goto-char point)))) ;;;###autoload (defmacro org-babel-map-inline-src-blocks (file &rest body) "Evaluate BODY forms on each inline source block in FILE. If FILE is nil evaluate BODY forms on source blocks in current buffer." (declare (indent 1) (debug (form body))) (org-with-gensyms (datum end point tempvar to-be-removed visitedp) `(let* ((case-fold-search t) (,tempvar ,file) (,visitedp (or (null ,tempvar) (get-file-buffer (expand-file-name ,tempvar)))) (,point (point)) ,to-be-removed) (save-window-excursion (when ,tempvar (find-file ,tempvar)) (setq ,to-be-removed (current-buffer)) (goto-char (point-min)) (while (re-search-forward "src_\\S-" nil t) (let ((,datum (org-element-context))) (when (org-element-type-p ,datum 'inline-src-block) (goto-char (org-element-begin ,datum)) (let ((,end (copy-marker (org-element-end ,datum)))) ,@body (goto-char ,end) (set-marker ,end nil)))))) (unless ,visitedp (kill-buffer ,to-be-removed)) (goto-char ,point)))) ;;;###autoload (defmacro org-babel-map-call-lines (file &rest body) "Evaluate BODY forms on each call line in FILE. If FILE is nil evaluate BODY forms on source blocks in current buffer." (declare (indent 1) (debug (form body))) (org-with-gensyms (datum end point tempvar to-be-removed visitedp) `(let* ((case-fold-search t) (,tempvar ,file) (,visitedp (or (null ,tempvar) (get-file-buffer (expand-file-name ,tempvar)))) (,point (point)) ,to-be-removed) (save-window-excursion (when ,tempvar (find-file ,tempvar)) (setq ,to-be-removed (current-buffer)) (goto-char (point-min)) (while (re-search-forward "call_\\S-\\|^[ \t]*#\\+CALL:" nil t) (let ((,datum (org-element-context))) (when (org-element-type-p ,datum '(babel-call inline-babel-call)) (goto-char (or (org-element-post-affiliated datum) (org-element-begin datum))) (let ((,end (copy-marker (org-element-end ,datum)))) ,@body (goto-char ,end) (set-marker ,end nil)))))) (unless ,visitedp (kill-buffer ,to-be-removed)) (goto-char ,point)))) ;;;###autoload (defmacro org-babel-map-executables (file &rest body) "Evaluate BODY forms on each active Babel code in FILE. If FILE is nil evaluate BODY forms on source blocks in current buffer." (declare (indent 1) (debug (form body))) (org-with-gensyms (datum end point tempvar to-be-removed visitedp) `(let* ((case-fold-search t) (,tempvar ,file) (,visitedp (or (null ,tempvar) (get-file-buffer (expand-file-name ,tempvar)))) (,point (point)) ,to-be-removed) (save-window-excursion (when ,tempvar (find-file ,tempvar)) (setq ,to-be-removed (current-buffer)) (goto-char (point-min)) (while (re-search-forward "\\(call\\|src\\)_\\|^[ \t]*#\\+\\(BEGIN_SRC\\|CALL:\\)" nil t) (let ((,datum (org-element-context))) (when (org-element-type-p ,datum '(babel-call inline-babel-call inline-src-block src-block)) (goto-char (or (org-element-post-affiliated ,datum) (org-element-begin ,datum))) (let ((,end (copy-marker (org-element-end ,datum)))) ,@body (goto-char ,end) (set-marker ,end nil)))))) (unless ,visitedp (kill-buffer ,to-be-removed)) (goto-char ,point)))) ;;;###autoload (defun org-babel-execute-buffer (&optional arg) "Execute source code blocks in a buffer. Prefix argument ARG is passed to `org-babel-execute-src-block'. Call `org-babel-execute-src-block' on every source block in the current buffer." (interactive "P") (org-babel-eval-wipe-error-buffer) (org-save-outline-visibility t (org-babel-map-executables nil (if (org-element-type-p (org-element-context) '(babel-call inline-babel-call)) (org-babel-lob-execute-maybe) (org-babel-execute-src-block arg))))) ;;;###autoload (defun org-babel-execute-subtree (&optional arg) "Execute source code blocks in a subtree. Call `org-babel-execute-src-block' on every source block in the current subtree, passing over the prefix argument ARG." (interactive "P") (save-restriction (save-excursion (org-narrow-to-subtree) (org-babel-execute-buffer arg) (widen)))) ;;;###autoload (defun org-babel-sha1-hash (&optional info context) "Generate a sha1 hash based on the value of INFO. CONTEXT specifies the context of evaluation. It can be `:eval', `:export', `:tangle'. A nil value means `:eval'." (interactive) (let ((print-level nil) (info (or info (org-babel-get-src-block-info))) (context (or context :eval))) (setf (nth 2 info) (sort (copy-sequence (nth 2 info)) (lambda (a b) (string< (car a) (car b))))) (let* ((rm (lambda (lst) (dolist (p '("replace" "silent" "none" "discard" "append" "prepend")) (setq lst (remove p lst))) lst)) (norm (lambda (arg) (let ((v (if (and (listp (cdr arg)) (null (cddr arg))) (copy-sequence (cdr arg)) (cdr arg)))) (when (and v (not (and (sequencep v) (not (consp v)) (= (length v) 0)))) (cond ((and (listp v) ; lists are sorted (member (car arg) '(:result-params))) (sort (funcall rm v) #'string<)) ((and (stringp v) ; strings are sorted (member (car arg) '(:results :exports))) (mapconcat #'identity (sort (funcall rm (split-string v)) #'string<) " ")) (t v)))))) ;; expanded body (lang (nth 0 info)) (params (nth 2 info)) (body (if (org-babel-noweb-p params context) (org-babel-expand-noweb-references info) (nth 1 info))) (expand-cmd (intern (concat "org-babel-expand-body:" lang))) (assignments-cmd (intern (concat "org-babel-variable-assignments:" lang))) (expanded (if (fboundp expand-cmd) (funcall expand-cmd body params) (org-babel-expand-body:generic body params (and (fboundp assignments-cmd) (funcall assignments-cmd params)))))) (let* ((it (format "%s-%s" (mapconcat #'identity (delq nil (mapcar (lambda (arg) (let ((normalized (funcall norm arg))) (when normalized (format "%S" normalized)))) (nth 2 info))) ":") expanded)) (hash (sha1 it))) (when (called-interactively-p 'interactive) (message hash)) hash)))) (defun org-babel-current-result-hash (&optional _info) "Return the current in-buffer hash." (let ((result (org-babel-where-is-src-block-result nil))) (when result (org-with-point-at result (let ((case-fold-search t)) (looking-at org-babel-result-regexp)) (match-string-no-properties 1))))) (defun org-babel-hide-hash () "Hide the hash in the current results line. Only the initial `org-babel-hash-show' characters of the hash will remain visible." (add-to-invisibility-spec '(org-babel-hide-hash . t)) (save-excursion (when (and (let ((case-fold-search t)) (re-search-forward org-babel-result-regexp nil t)) (match-string 1)) (let* ((start (match-beginning 1)) (hide-start (+ org-babel-hash-show start)) (end (match-end 1)) (hash (match-string 1)) ov1 ov2) (setq ov1 (make-overlay start hide-start)) (setq ov2 (make-overlay hide-start end)) (overlay-put ov2 'invisible 'org-babel-hide-hash) (overlay-put ov1 'babel-hash hash))))) (defun org-babel-hide-all-hashes () "Hide the hash in the current buffer. Only the initial `org-babel-hash-show' characters of each hash will remain visible. This function should be called as part of the `org-mode-hook'." (save-excursion (let ((case-fold-search t)) (while (and (not org-babel-hash-show-time) (re-search-forward org-babel-result-regexp nil t)) (goto-char (match-beginning 0)) (org-babel-hide-hash) (goto-char (match-end 0)))))) (add-hook 'org-mode-hook #'org-babel-hide-all-hashes) (defun org-babel-hash-at-point (&optional point) "Return the value of the hash at POINT. \\\ The hash is also added as the last element of the kill ring. This can be called with `\\[org-ctrl-c-ctrl-c]'." (interactive) (let ((hash (car (delq nil (mapcar (lambda (ol) (overlay-get ol 'babel-hash)) (overlays-at (or point (point)))))))) (when hash (kill-new hash) (message hash)))) (defun org-babel-result-hide-spec () "Hide portions of results lines. Add `org-babel-hide-result' as an invisibility spec for hiding portions of results lines." (add-to-invisibility-spec '(org-babel-hide-result . t))) (add-hook 'org-mode-hook #'org-babel-result-hide-spec) (defvar org-babel-hide-result-overlays nil "Overlays hiding results.") (defun org-babel-result-hide-all () "Fold all results in the current buffer." (interactive) (org-babel-show-result-all) (save-excursion (let ((case-fold-search t)) (while (re-search-forward org-babel-result-regexp nil t) (save-excursion (goto-char (match-beginning 0)) (org-babel-hide-result-toggle-maybe)))))) (defun org-babel-show-result-all () "Unfold all results in the current buffer." (mapc 'delete-overlay org-babel-hide-result-overlays) (setq org-babel-hide-result-overlays nil)) ;;;###autoload (defun org-babel-hide-result-toggle-maybe () "Toggle visibility of result at point." (interactive) (let ((case-fold-search t)) (and (org-match-line org-babel-result-regexp) (progn (org-babel-hide-result-toggle) t)))) (defun org-babel-hide-result-toggle (&optional force) "Toggle the visibility of the current result. When FORCE is symbol `off', unconditionally display the result. Otherwise, when FORCE is non-nil, unconditionally hide the result." (interactive) (save-excursion (forward-line 0) (let ((case-fold-search t)) (unless (re-search-forward org-babel-result-regexp nil t) (error "Not looking at a result line"))) (let ((start (progn (forward-line 1) (1- (point)))) (end (progn (while (looking-at org-babel-multi-line-header-regexp) (forward-line 1)) (goto-char (1- (org-babel-result-end))) (point))) ov) (if (memq t (mapcar (lambda (overlay) (eq (overlay-get overlay 'invisible) 'org-babel-hide-result)) (overlays-at start))) (when (or (not force) (eq force 'off)) (mapc (lambda (ov) (when (member ov org-babel-hide-result-overlays) (setq org-babel-hide-result-overlays (delq ov org-babel-hide-result-overlays))) (when (eq (overlay-get ov 'invisible) 'org-babel-hide-result) (delete-overlay ov))) (overlays-at start))) (setq ov (make-overlay start end)) (overlay-put ov 'invisible 'org-babel-hide-result) ;; make the block accessible to isearch (overlay-put ov 'isearch-open-invisible (lambda (ov) (when (member ov org-babel-hide-result-overlays) (setq org-babel-hide-result-overlays (delq ov org-babel-hide-result-overlays))) (when (eq (overlay-get ov 'invisible) 'org-babel-hide-result) (delete-overlay ov)))) (push ov org-babel-hide-result-overlays))))) ;; org-tab-after-check-for-cycling-hook (add-hook 'org-cycle-tab-first-hook #'org-babel-hide-result-toggle-maybe) ;; Remove overlays when changing major mode (add-hook 'org-mode-hook (lambda () (add-hook 'change-major-mode-hook #'org-babel-show-result-all 'append 'local))) (defun org-babel-params-from-properties (&optional lang no-eval) "Retrieve source block parameters specified as properties. LANG is the language of the source block, as a string. When optional argument NO-EVAL is non-nil, do not evaluate Lisp values in parameters. Return a list of association lists of source block parameters specified in the properties of the current outline entry." (save-match-data (list ;; Header arguments specified with the header-args property at ;; point of call. (org-babel-parse-header-arguments (org-entry-get (point) "header-args" 'inherit) no-eval) ;; Language-specific header arguments at point of call. (and lang (org-babel-parse-header-arguments (org-entry-get (point) (concat "header-args:" lang) 'inherit) no-eval))))) (defun org-babel-balanced-split (string alts) "Split STRING on instances of ALTS. ALTS is a character, or cons of two character options where each option may be either the numeric code of a single character or a list of character alternatives. For example, to split on balanced instances of \"[ \t]:\", set ALTS to ((32 9) . 58)." (with-temp-buffer (insert string) (goto-char (point-min)) (let ((splitp (lambda (past next) ;; Non-nil when there should be a split after NEXT ;; character. PAST is the character before NEXT. (pcase alts (`(,(and first (pred consp)) . ,(and second (pred consp))) (and (memq past first) (memq next second))) (`(,first . ,(and second (pred consp))) (and (eq past first) (memq next second))) (`(,(and first (pred consp)) . ,second) (and (memq past first) (eq next second))) (`(,first . ,second) (and (eq past first) (eq next second))) ((pred (eq next)) t) (_ nil)))) (partial nil) (result nil)) (while (not (eobp)) (cond ((funcall splitp (char-before) (char-after)) ;; There is a split after point. If ALTS is two-folds, ;; remove last parsed character as it belongs to ALTS. (when (consp alts) (pop partial)) ;; Include elements parsed so far in RESULTS and flush ;; partial parsing. (when partial (push (apply #'string (nreverse partial)) result) (setq partial nil)) (forward-char)) ((memq (char-after) '(?\( ?\[)) ;; Include everything between balanced brackets. (let* ((origin (point)) (after (char-after)) (openings (list after))) (forward-char) (while (and openings (re-search-forward "[]()]" nil t)) (pcase (char-before) ((and match (or ?\[ ?\()) (push match openings)) (?\] (when (eq ?\[ (car openings)) (pop openings))) (_ (when (eq ?\( (car openings)) (pop openings))))) (if (null openings) (setq partial (nconc (nreverse (string-to-list (buffer-substring origin (point)))) partial)) ;; Un-balanced bracket. Backtrack. (push after partial) (goto-char (1+ origin))))) ((and (eq ?\" (char-after)) (not (eq ?\\ (char-before)))) ;; Include everything from current double quote to next ;; non-escaped double quote. (let ((origin (point))) (if (re-search-forward "[^\\]\"" nil t) (setq partial (nconc (nreverse (string-to-list (buffer-substring origin (point)))) partial)) ;; No closing double quote. Backtrack. (push ?\" partial) (forward-char)))) (t (push (char-after) partial) (forward-char)))) ;; Add pending parsing and return result. (when partial (push (apply #'string (nreverse partial)) result)) (nreverse result)))) (defun org-babel-join-splits-near-ch (ch list) "Join strings in LIST where CH is on either end of the strings. This function will join list elements like \"a=\" \"2\" into \"a=2\"." (let ((last= (lambda (str) (= ch (aref str (1- (length str)))))) (first= (lambda (str) (= ch (aref str 0))))) (reverse (cl-reduce (lambda (acc el) (let ((head (car acc))) (if (and head (or (funcall last= head) (funcall first= el))) (cons (concat head el) (cdr acc)) (cons el acc)))) list :initial-value nil)))) (defun org-babel-parse-header-arguments (string &optional no-eval) "Parse header arguments in STRING. When optional argument NO-EVAL is non-nil, do not evaluate Lisp in parameters. Return an alist." (when (org-string-nw-p string) (org-babel-parse-multiple-vars (delq nil (mapcar (lambda (arg) (if (string-match "\\([^ \f\t\n\r\v]+\\)[ \f\t\n\r\v]+\\([^ \f\t\n\r\v]+.*\\)" arg) (cons (intern (match-string 1 arg)) (org-babel-read (org-babel-chomp (match-string 2 arg)) no-eval)) (cons (intern (org-babel-chomp arg)) nil))) (let ((raw (org-babel-balanced-split string '((32 9) . 58)))) (cons (car raw) (mapcar (lambda (r) (concat ":" r)) (cdr raw))))))))) (defun org-babel-parse-multiple-vars (header-arguments) "Expand multiple variable assignments behind a single :var keyword. This allows expression of multiple variables with one :var as shown below. #+PROPERTY: var foo=1, bar=2 HEADER-ARGUMENTS is an alist of all the arguments." (let (results) (mapc (lambda (pair) (if (eq (car pair) :var) (or (mapcar (lambda (v) (push (cons :var (org-trim v)) results)) (org-babel-join-splits-near-ch 61 (org-babel-balanced-split (or (cdr pair) "") 32))) (push `(:var) results)) (push pair results))) header-arguments) (nreverse results))) (defun org-babel-process-params (params) "Expand variables in PARAMS and add summary parameters." (let* ((processed-vars (mapcar (lambda (el) (if (consp el) el (org-babel-ref-parse el))) (org-babel--get-vars params))) (vars-and-names (if (and (assq :colname-names params) (assq :rowname-names params)) (list processed-vars) (org-babel-disassemble-tables processed-vars (cdr (assq :hlines params)) (cdr (assq :colnames params)) (cdr (assq :rownames params))))) (raw-result (or (cdr (assq :results params)) "")) (result-params (delete-dups (append (split-string (if (stringp raw-result) raw-result ;; FIXME: Arbitrary code evaluation. (eval raw-result t))) (cdr (assq :result-params params)))))) (append (mapcar (lambda (var) (cons :var var)) (car vars-and-names)) (list (cons :colname-names (or (cdr (assq :colname-names params)) (cadr vars-and-names))) (cons :rowname-names (or (cdr (assq :rowname-names params)) (cl-caddr vars-and-names))) (cons :result-params result-params) (cons :result-type (cond ((member "output" result-params) 'output) ((member "value" result-params) 'value) (t 'value)))) (cl-remove-if (lambda (x) (memq (car x) '(:colname-names :rowname-names :result-params :result-type :var))) params)))) ;; row and column names (defun org-babel-del-hlines (table) "Remove all `hline's from TABLE." (remq 'hline table)) (defun org-babel-get-colnames (table) "Return the column names of TABLE. Return a cons cell, the `car' of which contains the TABLE less colnames, and the `cdr' of which contains a list of the column names." ;; Skip over leading hlines. (while (eq 'hline (car table)) (pop table)) (if (eq 'hline (nth 1 table)) (cons (cddr table) (car table)) (cons (cdr table) (car table)))) (defun org-babel-get-rownames (table) "Return the row names of TABLE. Return a cons cell, the `car' of which contains the TABLE less rownames, and the `cdr' of which contains a list of the rownames. Note: this function removes any hlines in TABLE." (let* ((table (org-babel-del-hlines table)) (rownames (funcall (lambda () (let ((tp table)) (mapcar (lambda (_row) (prog1 (pop (car tp)) (setq tp (cdr tp)))) table)))))) (cons table rownames))) (defun org-babel-put-colnames (table colnames) "Add COLNAMES to TABLE if they exist." (if colnames (apply 'list colnames 'hline table) table)) (defun org-babel-put-rownames (table rownames) "Add ROWNAMES to TABLE if they exist." (if rownames (mapcar (lambda (row) (if (listp row) (cons (or (pop rownames) "") row) row)) table) table)) (defun org-babel-pick-name (names selector) "Select one out of an alist of row or column names. SELECTOR can be either a list of names in which case those names will be returned directly, or an index into the list NAMES in which case the indexed names will be return." (if (listp selector) selector (when names (if (and selector (symbolp selector) (not (equal t selector))) (cdr (assoc selector names)) (if (integerp selector) (nth (- selector 1) names) (cdr (car (last names)))))))) (defun org-babel-disassemble-tables (vars hlines colnames rownames) "Parse tables for further processing. Process the variables in VARS according to the HLINES, ROWNAMES and COLNAMES header arguments. Return a list consisting of the vars, cnames and rnames." (let (cnames rnames) (list (mapcar (lambda (var) (when (proper-list-p (cdr var)) (when (and (not (equal colnames "no")) ;; Compatibility note: avoid `length>', which ;; isn't available until Emacs 28. (or colnames ;; :colnames nil (default) ;; Auto-assign column names when the table ;; has hline as the second line after ;; non-hline row. (and (> (length (cdr var)) 1) (not (eq (car (cdr var)) 'hline)) ; first row (eq (nth 1 (cdr var)) 'hline) ; second row (not (member 'hline (cddr (cdr var)))) ; other rows ))) (let ((both (org-babel-get-colnames (cdr var)))) (setq cnames (cons (cons (car var) (cdr both)) cnames)) (setq var (cons (car var) (car both))))) (when (and rownames (not (equal rownames "no"))) (let ((both (org-babel-get-rownames (cdr var)))) (setq rnames (cons (cons (car var) (cdr both)) rnames)) (setq var (cons (car var) (car both))))) (when (and hlines (not (equal hlines "yes"))) (setq var (cons (car var) (org-babel-del-hlines (cdr var)))))) var) vars) (reverse cnames) (reverse rnames)))) (defun org-babel-reassemble-table (table colnames rownames) "Add column and row names to a table. Given a TABLE and set of COLNAMES and ROWNAMES add the names to the table for reinsertion to `org-mode'." (if (listp table) (let ((table (if (and rownames (= (length table) (length rownames))) (org-babel-put-rownames table rownames) table))) (if (and colnames (listp (car table)) (= (length (car table)) (length colnames))) (org-babel-put-colnames table colnames) table)) table)) (defun org-babel-where-is-src-block-head (&optional src-block) "Find where the current source block begins. If optional argument SRC-BLOCK is `src-block' type element, find its current beginning instead. Return the point at the beginning of the current source block. Specifically at the beginning of the #+BEGIN_SRC line. Also set `match-data' relatively to `org-babel-src-block-regexp', which see. If the point is not on a source block or within blank lines after an src block, then return nil." (let ((element (or src-block (org-element-at-point)))) (when (org-element-type-p element 'src-block) (let ((end (org-element-end element))) (org-with-wide-buffer ;; Ensure point is not on a blank line after the block. (forward-line 0) (skip-chars-forward " \r\t\n" end) (when (< (point) end) (prog1 (goto-char (org-element-post-affiliated element)) (looking-at org-babel-src-block-regexp)))))))) ;;;###autoload (defun org-babel-goto-src-block-head () "Go to the beginning of the current code block." (interactive) (let ((head (org-babel-where-is-src-block-head))) (if head (goto-char head) (error "Not currently in a code block")))) ;;;###autoload (defun org-babel-goto-named-src-block (name) "Go to a source-code block with NAME." (interactive (let ((completion-ignore-case t) (case-fold-search t) (all-block-names (org-babel-src-block-names))) (list (completing-read "source-block name: " all-block-names nil t (let* ((context (org-element-context)) (type (org-element-type context)) (noweb-ref (and (memq type '(inline-src-block src-block)) (org-in-regexp (org-babel-noweb-wrap))))) (cond (noweb-ref (buffer-substring (+ (car noweb-ref) (length org-babel-noweb-wrap-start)) (- (cdr noweb-ref) (length org-babel-noweb-wrap-end)))) ((memq type '(babel-call inline-babel-call)) ;#+CALL: (org-element-property :call context)) ((car (org-element-property :results context))) ;#+RESULTS: ((let ((symbol (thing-at-point 'symbol))) ;Symbol. (and symbol (member-ignore-case symbol all-block-names) symbol))) (t ""))))))) (let ((point (org-babel-find-named-block name))) (if point ;; Taken from `org-open-at-point'. (progn (org-mark-ring-push) (goto-char point) (org-fold-show-context)) (message "source-code block `%s' not found in this buffer" name)))) (defun org-babel-find-named-block (name) "Find a named source-code block. Return the location of the source block identified by source NAME, or nil if no such block exists. Set match data according to `org-babel-named-src-block-regexp'." (save-excursion (goto-char (point-min)) (let ((regexp (org-babel-named-src-block-regexp-for-name name))) (or (and (looking-at regexp) (progn (goto-char (match-beginning 1)) (line-beginning-position))) (ignore-errors (org-next-block 1 nil regexp)))))) (defun org-babel-src-block-names (&optional file) "Return the names of source blocks in FILE or the current buffer." (with-current-buffer (if file (find-file-noselect file) (current-buffer)) (org-with-point-at 1 (let ((regexp "^[ \t]*#\\+begin_src ") (case-fold-search t) (names nil)) (while (re-search-forward regexp nil t) (let ((element (org-element-at-point))) (when (org-element-type-p element 'src-block) (let ((name (org-element-property :name element))) (when name (push name names)))))) names)))) ;;;###autoload (defun org-babel-goto-named-result (name) "Go to a result with NAME." (interactive (let ((completion-ignore-case t)) (list (completing-read "Source-block name: " (org-babel-result-names) nil t)))) (let ((point (org-babel-find-named-result name))) (if point ;; taken from `org-open-at-point' (progn (goto-char point) (org-fold-show-context)) (message "result `%s' not found in this buffer" name)))) (defun org-babel-find-named-result (name) "Find a named result. Return the location of the result named NAME in the current buffer or nil if no such result exists." (save-excursion (goto-char (point-min)) (let ((case-fold-search t) (re (format "^[ \t]*#\\+%s.*?:[ \t]*%s[ \t]*$" org-babel-results-keyword (regexp-quote name)))) (catch :found (while (re-search-forward re nil t) (let ((element (org-element-at-point))) (when (or (org-element-type-p element 'keyword) (< (point) (org-element-post-affiliated element))) (throw :found (line-beginning-position))))))))) (defun org-babel-result-names (&optional file) "Return the names of results in FILE or the current buffer." (with-current-buffer (if file (find-file-noselect file) (current-buffer)) (org-with-point-at 1 (let ((case-fold-search t) names) (while (re-search-forward org-babel-result-w-name-regexp nil t) (setq names (cons (match-string-no-properties 9) names))) names)))) ;;;###autoload (defun org-babel-next-src-block (&optional arg) "Jump to the next source block. With optional prefix argument ARG, jump forward ARG many source blocks." (interactive "p") (org-next-block arg nil org-babel-src-block-regexp)) ;;;###autoload (defun org-babel-previous-src-block (&optional arg) "Jump to the previous source block. With optional prefix argument ARG, jump backward ARG many source blocks." (interactive "p") (org-previous-block arg org-babel-src-block-regexp)) (defvar org-babel-load-languages) ;;;###autoload (defun org-babel-mark-block () "Mark current source block." (interactive) (let ((head (org-babel-where-is-src-block-head))) (when head (save-excursion (goto-char head) (looking-at org-babel-src-block-regexp)) (push-mark (match-end 5) nil t) (goto-char (match-beginning 5))))) (defun org-babel-demarcate-block (&optional arg) "Wrap or split the code in an active region or at point. With prefix argument ARG, also create a new heading at point. When called from inside of a code block the current block is split. When called from outside of a code block a new code block is created. In both cases if the region is demarcated and if the region is not active then the point is demarcated. When called within blank lines after a code block, create a new code block of the same language as the previous." (interactive "P") (let* ((info (org-babel-get-src-block-info 'no-eval)) (start (org-babel-where-is-src-block-head)) ;; `start' will be nil when within space lines after src block. (block (and start (match-string 0))) (body-beg (and start (match-beginning 5))) (body-end (and start (match-end 5))) (stars (concat (make-string (or (org-current-level) 1) ?*) " ")) (upper-case-p (and block (let (case-fold-search) (string-match-p "#\\+BEGIN_SRC" block))))) (if (and info start) ;; At src block, but not within blank lines after it. (let* ((copy (org-element-copy (org-element-at-point))) (before (org-element-begin copy)) (beyond (org-element-end copy)) (parts (if (org-region-active-p) (list body-beg (region-beginning) (region-end) body-end) (list body-beg (point) body-end))) (pads ;; To calculate left-side white-space padding. (if (org-region-active-p) (list (region-beginning) (region-end)) (list (point)))) (n (- (length parts) 2)) ;; 1 or 2 parts in `dolist' below. ;; `post-blank' caches the property before setting it to 0. (post-blank (org-element-property :post-blank copy))) ;; Point or region are within body when parts is in increasing order. (unless (apply #'<= parts) (user-error "Select within the source block body to split it")) (setq parts (mapcar (lambda (p) (buffer-substring (car p) (cdr p))) (seq-mapn #'cons parts (cdr parts)))) ;; Map positions to columns for white-space padding. (setq pads (mapcar (lambda (p) (save-excursion (goto-char p) (current-column))) pads)) (push 0 pads) ;; The 1st part never requires white-space padding. (setq parts (mapcar (lambda (p) (string-join (list (make-string (car p) ?\s) (cdr p)))) (seq-mapn #'cons pads parts))) (delete-region before beyond) ;; Set `:post-blank' to 0. We take care of spacing between blocks. (org-element-put-property copy :post-blank 0) (org-element-put-property copy :value (car parts)) (insert (org-element-interpret-data copy)) ;; `org-indent-block' may see another `org-element' (e.g. paragraph) ;; immediately after the block. Ensure to indent the inserted block ;; and move point to its end. (org-babel-previous-src-block 1) (org-indent-block) (goto-char (org-element-end (org-element-at-point))) (org-element-put-property copy :caption nil) (org-element-put-property copy :name nil) ;; Insert the 2nd block, and the 3rd block when region is active. (dolist (part (cdr parts)) (org-element-put-property copy :value part) (insert (if arg (concat stars "\n") "\n")) (cl-decf n) (when (= n 0) ;; Use `post-blank' to reset the property of the last block. (org-element-put-property copy :post-blank post-blank)) (insert (org-element-interpret-data copy)) ;; Ensure to indent the inserted block and move point to its end. (org-babel-previous-src-block 1) (org-indent-block) (goto-char (org-element-end (org-element-at-point)))) ;; Leave point at the last inserted block. (goto-char (org-babel-previous-src-block 1))) (let ((start (point)) (lang (or (car info) ; Reuse language from previous block. (completing-read "Lang: " (mapcar #'symbol-name (delete-dups (append (mapcar #'car org-babel-load-languages) (mapcar (lambda (el) (intern (car el))) org-src-lang-modes))))))) (body (delete-and-extract-region (if (org-region-active-p) (mark) (point)) (point)))) (insert (concat (if (looking-at "^") "" "\n") (if arg (concat stars "\n") "") (if upper-case-p "#+BEGIN_SRC " "#+begin_src ") lang "\n" body (if (or (= (length body) 0) (string-suffix-p "\r" body) (string-suffix-p "\n" body)) "" "\n") (if upper-case-p "#+END_SRC\n" "#+end_src\n"))) (goto-char start) (move-end-of-line 1))))) (defun org-babel--insert-results-keyword (name hash) "Insert RESULTS keyword with NAME value at point. If NAME is nil, results are anonymous. HASH is a string used as the results hash, or nil. Leave point before the keyword." (save-excursion (insert "\n")) ;open line to indent. (org-indent-line) (delete-char 1) (insert (concat "#+" org-babel-results-keyword (cond ((not hash) nil) (org-babel-hash-show-time (format "[%s %s]" (format-time-string "(%F %T)") hash)) (t (format "[%s]" hash))) ":" (when name (concat " " name)) "\n")) ;; Make sure results are going to be followed by at least one blank ;; line so they do not get merged with the next element, e.g., ;; ;; #+results: ;; : 1 ;; ;; : fixed-width area, unrelated to the above. (unless (looking-at "^[ \t]*$") (save-excursion (insert "\n"))) (forward-line -1) (when hash (org-babel-hide-hash))) (defun org-babel--clear-results-maybe (hash) "Clear results when hash doesn't match HASH. When results hash does not match HASH, remove RESULTS keyword at point, along with related contents. Do nothing if HASH is nil. Return a non-nil value if results were cleared. In this case, leave point where new results should be inserted." (when hash (let ((case-fold-search t)) (looking-at org-babel-result-regexp)) (unless (string= (match-string 1) hash) (let* ((e (org-element-at-point)) (post (copy-marker (org-element-post-affiliated e)))) ;; Delete contents. (delete-region post (save-excursion (goto-char (org-element-end e)) (skip-chars-backward " \t\n") (line-beginning-position 2))) ;; Delete RESULT keyword. However, if RESULTS keyword is ;; orphaned, ignore this part. The deletion above already ;; took care of it. (unless (= (point) post) (delete-region (line-beginning-position) (line-beginning-position 2))) (goto-char post) (set-marker post nil) t)))) (defun org-babel-where-is-src-block-result (&optional insert _info hash) "Find where the current source block results begin. Return the point at the beginning of the result of the current source block, specifically at the beginning of the results line. If no result exists for this block return nil, unless optional argument INSERT is non-nil. In this case, create a results line following the source block and return the position at its beginning. In the case of inline code, remove the results part instead. If optional argument HASH is a string, remove contents related to RESULTS keyword if its hash is different. Then update the latter to HASH." (let ((context (org-element-context))) (catch :found (org-with-wide-buffer (pcase (org-element-type context) ((or `inline-babel-call `inline-src-block) ;; Results for inline objects are located right after them. ;; There is no RESULTS line to insert either. (let ((limit (or (org-element-contents-end (org-element-parent context)) (org-element-end (org-element-parent context))))) (goto-char (org-element-end context)) (skip-chars-forward " \t\n" limit) (throw :found (and (< (point) limit) (let ((result (org-element-context))) (and (org-element-type-p result 'macro) (string= (org-element-property :key result) "results") (if (not insert) (point) (delete-region (point) (progn (goto-char (org-element-end result)) (skip-chars-backward " \t") (point))) (point)))))))) ((or `babel-call `src-block) (let* ((name (org-element-property :name context)) (named-results (and name (org-babel-find-named-result name)))) (goto-char (or named-results (org-element-end context))) (cond ;; Existing results named after the current source. (named-results (when (org-babel--clear-results-maybe hash) (org-babel--insert-results-keyword name hash)) (throw :found (point))) ;; Named results expect but none to be found. (name) ;; No possible anonymous results at the very end of ;; buffer or outside CONTEXT parent. ((eq (point) (or (pcase (org-element-type (org-element-parent context)) ((or `section `org-data) (org-element-end (org-element-parent context))) (_ (org-element-contents-end (org-element-parent context)))) (point-max)))) ;; Check if next element is an anonymous result below ;; the current block. ((let* ((next (org-element-at-point)) (end (save-excursion (goto-char (org-element-post-affiliated next)) (line-end-position))) (empty-result-re (concat org-babel-result-regexp "$")) (case-fold-search t)) (re-search-forward empty-result-re end t)) (forward-line 0) (when (org-babel--clear-results-maybe hash) (org-babel--insert-results-keyword nil hash)) (throw :found (point)))))) ;; Ignore other elements. (_ (throw :found nil)))) ;; No result found. Insert a RESULTS keyword below element, if ;; appropriate. In this case, ensure there is an empty line ;; after the previous element. (when insert (save-excursion (goto-char (min (org-element-end context) (point-max))) (skip-chars-backward " \t\n") (forward-line) (unless (bolp) (insert "\n")) (insert "\n") (org-babel--insert-results-keyword (org-element-property :name context) hash) (point)))))) (defun org-babel-read-element (element) "Read ELEMENT into emacs-lisp. Return nil if ELEMENT cannot be read." (org-with-wide-buffer (goto-char (org-element-post-affiliated element)) (pcase (org-element-type element) (`fixed-width (let ((v (org-trim (org-element-property :value element)))) (or (org-babel--string-to-number v) v))) (`table (org-babel-read-table)) (`plain-list (org-babel-read-list)) ((or `example-block `src-block) (let ((v (org-element-property :value element))) (if (org-src-preserve-indentation-p element) v (org-remove-indentation v)))) (`export-block (org-remove-indentation (org-element-property :value element))) (`paragraph ;; Treat paragraphs containing a single link specially. (skip-chars-forward " \t") (if (and (looking-at org-link-bracket-re) (save-excursion (goto-char (match-end 0)) (skip-chars-forward " \r\t\n") (<= (org-element-end element) (point)))) (org-babel-read-link) (buffer-substring-no-properties (org-element-contents-begin element) (org-element-contents-end element)))) ((or `center-block `quote-block `verse-block `special-block) (org-remove-indentation (buffer-substring-no-properties (org-element-contents-begin element) (org-element-contents-end element)))) (_ nil)))) (defun org-babel-read-result () "Read the result at point into emacs-lisp." (and (not (save-excursion (forward-line 0) (looking-at-p "[ \t]*$"))) (org-babel-read-element (org-element-at-point)))) (defun org-babel-read-table () "Read the table at point into emacs-lisp." (mapcar (lambda (row) (if (and (symbolp row) (equal row 'hline)) row (mapcar (lambda (el) (org-babel-read el 'inhibit-lisp-eval)) row))) (org-table-to-lisp))) (defun org-babel-read-list () "Read the list at point into emacs-lisp. Return the list of strings representing top level items: (item1 item2 ...) Only consider top level items. See Info node `(org)Environment of a Code Block'." (mapcar (lambda (el) (org-babel-read (car el) 'inhibit-lisp-eval)) (cdr (org-list-to-lisp)))) (defvar org-link-types-re) (defun org-babel-read-link () "Read the link at point into emacs-lisp. If the path of the link is a file path it is expanded using `expand-file-name'." (let* ((case-fold-search t) (raw (and (looking-at org-link-bracket-re) (org-no-properties (match-string 1)))) (type (and (string-match org-link-types-re raw) (match-string 1 raw)))) (cond ((not type) (expand-file-name raw)) ((string= type "file") (and (string-match "file\\(.*\\):\\(.+\\)" raw) (expand-file-name (match-string 2 raw)))) (t raw)))) (defun org-babel-format-result (result &optional sep) "Format RESULT for writing to file. When RESULT is a list, write it as a table, use tab or SEP as column separator." (let ((echo-res (lambda (r) (if (stringp r) r (format "%S" r))))) (if (listp result) ;; table result (orgtbl-to-generic result (list :sep (or sep "\t") :fmt echo-res)) ;; scalar result (funcall echo-res result)))) (defun org-babel-insert-result (result &optional result-params info hash lang exec-time) "Insert RESULT into the current buffer. By default RESULT is inserted after the end of the current source block. The RESULT of an inline source block usually will be wrapped inside a `results' macro and placed on the same line as the inline source block. The macro is stripped upon export. Multiline and non-scalar RESULTS from inline source blocks are not allowed. When EXEC-TIME is provided it may be included in a generated message. With optional argument RESULT-PARAMS controls insertion of results in the Org mode file. RESULT-PARAMS is a list that can contain the following values: replace - (default option) insert results after the source block or inline source block replacing any previously inserted results. silent -- no results are inserted into the Org buffer but the results are echoed to the minibuffer and are ingested by Emacs (a potentially time consuming process). none ---- no results are inserted into the Org buffer nor echoed to the minibuffer. They are not processed into Emacs-lisp objects at all. file ---- the results are interpreted as a file path, and are inserted into the buffer using the Org file syntax. list ---- the results are interpreted as an Org list. raw ----- results are added directly to the Org file. This is a good option if you code block will output Org formatted text. drawer -- results are added directly to the Org file as with \"raw\", but are wrapped in a RESULTS drawer or results macro, allowing them to later be replaced or removed automatically. org ----- results are added inside of a \"src_org{}\" or \"#+BEGIN_SRC org\" block depending on whether the current source block is inline or not. They are not comma-escaped when inserted, but Org syntax here will be discarded when exporting the file. html ---- results are added inside of a #+BEGIN_EXPORT HTML block or html export snippet depending on whether the current source block is inline or not. This is a good option if your code block will output html formatted text. latex --- results are added inside of a #+BEGIN_EXPORT LATEX block or latex export snippet depending on whether the current source block is inline or not. This is a good option if your code block will output latex formatted text. code ---- the results are extracted in the syntax of the source code of the language being evaluated and are added inside of a source block with the source-code language set appropriately. Also, source block inlining is preserved in this case. Note this relies on the optional LANG argument. list ---- the results are rendered as a list. This option not allowed for inline source blocks. table --- the results are rendered as a table. This option not allowed for inline source blocks. INFO is the src block info, as returned by `org-babel-get-src-block-info' (which see). Some values from its PARAMETERS part (header argument alist) can affect the inserted result: :file-desc - when RESULT-PARAMS contains \"file\", use it as description of the inserted link. :wrap the effect is similar to `latex' in RESULT-PARAMS but using the argument supplied to specify the export block or snippet type." (cond ((stringp result) (setq result (substring-no-properties result)) (when (member "file" result-params) (setq result (org-babel-result-to-file result (org-babel--file-desc (nth 2 info) result) 'attachment)))) ((listp result)) (t (setq result (format "%S" result)))) (if (and result-params (member "silent" result-params)) (progn (message (replace-regexp-in-string "%" "%%" (format "%S" result))) result) (let ((inline (let ((context (org-element-context))) (and (org-element-type-p context '(inline-babel-call inline-src-block)) context)))) (when inline (let ((warning (or (and (member "table" result-params) "`:results table'") (and (member "drawer" result-params) "`:results drawer'") (and result (listp result) "list result") (and result (string-match-p "\n." result) "multiline result") (and (member "list" result-params) "`:results list'")))) (when warning (user-error "Inline error: %s cannot be used" warning)))) (save-excursion (let* ((visible-beg (point-min-marker)) (visible-end (copy-marker (point-max) t)) (existing-result (org-babel-where-is-src-block-result t nil hash)) (results-switches (cdr (assq :results_switches (nth 2 info)))) ;; When results exist outside of the current visible ;; region of the buffer, be sure to widen buffer to ;; update them. (outside-scope (and existing-result (buffer-narrowed-p) (or (> visible-beg existing-result) (<= visible-end existing-result)))) beg end indent) ;; Ensure non-inline results end in a newline. (when (and (org-string-nw-p result) (not inline) (not (string-equal (substring result -1) "\n"))) (setq result (concat result "\n"))) (unwind-protect (progn (when outside-scope (widen)) (if existing-result (goto-char existing-result) (goto-char (org-element-end inline)) (skip-chars-backward " \t")) (unless inline (setq indent (current-indentation)) (forward-line 1)) (setq beg (point)) (cond (inline ;; Make sure new results are separated from the ;; source code by one space. (unless existing-result (insert " ") (setq beg (point)))) ((member "replace" result-params) (delete-region (point) (org-babel-result-end))) ((member "append" result-params) (goto-char (org-babel-result-end)) (setq beg (point-marker))) ;; ((member "prepend" result-params)) ; already there ) (setq results-switches (if results-switches (concat " " results-switches) "")) (let ((wrap (lambda (start finish &optional no-escape no-newlines inline-start inline-finish) (when inline (setq start inline-start) (setq finish inline-finish) (setq no-newlines t)) (let ((before-finish (copy-marker end))) (goto-char end) (insert (concat finish (unless no-newlines "\n"))) (goto-char beg) (insert (concat start (unless no-newlines "\n"))) (unless no-escape (org-escape-code-in-region (min (point) before-finish) before-finish)) (goto-char end)))) (tabulablep (lambda (r) ;; Non-nil when result R can be turned into ;; a table. (and (proper-list-p r) (cl-every (lambda (e) (or (atom e) (proper-list-p e))) result))))) ;; insert results based on type (cond ;; Do nothing for an empty result. ((null result)) ;; Insert a list if preferred. ((member "list" result-params) (insert (org-trim (org-list-to-org ;; We arbitrarily choose to format non-strings ;; as %S. (cons 'unordered (mapcar (lambda (e) (cond ((stringp e) (list e)) ((listp e) (mapcar (lambda (x) (if (stringp x) x (format "%S" x))) e)) (t (list (format "%S" e))))) (if (listp result) result (split-string result "\n" t)))) '(:splicep nil :istart "- " :iend "\n"))) "\n")) ;; Try hard to print RESULT as a table. Give up if ;; it contains an improper list. ((funcall tabulablep result) (goto-char beg) (insert (concat (orgtbl-to-orgtbl (if (cl-every (lambda (e) (or (eq e 'hline) (listp e))) result) result (list result)) nil) "\n")) (goto-char beg) (when (org-at-table-p) (org-table-align)) (goto-char (org-table-end))) ;; Print verbatim a list that cannot be turned into ;; a table. ((listp result) (insert (format "%s\n" result))) ((member "file" result-params) (when inline (setq result (org-macro-escape-arguments result))) (insert result)) ((and inline (not (member "raw" result-params))) (insert (org-macro-escape-arguments (org-babel-chomp result "\n")))) (t (goto-char beg) (insert result))) (setq end (copy-marker (point) t)) ;; Possibly wrap result. (cond ((assq :wrap (nth 2 info)) (let* ((full (or (cdr (assq :wrap (nth 2 info))) "results")) (split (split-string full)) (type (car split)) (opening-line (concat "#+begin_" full)) (closing-line (concat "#+end_" type))) (cond ;; Escape contents from "export" wrap. Wrap ;; inline results within an export snippet with ;; appropriate value. ((org-string-equal-ignore-case type "export") (let ((backend (pcase split (`(,_) "none") (`(,_ ,b . ,_) b)))) (funcall wrap opening-line closing-line nil nil (format "{{{results(@@%s:" backend) "@@)}}}"))) ;; Escape contents from "example" wrap. Mark ;; inline results as verbatim. ((org-string-equal-ignore-case type "example") (funcall wrap opening-line closing-line nil nil "{{{results(=" "=)}}}")) ;; Escape contents from "src" wrap. Mark ;; inline results as inline source code. ((org-string-equal-ignore-case type "src") (let ((inline-open (pcase split (`(,_) "{{{results(src_none{") (`(,_ ,language) (format "{{{results(src_%s{" language)) (`(,_ ,language . ,rest) (let ((r (mapconcat #'identity rest " "))) (format "{{{results(src_%s[%s]{" language r)))))) (funcall wrap opening-line closing-line nil nil inline-open "})}}}"))) ;; Do not escape contents in non-verbatim ;; blocks. Return plain inline results. (t (funcall wrap opening-line closing-line t nil "{{{results(" ")}}}"))))) ((member "html" result-params) (funcall wrap "#+begin_export html" "#+end_export" nil nil "{{{results(@@html:" "@@)}}}")) ((member "latex" result-params) (funcall wrap "#+begin_export latex" "#+end_export" nil nil "{{{results(@@latex:" "@@)}}}")) ((member "org" result-params) (goto-char beg) (when (org-at-table-p) (org-cycle)) (funcall wrap "#+begin_src org" "#+end_src" nil nil "{{{results(src_org{" "})}}}")) ((member "code" result-params) (let ((lang (or lang "none"))) (funcall wrap (format "#+begin_src %s%s" lang results-switches) "#+end_src" nil nil (format "{{{results(src_%s[%s]{" lang results-switches) "})}}}"))) ((member "raw" result-params) (goto-char beg) (when (org-at-table-p) (org-cycle))) ((or (member "drawer" result-params) ;; Stay backward compatible with <7.9.2 (member "wrap" result-params)) (goto-char beg) (when (org-at-table-p) (org-cycle)) (funcall wrap ":results:" ":end:" 'no-escape nil "{{{results(" ")}}}")) ((and inline (member "file" result-params)) (funcall wrap nil nil nil nil "{{{results(" ")}}}")) ((and (not (funcall tabulablep result)) (not (member "file" result-params))) (let ((org-babel-inline-result-wrap ;; Hard code {{{results(...)}}} on top of ;; customization. (format "{{{results(%s)}}}" org-babel-inline-result-wrap))) (org-babel-examplify-region beg end results-switches inline))))) ;; Possibly indent results in par with #+results line. (when (and (not inline) (numberp indent) (> indent 0) ;; In this case `table-align' does the work ;; for us. (not (and (listp result) (member "append" result-params)))) (indent-rigidly beg end indent)) (unless noninteractive (let ((time-info ;; Only show the time when something other than ;; 0s will be shown, i.e. check if the time is at ;; least half of the displayed precision. (if (and exec-time (> (float-time exec-time) 0.05)) (format " (took %.1fs)" (float-time exec-time)) ""))) (if (null result) (if (member "value" result-params) (message "Code block returned no value%s." time-info) (message "Code block produced no output%s." time-info)) (message "Code block evaluation complete%s." time-info))))) (when end (set-marker end nil)) (when outside-scope (narrow-to-region visible-beg visible-end)) (set-marker visible-beg nil) (set-marker visible-end nil))))))) (defun org-babel-remove-result (&optional info keep-keyword) "Remove the result of the current source block. INFO argument is currently ignored. When KEEP-KEYWORD is non-nil, keep the #+RESULT keyword and just remove the rest of the result." (interactive) (let ((location (org-babel-where-is-src-block-result nil info)) (case-fold-search t)) (when location (save-excursion (goto-char location) (when (looking-at org-babel-result-regexp) (delete-region (if keep-keyword (line-beginning-position 2) (save-excursion (skip-chars-backward " \r\t\n") (line-beginning-position 2))) (progn (forward-line) (org-babel-result-end)))))))) (defun org-babel-remove-inline-result (&optional datum) "Remove the result of DATUM or the current inline-src-block or babel call. The result must be wrapped in a `results' macro to be removed. Leading white space is trimmed." (interactive) (let* ((el (or datum (org-element-context)))) (when (org-element-type-p el '(inline-src-block inline-babel-call)) (org-with-wide-buffer (goto-char (org-element-end el)) (skip-chars-backward " \t") (let ((result (save-excursion (skip-chars-forward " \t\n" (org-element-contents-end (org-element-parent el))) (org-element-context)))) (when (and (org-element-type-p result 'macro) (string= (org-element-property :key result) "results")) (delete-region ; And leading whitespace. (point) (progn (goto-char (org-element-end result)) (skip-chars-backward " \t\n") (point))))))))) (defun org-babel-remove-result-one-or-many (arg) "Remove the result of the current source block. If called with prefix argument ARG, remove all result blocks in the buffer." (interactive "P") (if arg (progn (org-babel-map-src-blocks nil (org-babel-remove-result)) (org-babel-map-call-lines nil (org-babel-remove-result))) (org-babel-remove-result))) (defun org-babel-result-end () "Return the point at the end of the current set of results." (cond ((looking-at-p "^[ \t]*$") (point)) ;no result ((looking-at-p (format "^[ \t]*%s[ \t]*$" org-link-bracket-re)) (line-beginning-position 2)) (t (let ((element (org-element-at-point))) (if (org-element-type-p element ;; Possible results types. '(drawer example-block export-block fixed-width special-block src-block item plain-list table latex-environment)) (save-excursion (goto-char (min (point-max) ;for narrowed buffers (org-element-end element))) (skip-chars-backward " \r\t\n") (line-beginning-position 2)) (point)))))) (defun org-babel-result-to-file (result &optional description type) "Convert RESULT into an Org link with optional DESCRIPTION. If the `default-directory' is different from the containing file's directory then expand relative links. If the optional TYPE is passed as `attachment' and the path is a descendant of the DEFAULT-DIRECTORY, the generated link will be specified as an \"attachment:\" style link." (when (stringp result) (let* ((result-file-name (expand-file-name result)) (base-file-name (buffer-file-name (buffer-base-buffer))) (base-directory (and base-file-name (file-name-directory base-file-name))) (same-directory? (and base-file-name (not (string= (expand-file-name default-directory) (expand-file-name base-directory))))) (request-attachment (eq type 'attachment)) (attach-dir (let* ((default-directory base-directory) (dir (org-attach-dir nil t))) (when dir (expand-file-name dir)))) (in-attach-dir (and request-attachment attach-dir (string-prefix-p attach-dir result-file-name)))) (format "[[%s:%s]%s]" (pcase type ((and 'attachment (guard in-attach-dir)) "attachment") (_ "file")) (if (and request-attachment in-attach-dir) (file-relative-name result-file-name (file-name-as-directory attach-dir)) (if (and default-directory base-file-name same-directory?) (if (eq org-link-file-path-type 'adaptive) (file-relative-name result-file-name (file-name-directory base-file-name)) result-file-name) result)) (if description (concat "[" description "]") ""))))) (defun org-babel-examplify-region (beg end &optional results-switches inline) "Comment out region BEG..END using the inline `==' or `: ' org example quote. When INLINE is non-nil, use the inline verbatim markup. When INLINE is nil and RESULTS-SWITCHES is non-nil, RESULTS-SWITCHES is used as a string to be appended to #+begin_example line." (interactive "*r") (let ((maybe-cap (lambda (str) (if org-babel-uppercase-example-markers (upcase str) str)))) (if inline (save-excursion (goto-char beg) (insert (format org-babel-inline-result-wrap (delete-and-extract-region beg end)))) (let ((size (count-lines beg end))) (save-excursion (cond ((= size 0)) ; do nothing for an empty result ((< size org-babel-min-lines-for-block-output) (goto-char beg) (dotimes (_ size) (forward-line 0) (insert ": ") (forward-line 1))) (t (goto-char beg) (insert (if results-switches (format "%s%s\n" (funcall maybe-cap "#+begin_example") results-switches) (funcall maybe-cap "#+begin_example\n"))) (let ((p (point))) (if (markerp end) (goto-char end) (forward-char (- end beg))) (org-escape-code-in-region p (point))) (insert (funcall maybe-cap "#+end_example\n"))))))))) (defun org-babel-update-block-body (new-body) "Update the body of the current code block to NEW-BODY." (let ((element (org-element-at-point))) (unless (org-element-type-p element 'src-block) (error "Not in a source block")) (goto-char (org-babel-where-is-src-block-head element)) (let* ((ind (org-current-text-indentation)) (body-start (line-beginning-position 2)) (body (org-element-normalize-string (if (org-src-preserve-indentation-p element) new-body (with-temp-buffer (insert (org-remove-indentation new-body)) (indent-rigidly (point-min) (point-max) (+ ind org-edit-src-content-indentation)) (buffer-string)))))) (delete-region body-start (org-with-wide-buffer (goto-char (org-element-end element)) (skip-chars-backward " \t\n") (line-beginning-position))) (goto-char body-start) (insert body)))) (defun org-babel-merge-params (&rest alists) "Combine all parameter association lists in ALISTS. Later elements of ALISTS override the values of previous elements. This takes into account some special considerations for certain parameters when merging lists." (let* ((results-exclusive-groups (mapcar (lambda (group) (mapcar #'symbol-name group)) (cdr (assq 'results org-babel-common-header-args-w-values)))) (exports-exclusive-groups (mapcar (lambda (group) (mapcar #'symbol-name group)) (cdr (assq 'exports org-babel-common-header-args-w-values)))) (merge (lambda (exclusive-groups &rest result-params) ;; Maintain exclusivity of mutually exclusive parameters, ;; as defined in EXCLUSIVE-GROUPS while merging lists in ;; RESULT-PARAMS. (let (output) (dolist (new-params result-params (delete-dups output)) (dolist (new-param new-params) (dolist (exclusive-group exclusive-groups) (when (member new-param exclusive-group) (setq output (cl-remove-if (lambda (o) (member o exclusive-group)) output)))) (push new-param output)))))) (variable-index 0) ;Handle positional arguments. clearnames params ;Final parameters list. ;; Some keywords accept multiple values. We need to treat ;; them specially. vars results exports) (dolist (alist alists) (dolist (pair alist) (pcase pair (`(:var . ,value) (let ((name (cond ;; Default header arguments can accept lambda ;; functions. We uniquely identify the var ;; according to the full string contents of ;; the lambda function. ((functionp value) value) ((listp value) (car value)) ((string-match "^\\([^= \f\t\n\r\v]+\\)[ \t]*=" value) (intern (match-string 1 value))) (t nil)))) (cond (name (setq vars (append (if (not (assoc name vars)) vars (push name clearnames) (cl-remove-if (lambda (p) (equal name (car p))) vars)) (list (cons name pair))))) ((and vars (nth variable-index vars)) ;; If no name is given and we already have named ;; variables then assign to named variables in order. (let ((name (car (nth variable-index vars)))) ;; Clear out colnames and rownames for replace vars. (push name clearnames) (setf (cddr (nth variable-index vars)) (concat (symbol-name name) "=" value)) (cl-incf variable-index))) (t (error "Variable \"%s\" must be assigned a default value" (cdr pair)))))) (`(:results . ,value) (setq results (funcall merge results-exclusive-groups results (split-string (cond ((stringp value) value) ((functionp value) (funcall value)) ;; FIXME: Arbitrary code evaluation. (t (eval value t))))))) (`(:exports . ,value) (setq exports (funcall merge exports-exclusive-groups exports (split-string (cond ((and value (functionp value)) (funcall value)) (value value) (t "")))))) ((or '(:dir . attach) '(:dir . "'attach")) (unless (org-attach-dir nil t) (error "No attachment directory for element (add :ID: or :DIR: property)")) (setq params (append `((:dir . ,(org-attach-dir nil t)) (:mkdirp . "yes")) (assq-delete-all :dir (assq-delete-all :mkdir params))))) ;; Regular keywords: any value overwrites the previous one. (_ (setq params (cons pair (assq-delete-all (car pair) params))))))) ;; Handle `:var' and clear out colnames and rownames for replaced ;; variables. (setq params (nconc (mapcar (lambda (v) (cons :var (cddr v))) vars) params)) (dolist (name clearnames) (dolist (param '(:colname-names :rowname-names)) (when (assq param params) (setf (cdr (assq param params)) (cl-remove-if (lambda (pair) (equal name (car pair))) (cdr (assq param params)))) (setq params (cl-remove-if (lambda (pair) (and (equal (car pair) param) (null (cdr pair)))) params))))) ;; Handle other special keywords, which accept multiple values. (setq params (nconc (list (cons :results (mapconcat #'identity results " ")) (cons :exports (mapconcat #'identity exports " "))) params)) ;; Return merged params. (org-babel-eval-headers params))) (defun org-babel-noweb-p (params context) "Check if PARAMS require expansion in CONTEXT. CONTEXT may be one of :tangle, :export or :eval." (let ((allowed-values (cl-case context (:tangle '("yes" "tangle" "no-export" "strip-export" "strip-tangle")) (:eval '("yes" "no-export" "strip-export" "eval" "strip-tangle")) (:export '("yes" "strip-tangle"))))) (cl-some (lambda (v) (member v allowed-values)) (split-string (or (cdr (assq :noweb params)) ""))))) (defvar org-babel-expand-noweb-references--cache nil "Noweb reference cache used during expansion.") (defvar org-babel-expand-noweb-references--cache-buffer nil "Cons (BUFFER . MODIFIED-TICK) for cached noweb references. See `org-babel-expand-noweb-references--cache'.") (defun org-babel-expand-noweb-references (&optional info parent-buffer) "Expand Noweb references in the body of the current source code block. When optional argument INFO is non-nil, use the block defined by INFO instead. The block is assumed to be located in PARENT-BUFFER or current buffer \(when PARENT-BUFFER is nil). For example the following reference would be replaced with the body of the source-code block named `example-block'. <> Note that any text preceding the <> construct on a line will be interposed between the lines of the replacement text. So for example if <> is placed behind a comment, then the entire replacement text will also be commented. This function must be called from inside of the buffer containing the source-code block which holds BODY. In addition the following syntax can be used to insert the results of evaluating the source-code block named `example-block'. <> Any optional arguments can be passed to example-block by placing the arguments inside the parenthesis following the convention defined by `org-babel-lob'. For example <> would set the value of argument \"a\" equal to \"9\". Note that these arguments are not evaluated in the current source-code block but are passed literally to the \"example-block\"." (let* ((parent-buffer (or parent-buffer (current-buffer))) (info (or info (org-babel-get-src-block-info 'no-eval))) (lang (nth 0 info)) (body (nth 1 info)) (comment (string= "noweb" (cdr (assq :comments (nth 2 info))))) (noweb-prefix (let ((v (assq :noweb-prefix (nth 2 info)))) (or (not v) (and (org-not-nil (cdr v)) (not (equal (cdr v) "no")))))) (noweb-re (format "\\(.*?\\)\\(%s\\)" (with-current-buffer parent-buffer (org-babel-noweb-wrap))))) (unless (equal (cons parent-buffer (with-current-buffer parent-buffer (buffer-chars-modified-tick))) org-babel-expand-noweb-references--cache-buffer) (setq org-babel-expand-noweb-references--cache nil org-babel-expand-noweb-references--cache-buffer (cons parent-buffer (with-current-buffer parent-buffer (buffer-chars-modified-tick))))) (cl-macrolet ((c-wrap (s) ;; Comment string S, according to LANG mode. Return new ;; string. `(unless org-babel-tangle-uncomment-comments (with-temp-buffer (funcall (org-src-get-lang-mode lang)) (comment-region (point) (progn (insert ,s) (point))) (org-trim (buffer-string))))) (expand-body (i) ;; Expand body of code represented by block info I. `(let ((b (if (org-babel-noweb-p (nth 2 ,i) :eval) (org-babel-expand-noweb-references ,i) (nth 1 ,i)))) (if (not comment) b (let ((cs (org-babel-tangle-comment-links ,i))) (concat (c-wrap (car cs)) "\n" b "\n" (c-wrap (cadr cs)) "\n"))))) (expand-references (ref) `(pcase (gethash ,ref org-babel-expand-noweb-references--cache) (`(,last . ,previous) ;; Ignore separator for last block. (let ((strings (list (expand-body last)))) (dolist (i previous) (let ((parameters (nth 2 i))) ;; Since we're operating in reverse order, first ;; push separator, then body. (push (or (cdr (assq :noweb-sep parameters)) "\n") strings) (push (expand-body i) strings))) (mapconcat #'identity strings ""))) ;; Raise an error about missing reference, or return the ;; empty string. ((guard (or org-babel-noweb-error-all-langs (member lang org-babel-noweb-error-langs))) (error "Cannot resolve %s (see `org-babel-noweb-error-langs')" (org-babel-noweb-wrap ,ref))) (_ "")))) (replace-regexp-in-string noweb-re (lambda (m) (with-current-buffer parent-buffer (save-match-data (let* ((prefix (match-string 1 m)) (id (match-string 3 m)) (evaluate (string-match-p "(.*)" id)) (expansion (cond (evaluate (prog1 (let ((raw (org-babel-ref-resolve id))) (if (stringp raw) raw (format "%S" raw))) ;; Evaluation can potentially modify the buffer ;; and invalidate the cache: reset it. (unless (equal org-babel-expand-noweb-references--cache-buffer (cons parent-buffer (buffer-chars-modified-tick))) (setq org-babel-expand-noweb-references--cache nil org-babel-expand-noweb-references--cache-buffer (cons parent-buffer (with-current-buffer parent-buffer (buffer-chars-modified-tick))))))) ;; Already cached. ((and (hash-table-p org-babel-expand-noweb-references--cache) (gethash id org-babel-expand-noweb-references--cache)) (expand-references id)) ;; Return the contents of headlines literally. ((org-babel-ref-goto-headline-id id) (org-babel-ref-headline-body)) ;; Look for a source block named SOURCE-NAME. If ;; found, assume it is unique; do not look after ;; `:noweb-ref' header argument. ((org-with-point-at 1 (let ((r (org-babel-named-src-block-regexp-for-name id))) (and (re-search-forward r nil t) (not (org-in-commented-heading-p)) (let ((info (org-babel-get-src-block-info t))) (unless (hash-table-p org-babel-expand-noweb-references--cache) (setq org-babel-expand-noweb-references--cache (make-hash-table :test #'equal))) (push info (gethash id org-babel-expand-noweb-references--cache)) (expand-body info)))))) ;; Retrieve from the Library of Babel. ((nth 2 (assoc-string id org-babel-library-of-babel))) ;; All Noweb references were cached in a previous ;; run. Yet, ID is not in cache (see the above ;; condition). Process missing reference in ;; `expand-references'. ((and (hash-table-p org-babel-expand-noweb-references--cache) (gethash 'buffer-processed org-babel-expand-noweb-references--cache)) (expand-references id)) ;; Though luck. We go into the long process of ;; checking each source block and expand those ;; with a matching Noweb reference. Since we're ;; going to visit all source blocks in the ;; document, cache information about them as well. (t (setq org-babel-expand-noweb-references--cache (make-hash-table :test #'equal)) (org-with-wide-buffer (org-babel-map-src-blocks nil (if (org-in-commented-heading-p) (org-forward-heading-same-level nil t) (let* ((info (org-babel-get-src-block-info t)) (ref (cdr (assq :noweb-ref (nth 2 info))))) (push info (gethash ref org-babel-expand-noweb-references--cache)))))) (puthash 'buffer-processed t org-babel-expand-noweb-references--cache) (expand-references id))))) ;; Interpose PREFIX between every line. (if noweb-prefix (mapconcat #'identity (split-string expansion "[\n\r]") (concat "\n" prefix)) expansion))))) body t t 2)))) (defun org-babel--script-escape-inner (str) (let (in-single in-double backslash out) (mapc (lambda (ch) (setq out (if backslash (progn (setq backslash nil) (cond ((and in-single (eq ch ?')) ;; Escaped single quote inside single quoted string: ;; emit just a single quote, since we've changed the ;; outer quotes to double. (cons ch out)) ((eq ch ?\") ;; Escaped double quote (if in-single ;; This should be interpreted as backslash+quote, ;; not an escape. Emit a three backslashes ;; followed by a quote (because one layer of ;; quoting will be stripped by `org-babel-read'). (append (list ch ?\\ ?\\ ?\\) out) ;; Otherwise we are in a double-quoted string. Emit ;; a single escaped quote (append (list ch ?\\) out))) ((eq ch ?\\) ;; Escaped backslash: emit a single escaped backslash (append (list ?\\ ?\\) out)) ;; Other: emit a quoted backslash followed by whatever ;; the character was (because one layer of quoting will ;; be stripped by `org-babel-read'). (t (append (list ch ?\\ ?\\) out)))) (cl-case ch (?\[ (if (or in-double in-single) (cons ?\[ out) (cons ?\( out))) (?\] (if (or in-double in-single) (cons ?\] out) (cons ?\) out))) (?\{ (if (or in-double in-single) (cons ?\{ out) (cons ?\( out))) (?\} (if (or in-double in-single) (cons ?\} out) (cons ?\) out))) (?, (if (or in-double in-single) (cons ?, out) (cons ?\s out))) (?\' (if in-double (cons ?\' out) (setq in-single (not in-single)) (cons ?\" out))) (?\" (if in-single (append (list ?\" ?\\) out) (setq in-double (not in-double)) (cons ?\" out))) (?\\ (unless (or in-single in-double) (error "Can't handle backslash outside string in `org-babel-script-escape'")) (setq backslash t) out) (t (cons ch out)))))) (string-to-list str)) (when (or in-single in-double) (error "Unterminated string in `org-babel-script-escape'")) (apply #'string (reverse out)))) (defun org-babel-script-escape (str &optional force) "Safely convert tables into elisp lists." (unless (stringp str) (error "`org-babel-script-escape' expects a string")) (let ((escaped (cond ((and (>= (length str) 2) (or (and (string-equal "[" (substring str 0 1)) (string-equal "]" (substring str -1))) (and (string-equal "{" (substring str 0 1)) (string-equal "}" (substring str -1))) (and (string-equal "(" (substring str 0 1)) (string-equal ")" (substring str -1))))) (concat "'" (org-babel--script-escape-inner str))) ((or force (and (> (length str) 2) (or (and (string-equal "'" (substring str 0 1)) (string-equal "'" (substring str -1))) ;; We need to pass double-quoted strings ;; through the backslash-twiddling bits, even ;; though we don't need to change their ;; delimiters. (and (string-equal "\"" (substring str 0 1)) (string-equal "\"" (substring str -1)))))) (org-babel--script-escape-inner str)) (t str)))) (condition-case nil (org-babel-read escaped) (error escaped)))) (defun org-babel-read (cell &optional inhibit-lisp-eval) "Convert the string value of CELL to a number if appropriate. Otherwise if CELL looks like Lisp (meaning it starts with a \"(\", \"\\='\", \"\\=`\" or a \"[\") then read and evaluate it as lisp, otherwise return it unmodified as a string. Optional argument INHIBIT-LISP-EVAL inhibits lisp evaluation for situations in which is it not appropriate." (cond ((not (org-string-nw-p cell)) cell) ((org-babel--string-to-number cell)) ((and (not inhibit-lisp-eval) (or (memq (string-to-char cell) '(?\( ?' ?` ?\[)) (string= cell "*this*"))) ;; FIXME: Arbitrary code evaluation. (eval (read cell) t)) ((let (read-val) (and (string-match-p (rx bos (0+ (any space ?\n)) ?\" (0+ anychar) ?\" (0+ (any space ?\n)) eos) cell) ;; CELL is a single string (with-temp-buffer (insert cell) (goto-char 1) (when (setq read-val (ignore-errors (read (current-buffer)))) (skip-chars-forward "[:space:]") (eobp))) read-val))) (t (org-no-properties cell)))) (defun org-babel--string-to-number (string) "If STRING represents a number return its value. Otherwise return nil." (unless (or (string-match-p "\\s-" (org-trim string)) (not (string-match-p "^[0-9e.+ -]+$" string))) (let ((interned-string (ignore-errors (read string)))) (when (numberp interned-string) interned-string)))) (defun org-babel-import-elisp-from-file (file-name &optional separator) "Read the results located at FILE-NAME into an elisp table. If the table is trivial, then return it as a scalar. SEPARATOR is passed to `org-table-convert-region', which see." (let ((result (with-temp-buffer (condition-case err (progn (insert-file-contents file-name) (delete-file file-name) (let ((pmax (point-max))) ;; If the file was empty, don't bother trying to ;; convert the table. (when (> pmax 1) (org-table-convert-region (point-min) pmax (or separator 'babel-auto)) (delq nil (mapcar (lambda (row) (and (not (eq row 'hline)) (mapcar #'org-babel-string-read row))) (org-table-to-lisp)))))) (error (display-warning 'org-babel (format "Error reading results: %S" err) :error) nil))))) (pcase result (`((,scalar)) scalar) (`((,_ ,_ . ,_)) result) (`(,scalar) scalar) (_ result)))) (defun org-babel-string-read (cell) "Strip nested \"s from around CELL string. When CELL is not a string, return CELL." (org-babel-read (or (and (stringp cell) (string-match "^[[:space:]]*\"\\(.+\\)\"[[:space:]]*$" cell) (match-string 1 cell)) cell) t)) (defun org-babel-chomp (string &optional regexp) "Strip a trailing space or carriage return from STRING. The default regexp used is \"[ \\f\\t\\n\\r\\v]\" but another one can be specified as the REGEXP argument." (let ((regexp (or regexp "[ \f\t\n\r\v]"))) (while (and (> (length string) 0) (string-match regexp (substring string -1))) (setq string (substring string 0 -1))) string)) (defun org-babel-process-file-name (name &optional no-quote-p) "Prepare NAME to be used in an external process. If NAME specifies a remote location, the remote portion of the name is removed, since in that case the process will be executing remotely. The file name is then processed by `expand-file-name'. Unless second argument NO-QUOTE-P is non-nil, the file name is additionally processed by `shell-quote-argument'." (let ((f (org-babel-local-file-name (expand-file-name name)))) (if no-quote-p f (shell-quote-argument f)))) (defvar org-babel-temporary-directory (unless noninteractive (make-temp-file "babel-" t)) "Directory to hold temporary files created to execute code blocks. Used by `org-babel-temp-file'. This directory will be removed on Emacs shutdown.") (defvar org-babel-temporary-stable-directory (unless noninteractive (let (dir) (while (or (not dir) (file-exists-p dir)) (setq dir (expand-file-name (format "babel-stable-%d" (random 1000)) temporary-file-directory))) (make-directory dir) dir)) "Directory to hold temporary files created to execute code blocks. Used by `org-babel-temp-file'. This directory will be removed on Emacs shutdown.") (defcustom org-babel-remote-temporary-directory "/tmp/" "Directory to hold temporary files on remote hosts." :group 'org-babel :type 'string) (defmacro org-babel-result-cond (result-params scalar-form &rest table-forms) "Call the code to parse raw string results according to RESULT-PARAMS. Do nothing with :results discard. Execute SCALAR-FORM when result should be treated as a string. Execute TABLE-FORMS when result should be considered sexp and parsed." (declare (indent 1) (debug t)) (org-with-gensyms (params) `(let ((,params ,result-params)) (unless (member "discard" ,params) (if (or (member "scalar" ,params) (member "verbatim" ,params) (member "html" ,params) (member "code" ,params) (member "pp" ,params) (member "file" ,params) (and (or (member "output" ,params) (member "raw" ,params) (member "org" ,params) (member "drawer" ,params)) (not (member "table" ,params)))) ,scalar-form ,@table-forms))))) (defmacro org-babel-temp-directory () "Return temporary directory suitable for `default-directory'." `(if (file-remote-p default-directory) (concat (file-remote-p default-directory) org-babel-remote-temporary-directory) (or (and org-babel-temporary-directory (file-exists-p org-babel-temporary-directory) org-babel-temporary-directory) temporary-file-directory))) (defun org-babel-temp-file (prefix &optional suffix) "Create a temporary file in the `org-babel-temporary-directory'. Passes PREFIX and SUFFIX directly to `make-temp-file' with the value of function `temporary-file-directory' temporarily set to the value of `org-babel-temporary-directory'." (make-temp-file (concat (file-name-as-directory (org-babel-temp-directory)) prefix) nil suffix)) (defmacro org-babel-temp-stable-directory () "Return temporary stable directory." `(let ((org-babel-temporary-directory org-babel-temporary-stable-directory)) (org-babel-temp-directory))) (defun org-babel-temp-stable-file (data prefix &optional suffix) "Create a temporary file in the `org-babel-remove-temporary-stable-directory'. The file name is stable with respect to DATA. The file name is constructed like the following: ." (let ((path (format "%s%s%s%s" (file-name-as-directory (org-babel-temp-stable-directory)) prefix (org-sxhash-safe data) (or suffix "")))) ;; Create file. (with-temp-file path) ;; Return it. path)) (defun org-babel-remove-temporary-directory () "Remove `org-babel-temporary-directory' on Emacs shutdown." (when (and org-babel-temporary-directory (file-exists-p org-babel-temporary-directory)) ;; taken from `delete-directory' in files.el (condition-case nil (progn (mapc (lambda (file) ;; This test is equivalent to ;; (and (file-directory-p fn) (not (file-symlink-p fn))) ;; but more efficient (if (eq t (car (file-attributes file))) (delete-directory file) (delete-file file))) (directory-files org-babel-temporary-directory 'full directory-files-no-dot-files-regexp)) (delete-directory org-babel-temporary-directory)) (error (message "Failed to remove temporary Org-babel directory %s" (or org-babel-temporary-directory "[directory not defined]")))))) (defun org-babel-remove-temporary-stable-directory () "Remove `org-babel-temporary-stable-directory' and on Emacs shutdown." (when (and org-babel-temporary-stable-directory (file-exists-p org-babel-temporary-stable-directory)) (let ((org-babel-temporary-directory org-babel-temporary-stable-directory)) (org-babel-remove-temporary-directory)))) (add-hook 'kill-emacs-hook #'org-babel-remove-temporary-directory) (add-hook 'kill-emacs-hook #'org-babel-remove-temporary-stable-directory) (defun org-babel-one-header-arg-safe-p (pair safe-list) "Determine if the PAIR is a safe babel header arg according to SAFE-LIST. For the format of SAFE-LIST, see `org-babel-safe-header-args'." (and (consp pair) (keywordp (car pair)) (stringp (cdr pair)) (or (memq (car pair) safe-list) (let ((entry (assq (car pair) safe-list))) (and entry (consp entry) (cond ((functionp (cdr entry)) (funcall (cdr entry) (cdr pair))) ((listp (cdr entry)) (member (cdr pair) (cdr entry))) (t nil))))))) (defun org-babel-generate-file-param (src-name params) "Calculate the filename for source block results. The directory is calculated from the :output-dir property of the source block; if not specified, use the current directory. If the source block has a #+NAME and the :file parameter does not contain any period characters, then the :file parameter is treated as an extension, and the output file name is the concatenation of the directory (as calculated above), the block name, a period, and the parameter value as a file extension. Otherwise, the :file parameter is treated as a full file name, and the output file name is the directory (as calculated above) plus the parameter value." (let* ((file-cons (assq :file params)) (file-ext-cons (assq :file-ext params)) (file-ext (cdr-safe file-ext-cons)) (dir (cdr-safe (assq :output-dir params))) fname) ;; create the output-dir if it does not exist (when dir (make-directory dir t)) (if file-cons ;; :file given; add :output-dir if given (when dir (setcdr file-cons (concat (file-name-as-directory dir) (cdr file-cons)))) ;; :file not given; compute from name and :file-ext if possible (when (and src-name file-ext) (if dir (setq fname (concat (file-name-as-directory (or dir "")) src-name "." file-ext)) (setq fname (concat src-name "." file-ext))) (setq params (cons (cons :file fname) params)))) params)) (defun org-babel-graphical-output-file (params) "File where a babel block should send graphical output, per PARAMS. Return nil if no graphical output is expected. Raise an error if the output file is ill-defined." (let ((file (cdr (assq :file params)))) (cond (file (and (member "graphics" (cdr (assq :result-params params))) file)) ((assq :file-ext params) (user-error ":file-ext given but no :file generated; did you forget \ to name a block?")) (t (user-error "No :file header argument given; cannot create \ graphical result"))))) (defun org-babel-make-language-alias (new old) "Make source blocks of type NEW aliases for those of type OLD. NEW and OLD should be strings. This function should be called after the babel API for OLD-type source blocks is fully defined. Callers of this function will probably want to add an entry to `org-src-lang-modes' as well." (dolist (fn '("execute" "expand-body" "prep-session" "variable-assignments" "load-session" "edit-prep")) (let ((sym (intern-soft (concat "org-babel-" fn ":" old)))) (when (and sym (fboundp sym)) (defalias (intern (concat "org-babel-" fn ":" new)) sym)))) ;; Technically we don't need a `dolist' for just one variable, but ;; we keep it for symmetry/ease of future expansion. (dolist (var '("default-header-args")) (let ((sym (intern-soft (concat "org-babel-" var ":" old)))) (when (and sym (boundp sym)) (defvaralias (intern (concat "org-babel-" var ":" new)) sym))))) (provide 'ob-core) ;; Local variables: ;; generated-autoload-file: "org-loaddefs.el" ;; End: ;;; ob-core.el ends here org-mode-9.7.29+dfsg/lisp/ob-css.el000066400000000000000000000027571500430433700167430ustar00rootroot00000000000000;;; ob-css.el --- Babel Functions for CSS -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2025 Free Software Foundation, Inc. ;; Author: Eric Schulte ;; Keywords: literate programming, reproducible research ;; URL: https://orgmode.org ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; Since CSS can't be executed, this file exists solely for tangling ;; CSS from Org files. ;;; Code: (require 'org-macs) (org-assert-version) (require 'ob) (defvar org-babel-default-header-args:css '()) (defun org-babel-execute:css (body _params) "Execute BODY of CSS code. This function is called by `org-babel-execute-src-block'." body) (defun org-babel-prep-session:css (_session _params) "Return an error if the :session header argument is set. CSS does not support sessions." (error "CSS sessions are nonsensical")) (provide 'ob-css) ;;; ob-css.el ends here org-mode-9.7.29+dfsg/lisp/ob-ditaa.el000066400000000000000000000101261500430433700172220ustar00rootroot00000000000000;;; ob-ditaa.el --- Babel Functions for ditaa -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2025 Free Software Foundation, Inc. ;; Author: Eric Schulte ;; Keywords: literate programming, reproducible research ;; URL: https://orgmode.org ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; Org-Babel support for evaluating ditaa source code. ;; ;; This differs from most standard languages in that ;; ;; 1) there is no such thing as a "session" in ditaa ;; ;; 2) we are generally only going to return results of type "file" ;; ;; 3) we are adding the "file" and "cmdline" header arguments ;; ;; 4) there are no variables (at least for now) ;;; Code: (require 'org-macs) (org-assert-version) (require 'ob) (require 'org-compat) (defvar org-babel-default-header-args:ditaa '((:results . "file") (:exports . "results") (:java . "-Dfile.encoding=UTF-8")) "Default arguments for evaluating a ditaa source block.") (defcustom org-ditaa-jar-path (expand-file-name "ditaa.jar" (file-name-as-directory (expand-file-name "scripts" (file-name-as-directory (expand-file-name "../contrib" (file-name-directory (org-find-library-dir "org"))))))) "Path to the ditaa jar executable." :group 'org-babel :type 'string) (defcustom org-babel-ditaa-java-cmd "java" "Java executable to use when evaluating ditaa blocks." :group 'org-babel :type 'string) (defcustom org-ditaa-eps-jar-path (expand-file-name "DitaaEps.jar" (file-name-directory org-ditaa-jar-path)) "Path to the DitaaEps.jar executable." :group 'org-babel :version "24.4" :package-version '(Org . "8.0") :type 'string) (defcustom org-ditaa-jar-option "-jar" "Option for the ditaa jar file. Do not leave leading or trailing spaces in this string." :group 'org-babel :version "24.1" :type 'string) (defun org-babel-execute:ditaa (body params) "Execute BODY of Ditaa code with org-babel according to PARAMS. This function is called by `org-babel-execute-src-block'." (let* ((out-file (or (cdr (assq :file params)) (error "Ditaa code block requires :file header argument"))) (cmdline (cdr (assq :cmdline params))) (java (cdr (assq :java params))) (in-file (org-babel-temp-file "ditaa-")) (eps (cdr (assq :eps params))) (eps-file (when eps (org-babel-process-file-name (concat in-file ".eps")))) (pdf-cmd (when (and (or (string= (file-name-extension out-file) "pdf") (cdr (assq :pdf params)))) (concat "epstopdf" " " eps-file " -o=" (org-babel-process-file-name out-file)))) (cmd (concat org-babel-ditaa-java-cmd " " java " " org-ditaa-jar-option " " (shell-quote-argument (expand-file-name (if eps org-ditaa-eps-jar-path org-ditaa-jar-path))) " " cmdline " " (org-babel-process-file-name in-file) " " (if pdf-cmd eps-file (org-babel-process-file-name out-file))))) (unless (file-exists-p org-ditaa-jar-path) (error "Could not find ditaa.jar at %s" org-ditaa-jar-path)) (with-temp-file in-file (insert body)) (unless noninteractive (message cmd)) (shell-command cmd) (when pdf-cmd (unless noninteractive (message pdf-cmd)) (shell-command pdf-cmd)) nil)) ;; signal that output has already been written to file (defun org-babel-prep-session:ditaa (_session _params) "Return an error because ditaa does not support sessions." (error "Ditaa does not support sessions")) (provide 'ob-ditaa) ;;; ob-ditaa.el ends here org-mode-9.7.29+dfsg/lisp/ob-dot.el000066400000000000000000000063601500430433700167330ustar00rootroot00000000000000;;; ob-dot.el --- Babel Functions for dot -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2025 Free Software Foundation, Inc. ;; Author: Eric Schulte ;; Maintainer: Justin Abrahms ;; Keywords: literate programming, reproducible research ;; URL: https://orgmode.org ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; Org-Babel support for evaluating dot source code. ;; ;; For information on dot see https://www.graphviz.org/ ;; ;; This differs from most standard languages in that ;; ;; 1) there is no such thing as a "session" in dot ;; ;; 2) we are generally only going to return results of type "file" ;; ;; 3) we are adding the "file" and "cmdline" header arguments ;; ;; 4) there are no variables (at least for now) ;;; Code: (require 'org-macs) (org-assert-version) (require 'ob) (defvar org-babel-default-header-args:dot '((:results . "file") (:exports . "results")) "Default arguments to use when evaluating a dot source block.") (defun org-babel-expand-body:dot (body params) "Expand BODY according to PARAMS, return the expanded body." (let ((vars (org-babel--get-vars params)) (prologue (cdr (assq :prologue params))) (epilogue (cdr (assq :epilogue params)))) (mapc (lambda (pair) (let ((name (symbol-name (car pair))) (value (cdr pair))) (setq body (replace-regexp-in-string (concat "$" (regexp-quote name)) (if (stringp value) value (format "%S" value)) body t t)))) vars) (concat (and prologue (concat prologue "\n")) body (and epilogue (concat "\n" epilogue "\n"))))) (defun org-babel-execute:dot (body params) "Execute Dot BODY with org-babel according to PARAMS. This function is called by `org-babel-execute-src-block'." (let* ((out-file (cdr (or (assq :file params) (error "You need to specify a :file parameter")))) (cmdline (or (cdr (assq :cmdline params)) (format "-T%s" (file-name-extension out-file)))) (cmd (or (cdr (assq :cmd params)) "dot")) (coding-system-for-read 'utf-8) ;use utf-8 with sub-processes (coding-system-for-write 'utf-8) (in-file (org-babel-temp-file "dot-"))) (with-temp-file in-file (insert (org-babel-expand-body:dot body params))) (org-babel-eval (concat cmd " " (org-babel-process-file-name in-file) " " cmdline " -o " (org-babel-process-file-name out-file)) "") nil)) ;; signal that output has already been written to file (defun org-babel-prep-session:dot (_session _params) "Return an error because Dot does not support sessions." (error "Dot does not support sessions")) (provide 'ob-dot) ;;; ob-dot.el ends here org-mode-9.7.29+dfsg/lisp/ob-emacs-lisp.el000066400000000000000000000112561500430433700202020ustar00rootroot00000000000000;;; ob-emacs-lisp.el --- Babel Functions for Emacs-lisp Code -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2025 Free Software Foundation, Inc. ;; Author: Eric Schulte ;; Keywords: literate programming, reproducible research ;; URL: https://orgmode.org ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; Org-Babel support for evaluating emacs-lisp code ;;; Code: (require 'org-macs) (org-assert-version) (require 'ob-core) (declare-function org-babel--get-vars "ob" (params)) (declare-function org-babel-result-cond "ob" (result-params scalar-form &rest table-forms)) (declare-function org-babel-reassemble-table "ob" (table colnames rownames)) (declare-function org-babel-pick-name "ob" (names selector)) (defconst org-babel-header-args:emacs-lisp '((lexical . :any)) "Emacs-lisp specific header arguments.") (defvar org-babel-default-header-args:emacs-lisp '((:lexical . "no")) "Default arguments for evaluating an emacs-lisp source block. A value of \"yes\" or t causes source blocks to be eval'd using lexical scoping. It can also be an alist mapping symbols to their value. It is used both as the optional LEXICAL argument to `eval', and as the value for `lexical-binding' in buffers created by `org-edit-src-code'.") (defun org-babel-expand-body:emacs-lisp (body params) "Expand BODY according to PARAMS, return the expanded body." (let ((vars (org-babel--get-vars params)) (print-level nil) (print-length nil) (prologue (cdr (assq :prologue params))) (epilogue (cdr (assq :epilogue params)))) (if (null vars) (concat body "\n") (format "(let (%s)\n%s%s%s\n)" (mapconcat (lambda (var) (format "%S" `(,(car var) ',(cdr var)))) vars "\n ") (if prologue (concat prologue "\n ") "") body (if epilogue (concat "\n " epilogue "\n") ""))))) (defun org-babel-execute:emacs-lisp (body params) "Execute emacs-lisp code BODY according to PARAMS." (let* ((lexical (cdr (assq :lexical params))) (session (cdr (assq :session params))) (result-params (cdr (assq :result-params params))) (body (format (if (member "output" result-params) "(with-output-to-string %s\n)" "(progn %s\n)") (org-babel-expand-body:emacs-lisp body params))) (result (eval (read (if (or (member "code" result-params) (member "pp" result-params)) (concat "(pp " body ")") body)) (org-babel-emacs-lisp-lexical lexical)))) (when (and session (not (equal session "none"))) (error "ob-emacs-lisp backend does not support sessions")) (org-babel-result-cond result-params (let ((print-level nil) (print-length nil)) (if (or (member "scalar" result-params) (member "verbatim" result-params)) (format "%S" result) (format "%s" result))) (org-babel-reassemble-table result (org-babel-pick-name (cdr (assq :colname-names params)) (cdr (assq :colnames params))) (org-babel-pick-name (cdr (assq :rowname-names params)) (cdr (assq :rownames params))))))) (defun org-babel-emacs-lisp-lexical (lexical) "Interpret :lexical source block argument. Convert LEXICAL into the form appropriate for `lexical-binding' and the LEXICAL argument to `eval'." (if (listp lexical) lexical (not (null (member lexical '("yes" "t")))))) (defun org-babel-edit-prep:emacs-lisp (info) "Set `lexical-binding' in Org edit buffer. Set `lexical-binding' in Org edit buffer according to the corresponding :lexical source block argument provide in the INFO channel, as returned by `org-babel-get-src-block-info'." (setq lexical-binding (org-babel-emacs-lisp-lexical (org-babel-read (cdr (assq :lexical (nth 2 info))))))) (defun org-babel-prep-session:emacs-lisp (_session _params) "Return an error because we do not support sessions." (error "ob-emacs-lisp backend does not support sessions")) (org-babel-make-language-alias "elisp" "emacs-lisp") (provide 'ob-emacs-lisp) ;;; ob-emacs-lisp.el ends here org-mode-9.7.29+dfsg/lisp/ob-eshell.el000066400000000000000000000077101500430433700174210ustar00rootroot00000000000000;;; ob-eshell.el --- Babel Functions for Eshell -*- lexical-binding: t; -*- ;; Copyright (C) 2018-2025 Free Software Foundation, Inc. ;; Author: stardiviner ;; Maintainer: stardiviner ;; URL: https://github.com/stardiviner/ob-eshell ;; Keywords: literate programming, reproducible research ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; Org Babel support for evaluating Eshell source code. ;;; Code: (require 'org-macs) (org-assert-version) (require 'ob) (require 'eshell) (declare-function eshell-send-input "esh-mode" (&optional use-region queue-p no-newline)) (defvar eshell-last-output-start) (defvar eshell-last-output-end) (defvar eshell-last-input-end) (defvar org-babel-default-header-args:eshell '()) (defun org-babel-execute:eshell (body params) "Execute a block of Eshell code BODY with PARAMS. This function is called by `org-babel-execute-src-block'. The BODY argument is code which can be executed in Eshell. Eshell allows executing normal shell command and Elisp code. For more details, see Info node `(eshell) Top'. The PARAMS argument is passed to `org-babel-expand-body:generic' (which see)." (let* ((session (org-babel-eshell-initiate-session (cdr (assq :session params)))) (full-body (org-babel-expand-body:generic body params (org-babel-variable-assignments:eshell params)))) (if session (progn (with-current-buffer session (dolist (line (split-string full-body "\n")) (goto-char eshell-last-output-end) (insert line) (eshell-send-input)) ;; get output of last input ;; TODO: collect all output instead of last command's output. (goto-char eshell-last-input-end) (buffer-substring-no-properties (point) eshell-last-output-start))) (with-temp-buffer (eshell-command full-body t) (buffer-string))))) (defun org-babel-prep-session:eshell (session params) "Prepare SESSION according to the header arguments specified in PARAMS." (let* ((session (org-babel-eshell-initiate-session session)) ;; Eshell session buffer is read from variable `eshell-buffer-name'. (eshell-buffer-name session) (var-lines (org-babel-variable-assignments:eshell params))) (call-interactively #'eshell) (mapc #'eshell-command var-lines) session)) (defun ob-eshell-session-live-p (session) "Non-nil if Eshell SESSION exists." (get-buffer session)) (defun org-babel-eshell-initiate-session (&optional session _params) "Initiate a session named SESSION." (when (and session (not (string= session "none"))) (save-window-excursion (unless (ob-eshell-session-live-p session) (let ((eshell-buffer-name session)) (eshell)))) session)) (defun org-babel-variable-assignments:eshell (params) "Convert ob-eshell variables from PARAMS into Eshell variables assignments." (mapcar (lambda (pair) ;; Use `ignore' to suppress value in the command output. (format "(ignore (setq %s %S))" (car pair) (cdr pair))) (org-babel--get-vars params))) (defun org-babel-load-session:eshell (session body params) "Load BODY into SESSION with PARAMS." (save-window-excursion (let ((buffer (org-babel-prep-session:eshell session params))) (with-current-buffer buffer (goto-char (point-max)) (insert (org-babel-chomp body))) buffer))) (provide 'ob-eshell) ;;; ob-eshell.el ends here org-mode-9.7.29+dfsg/lisp/ob-eval.el000066400000000000000000000153771500430433700171040ustar00rootroot00000000000000;;; ob-eval.el --- Babel Functions for External Code Evaluation -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2025 Free Software Foundation, Inc. ;; Author: Eric Schulte ;; Keywords: literate programming, reproducible research, comint ;; URL: https://orgmode.org ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; These functions build existing Emacs support for executing external ;; shell commands. ;;; Code: (require 'org-macs) (org-assert-version) (eval-when-compile (require 'subr-x)) ; For `string-empty-p', Emacs < 29 (defvar org-babel-error-buffer-name "*Org-Babel Error Output*") (declare-function org-babel-temp-file "ob-core" (prefix &optional suffix)) (defun org-babel-eval-error-notify (exit-code stderr) "Open a buffer to display STDERR and a message with the value of EXIT-CODE. If EXIT-CODE is nil, display the message without a code." (let ((buf (get-buffer-create org-babel-error-buffer-name))) (with-current-buffer buf (goto-char (point-max)) (save-excursion (unless (bolp) (insert "\n")) (insert stderr) (if exit-code (insert (format "[ Babel evaluation exited with code %S ]" exit-code)) (insert "[ Babel evaluation exited abnormally ]")))) (display-buffer buf)) (if exit-code (message "Babel evaluation exited with code %S" exit-code) (message "Babel evaluation exited abnormally"))) (defun org-babel-eval (command query) "Run COMMAND on QUERY. Return standard output produced by COMMAND. If COMMAND exits with a non-zero code or produces error output, show it with `org-babel-eval-error-notify'. Writes QUERY into a temp-buffer that is processed with `org-babel--shell-command-on-region'." (let ((error-buffer (get-buffer-create " *Org-Babel Error*")) exit-code) (with-current-buffer error-buffer (erase-buffer)) (with-temp-buffer ;; Ensure trailing newline. It is required for cmdproxy.exe. (insert query "\n") (setq exit-code (org-babel--shell-command-on-region command error-buffer)) (let ((stderr (with-current-buffer error-buffer (buffer-string)))) (if (or (not (numberp exit-code)) (> exit-code 0) (not (string-empty-p stderr))) (progn (org-babel-eval-error-notify exit-code stderr) (save-excursion (when (get-buffer org-babel-error-buffer-name) (with-current-buffer org-babel-error-buffer-name (unless (derived-mode-p 'compilation-mode) (compilation-mode)) ;; Compilation-mode enforces read-only, but ;; Babel expects the buffer modifiable. (setq buffer-read-only nil)))) ;; Return output, if any. (buffer-string)) (buffer-string)))))) (defun org-babel-eval-read-file (file) "Return the contents of FILE as a string." (with-temp-buffer (insert-file-contents file) (buffer-string))) (defun org-babel--shell-command-on-region (command error-buffer) "Execute COMMAND in an inferior shell with region as input. Stripped down version of `shell-command-on-region' for internal use in Babel only. This lets us work around errors in the original function in various versions of Emacs. This expects the query to be run to be in the current temp buffer. This is written into input-file. ERROR-BUFFER is the name of the file which `org-babel-eval' has created to use for any error messages that are returned." (let ((input-file (org-babel-temp-file "ob-input-")) (error-file (if error-buffer (org-babel-temp-file "ob-error-") nil)) (shell-file-name (org-babel--get-shell-file-name)) exit-status) ;; we always call this with 'replace, remove conditional ;; Replace specified region with output from command. (org-babel--write-temp-buffer-input-file input-file) (setq exit-status (process-file shell-file-name input-file (if error-file (list t error-file) t) nil shell-command-switch command)) (when (and input-file (file-exists-p input-file) ;; bind org-babel--debug-input around the call to keep ;; the temporary input files available for inspection (not (when (boundp 'org-babel--debug-input) org-babel--debug-input))) (delete-file input-file)) (when (and error-file (file-exists-p error-file)) (when (< 0 (file-attribute-size (file-attributes error-file))) (with-current-buffer (get-buffer-create error-buffer) (let ((pos-from-end (- (point-max) (point)))) (or (bobp) (insert "\f\n")) ;; Do no formatting while reading error file, ;; because that can run a shell command, and we ;; don't want that to cause an infinite recursion. (format-insert-file error-file nil) ;; Put point after the inserted errors. (goto-char (- (point-max) pos-from-end))) (current-buffer))) (delete-file error-file)) exit-status)) (defun org-babel--write-temp-buffer-input-file (input-file) "Write the contents of the current temp buffer into INPUT-FILE." (let ((start (point-min)) (end (point-max))) (goto-char start) (push-mark (point) 'nomsg) (write-region start end input-file) (delete-region start end) (exchange-point-and-mark))) (defun org-babel-eval-wipe-error-buffer () "Delete the contents of the Org code block error buffer. This buffer is named by `org-babel-error-buffer-name'." (when (get-buffer org-babel-error-buffer-name) (with-current-buffer org-babel-error-buffer-name (delete-region (point-min) (point-max))))) (defun org-babel--get-shell-file-name () "Return system `shell-file-name', defaulting to /bin/sh. Unfortunately, `executable-find' does not support file name handlers. Therefore, we could use it in the local case only." ;; FIXME: Since Emacs 27, `executable-find' accepts optional second ;; argument supporting remote hosts. (cond ((and (not (file-remote-p default-directory)) (executable-find shell-file-name)) shell-file-name) ((file-executable-p (concat (file-remote-p default-directory) shell-file-name)) shell-file-name) ("/bin/sh"))) (provide 'ob-eval) ;;; ob-eval.el ends here org-mode-9.7.29+dfsg/lisp/ob-exp.el000066400000000000000000000446411500430433700167450ustar00rootroot00000000000000;;; ob-exp.el --- Exportation of Babel Source Blocks -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2025 Free Software Foundation, Inc. ;; Authors: Eric Schulte ;; Dan Davison ;; Keywords: literate programming, reproducible research ;; URL: https://orgmode.org ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;;; Code: (require 'org-macs) (org-assert-version) (require 'ob-core) (declare-function org-babel-lob-get-info "ob-lob" (&optional datum no-eval)) (declare-function org-element-at-point "org-element" (&optional pom cached-only)) (declare-function org-element-context "org-element" (&optional element)) (declare-function org-element-property "org-element-ast" (property node)) (declare-function org-element-begin "org-element" (node)) (declare-function org-element-end "org-element" (node)) (declare-function org-element-type "org-element-ast" (node &optional anonymous)) (declare-function org-escape-code-in-string "org-src" (s)) (declare-function org-export-copy-buffer "ox" (&optional buffer drop-visibility drop-narrowing drop-contents drop-locals)) (declare-function org-in-commented-heading-p "org" (&optional no-inheritance element)) (declare-function org-in-archived-heading-p "org" (&optional no-inheritance element)) (declare-function org-src-preserve-indentation-p "org-src" (node)) (defcustom org-export-use-babel t "Switch controlling code evaluation and header processing during export. When set to nil no code will be evaluated as part of the export process and no header arguments will be obeyed. Users who wish to avoid evaluating code on export should use the header argument `:eval never-export'." :group 'org-babel :version "24.1" :type '(choice (const :tag "Never" nil) (const :tag "Always" t)) :safe #'null) (defmacro org-babel-exp--at-source (&rest body) "Evaluate BODY at the source of the Babel block at point. Source is located in `org-babel-exp-reference-buffer'. The value returned is the value of the last form in BODY. Assume that point is at the beginning of the Babel block." (declare (indent 1) (debug body)) `(let ((source (get-text-property (point) 'org-reference))) ;; Source blocks created during export process (e.g., by other ;; source blocks) are not referenced. In this case, do not move ;; point at all. (with-current-buffer (if source org-babel-exp-reference-buffer (current-buffer)) (org-with-wide-buffer (when source (goto-char source)) ,@body)))) (defun org-babel-exp-src-block (&optional element) "Process source block for export. Depending on the \":export\" header argument, replace the source code block like this: both ---- display the code and the results code ---- the default, display the code inside the block but do not process results - just like none only the block is run on export ensuring that its results are present in the Org mode buffer none ---- do not display either code or results upon export Optional argument ELEMENT must contain source block element at point. Assume point is at block opening line." (interactive) (save-excursion (let* ((info (org-babel-get-src-block-info nil element)) (lang (nth 0 info)) (raw-params (nth 2 info)) hash) ;; bail if we couldn't get any info from the block (unless noninteractive (message "org-babel-exp process %s at position %d..." lang (line-beginning-position))) (when info ;; if we're actually going to need the parameters (when (member (cdr (assq :exports (nth 2 info))) '("both" "results")) (let ((lang-headers (intern (concat "org-babel-default-header-args:" lang)))) (org-babel-exp--at-source (setf (nth 2 info) (org-babel-process-params (apply #'org-babel-merge-params org-babel-default-header-args (and (boundp lang-headers) (symbol-value lang-headers)) (append (org-babel-params-from-properties lang) (list raw-params))))))) (setf hash (org-babel-sha1-hash info :export))) (org-babel-exp-do-export info 'block hash))))) (defcustom org-babel-exp-call-line-template "" "Template used to export call lines. This template may be customized to include the call line name with any export markup. The template is filled out using `org-fill-template', and the following %keys may be used. line --- call line An example value would be \"\\n: call: %line\" to export the call line wrapped in a verbatim environment. Note: the results are inserted separately after the contents of this template." :group 'org-babel :type 'string) (defun org-babel-exp-process-buffer () "Execute all Babel blocks in current buffer." (interactive) (when org-export-use-babel (let ((case-fold-search t) (regexp "\\(call\\|src\\)_\\|^[ \t]*#\\+\\(BEGIN_SRC\\|CALL:\\)") ;; Get a pristine copy of current buffer so Babel ;; references are properly resolved and source block ;; context is preserved. (org-babel-exp-reference-buffer (org-export-copy-buffer)) element) (unwind-protect (save-excursion ;; First attach to every source block their original ;; position, so that they can be retrieved within ;; `org-babel-exp-reference-buffer', even after heavy ;; modifications on current buffer. ;; ;; False positives are harmless, so we don't check if ;; we're really at some Babel object. Moreover, ;; `line-end-position' ensures that we propertize ;; a noticeable part of the object, without affecting ;; multiple objects on the same line. (goto-char (point-min)) (while (re-search-forward regexp nil t) (let ((s (match-beginning 0))) (put-text-property s (line-end-position) 'org-reference s))) ;; Evaluate from top to bottom every Babel block ;; encountered. (goto-char (point-min)) ;; We are about to do a large number of changes in ;; buffer, but we do not care about folding in this ;; buffer. (org-fold-core-ignore-modifications (while (re-search-forward regexp nil t) (setq element (save-match-data (org-element-at-point))) (unless (save-match-data (or (org-in-commented-heading-p nil element) (org-in-archived-heading-p nil element))) (let* ((object? (match-end 1)) (element (save-match-data (if object? (org-element-context element) ;; No deep inspection if we're ;; just looking for an element. element))) (type (pcase (org-element-type element) ;; Discard block elements if we're looking ;; for inline objects. False results ;; happen when, e.g., "call_" syntax is ;; located within affiliated keywords: ;; ;; #+name: call_src ;; #+begin_src ... ((and (or `babel-call `src-block) (guard object?)) nil) (type type))) (begin (copy-marker (org-element-begin element))) (end (copy-marker (save-excursion (goto-char (org-element-end element)) (skip-chars-backward " \r\t\n") (point))))) (pcase type (`inline-src-block (let* ((info (org-babel-get-src-block-info nil element)) (params (nth 2 info))) (setf (nth 1 info) (if (and (cdr (assq :noweb params)) (string= "yes" (cdr (assq :noweb params)))) (org-babel-expand-noweb-references info org-babel-exp-reference-buffer) (nth 1 info))) (goto-char begin) (let ((replacement (org-babel-exp-do-export info 'inline))) (cond ((equal replacement "") ;; Replacement code is empty: remove ;; inline source block, including extra ;; white space that might have been ;; created when inserting results. (delete-region begin (progn (goto-char end) (skip-chars-forward " \t") (point)))) ((not replacement) ;; Replacement code cannot be determined. ;; Leave the code block as is. (goto-char end)) ;; Otherwise: remove inline source block ;; but preserve following white spaces. ;; Then insert value. ((not (string= replacement (buffer-substring begin end))) (delete-region begin end) (insert replacement)) ;; Replacement is the same as the source ;; block. Continue onwards. (t (goto-char end)))))) ((or `babel-call `inline-babel-call) (org-babel-exp-do-export (or (org-babel-lob-get-info element) (user-error "Unknown Babel reference: %s" (org-element-property :call element))) 'lob) (let ((rep (org-fill-template org-babel-exp-call-line-template `(("line" . ,(org-element-property :value element)))))) ;; If replacement is empty, completely remove ;; the object/element, including any extra ;; white space that might have been created ;; when including results. (cond ((equal rep "") (delete-region begin (progn (goto-char end) (if (not (eq type 'babel-call)) (progn (skip-chars-forward " \t") (point)) (unless (eobp) (skip-chars-forward " \r\t\n") (line-beginning-position)))))) ((not rep) ;; Replacement code cannot be determined. ;; Leave the code block as is. (goto-char end)) (t ;; Otherwise, preserve trailing ;; spaces/newlines and then, insert ;; replacement string. (goto-char begin) (delete-region begin end) (insert rep))))) (`src-block (let ((match-start (copy-marker (match-beginning 0))) (ind (org-current-text-indentation))) ;; Take care of matched block: compute ;; replacement string. In particular, a nil ;; REPLACEMENT means the block is left as-is ;; while an empty string removes the block. (let ((replacement (progn (goto-char match-start) (org-babel-exp-src-block element)))) (cond ((not replacement) (goto-char end)) ((equal replacement "") (goto-char end) (unless (eobp) (skip-chars-forward " \r\t\n") (forward-line 0)) (delete-region begin (point))) (t (if (org-src-preserve-indentation-p element) ;; Indent only code block ;; markers. (with-temp-buffer ;; Do not use tabs for block ;; indentation. (when (fboundp 'indent-tabs-mode) (indent-tabs-mode -1) ;; FIXME: Emacs 26 ;; compatibility. (setq-local indent-tabs-mode nil)) (insert replacement) (skip-chars-backward " \r\t\n") (indent-line-to ind) (goto-char 1) (indent-line-to ind) (setq replacement (buffer-string))) ;; Indent everything. (with-temp-buffer ;; Do not use tabs for block ;; indentation. (when (fboundp 'indent-tabs-mode) (indent-tabs-mode -1) ;; FIXME: Emacs 26 ;; compatibility. (setq-local indent-tabs-mode nil)) (insert replacement) (indent-rigidly 1 (point) ind) (setq replacement (buffer-string)))) (goto-char match-start) (let ((rend (save-excursion (goto-char end) (line-end-position)))) (if (string-equal replacement (buffer-substring match-start rend)) (goto-char rend) (delete-region match-start (save-excursion (goto-char end) (line-end-position))) (insert replacement)))))) (set-marker match-start nil)))) (set-marker begin nil) (set-marker end nil)))))) (kill-buffer org-babel-exp-reference-buffer) (remove-text-properties (point-min) (point-max) '(org-reference nil)))))) (defun org-babel-exp-do-export (info type &optional hash) "Return a string with the exported content of a code block defined by INFO. TYPE is the code block type: `block', `inline', or `lob'. HASH is the result hash. Return nil when exported content cannot be determined. The function respects the value of the :exports header argument." (let ((silently (lambda () (let ((session (cdr (assq :session (nth 2 info))))) (unless (equal "none" session) (org-babel-exp-results info type 'silent))))) (clean (lambda () (if (eq type 'inline) (org-babel-remove-inline-result) (org-babel-remove-result info))))) (pcase (or (cdr (assq :exports (nth 2 info))) "code") ("none" (funcall silently) (funcall clean) "") ("code" (funcall silently) (funcall clean) (org-babel-exp-code info type)) ("results" (org-babel-exp-results info type nil hash) "") ("both" (org-babel-exp-results info type nil hash) (org-babel-exp-code info type)) (unknown-value (warn "Unknown value of src block parameter :exports %S" unknown-value) nil)))) (defcustom org-babel-exp-code-template "#+begin_src %lang%switches%header-args\n%body\n#+end_src" "Template used to export the body of code blocks. This template may be customized to include additional information such as the code block name, or the values of particular header arguments. The template is filled out using `org-fill-template', and the following %keys may be used. lang ------ the language of the code block name ------ the name of the code block body ------ the body of the code block switches -- the switches associated to the code block header-args the header arguments of the code block In addition to the keys mentioned above, every header argument defined for the code block may be used as a key and will be replaced with its value." :group 'org-babel :type 'string :package-version '(Org . "9.7")) (defcustom org-babel-exp-inline-code-template "src_%lang[%switches%header-args]{%body}" "Template used to export the body of inline code blocks. This template may be customized to include additional information such as the code block name, or the values of particular header arguments. The template is filled out using `org-fill-template', and the following %keys may be used. lang ------ the language of the code block name ------ the name of the code block body ------ the body of the code block switches -- the switches associated to the code block header-args the header arguments of the code block In addition to the keys mentioned above, every header argument defined for the code block may be used as a key and will be replaced with its value." :group 'org-babel :type 'string :package-version '(Org . "9.7")) (defun org-babel-exp-code (info type) "Return the original code block of TYPE defined by INFO, formatted for export." (setf (nth 1 info) (if (string= "strip-export" (cdr (assq :noweb (nth 2 info)))) (replace-regexp-in-string (org-babel-noweb-wrap) "" (nth 1 info)) (if (org-babel-noweb-p (nth 2 info) :export) (org-babel-expand-noweb-references info org-babel-exp-reference-buffer) (nth 1 info)))) (org-fill-template (if (eq type 'inline) org-babel-exp-inline-code-template org-babel-exp-code-template) `(("lang" . ,(nth 0 info)) ;; Inline source code should not be escaped. ("body" . ,(let ((body (nth 1 info))) (if (eq type 'inline) body (org-escape-code-in-string body)))) ("switches" . ,(let ((f (nth 3 info))) (and (org-string-nw-p f) (concat " " f)))) ("flags" . ,(let ((f (assq :flags (nth 2 info)))) (and f (concat " " (cdr f))))) ("header-args" . ,(org-babel-exp--at-source (when-let* ((params (org-element-property :parameters (org-element-context)))) (concat " " params)))) ,@(mapcar (lambda (pair) (cons (substring (symbol-name (car pair)) 1) (format "%S" (cdr pair)))) (nth 2 info)) ("name" . ,(or (nth 4 info) ""))))) (defun org-babel-exp-results (info type &optional silent hash) "Evaluate and return the results of the current code block for export. INFO is as returned by `org-babel-get-src-block-info'. TYPE is the code block type. HASH is the result hash. Results are prepared in a manner suitable for export by Org mode. This function is called by `org-babel-exp-do-export'. The code block will be evaluated. Optional argument SILENT can be used to inhibit insertion of results into the buffer." (unless (and hash (equal hash (org-babel-current-result-hash))) (let ((lang (nth 0 info)) (body (if (org-babel-noweb-p (nth 2 info) :eval) (org-babel-expand-noweb-references info org-babel-exp-reference-buffer) (nth 1 info))) (info (copy-sequence info)) (org-babel-current-src-block-location (point-marker))) ;; Skip code blocks which we can't evaluate. (if (not (fboundp (intern (concat "org-babel-execute:" lang)))) (warn "org-export: No org-babel-execute function for %s. Not updating exported results." lang) (org-babel-eval-wipe-error-buffer) (setf (nth 1 info) body) (setf (nth 2 info) (org-babel-exp--at-source (org-babel-process-params (org-babel-merge-params (nth 2 info) `((:results . ,(if silent "silent" "replace"))))))) (pcase type (`block (org-babel-execute-src-block nil info)) (`inline ;; Position the point on the inline source block ;; allowing `org-babel-insert-result' to check that the ;; block is inline. (goto-char (nth 5 info)) (org-babel-execute-src-block nil info)) (`lob (save-excursion (goto-char (nth 5 info)) (org-babel-execute-src-block nil info)))))))) (provide 'ob-exp) ;;; ob-exp.el ends here org-mode-9.7.29+dfsg/lisp/ob-forth.el000066400000000000000000000062031500430433700172630ustar00rootroot00000000000000;;; ob-forth.el --- Babel Functions for Forth -*- lexical-binding: t; -*- ;; Copyright (C) 2014-2025 Free Software Foundation, Inc. ;; Author: Eric Schulte ;; Keywords: literate programming, reproducible research, forth ;; URL: https://orgmode.org ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; Requires the gforth forth compiler and `forth-mode' (see below). ;; https://www.gnu.org/software/gforth/ ;;; Requirements: ;; Session evaluation requires the gforth forth compiler as well as ;; `forth-mode' which is distributed with gforth (in gforth.el). ;;; Code: (require 'org-macs) (org-assert-version) (require 'ob) (require 'org-macs) (declare-function forth-proc "ext:gforth" ()) (defvar org-babel-default-header-args:forth '((:session . "yes")) "Default header arguments for forth code blocks.") (defun org-babel-execute:forth (body params) "Execute Forth BODY according to PARAMS. This function is called by `org-babel-execute-src-block'." (if (string= "none" (cdr (assq :session params))) (error "Non-session evaluation not supported for Forth code blocks") (let ((all-results (org-babel-forth-session-execute body params))) (if (member "output" (cdr (assq :result-params params))) (mapconcat #'identity all-results "\n") (car (last all-results)))))) (defun org-babel-forth-session-execute (body params) "Execute Forth BODY in session defined via PARAMS." (org-require-package 'forth-mode) (let ((proc (forth-proc)) (rx " \\(\n:\\|compiled\n\\|ok\n\\)") (result-start)) (with-current-buffer (process-buffer (forth-proc)) (mapcar (lambda (line) (setq result-start (progn (goto-char (process-mark proc)) (point))) (comint-send-string proc (concat line "\n")) ;; wait for forth to say "ok" (while (not (progn (goto-char result-start) (re-search-forward rx nil t))) (accept-process-output proc 0.01)) (let ((case (match-string 1))) (cond ((string= "ok\n" case) ;; Collect intermediate output. (buffer-substring (+ result-start 1 (length line)) (match-beginning 0))) ((string= "compiled\n" case)) ;; Ignore partial compilation. ((string= "\n:" case) ;; Report errors. (org-babel-eval-error-notify 1 (buffer-substring (+ (match-beginning 0) 1) (point-max))) nil)))) (split-string (org-trim (org-babel-expand-body:generic body params)) "\n" 'omit-nulls))))) (provide 'ob-forth) ;;; ob-forth.el ends here org-mode-9.7.29+dfsg/lisp/ob-fortran.el000066400000000000000000000153701500430433700176210ustar00rootroot00000000000000;;; ob-fortran.el --- Babel Functions for Fortran -*- lexical-binding: t; -*- ;; Copyright (C) 2011-2025 Free Software Foundation, Inc. ;; Authors: Sergey Litvinov ;; Eric Schulte ;; Keywords: literate programming, reproducible research, fortran ;; URL: https://orgmode.org ;; This file is part of GNU Emacs. ;; ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; Org-Babel support for evaluating fortran code. ;;; Code: (require 'org-macs) (org-assert-version) (require 'ob) (require 'org-macs) (require 'cc-mode) (require 'cl-lib) (declare-function org-entry-get "org" (pom property &optional inherit literal-nil)) (defvar org-babel-tangle-lang-exts) (add-to-list 'org-babel-tangle-lang-exts '("fortran" . "F90")) (defvar org-babel-default-header-args:fortran '()) (defcustom org-babel-fortran-compiler "gfortran" "Fortran command used to compile Fortran source code file." :group 'org-babel :package-version '(Org . "9.5") :type 'string) (defun org-babel-execute:fortran (body params) "Execute Fortran BODY according to PARAMS. This function is called by `org-babel-execute-src-block'." (let* ((tmp-src-file (org-babel-temp-file "fortran-src-" ".F90")) (tmp-bin-file (org-babel-temp-file "fortran-bin-" org-babel-exeext)) (cmdline (cdr (assq :cmdline params))) (flags (cdr (assq :flags params))) (full-body (org-babel-expand-body:fortran body params))) (with-temp-file tmp-src-file (insert full-body)) (org-babel-eval (format "%s -o %s %s %s" org-babel-fortran-compiler (org-babel-process-file-name tmp-bin-file) (mapconcat 'identity (if (listp flags) flags (list flags)) " ") (org-babel-process-file-name tmp-src-file)) "") (let ((results (org-trim (org-remove-indentation (org-babel-eval (concat tmp-bin-file (if cmdline (concat " " cmdline) "")) ""))))) (org-babel-reassemble-table (org-babel-result-cond (cdr (assq :result-params params)) (org-babel-read results) (let ((tmp-file (org-babel-temp-file "f-"))) (with-temp-file tmp-file (insert results)) (org-babel-import-elisp-from-file tmp-file))) (org-babel-pick-name (cdr (assq :colname-names params)) (cdr (assq :colnames params))) (org-babel-pick-name (cdr (assq :rowname-names params)) (cdr (assq :rownames params))))))) (defun org-babel-expand-body:fortran (body params) "Expand a fortran BODY according to its header arguments defined in PARAMS." (let ((vars (org-babel--get-vars params)) (prologue (cdr (assq :prologue params))) (epilogue (cdr (assq :epilogue params))) (main-p (not (string= (cdr (assq :main params)) "no"))) (includes (or (cdr (assq :includes params)) (org-babel-read (org-entry-get nil "includes" t)))) (defines (org-babel-read (or (cdr (assq :defines params)) (org-babel-read (org-entry-get nil "defines" t)))))) (mapconcat 'identity (list ;; includes (mapconcat (lambda (inc) (format "#include %s" inc)) (if (listp includes) includes (list includes)) "\n") ;; defines (mapconcat (lambda (inc) (format "#define %s" inc)) (if (listp defines) defines (list defines)) "\n") ;; body (if main-p (org-babel-fortran-ensure-main-wrap (concat ;; variables (mapconcat 'org-babel-fortran-var-to-fortran vars "\n") (and prologue (concat prologue "\n")) body (and prologue (concat prologue "\n"))) params) (concat (and prologue (concat prologue "\n")) body (and epilogue (concat "\n" epilogue "\n")))) "\n") "\n"))) (defun org-babel-fortran-ensure-main-wrap (body params) "Wrap BODY in a \"program ... end program\" block if none exists. Variable assignments are derived from PARAMS." (if (string-match "^[ \t]*program\\>" (capitalize body)) (let ((vars (org-babel--get-vars params))) (when vars (error "Cannot use :vars if `program' statement is present")) body) (format "program main\n%s\nend program main\n" body))) (defun org-babel-prep-session:fortran (_session _params) "Do nothing. This function does nothing as fortran is a compiled language with no support for sessions." (error "Fortran is a compiled languages -- no support for sessions")) (defun org-babel-load-session:fortran (_session _body _params) "Do nothing. This function does nothing as fortran is a compiled language with no support for sessions." (error "Fortran is a compiled languages -- no support for sessions")) ;; helper functions (defun org-babel-fortran-var-to-fortran (pair) "Convert PAIR of (VAR . VAL) into a string of fortran code. The fortran code will assign VAL to VAR variable." ;; TODO list support (let ((var (car pair)) (val (cdr pair))) (when (symbolp val) (setq val (symbol-name val)) (when (= (length val) 1) (setq val (string-to-char val)))) (cond ((integerp val) (format "integer, parameter :: %S = %S\n" var val)) ((floatp val) (format "real, parameter :: %S = %S\n" var val)) ((or (integerp val)) (format "character, parameter :: %S = '%S'\n" var val)) ((stringp val) (format "character(len=%d), parameter :: %S = '%s'\n" (length val) var val)) ;; val is a matrix ((and (listp val) (cl-every #'listp val)) (format "real, parameter :: %S(%d,%d) = transpose( reshape( %s , (/ %d, %d /) ) )\n" var (length val) (length (car val)) (org-babel-fortran-transform-list val) (length (car val)) (length val))) ((listp val) (format "real, parameter :: %S(%d) = %s\n" var (length val) (org-babel-fortran-transform-list val))) (t (error "The type of parameter %s is not supported by ob-fortran" var))))) (defun org-babel-fortran-transform-list (val) "Return a fortran representation of enclose syntactic list VAL." (if (listp val) (concat "(/" (mapconcat #'org-babel-fortran-transform-list val ", ") "/)") (format "%S" val))) (provide 'ob-fortran) ;;; ob-fortran.el ends here org-mode-9.7.29+dfsg/lisp/ob-gnuplot.el000066400000000000000000000306731500430433700176410ustar00rootroot00000000000000;;; ob-gnuplot.el --- Babel Functions for Gnuplot -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2025 Free Software Foundation, Inc. ;; Author: Eric Schulte ;; Maintainer: Ihor Radchenko ;; Keywords: literate programming, reproducible research ;; URL: https://orgmode.org ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; Org-Babel support for evaluating gnuplot source code. ;; ;; This differs from most standard languages in that ;; ;; 1) we are generally only going to return results of type "file" ;; ;; 2) we are adding the "file" and "cmdline" header arguments ;;; Requirements: ;; - gnuplot :: https://www.gnuplot.info/ ;; ;; - gnuplot-mode :: you can search the web for the latest active one. ;;; Code: (require 'org-macs) (org-assert-version) (require 'ob) (require 'org-macs) (declare-function org-time-string-to-time "org" (s)) (declare-function orgtbl-to-generic "org-table" (table params)) (declare-function gnuplot-mode "ext:gnuplot-mode" ()) (declare-function gnuplot-send-string-to-gnuplot "ext:gnuplot-mode" (str txt)) (declare-function gnuplot-send-buffer-to-gnuplot "ext:gnuplot-mode" ()) (defvar org-babel-temporary-directory) (defvar org-babel-default-header-args:gnuplot '((:results . "file") (:exports . "results") (:session . nil)) "Default arguments to use when evaluating a gnuplot source block.") (defvar org-babel-header-args:gnuplot '((title . :any) (lines . :any) (sets . :any) (x-labels . :any) (y-labels . :any) (timefmt . :any) (time-ind . :any) (missing . :any) (term . :any)) "Gnuplot specific header args.") (defvar org-babel-gnuplot-timestamp-fmt nil) ; Dynamically scoped. (defvar *org-babel-gnuplot-missing* nil) (defcustom *org-babel-gnuplot-terms* '((eps . "postscript eps")) "List of file extensions and the associated gnuplot terminal." :group 'org-babel :type '(repeat (cons (symbol :tag "File extension") (string :tag "Gnuplot terminal")))) (defun org-babel-gnuplot-process-vars (params) "Extract variables from PARAMS and process the variables. Dumps all vectors into files and returns an association list of variable names and the related value to be used in the gnuplot code." (let ((*org-babel-gnuplot-missing* (cdr (assq :missing params)))) (mapcar (lambda (pair) (cons (car pair) ;; variable name (let* ((val (cdr pair)) ;; variable value (lp (proper-list-p val))) (if lp (org-babel-gnuplot-table-to-data (let* ((first (car val)) (tablep (or (listp first) (symbolp first)))) (if tablep val (mapcar 'list val))) ;; Make temporary file name stable with respect to data. ;; If we do not do it, :cache argument becomes useless. (org-babel-temp-stable-file (cons val params) "gnuplot-") params) (if (and (stringp val) (file-remote-p val) ;; check if val is a remote file (file-exists-p val)) ;; call to file-exists-p is slow, maybe remove it (let* ((local-name (concat ;; create a unique filename to avoid multiple downloads (org-babel-temp-directory) "/gnuplot/" (file-remote-p val 'host) (org-babel-local-file-name val)))) (if (and (file-exists-p local-name) ;; only download file if remote is newer (file-newer-than-file-p local-name val)) local-name (make-directory (file-name-directory local-name) t) (copy-file val local-name t) )) val ))))) (org-babel--get-vars params)))) (defun org-babel-expand-body:gnuplot (body params) "Expand BODY according to PARAMS, return the expanded body." (save-window-excursion (let* ((vars (org-babel-gnuplot-process-vars params)) (out-file (cdr (assq :file params))) (prologue (cdr (assq :prologue params))) (epilogue (cdr (assq :epilogue params))) (term (or (cdr (assq :term params)) (when out-file (let ((ext (file-name-extension out-file))) (or (cdr (assoc (intern (downcase ext)) *org-babel-gnuplot-terms*)) ext))))) (title (cdr (assq :title params))) (lines (cdr (assq :line params))) (sets (cdr (assq :set params))) (missing (cdr (assq :missing params))) (x-labels (cdr (assq :xlabels params))) (y-labels (cdr (assq :ylabels params))) (timefmt (cdr (assq :timefmt params))) (time-ind (or (cdr (assq :timeind params)) (when timefmt 1))) (directory default-directory) (add-to-body (lambda (text) (setq body (concat text "\n" body))))) ;; append header argument settings to body (when missing (funcall add-to-body (format "set datafile missing '%s'" missing))) (when title (funcall add-to-body (format "set title '%s'" title))) (when lines (mapc (lambda (el) (funcall add-to-body el)) lines)) (when sets (mapc (lambda (el) (funcall add-to-body (format "set %s" el))) sets)) (when x-labels (funcall add-to-body (format "set xtics (%s)" (mapconcat (lambda (pair) (format "\"%s\" %d" (cdr pair) (car pair))) x-labels ", ")))) (when y-labels (funcall add-to-body (format "set ytics (%s)" (mapconcat (lambda (pair) (format "\"%s\" %d" (cdr pair) (car pair))) y-labels ", ")))) (when time-ind (funcall add-to-body "set xdata time") (funcall add-to-body (concat "set timefmt \"" (or timefmt "%Y-%m-%d-%H:%M:%S") "\""))) (when out-file ;; set the terminal at the top of the block (funcall add-to-body (format "set output \"%s\"" out-file)) ;; and close the terminal at the bottom of the block (setq body (concat body "\nset output\n"))) (when term (funcall add-to-body (format "set term %s" term))) ;; insert variables into code body: this should happen last ;; placing the variables at the *top* of the code in case their ;; values are used later (funcall add-to-body (mapconcat #'identity (org-babel-variable-assignments:gnuplot params) "\n")) ;; replace any variable names preceded by '$' with the actual ;; value of the variable (mapc (lambda (pair) (setq body (replace-regexp-in-string (format "\\$%s" (car pair)) (cdr pair) body t t))) vars) (when prologue (funcall add-to-body prologue)) (when epilogue (setq body (concat body "\n" epilogue))) ;; Setting the directory needs to be done first so that ;; subsequent 'output' directive goes to the right place. (when directory (funcall add-to-body (format "cd '%s'" directory)))) body)) (defun org-babel-execute:gnuplot (body params) "Execute Gnuplot BODY according to PARAMS. This function is called by `org-babel-execute-src-block'." (org-require-package 'gnuplot) (let ((session (cdr (assq :session params))) (result-type (cdr (assq :results params))) (body (org-babel-expand-body:gnuplot body params)) output) (save-window-excursion ;; evaluate the code body with gnuplot (if (string= session "none") (let ((script-file (org-babel-temp-file "gnuplot-script-"))) (with-temp-file script-file (insert (concat body "\n"))) (unless noninteractive (message "gnuplot \"%s\"" script-file)) (setq output (shell-command-to-string (format "gnuplot \"%s\"" (org-babel-process-file-name script-file (if (member system-type '(cygwin windows-nt ms-dos)) t nil))))) (unless noninteractive (message "%s" output))) (with-temp-buffer (insert (concat body "\n")) (gnuplot-mode) (gnuplot-send-buffer-to-gnuplot))) (if (member "output" (split-string result-type)) output nil)))) ;; signal that output has already been written to file (defun org-babel-prep-session:gnuplot (session params) "Prepare SESSION according to the header arguments in PARAMS." (let* ((session (org-babel-gnuplot-initiate-session session)) (var-lines (org-babel-variable-assignments:gnuplot params))) (unless noninteractive (message "%S" session)) (org-babel-comint-in-buffer session (dolist (var-line var-lines) (insert var-line) (comint-send-input nil t) (org-babel-comint-wait-for-output session) (sit-for .1) (goto-char (point-max)))) session)) (defun org-babel-load-session:gnuplot (session body params) "Load BODY into SESSION." (save-window-excursion (let ((buffer (org-babel-prep-session:gnuplot session params))) (with-current-buffer buffer (goto-char (process-mark (get-buffer-process (current-buffer)))) (insert (org-babel-chomp body))) buffer))) (defun org-babel-variable-assignments:gnuplot (params) "Return list of gnuplot statements assigning the block's variables. PARAMS is src block parameters alist defining variable assignments." (mapcar (lambda (pair) (format "%s = \"%s\"" (car pair) (cdr pair))) (org-babel-gnuplot-process-vars params))) (defvar gnuplot-buffer) (defun org-babel-gnuplot-initiate-session (&optional session _params) "Initiate a gnuplot session. If there is not a current inferior-process-buffer in SESSION then create one. Return the initialized session. The current `gnuplot-mode' doesn't provide support for multiple sessions." (org-require-package 'gnuplot) (unless (string= session "none") (save-window-excursion (gnuplot-send-string-to-gnuplot "" "line") gnuplot-buffer))) (defun org-babel-gnuplot-quote-timestamp-field (s) "Convert S from timestamp to Unix time and export to gnuplot." (format-time-string org-babel-gnuplot-timestamp-fmt (org-time-string-to-time s))) (defvar org-table-number-regexp) (defvar org-ts-regexp3) (defun org-babel-gnuplot-quote-tsv-field (s) "Quote S for export to gnuplot." (unless (stringp s) (setq s (format "%s" s))) (if (string-match org-table-number-regexp s) s (if (string-match org-ts-regexp3 s) (org-babel-gnuplot-quote-timestamp-field s) (if (zerop (length s)) (or *org-babel-gnuplot-missing* s) (if (string-match "[ \"]" s) (concat "\"" (mapconcat 'identity (split-string s "\"") "\"\"") "\"") s))))) (defun org-babel-gnuplot-table-to-data (table data-file params) "Export TABLE to DATA-FILE in a format readable by gnuplot. Pass PARAMS through to `orgtbl-to-generic' when exporting TABLE." (require 'ox-org) (require 'ox-ascii) (declare-function org-export-create-backend "ox") (with-temp-file data-file (insert (let ((org-babel-gnuplot-timestamp-fmt (or (plist-get params :timefmt) "%Y-%m-%d-%H:%M:%S")) ;; Create custom limited backend that will disable ;; advanced ASCII export features that may alter the ;; original data. (ob-gnuplot-data (org-export-create-backend :parent 'ascii :transcoders `(;; Do not try to resolve links. Export them verbatim. (link . (lambda (link _ _) (org-element-interpret-data link))) ;; Drop emphasis markers from verbatim and code. ;; This way, data can use verbatim when escaping ;; is necessary and yet be readable by Gnuplot, ;; which is not aware about Org's markup. (verbatim . (lambda (verbatim _ _) (org-element-property :value verbatim))) (code . (lambda (code _ _) (org-element-property :value code))))))) (orgtbl-to-generic table (org-combine-plists `( :sep "\t" :fmt org-babel-gnuplot-quote-tsv-field ;; Two setting below are needed to make :fmt work. :raw t :backend ,ob-gnuplot-data) params))))) data-file) (provide 'ob-gnuplot) ;;; ob-gnuplot.el ends here org-mode-9.7.29+dfsg/lisp/ob-groovy.el000066400000000000000000000103351500430433700174670ustar00rootroot00000000000000;;; ob-groovy.el --- Babel Functions for Groovy -*- lexical-binding: t; -*- ;; Copyright (C) 2013-2025 Free Software Foundation, Inc. ;; Author: Miro Bezjak ;; Maintainer: Palak Mathur ;; Keywords: literate programming, reproducible research ;; URL: https://orgmode.org ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; Currently only supports the external execution. No session support yet. ;;; Requirements: ;; - Groovy language :: https://groovy-lang.org ;; - Groovy major mode :: Can be installed from MELPA or ;; https://github.com/russel/Emacs-Groovy-Mode ;;; Code: (require 'org-macs) (org-assert-version) (require 'ob) (defvar org-babel-tangle-lang-exts) ;; Autoloaded (add-to-list 'org-babel-tangle-lang-exts '("groovy" . "groovy")) (defvar org-babel-default-header-args:groovy '()) (defcustom org-babel-groovy-command "groovy" "Name of the command to use for executing Groovy code. May be either a command in the path, like groovy or an absolute path name, like /usr/local/bin/groovy parameters may be used, like groovy -v" :group 'org-babel :version "24.3" :type 'string) (defun org-babel-execute:groovy (body params) "Execute Groovy BODY according to PARAMS. This function is called by `org-babel-execute-src-block'." (unless noninteractive (message "Executing Groovy source code block")) (let* ((processed-params (org-babel-process-params params)) (session (org-babel-groovy-initiate-session (nth 0 processed-params))) (result-params (nth 2 processed-params)) (result-type (cdr (assq :result-type params))) (full-body (org-babel-expand-body:generic body params)) (result (org-babel-groovy-evaluate session full-body result-type result-params))) (org-babel-reassemble-table result (org-babel-pick-name (cdr (assq :colname-names params)) (cdr (assq :colnames params))) (org-babel-pick-name (cdr (assq :rowname-names params)) (cdr (assq :rownames params)))))) (defvar org-babel-groovy-wrapper-method "class Runner extends Script { def out = new PrintWriter(new ByteArrayOutputStream()) def run() { %s } } println(new Runner().run()) ") (defun org-babel-groovy-evaluate (session body &optional result-type result-params) "Evaluate BODY in external Groovy process. SESSION must be nil as sessions are not yet supported. If RESULT-TYPE equals `output' then return standard output as a string. If RESULT-TYPE equals `value' then return the value of the last statement in BODY as elisp." (when session (error "Sessions are not (yet) supported for Groovy")) (pcase result-type (`output (let ((src-file (org-babel-temp-file "groovy_"))) (progn (with-temp-file src-file (insert body)) (org-babel-eval (concat org-babel-groovy-command " " src-file) "")))) (`value (let* ((src-file (org-babel-temp-file "groovy_")) (wrapper (format org-babel-groovy-wrapper-method body))) (with-temp-file src-file (insert wrapper)) (let ((raw (org-babel-eval (concat org-babel-groovy-command " " src-file) ""))) (org-babel-result-cond result-params raw (org-babel-script-escape raw))))))) (defun org-babel-prep-session:groovy (_session _params) "Prepare SESSION according to the header arguments specified in PARAMS." (error "Sessions are not (yet) supported for Groovy")) (defun org-babel-groovy-initiate-session (&optional _session) "Do nothing. Sessions are not supported in Groovy." nil) (provide 'ob-groovy) ;;; ob-groovy.el ends here org-mode-9.7.29+dfsg/lisp/ob-haskell.el000066400000000000000000000375541500430433700176010ustar00rootroot00000000000000;;; ob-haskell.el --- Babel Functions for Haskell -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2025 Free Software Foundation, Inc. ;; Author: Eric Schulte ;; Maintainer: Lawrence Bottorff ;; Keywords: literate programming, reproducible research ;; URL: https://orgmode.org ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; Org Babel support for evaluating Haskell source code. ;; Haskell programs must be compiled before ;; they can be run, but haskell code can also be run through an ;; interactive interpreter. ;; ;; By default we evaluate using the Haskell interpreter. ;; To use the compiler, specify :compile yes in the header. ;;; Requirements: ;; - haskell-mode: https://www.iro.umontreal.ca/~monnier/elisp/#haskell-mode ;; - inf-haskell: https://www.iro.umontreal.ca/~monnier/elisp/#haskell-mode ;; - (optionally) lhs2tex: https://people.cs.uu.nl/andres/lhs2tex/ ;;; Code: (require 'org-macs) (org-assert-version) (require 'ob) (require 'org-macs) (require 'comint) (declare-function haskell-mode "ext:haskell-mode" ()) (declare-function run-haskell "ext:inf-haskell" (&optional arg)) (declare-function inferior-haskell-load-file "ext:inf-haskell" (&optional reload)) (declare-function org-entry-get "org" (pom property &optional inherit literal-nil)) (defvar org-babel-tangle-lang-exts) (add-to-list 'org-babel-tangle-lang-exts '("haskell" . "hs")) (defvar org-babel-default-header-args:haskell '((:padlines . "no"))) (defvar org-babel-haskell-lhs2tex-command "lhs2tex") (defvar org-babel-haskell-eoe "org-babel-haskell-eoe") (defvar haskell-prompt-regexp) (defcustom org-babel-haskell-compiler "ghc" "Command used to compile a Haskell source code file into an executable. May be either a command in the path, like \"ghc\" or an absolute path name, like \"/usr/local/bin/ghc\". The command can include a parameter, such as \"ghc -v\"." :group 'org-babel :package-version '(Org "9.4") :type 'string) (defconst org-babel-header-args:haskell '((compile . :any)) "Haskell-specific header arguments.") (defun org-babel-haskell-with-session--worker (params todo) "See `org-babel-haskell-with-session'." (let* ((sn (cdr (assq :session params))) (session (org-babel-haskell-initiate-session sn params)) (one-shot (equal sn "none"))) (unwind-protect (funcall todo session) (when (and one-shot (buffer-live-p session)) ;; As we don't control how the session temporary buffer is ;; created, we need to explicitly work around the hooks and ;; query functions. (with-current-buffer session (let ((kill-buffer-query-functions nil) (kill-buffer-hook nil)) (kill-buffer session))))))) (defmacro org-babel-haskell-with-session (session-symbol params &rest body) "Get the session identified by PARAMS and run BODY with it. Get or create a session, as needed to match PARAMS. Assign the session to SESSION-SYMBOL. Execute BODY. Destroy the session if needed. Return the value of the last form of BODY." (declare (indent 2) (debug (symbolp form body))) `(org-babel-haskell-with-session--worker ,params (lambda (,session-symbol) ,@body))) (defun org-babel-haskell-execute (body params) "Execute Haskell BODY according to PARAMS. This function should only be called by `org-babel-execute:haskell'." (let* ((tmp-src-file (org-babel-temp-file "Haskell-src-" ".hs")) (tmp-bin-file (org-babel-process-file-name (org-babel-temp-file "Haskell-bin-" org-babel-exeext))) (cmdline (cdr (assq :cmdline params))) (cmdline (if cmdline (concat " " cmdline) "")) (flags (cdr (assq :flags params))) (flags (mapconcat #'identity (if (listp flags) flags (list flags)) " ")) (libs (org-babel-read (or (cdr (assq :libs params)) (org-entry-get nil "libs" t)) nil)) (libs (mapconcat #'identity (if (listp libs) libs (list libs)) " "))) (with-temp-file tmp-src-file (insert body)) (org-babel-eval (format "%s -o %s %s %s %s" org-babel-haskell-compiler tmp-bin-file flags (org-babel-process-file-name tmp-src-file) libs) "") (let ((results (org-babel-eval (concat tmp-bin-file cmdline) ""))) (when results (setq results (org-trim (org-remove-indentation results))) (org-babel-reassemble-table (org-babel-result-cond (cdr (assq :result-params params)) (org-babel-read results t) (let ((tmp-file (org-babel-temp-file "Haskell-"))) (with-temp-file tmp-file (insert results)) (org-babel-import-elisp-from-file tmp-file))) (org-babel-pick-name (cdr (assq :colname-names params)) (cdr (assq :colnames params))) (org-babel-pick-name (cdr (assq :rowname-names params)) (cdr (assq :rownames params)))))))) (defun org-babel-interpret-haskell (body params) (org-require-package 'inf-haskell "haskell-mode") (add-hook 'inferior-haskell-hook (lambda () (setq-local org-babel-comint-prompt-regexp-old comint-prompt-regexp comint-prompt-regexp (concat haskell-prompt-regexp "\\|^λ?> ")))) (org-babel-haskell-with-session session params (cl-labels ((send-txt-to-ghci (txt) (insert txt) (comint-send-input nil t)) (send-eoe () (send-txt-to-ghci (concat "putStrLn \"" org-babel-haskell-eoe "\"\n"))) (comint-with-output (todo) (let ((comint-preoutput-filter-functions (cons 'ansi-color-filter-apply comint-preoutput-filter-functions))) (org-babel-comint-with-output (session org-babel-haskell-eoe nil nil) (funcall todo))))) (let* ((result-type (cdr (assq :result-type params))) (full-body (org-babel-expand-body:generic body params (org-babel-variable-assignments:haskell params))) (raw (pcase result-type (`output (comint-with-output (lambda () (send-txt-to-ghci (org-trim full-body)) (send-eoe)))) (`value ;; We first compute the value and store it, ;; ignoring any output. (comint-with-output (lambda () (send-txt-to-ghci "__LAST_VALUE_IMPROBABLE_NAME__=()::()\n") (send-txt-to-ghci (org-trim full-body)) (send-txt-to-ghci "__LAST_VALUE_IMPROBABLE_NAME__=it\n") (send-eoe))) ;; We now display and capture the value. (comint-with-output (lambda() (send-txt-to-ghci "__LAST_VALUE_IMPROBABLE_NAME__\n") (send-eoe)))))) (results (mapcar #'org-strip-quotes (cdr (member org-babel-haskell-eoe (reverse (mapcar #'org-trim raw))))))) (org-babel-reassemble-table (let ((result (pcase result-type (`output (mapconcat #'identity (reverse results) "\n")) (`value (car results))))) (org-babel-result-cond (cdr (assq :result-params params)) result (when result (org-babel-script-escape result)))) (org-babel-pick-name (cdr (assq :colname-names params)) (cdr (assq :colname-names params))) (org-babel-pick-name (cdr (assq :rowname-names params)) (cdr (assq :rowname-names params)))))))) (defun org-babel-execute:haskell (body params) "Execute a block of Haskell code." (let ((compile (string= "yes" (cdr (assq :compile params))))) (if (not compile) (org-babel-interpret-haskell body params) (org-babel-haskell-execute body params)))) ;; Variable defined in inf-haskell (haskell-mode package). (defvar inferior-haskell-buffer) (defvar inferior-haskell-root-dir) (defun org-babel-haskell-initiate-session (&optional session-name _params) "Initiate a haskell session. Return the initialized session, i.e. the buffer for this session. When SESSION-NAME is nil, use a global session named \"*ob-haskell*\". When SESSION-NAME is the string \"none\", use a temporary buffer. Else, (re)use the session named SESSION-NAME. The buffer name is the session name. See also `org-babel-haskell-with-session'." (org-require-package 'inf-haskell "haskell-mode") (cond ((equal "none" session-name) ;; Temporary buffer name. (setq session-name (generate-new-buffer-name " *ob-haskell-tmp*"))) ((eq nil session-name) ;; The global default session. As haskell-mode is using the buffer ;; named "*haskell*", we stay away from it. (setq session-name "*ob-haskell*")) ((not (stringp session-name)) (error "session-name must be a string"))) (let ((session (get-buffer session-name))) ;; NOTE: By construction, as SESSION-NAME is a string, session is ;; either nil or a live buffer. (save-window-excursion (or (org-babel-comint-buffer-livep session) (let ((inferior-haskell-buffer session)) ;; As inferior-haskell expects the buffer to be named ;; "*haskell*", we temporarily rename it while executing ;; `run-haskell' (unless the user explicitly requested to ;; use the name "*haskell*"). (when (not (equal "*haskell*" session-name)) (when (bufferp session) (when (bufferp "*haskell*") (user-error "Conflicting buffer '*haskell*', rename it or kill it")) (with-current-buffer session (rename-buffer "*haskell*")))) (unwind-protect (let ((inferior-haskell-root-dir default-directory)) (run-haskell) (sleep-for 0.25) (setq session inferior-haskell-buffer)) (when (and (not (equal "*haskell*" session-name)) (bufferp session)) (with-current-buffer session (rename-buffer session-name)))) ;; Disable secondary prompt: If we do not do this, ;; org-comint may treat secondary prompts as a part of ;; output. (org-babel-comint-input-command session ":set prompt-cont \"\"") session) )) session)) (defun org-babel-load-session:haskell (session body params) "Load BODY into SESSION." (save-window-excursion (let* ((buffer (org-babel-prep-session:haskell session params)) (load-file (concat (org-babel-temp-file "haskell-load-") ".hs"))) (with-temp-buffer (insert body) (write-file load-file) (haskell-mode) (inferior-haskell-load-file)) buffer))) (defun org-babel-prep-session:haskell (session params) "Prepare SESSION according to the header arguments in PARAMS." (save-window-excursion (let ((buffer (org-babel-haskell-initiate-session session))) (org-babel-comint-in-buffer buffer (mapc (lambda (line) (insert line) (comint-send-input nil t)) (org-babel-variable-assignments:haskell params))) (current-buffer)))) (defun org-babel-variable-assignments:haskell (params) "Return list of haskell statements assigning the block's variables." (mapcar (lambda (pair) (format "let %s = %s" (car pair) (org-babel-haskell-var-to-haskell (cdr pair)))) (org-babel--get-vars params))) (defun org-babel-haskell-var-to-haskell (var) "Convert an elisp value VAR into a haskell variable. The elisp VAR is converted to a string of haskell source code specifying a variable of the same value." (if (listp var) (concat "[" (mapconcat #'org-babel-haskell-var-to-haskell var ", ") "]") (format "%S" var))) (defvar org-export-copy-to-kill-ring) (declare-function org-export-to-file "ox" (backend file &optional async subtreep visible-only body-only ext-plist post-process)) (defun org-babel-haskell-export-to-lhs (&optional arg) "Export to a .lhs file with all haskell code blocks escaped. When called with a prefix argument the resulting .lhs file will be exported to a .tex file. This function will create two new files, base-name.lhs and base-name.tex where base-name is the name of the current Org file. Note that all standard Babel literate programming constructs (header arguments, no-web syntax etc...) are ignored." (interactive "P") (let* ((contents (buffer-string)) (haskell-regexp (concat "^\\([ \t]*\\)#\\+begin_src[ \t]haskell*\\(.*\\)[\r\n]" "\\(\\(?:.\\|\n\\)*?\\)[\r\n][ \t]*#\\+end_src.*")) (base-name (file-name-sans-extension (buffer-file-name))) (tmp-file (org-babel-temp-file "haskell-")) (tmp-org-file (concat tmp-file ".org")) (tmp-tex-file (concat tmp-file ".tex")) (lhs-file (concat base-name ".lhs")) (tex-file (concat base-name ".tex")) (command (concat org-babel-haskell-lhs2tex-command " " (org-babel-process-file-name lhs-file) " > " (org-babel-process-file-name tex-file))) (preserve-indentp org-src-preserve-indentation) indentation) ;; escape haskell source-code blocks (with-temp-file tmp-org-file (insert contents) (goto-char (point-min)) (while (re-search-forward haskell-regexp nil t) (save-match-data (setq indentation (length (match-string 1)))) (replace-match (save-match-data (concat "#+begin_export latex\n\\begin{code}\n" (if (or preserve-indentp (string-match "-i" (match-string 2))) (match-string 3) (org-remove-indentation (match-string 3))) "\n\\end{code}\n#+end_export\n")) t t) (indent-code-rigidly (match-beginning 0) (match-end 0) indentation))) (save-excursion (unwind-protect (with-temp-buffer ;; Export to latex w/org and save as .lhs (require 'ox-latex) (insert-file-contents tmp-org-file) ;; Ensure we do not clutter kill ring with incomplete results. (let (org-export-copy-to-kill-ring) (org-export-to-file 'latex tmp-tex-file))) (delete-file tmp-org-file)) (unwind-protect (with-temp-buffer (insert-file-contents tmp-tex-file) (goto-char (point-min)) (forward-line 2) (insert "%include polycode.fmt\n") ;; ensure all \begin/end{code} statements start at the first column (while (re-search-forward "^[ \t]+\\\\begin{code}\\(?:.\\|\n\\)+\\\\end{code}" nil t) (replace-match (save-match-data (org-remove-indentation (match-string 0))) t t)) ;; save org exported latex to a .lhs file (write-region nil nil lhs-file)) (delete-file tmp-tex-file))) (if (not arg) (find-file lhs-file) ;; process .lhs file with lhs2tex (message "running %s" command) (shell-command command) (find-file tex-file)))) (provide 'ob-haskell) ;;; ob-haskell.el ends here org-mode-9.7.29+dfsg/lisp/ob-java.el000066400000000000000000000504501500430433700170650ustar00rootroot00000000000000;;; ob-java.el --- org-babel functions for java evaluation -*- lexical-binding: t -*- ;; Copyright (C) 2011-2025 Free Software Foundation, Inc. ;; Authors: Eric Schulte ;; Dan Davison ;; Maintainer: Ian Martins ;; Keywords: literate programming, reproducible research ;; URL: https://orgmode.org ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; Org-Babel support for evaluating java source code. ;;; Code: (require 'org-macs) (org-assert-version) (require 'ob) (defvar org-babel-tangle-lang-exts) (add-to-list 'org-babel-tangle-lang-exts '("java" . "java")) (defvar org-babel-temporary-directory) ; from ob-core (defvar org-babel-default-header-args:java '((:results . "output") (:dir . ".")) "Default header args for java source blocks. The docs say functional mode should be the default [1], but ob-java didn't originally support functional mode, so we keep scripting mode as the default for now to maintain previous behavior. Most languages write tempfiles to babel's temporary directory, but ob-java originally had to write them to the current directory, so we keep that as the default behavior. [1] https://orgmode.org/manual/Results-of-Evaluation.html") (defconst org-babel-header-args:java '((dir . :any) (classname . :any) (imports . :any) (cmpflag . :any) (cmdline . :any) (cmdarg . :any)) "Java-specific header arguments.") (defcustom org-babel-java-command "java" "Name of the java command. May be either a command in the path, like java or an absolute path name, like /usr/local/bin/java. Parameters may be used, like java -verbose." :group 'org-babel :package-version '(Org . "9.5") :type 'string) (defcustom org-babel-java-compiler "javac" "Name of the java compiler. May be either a command in the path, like javac or an absolute path name, like /usr/local/bin/javac. Parameters may be used, like javac -verbose." :group 'org-babel :package-version '(Org . "9.5") :type 'string) (defcustom org-babel-java-hline-to "null" "Replace hlines in incoming tables with this when translating to java." :group 'org-babel :package-version '(Org . "9.5") :type 'string) (defcustom org-babel-java-null-to 'hline "Replace `null' in java tables with this before returning." :group 'org-babel :package-version '(Org . "9.5") :type 'symbol) (defconst org-babel-java--package-re (rx line-start (0+ space) "package" (1+ space) (group (1+ (in alnum ?_ ?.))) ; capture the package name (0+ space) ?\; line-end) "Regexp for the package statement.") (defconst org-babel-java--imports-re (rx line-start (0+ space) "import" (opt (1+ space) "static") (1+ space) (group (1+ (in alnum ?_ ?. ?*))) ; capture the fully qualified class name (0+ space) ?\; line-end) "Regexp for import statements.") (defconst org-babel-java--class-re (rx line-start (0+ space) (opt (seq "public" (1+ space))) "class" (1+ space) (group (1+ (in alnum ?_))) ; capture the class name (0+ space) ?{) "Regexp for the class declaration.") (defconst org-babel-java--main-re (rx line-start (0+ space) "public" (1+ space) "static" (1+ space) "void" (1+ space) "main" (0+ space) ?\( (0+ space) "String" (1+ (in alnum ?_ ?\[ ?\] space)) ; "[] args" or "args[]" ?\) (0+ space) (opt "throws" (1+ (in alnum ?_ ?, ?. space))) ?{) "Regexp for the main method declaration.") (defconst org-babel-java--any-method-re (rx line-start (0+ space) (opt (seq (1+ alnum) (1+ space))) ; visibility (opt (seq "static" (1+ space))) ; binding (1+ (in alnum ?_ ?\[ ?\])) ; return type (1+ space) (1+ (in alnum ?_)) ; method name (0+ space) ?\( (0+ (in alnum ?_ ?\[ ?\] ?, space)) ; params ?\) (0+ space) (opt "throws" (1+ (in alnum ?_ ?, ?. space))) ?{) "Regexp for any method.") (defconst org-babel-java--result-wrapper "\n public static String __toString(Object val) { if (val instanceof String) { return \"\\\"\" + val + \"\\\"\"; } else if (val == null) { return \"null\"; } else if (val.getClass().isArray()) { StringBuffer sb = new StringBuffer(); Object[] vals = (Object[])val; sb.append(\"[\"); for (int ii=0; ii
" str contents))))) ;;;; Src Block (defun org-html-src-block (src-block _contents info) "Transcode a SRC-BLOCK element from Org to HTML. CONTENTS holds the contents of the item. INFO is a plist holding contextual information." (if (org-export-read-attribute :attr_html src-block :textarea) (org-html--textarea-block src-block) (let* ((lang (org-element-property :language src-block)) (code (org-html-format-code src-block info)) (label (let ((lbl (org-html--reference src-block info t))) (if lbl (format " id=\"%s\"" lbl) ""))) (klipsify (and (plist-get info :html-klipsify-src) (member lang '("javascript" "js" "ruby" "scheme" "clojure" "php" "html"))))) (format "
\n%s%s\n
" ;; Build caption. (let ((caption (org-export-get-caption src-block))) (if (not caption) "" (let ((listing-number (format "%s " (format (org-html--translate "Listing %d:" info) (org-export-get-ordinal src-block info nil #'org-html--has-caption-p))))) (format "" listing-number (org-trim (org-export-data caption info)))))) ;; Contents. (if klipsify (format "
%s
" lang ; lang being nil is OK. label (if (string= lang "html") " data-editor-type=\"html\"" "") code) (format "
%s
" ;; Lang being nil is OK. lang label code)))))) ;;;; Statistics Cookie (defun org-html-statistics-cookie (statistics-cookie _contents _info) "Transcode a STATISTICS-COOKIE object from Org to HTML. CONTENTS is nil. INFO is a plist holding contextual information." (let ((cookie-value (org-element-property :value statistics-cookie))) (format "%s" cookie-value))) ;;;; Strike-Through (defun org-html-strike-through (_strike-through contents info) "Transcode STRIKE-THROUGH from Org to HTML. CONTENTS is the text with strike-through markup. INFO is a plist holding contextual information." (format (or (cdr (assq 'strike-through (plist-get info :html-text-markup-alist))) "%s") contents)) ;;;; Subscript (defun org-html-subscript (_subscript contents _info) "Transcode a SUBSCRIPT object from Org to HTML. CONTENTS is the contents of the object. INFO is a plist holding contextual information." (format "%s" contents)) ;;;; Superscript (defun org-html-superscript (_superscript contents _info) "Transcode a SUPERSCRIPT object from Org to HTML. CONTENTS is the contents of the object. INFO is a plist holding contextual information." (format "%s" contents)) ;;;; Table Cell (defun org-html-table-cell (table-cell contents info) "Transcode a TABLE-CELL element from Org to HTML. CONTENTS is nil. INFO is a plist used as a communication channel." (let* ((table-row (org-element-parent table-cell)) (table (org-element-lineage table-cell 'table)) (cell-attrs (if (not (plist-get info :html-table-align-individual-fields)) "" (format (if (and (boundp 'org-html-format-table-no-css) org-html-format-table-no-css) " align=\"%s\"" " class=\"org-%s\"") (org-export-table-cell-alignment table-cell info))))) (when (or (not contents) (string= "" (org-trim contents))) (setq contents " ")) (cond ((and (org-export-table-has-header-p table info) (= 1 (org-export-table-row-group table-row info))) (let ((header-tags (plist-get info :html-table-header-tags))) (concat "\n" (format (car header-tags) "col" cell-attrs) contents (cdr header-tags)))) ((and (plist-get info :html-table-use-header-tags-for-first-column) (zerop (cdr (org-export-table-cell-address table-cell info)))) (let ((header-tags (plist-get info :html-table-header-tags))) (concat "\n" (format (car header-tags) "row" cell-attrs) contents (cdr header-tags)))) (t (let ((data-tags (plist-get info :html-table-data-tags))) (concat "\n" (format (car data-tags) cell-attrs) contents (cdr data-tags))))))) ;;;; Table Row (defun org-html-table-row (table-row contents info) "Transcode a TABLE-ROW element from Org to HTML. CONTENTS is the contents of the row. INFO is a plist used as a communication channel." ;; Rules are ignored since table separators are deduced from ;; borders of the current row. (when (eq (org-element-property :type table-row) 'standard) (let* ((group (org-export-table-row-group table-row info)) (number (org-export-table-row-number table-row info)) (start-group-p (org-export-table-row-starts-rowgroup-p table-row info)) (end-group-p (org-export-table-row-ends-rowgroup-p table-row info)) (topp (and (equal start-group-p '(top)) (equal end-group-p '(below top)))) (bottomp (and (equal start-group-p '(above)) (equal end-group-p '(bottom above)))) (row-open-tag (pcase (plist-get info :html-table-row-open-tag) ((and accessor (pred functionp)) (funcall accessor number group start-group-p end-group-p topp bottomp)) (accessor accessor))) (row-close-tag (pcase (plist-get info :html-table-row-close-tag) ((and accessor (pred functionp)) (funcall accessor number group start-group-p end-group-p topp bottomp)) (accessor accessor))) (group-tags (cond ;; Row belongs to second or subsequent groups. ((not (= 1 group)) '("" . "\n")) ;; Row is from first group. Table has >=1 groups. ((org-export-table-has-header-p (org-element-lineage table-row 'table) info) '("" . "\n")) ;; Row is from first and only group. (t '("" . "\n"))))) (concat (and start-group-p (car group-tags)) (concat "\n" row-open-tag contents "\n" row-close-tag) (and end-group-p (cdr group-tags)))))) ;;;; Table (defun org-html-table-first-row-data-cells (table info) "Transcode the first row of TABLE. INFO is a plist used as a communication channel." (let ((table-row (org-element-map table 'table-row (lambda (row) (unless (eq (org-element-property :type row) 'rule) row)) info 'first-match)) (special-column-p (org-export-table-has-special-column-p table))) (if (not special-column-p) (org-element-contents table-row) (cdr (org-element-contents table-row))))) (defun org-html-table--table.el-table (table _info) "Format table.el TABLE into HTML. INFO is a plist used as a communication channel." (when (eq (org-element-property :type table) 'table.el) (require 'table) (let ((outbuf (with-current-buffer (get-buffer-create "*org-export-table*") (erase-buffer) (current-buffer)))) (with-temp-buffer (insert (org-element-property :value table)) (goto-char 1) (re-search-forward "^[ \t]*|[^|]" nil t) (table-generate-source 'html outbuf)) (with-current-buffer outbuf (prog1 (org-trim (buffer-string)) (kill-buffer) ))))) (defun org-html-table (table contents info) "Transcode a TABLE element from Org to HTML. CONTENTS is the contents of the table. INFO is a plist holding contextual information." (if (eq (org-element-property :type table) 'table.el) ;; "table.el" table. Convert it using appropriate tools. (org-html-table--table.el-table table info) ;; Standard table. (let* ((caption (org-export-get-caption table)) (number (org-export-get-ordinal table info nil #'org-html--has-caption-p)) (attributes (org-html--make-attribute-string (org-combine-plists (list :id (org-html--reference table info t)) (and (not (org-html-html5-p info)) (plist-get info :html-table-attributes)) (org-export-read-attribute :attr_html table)))) (alignspec (if (bound-and-true-p org-html-format-table-no-css) "align=\"%s\"" "class=\"org-%s\"")) (table-column-specs (lambda (table info) (mapconcat (lambda (table-cell) (let ((alignment (org-export-table-cell-alignment table-cell info))) (concat ;; Begin a colgroup? (when (org-export-table-cell-starts-colgroup-p table-cell info) "\n") ;; Add a column. Also specify its alignment. (format "\n%s" (org-html-close-tag "col" (concat " " (format alignspec alignment)) info)) ;; End a colgroup? (when (org-export-table-cell-ends-colgroup-p table-cell info) "\n")))) (org-html-table-first-row-data-cells table info) "\n")))) (format "\n%s\n%s\n%s" (if (equal attributes "") "" (concat " " attributes)) (if (not caption) "" (format (if (plist-get info :html-table-caption-above) "%s" "%s") (concat "" (format (org-html--translate "Table %d:" info) number) " " (org-export-data caption info)))) (funcall table-column-specs table info) contents)))) ;;;; Target (defun org-html-target (target _contents info) "Transcode a TARGET object from Org to HTML. CONTENTS is nil. INFO is a plist holding contextual information." (let ((ref (org-html--reference target info))) (org-html--anchor ref nil nil info))) ;;;; Timestamp (defun org-html-timestamp (timestamp _contents info) "Transcode a TIMESTAMP object from Org to HTML. CONTENTS is nil. INFO is a plist holding contextual information." (let ((value (org-html-plain-text (org-timestamp-translate timestamp) info))) (format "%s" (replace-regexp-in-string "--" "–" value)))) ;;;; Underline (defun org-html-underline (_underline contents info) "Transcode UNDERLINE from Org to HTML. CONTENTS is the text with underline markup. INFO is a plist holding contextual information." (format (or (cdr (assq 'underline (plist-get info :html-text-markup-alist))) "%s") contents)) ;;;; Verbatim (defun org-html-verbatim (verbatim _contents info) "Transcode VERBATIM from Org to HTML. CONTENTS is nil. INFO is a plist holding contextual information." (format (or (cdr (assq 'verbatim (plist-get info :html-text-markup-alist))) "%s") (org-html-encode-plain-text (org-element-property :value verbatim)))) ;;;; Verse Block (defun org-html-verse-block (_verse-block contents info) "Transcode a VERSE-BLOCK element from Org to HTML. CONTENTS is verse block contents. INFO is a plist holding contextual information." (format "

\n%s

" ;; Replace leading white spaces with non-breaking spaces. (replace-regexp-in-string "^[ \t]+" (lambda (m) (org-html--make-string (length m) " ")) ;; Replace each newline character with line break. Also ;; remove any trailing "br" close-tag so as to avoid ;; duplicates. (let* ((br (org-html-close-tag "br" nil info)) (re (format "\\(?:%s\\)?[ \t]*\n" (regexp-quote br)))) (replace-regexp-in-string re (concat br "\n") contents))))) ;;; Filter Functions (defun org-html-final-function (contents _backend info) "Filter to indent the HTML and convert HTML entities. CONTENTS is the exported HTML code. INFO is the info plist." (with-temp-buffer (insert contents) (delay-mode-hooks (set-auto-mode t)) (when (plist-get info :html-indent) (indent-region (point-min) (point-max))) (buffer-substring-no-properties (point-min) (point-max)))) ;;; End-user functions ;;;###autoload (defun org-html-export-as-html (&optional async subtreep visible-only body-only ext-plist) "Export current buffer to an HTML buffer. If narrowing is active in the current buffer, only export its narrowed part. If a region is active, export that region. A non-nil optional argument ASYNC means the process should happen asynchronously. The resulting buffer should be accessible through the `org-export-stack' interface. When optional argument SUBTREEP is non-nil, export the sub-tree at point, extracting information from the headline properties first. When optional argument VISIBLE-ONLY is non-nil, don't export contents of hidden elements. When optional argument BODY-ONLY is non-nil, only write code between \"\" and \"\" tags. EXT-PLIST, when provided, is a property list with external parameters overriding Org default settings, but still inferior to file-local settings. Export is done in a buffer named \"*Org HTML Export*\", which will be displayed when `org-export-show-temporary-export-buffer' is non-nil." (interactive) (org-export-to-buffer 'html "*Org HTML Export*" async subtreep visible-only body-only ext-plist (lambda () (set-auto-mode t)))) ;;;###autoload (defun org-html-convert-region-to-html () "Assume the current region has Org syntax, and convert it to HTML. This can be used in any buffer. For example, you can write an itemized list in Org syntax in an HTML buffer and use this command to convert it." (interactive) (org-export-replace-region-by 'html)) (defalias 'org-export-region-to-html #'org-html-convert-region-to-html) ;;;###autoload (defun org-html-export-to-html (&optional async subtreep visible-only body-only ext-plist) "Export current buffer to a HTML file. If narrowing is active in the current buffer, only export its narrowed part. If a region is active, export that region. A non-nil optional argument ASYNC means the process should happen asynchronously. The resulting file should be accessible through the `org-export-stack' interface. When optional argument SUBTREEP is non-nil, export the sub-tree at point, extracting information from the headline properties first. When optional argument VISIBLE-ONLY is non-nil, don't export contents of hidden elements. When optional argument BODY-ONLY is non-nil, only write code between \"\" and \"\" tags. EXT-PLIST, when provided, is a property list with external parameters overriding Org default settings, but still inferior to file-local settings. Return output file's name." (interactive) (let* ((extension (concat (when (> (length org-html-extension) 0) ".") (or (plist-get ext-plist :html-extension) org-html-extension "html"))) (file (org-export-output-file-name extension subtreep)) (org-export-coding-system org-html-coding-system)) (org-export-to-file 'html file async subtreep visible-only body-only ext-plist))) ;;;###autoload (defun org-html-publish-to-html (plist filename pub-dir) "Publish an org file to HTML. FILENAME is the filename of the Org file to be published. PLIST is the property list for the given project. PUB-DIR is the publishing directory. Return output file name." (org-publish-org-to 'html filename (concat (when (> (length org-html-extension) 0) ".") (or (plist-get plist :html-extension) org-html-extension "html")) plist pub-dir)) (provide 'ox-html) ;; Local variables: ;; generated-autoload-file: "org-loaddefs.el" ;; End: ;;; ox-html.el ends here org-mode-9.7.29+dfsg/lisp/ox-icalendar.el000066400000000000000000001372461500430433700201250ustar00rootroot00000000000000;;; ox-icalendar.el --- iCalendar Backend for Org Export Engine -*- lexical-binding: t; -*- ;; Copyright (C) 2004-2025 Free Software Foundation, Inc. ;; Author: Carsten Dominik ;; Nicolas Goaziou ;; Maintainer: Jack Kamm ;; Keywords: outlines, hypermedia, calendar, text ;; URL: https://orgmode.org ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; ;; This library implements an iCalendar backend for Org generic ;; exporter. See Org manual for more information. ;; ;; It is expected to conform to RFC 5545. ;;; Code: (require 'org-macs) (org-assert-version) (require 'cl-lib) (require 'org-agenda) (require 'ox-ascii) (declare-function org-bbdb-anniv-export-ical "ol-bbdb" nil) (declare-function org-at-heading-p "org" (&optional _)) (declare-function org-back-to-heading "org" (&optional invisible-ok)) (declare-function org-next-visible-heading "org" (arg)) ;;; User-Configurable Variables (defgroup org-export-icalendar nil "Options specific for iCalendar export backend." :tag "Org Export iCalendar" :group 'org-export) (defcustom org-icalendar-combined-agenda-file "~/org.ics" "The file name for the iCalendar file covering all agenda files. This file is created with the command `\\[org-icalendar-combine-agenda-files]'. The file name should be absolute. It will be overwritten without warning." :group 'org-export-icalendar :type 'file) (defcustom org-icalendar-alarm-time 0 "Number of minutes for triggering an alarm for exported timed events. A zero value (the default) turns off the definition of an alarm trigger for timed events. If non-zero, alarms are created. - a single alarm per entry is defined - The alarm will go off N minutes before the event - only a DISPLAY action is defined." :group 'org-export-icalendar :version "24.1" :type 'integer) (defcustom org-icalendar-force-alarm nil "Non-nil means alarm will be created even if is set to zero. This overrides default behavior where zero means no alarm. With this set to non-nil and alarm set to zero, alarm will be created and will fire at the event start." :group 'org-export-icalendar :type 'boolean :package-version '(Org . "9.6") :safe #'booleanp) (defcustom org-icalendar-combined-name "OrgMode" "Calendar name for the combined iCalendar representing all agenda files." :group 'org-export-icalendar :type 'string) (defcustom org-icalendar-combined-description "" "Calendar description for the combined iCalendar (all agenda files)." :group 'org-export-icalendar :type 'string) (defcustom org-icalendar-exclude-tags nil "Tags that exclude a tree from export. This variable allows specifying different exclude tags from other backends. It can also be set with the ICALENDAR_EXCLUDE_TAGS keyword." :group 'org-export-icalendar :type '(repeat (string :tag "Tag"))) (defcustom org-icalendar-scheduled-summary-prefix "S: " "String prepended to exported scheduled headlines." :group 'org-export-icalendar :type 'string :package-version '(Org . "9.6") :safe #'stringp) (defcustom org-icalendar-deadline-summary-prefix "DL: " "String prepended to exported headlines with a deadline." :group 'org-export-icalendar :type 'string :package-version '(Org . "9.6") :safe #'stringp) (defcustom org-icalendar-use-deadline '(event-if-not-todo todo-due) "Contexts where iCalendar export should use a deadline time stamp. This is a list with possibly several symbols in it. Valid symbols are: `event-if-todo' Deadlines in TODO entries become calendar events. `event-if-todo-not-done' Deadlines in TODO entries with not-DONE state become events. `event-if-not-todo' Deadlines in non-TODO entries become calendar events. `todo-due' Use deadlines in TODO entries as due-dates." :group 'org-export-icalendar :type '(set :greedy t (const :tag "DEADLINE in non-TODO entries become events" event-if-not-todo) (const :tag "DEADLINE in TODO entries become events" event-if-todo) (const :tag "DEADLINE in TODO entries with not-DONE state become events" event-if-todo-not-done) (const :tag "DEADLINE in TODO entries become due-dates" todo-due))) (defcustom org-icalendar-use-scheduled '(todo-start) "Contexts where iCalendar export should use a scheduling time stamp. This is a list with possibly several symbols in it. Valid symbols are: `event-if-todo' Scheduling time stamps in TODO entries become an event. `event-if-todo-not-done' Scheduling time stamps in TODO entries with not-DONE state become events. `event-if-not-todo' Scheduling time stamps in non-TODO entries become an event. `todo-start' Scheduling time stamps in TODO entries become start date. (See also `org-icalendar-todo-unscheduled-start', which controls the start date for TODO entries without a scheduling time stamp)" :group 'org-export-icalendar :type '(set :greedy t (const :tag "SCHEDULED timestamps in non-TODO entries become events" event-if-not-todo) (const :tag "SCHEDULED timestamps in TODO entries become events" event-if-todo) (const :tag "SCHEDULED in TODO entries with not-DONE state become events" event-if-todo-not-done) (const :tag "SCHEDULED in TODO entries become start date" todo-start))) (defcustom org-icalendar-categories '(local-tags category) "Items that should be entered into the \"categories\" field. This is a list of symbols, the following are valid: `category' The Org mode category of the current file or tree `todo-state' The todo state, if any `local-tags' The tags, defined in the current line `all-tags' All tags, including inherited ones." :group 'org-export-icalendar :type '(repeat (choice (const :tag "The file or tree category" category) (const :tag "The TODO state" todo-state) (const :tag "Tags defined in current line" local-tags) (const :tag "All tags, including inherited ones" all-tags)))) (defcustom org-icalendar-with-timestamps 'active "Non-nil means make an event from plain time stamps. It can be set to `active', `inactive', t or nil, in order to make an event from, respectively, only active timestamps, only inactive ones, all of them or none. This variable has precedence over `org-export-with-timestamps'. It can also be set with the #+OPTIONS line, e.g. \"<:t\"." :group 'org-export-icalendar :type '(choice (const :tag "All timestamps" t) (const :tag "Only active timestamps" active) (const :tag "Only inactive timestamps" inactive) (const :tag "No timestamp" nil))) (defcustom org-icalendar-include-todo nil "Non-nil means create VTODO components from TODO items. Valid values are: nil don't include any task. t include tasks that are not in DONE state. `unblocked' include all TODO items that are not blocked. `all' include both done and not done items. \\(\"TODO\" ...) include specific TODO keywords." :group 'org-export-icalendar :type '(choice (const :tag "None" nil) (const :tag "Unfinished" t) (const :tag "Unblocked" unblocked) (const :tag "All" all) (repeat :tag "Specific TODO keywords" (string :tag "Keyword")))) (defcustom org-icalendar-todo-unscheduled-start 'recurring-deadline-warning "Exported start date of unscheduled TODOs. If `org-icalendar-use-scheduled' contains `todo-start' and a task has a \"SCHEDULED\" timestamp, that is always used as the start date. Otherwise, this variable controls whether a start date is exported and what its value is. Note that the iCalendar spec RFC 5545 does not generally require tasks to have a start date, except for repeating tasks which do require a start date. However some iCalendar programs ignore the requirement for repeating tasks, and allow repeating deadlines without a matching start date. This variable has no effect when `org-icalendar-include-todo' is nil. Valid values are: `recurring-deadline-warning' If deadline repeater present, use `org-deadline-warning-days' as start. `deadline-warning' If deadline present, use `org-deadline-warning-days' as start. `current-datetime' Use the current date-time as start. nil Never add a start time for unscheduled tasks." :group 'org-export-icalendar :type '(choice (const :tag "Warning days if deadline recurring" recurring-deadline-warning) (const :tag "Warning days if deadline present" deadline-warning) (const :tag "Now" current-datetime) (const :tag "No start date" nil)) :package-version '(Org . "9.7") :safe #'symbolp) (defcustom org-icalendar-include-bbdb-anniversaries nil "Non-nil means a combined iCalendar file should include anniversaries. The anniversaries are defined in the BBDB database." :group 'org-export-icalendar :type 'boolean) (defcustom org-icalendar-include-sexps t "Non-nil means export to iCalendar files should also cover sexp entries. These are entries like in the diary, but directly in an Org file." :group 'org-export-icalendar :type 'boolean) (defcustom org-icalendar-include-body t "Amount of text below headline to be included in iCalendar export. This is a number of characters that should maximally be included. Properties, scheduling and clocking lines will always be removed. The text will be inserted into the DESCRIPTION field." :group 'org-export-icalendar :type '(choice (const :tag "Nothing" nil) (const :tag "Everything" t) (integer :tag "Max characters"))) (defcustom org-icalendar-store-UID nil "Non-nil means store any created UIDs in properties. The iCalendar standard requires that all entries have a unique identifier. Org will create these identifiers as needed. When this variable is non-nil, the created UIDs will be stored in the ID property of the entry. Then the next time this entry is exported, it will be exported with the same UID, superseding the previous form of it. This is essential for synchronization services. This variable is not turned on by default because we want to avoid creating a property drawer in every entry if people are only playing with this feature, or if they are only using it locally." :group 'org-export-icalendar :type 'boolean) (defcustom org-icalendar-timezone (getenv "TZ") "The time zone string for iCalendar export. When nil or the empty string, use output from (current-time-zone)." :group 'org-export-icalendar :type '(choice (const :tag "Unspecified" nil) (string :tag "Time zone"))) (defcustom org-icalendar-date-time-format ":%Y%m%dT%H%M%S" "Format-string for exporting icalendar DATE-TIME. See `format-time-string' for a full documentation. The only difference is that `org-icalendar-timezone' is used for %Z. Interesting value are: - \":%Y%m%dT%H%M%S\" for local time - \";TZID=%Z:%Y%m%dT%H%M%S\" for local time with explicit timezone - \":%Y%m%dT%H%M%SZ\" for time expressed in Universal Time" :group 'org-export-icalendar :version "24.1" :type '(choice (const :tag "Local time" ":%Y%m%dT%H%M%S") (const :tag "Explicit local time" ";TZID=%Z:%Y%m%dT%H%M%S") (const :tag "Universal time" ":%Y%m%dT%H%M%SZ") (string :tag "Explicit format"))) (defcustom org-icalendar-ttl nil "Time to live for the exported calendar. Subscribing clients to the exported ics file can derive the time interval to read the file again from the server. One example of such client is Nextcloud calendar, which respects the setting of X-PUBLISHED-TTL in ICS files. Setting `org-icalendar-ttl' to \"PT1H\" would advise a server to reload the file every hour. See https://icalendar.org/iCalendar-RFC-5545/3-8-2-5-duration.html for a complete description of possible specifications of this option. For example, \"PT1H\" stands for 1 hour and \"PT0H27M34S\" stands for 0 hours, 27 minutes and 34 seconds. The default value is nil, which means no such option is set in the ICS file. This option can also be set on a per-document basis with the ICAL-TTL export keyword." :group 'org-export-icalendar :type '(choice (const :tag "No refresh period" nil) (const :tag "One hour" "PT1H") (const :tag "One day" "PT1D") (const :tag "One week" "PT7D") (string :tag "Other")) :package-version '(Org . "9.7")) (defvar org-icalendar-after-save-hook nil "Hook run after an iCalendar file has been saved. This hook is run with the name of the file as argument. A good way to use this is to tell a desktop calendar application to re-read the iCalendar file.") ;;; Define Backend (org-export-define-derived-backend 'icalendar 'ascii :translate-alist '((clock . nil) (footnote-definition . nil) (footnote-reference . nil) (headline . org-icalendar-entry) (inner-template . org-icalendar-inner-template) (inlinetask . nil) (planning . nil) (section . nil) (template . org-icalendar-template)) :options-alist '((:exclude-tags "ICALENDAR_EXCLUDE_TAGS" nil org-icalendar-exclude-tags split) (:with-timestamps nil "<" org-icalendar-with-timestamps) ;; Other variables. (:icalendar-alarm-time nil nil org-icalendar-alarm-time) (:icalendar-categories nil nil org-icalendar-categories) (:icalendar-date-time-format nil nil org-icalendar-date-time-format) (:icalendar-include-bbdb-anniversaries nil nil org-icalendar-include-bbdb-anniversaries) (:icalendar-include-body nil nil org-icalendar-include-body) (:icalendar-include-sexps nil nil org-icalendar-include-sexps) (:icalendar-include-todo nil nil org-icalendar-include-todo) (:icalendar-store-UID nil nil org-icalendar-store-UID) (:icalendar-timezone nil nil org-icalendar-timezone) (:icalendar-use-deadline nil nil org-icalendar-use-deadline) (:icalendar-use-scheduled nil nil org-icalendar-use-scheduled) (:icalendar-scheduled-summary-prefix nil nil org-icalendar-scheduled-summary-prefix) (:icalendar-deadline-summary-prefix nil nil org-icalendar-deadline-summary-prefix) (:icalendar-ttl "ICAL-TTL" nil org-icalendar-ttl)) :filters-alist '((:filter-headline . org-icalendar-clear-blank-lines)) :menu-entry '(?c "Export to iCalendar" ((?f "Current file" org-icalendar-export-to-ics) (?a "All agenda files" (lambda (a s v b) (org-icalendar-export-agenda-files a))) (?c "Combine all agenda files" (lambda (a s v b) (org-icalendar-combine-agenda-files a)))))) ;;; Internal Functions (defun org-icalendar-create-uid (file &optional bell) "Set ID property on headlines missing it in FILE. When optional argument BELL is non-nil, inform the user with a message if the file was modified." (let (modified-flag) (org-map-entries (lambda () (let ((entry (org-element-at-point))) (unless (org-element-property :ID entry) (org-id-get-create) (setq modified-flag t) (forward-line)))) nil nil 'comment) (when (and bell modified-flag) (message "ID properties created in file \"%s\"" file) (sit-for 2)))) (defun org-icalendar-blocked-headline-p (headline info) "Non-nil when HEADLINE is considered to be blocked. INFO is a plist used as a communication channel. A headline is blocked when either - it has children which are not all in a completed state; - it has a parent with the property :ORDERED:, and there are siblings prior to it with incomplete status; - its parent is blocked because it has siblings that should be done first or is a child of a blocked grandparent entry." (or ;; Check if any child is not done. (org-element-map (org-element-contents headline) 'headline (lambda (hl) (eq (org-element-property :todo-type hl) 'todo)) info 'first-match) ;; Check :ORDERED: node property. (catch 'blockedp (let ((current headline)) (dolist (parent (org-element-lineage headline)) (cond ((not (org-element-property :todo-keyword parent)) (throw 'blockedp nil)) ((org-not-nil (org-element-property :ORDERED parent)) (let ((sibling current)) (while (setq sibling (org-export-get-previous-element sibling info)) (when (eq (org-element-property :todo-type sibling) 'todo) (throw 'blockedp t))))) (t (setq current parent)))))))) (defun org-icalendar-use-UTC-date-time-p () "Non-nil when `org-icalendar-date-time-format' requires UTC time." (char-equal (elt org-icalendar-date-time-format (1- (length org-icalendar-date-time-format))) ?Z)) (defun org-icalendar-convert-timestamp (timestamp keyword &optional end tz) "Convert TIMESTAMP to iCalendar format. TIMESTAMP is a timestamp object. KEYWORD is added in front of it, in order to make a complete line (e.g. \"DTSTART\"). When optional argument END is non-nil, use end of time range. Also increase the hour by two (if time string contains a time), or the day by one (if it does not contain a time) when no explicit ending time is specified. When optional argument TZ is non-nil, timezone data time will be added to the timestamp. It can be the string \"UTC\", to use UTC time, or a string in the IANA TZ database format (e.g. \"Europe/London\"). In either case, the value of `org-icalendar-date-time-format' will be ignored." (let* ((year-start (org-element-property :year-start timestamp)) (year-end (org-element-property :year-end timestamp)) (month-start (org-element-property :month-start timestamp)) (month-end (org-element-property :month-end timestamp)) (day-start (org-element-property :day-start timestamp)) (day-end (org-element-property :day-end timestamp)) (hour-start (org-element-property :hour-start timestamp)) (hour-end (org-element-property :hour-end timestamp)) (minute-start (org-element-property :minute-start timestamp)) (minute-end (org-element-property :minute-end timestamp)) (with-time-p minute-start) (equal-bounds-p (equal (list year-start month-start day-start hour-start minute-start) (list year-end month-end day-end hour-end minute-end))) (mi (cond ((not with-time-p) 0) ((not end) minute-start) ((and org-agenda-default-appointment-duration equal-bounds-p) (+ minute-end org-agenda-default-appointment-duration)) (t minute-end))) (h (cond ((not with-time-p) 0) ((not end) hour-start) ((or (not equal-bounds-p) org-agenda-default-appointment-duration) hour-end) (t (+ hour-end 2)))) (d (cond ((not end) day-start) ((not with-time-p) (1+ day-end)) (t day-end))) (m (if end month-end month-start)) (y (if end year-end year-start))) (concat keyword (format-time-string (cond ((string-equal tz "UTC") ":%Y%m%dT%H%M%SZ") ((not with-time-p) ";VALUE=DATE:%Y%m%d") ((stringp tz) (concat ";TZID=" tz ":%Y%m%dT%H%M%S")) (t (replace-regexp-in-string "%Z" org-icalendar-timezone org-icalendar-date-time-format t))) ;; Convert timestamp into internal time in order to use ;; `format-time-string' and fix any mistake (i.e. MI >= 60). (org-encode-time 0 mi h d m y) (and (or (string-equal tz "UTC") (and (null tz) with-time-p (org-icalendar-use-UTC-date-time-p))) t))))) (defun org-icalendar-dtstamp () "Return DTSTAMP property, as a string." (format-time-string "DTSTAMP:%Y%m%dT%H%M%SZ" nil t)) (defun org-icalendar-get-categories (entry info) "Return categories according to `org-icalendar-categories'. ENTRY is a headline or an inlinetask element. INFO is a plist used as a communication channel." (mapconcat #'identity (org-uniquify (let (categories) (dolist (type org-icalendar-categories (nreverse categories)) (cl-case type (category (push (org-export-get-category entry info) categories)) (todo-state (let ((todo (org-element-property :todo-keyword entry))) (and todo (push todo categories)))) (local-tags (setq categories (append (nreverse (org-export-get-tags entry info)) categories))) (all-tags (setq categories (append (nreverse (org-export-get-tags entry info nil t)) categories))))))) ",")) (defun org-icalendar-transcode-diary-sexp (sexp uid summary) "Transcode a diary sexp into iCalendar format. SEXP is the diary sexp being transcoded, as a string. UID is the unique identifier for the entry. SUMMARY defines a short summary or subject for the event." (when (require 'icalendar nil t) (org-element-normalize-string (with-temp-buffer (let ((sexp (if (not (string-match "\\`<%%" sexp)) sexp (concat (substring sexp 1 -1) " " summary)))) (put-text-property 0 1 'uid uid sexp) (insert sexp "\n")) (org-diary-to-ical-string (current-buffer)))))) (defun org-icalendar-cleanup-string (s) "Cleanup string S according to RFC 5545." (when s ;; Protect "\", "," and ";" characters. and replace newline ;; characters with literal \n. (replace-regexp-in-string "[ \t]*\n" "\\n" (replace-regexp-in-string "[\\,;]" "\\\\\\&" s) nil t))) (defun org-icalendar-fold-string (s) "Fold string S according to RFC 5545." (org-element-normalize-string (mapconcat (lambda (line) ;; Limit each line to a maximum of 75 characters. If it is ;; longer, fold it by using "\r\n " as a continuation marker. (let ((len (length line))) (if (<= len 75) line (let ((folded-line (substring line 0 75)) (chunk-start 75) chunk-end) ;; Since continuation marker takes up one character on the ;; line, real contents must be split at 74 chars. (while (< (setq chunk-end (+ chunk-start 74)) len) (setq folded-line (concat folded-line "\n " (substring line chunk-start chunk-end)) chunk-start chunk-end)) (concat folded-line "\n " (substring line chunk-start)))))) (org-split-string s "\n") "\n"))) (defun org-icalendar--post-process-file (file) "Post-process the exported iCalendar FILE. Converts line endings to dos-style CRLF as per RFC 5545, then runs `org-icalendar-after-save-hook'." (with-temp-buffer (insert-file-contents file) (let ((coding-system-for-write (coding-system-change-eol-conversion last-coding-system-used 'dos))) (write-region nil nil file))) (run-hook-with-args 'org-icalendar-after-save-hook file) nil) ;;; Filters (defun org-icalendar-clear-blank-lines (headline _backend _info) "Remove blank lines in HEADLINE export. HEADLINE is a string representing a transcoded headline. BACKEND and INFO are ignored." (replace-regexp-in-string "^\\(?:[ \t]*\n\\)+" "" headline)) ;;; Transcode Functions ;;;; Headline and Inlinetasks ;; The main function is `org-icalendar-entry', which extracts ;; information from a headline or an inlinetask (summary, ;; description...) and then delegates code generation to ;; `org-icalendar--vtodo' and `org-icalendar--vevent', depending ;; on the component needed. ;; Obviously, `org-icalendar--valarm' handles alarms, which can ;; happen within a VTODO component. (defun org-icalendar-entry (entry contents info) "Transcode ENTRY element into iCalendar format. ENTRY is either a headline or an inlinetask. CONTENTS is ignored. INFO is a plist used as a communication channel. This function is called on every headline, the section below it (minus inlinetasks) being its contents. It tries to create VEVENT and VTODO components out of scheduled date, deadline date, plain timestamps, diary sexps. It also calls itself on every inlinetask within the section." (unless (org-element-property :footnote-section-p entry) (let* ((type (org-element-type entry)) ;; Determine contents really associated to the entry. For ;; a headline, limit them to section, if any. For an ;; inlinetask, this is every element within the task. (inside (if (eq type 'inlinetask) (cons 'org-data (cons nil (org-element-contents entry))) (let ((first (car (org-element-contents entry)))) (and (org-element-type-p first 'section) (cons 'org-data (cons nil (org-element-contents first)))))))) (concat (let ((todo-type (org-element-property :todo-type entry)) (uid (or (org-element-property :ID entry) (org-id-new))) (summary (org-icalendar-cleanup-string (or (let ((org-property-separators '(("SUMMARY" . "\n")))) (org-entry-get entry "SUMMARY" 'selective)) (org-export-data (org-element-property :title entry) info)))) (loc (let ((org-property-separators '(("LOCATION" . "\n")))) (org-icalendar-cleanup-string (org-entry-get entry "LOCATION" 'selective)))) (class (org-icalendar-cleanup-string (org-export-get-node-property :CLASS entry (org-property-inherit-p "CLASS")))) ;; Build description of the entry from associated section ;; (headline) or contents (inlinetask). (desc (org-icalendar-cleanup-string (or (let ((org-property-separators '(("DESCRIPTION" . "\n")))) (org-entry-get entry "DESCRIPTION" 'selective)) (let ((contents (org-export-data inside info))) (cond ((not (org-string-nw-p contents)) nil) ((wholenump org-icalendar-include-body) (let ((contents (org-trim contents))) (substring contents 0 (min (length contents) org-icalendar-include-body)))) (org-icalendar-include-body (org-trim contents))))))) (cat (org-icalendar-get-categories entry info)) (tz (org-export-get-node-property :TIMEZONE entry (org-property-inherit-p "TIMEZONE")))) (concat ;; Events: Delegate to `org-icalendar--vevent' to generate ;; "VEVENT" component from scheduled, deadline, or any ;; timestamp in the entry. (let ((deadline (org-element-property :deadline entry)) (use-deadline (plist-get info :icalendar-use-deadline)) (deadline-summary-prefix (org-icalendar-cleanup-string (plist-get info :icalendar-deadline-summary-prefix)))) (and deadline (pcase todo-type (`todo (or (memq 'event-if-todo-not-done use-deadline) (memq 'event-if-todo use-deadline))) (`done (memq 'event-if-todo use-deadline)) (_ (memq 'event-if-not-todo use-deadline))) (org-icalendar--vevent entry deadline (concat "DL-" uid) (concat deadline-summary-prefix summary) loc desc cat tz class))) (let ((scheduled (org-element-property :scheduled entry)) (use-scheduled (plist-get info :icalendar-use-scheduled)) (scheduled-summary-prefix (org-icalendar-cleanup-string (plist-get info :icalendar-scheduled-summary-prefix)))) (and scheduled (pcase todo-type (`todo (or (memq 'event-if-todo-not-done use-scheduled) (memq 'event-if-todo use-scheduled))) (`done (memq 'event-if-todo use-scheduled)) (_ (memq 'event-if-not-todo use-scheduled))) (org-icalendar--vevent entry scheduled (concat "SC-" uid) (concat scheduled-summary-prefix summary) loc desc cat tz class))) ;; When collecting plain timestamps from a headline and its ;; title, skip inlinetasks since collection will happen once ;; ENTRY is one of them. (let ((counter 0)) (mapconcat #'identity (org-element-map (cons (org-element-property :title entry) (org-element-contents inside)) 'timestamp (lambda (ts) (when (let ((type (org-element-property :type ts))) (cl-case (plist-get info :with-timestamps) (active (memq type '(active active-range))) (inactive (memq type '(inactive inactive-range))) ((t) t))) (let ((uid (format "TS%d-%s" (cl-incf counter) uid))) (org-icalendar--vevent entry ts uid summary loc desc cat tz class)))) info nil (and (eq type 'headline) 'inlinetask)) "")) ;; Task: First check if it is appropriate to export it. If ;; so, call `org-icalendar--vtodo' to transcode it into ;; a "VTODO" component. (when (and todo-type (pcase (plist-get info :icalendar-include-todo) (`all t) (`unblocked (and (eq type 'headline) (not (org-icalendar-blocked-headline-p entry info)))) ;; unfinished (`t (eq todo-type 'todo)) ((and (pred listp) kwd-list) (member (org-element-property :todo-keyword entry) kwd-list)))) (org-icalendar--vtodo entry uid summary loc desc cat tz class)) ;; Diary-sexp: Collect every diary-sexp element within ENTRY ;; and its title, and transcode them. If ENTRY is ;; a headline, skip inlinetasks: they will be handled ;; separately. (when org-icalendar-include-sexps (let ((counter 0)) (mapconcat #'identity (org-element-map (cons (org-element-property :title entry) (org-element-contents inside)) 'diary-sexp (lambda (sexp) (org-icalendar-transcode-diary-sexp (org-element-property :value sexp) (format "DS%d-%s" (cl-incf counter) uid) summary)) info nil (and (eq type 'headline) 'inlinetask)) ""))))) ;; If ENTRY is a headline, call current function on every ;; inlinetask within it. In agenda export, this is independent ;; from the mark (or lack thereof) on the entry. (when (eq type 'headline) (mapconcat #'identity (org-element-map inside 'inlinetask (lambda (task) (org-icalendar-entry task nil info)) info) "")) ;; Don't forget components from inner entries. contents)))) (defun org-icalendar--rrule (unit value) "Format RRULE icalendar entry for UNIT frequency and VALUE interval. UNIT is a symbol `hour', `day', `week', `month', or `year'." (format "RRULE:FREQ=%s;INTERVAL=%d" (cl-case unit (hour "HOURLY") (day "DAILY") (week "WEEKLY") (month "MONTHLY") (year "YEARLY")) value)) (defun org-icalendar--vevent (entry timestamp uid summary location description categories timezone class) "Create a VEVENT component. ENTRY is either a headline or an inlinetask element. TIMESTAMP is a timestamp object defining the date-time of the event. UID is the unique identifier for the event. SUMMARY defines a short summary or subject for the event. LOCATION defines the intended venue for the event. DESCRIPTION provides the complete description of the event. CATEGORIES defines the categories the event belongs to. TIMEZONE specifies a time zone for this event only. CLASS contains the visibility attribute. Three of them \\(\"PUBLIC\", \"CONFIDENTIAL\", and \"PRIVATE\") are predefined, others should be treated as \"PRIVATE\" if they are unknown to the iCalendar server. Return VEVENT component as a string." (if (eq (org-element-property :type timestamp) 'diary) (org-icalendar-transcode-diary-sexp (org-element-property :raw-value timestamp) uid summary) (concat "BEGIN:VEVENT\n" (org-icalendar-dtstamp) "\n" "UID:" uid "\n" (org-icalendar-convert-timestamp timestamp "DTSTART" nil timezone) "\n" (org-icalendar-convert-timestamp timestamp "DTEND" t timezone) "\n" ;; RRULE. (when (org-element-property :repeater-type timestamp) (concat (org-icalendar--rrule (org-element-property :repeater-unit timestamp) (org-element-property :repeater-value timestamp)) "\n")) "SUMMARY:" summary "\n" (and (org-string-nw-p location) (format "LOCATION:%s\n" location)) (and (org-string-nw-p class) (format "CLASS:%s\n" class)) (and (org-string-nw-p description) (format "DESCRIPTION:%s\n" description)) "CATEGORIES:" categories "\n" ;; VALARM. (org-icalendar--valarm entry timestamp summary) "END:VEVENT\n"))) (defun org-icalendar--repeater-type (elem) "Return ELEM's repeater-type if supported, else warn and return nil." (let ((repeater-value (org-element-property :repeater-value elem)) (repeater-type (org-element-property :repeater-type elem))) (cond ((not (and repeater-type repeater-value (> repeater-value 0))) nil) ;; TODO Add catch-up to supported repeaters (use EXDATE to implement) ((not (memq repeater-type '(cumulate))) (org-display-warning (format "Repeater-type %s not currently supported by iCalendar export" (symbol-name repeater-type))) nil) (repeater-type)))) (defun org-icalendar--vtodo (entry uid summary location description categories timezone class) "Create a VTODO component. ENTRY is either a headline or an inlinetask element. UID is the unique identifier for the task. SUMMARY defines a short summary or subject for the task. LOCATION defines the intended venue for the task. CLASS sets the task class (e.g. confidential). DESCRIPTION provides the complete description of the task. CATEGORIES defines the categories the task belongs to. TIMEZONE specifies a time zone for this TODO only. Return VTODO component as a string." (let* ((sc (and (memq 'todo-start org-icalendar-use-scheduled) (org-element-property :scheduled entry))) (dl (and (memq 'todo-due org-icalendar-use-deadline) (org-element-property :deadline entry))) (sc-repeat-p (org-icalendar--repeater-type sc)) (dl-repeat-p (org-icalendar--repeater-type dl)) (repeat-value (or (org-element-property :repeater-value sc) (org-element-property :repeater-value dl))) (repeat-unit (or (org-element-property :repeater-unit sc) (org-element-property :repeater-unit dl))) (repeat-until (and sc-repeat-p (not dl-repeat-p) dl)) (start (cond (sc) ((eq org-icalendar-todo-unscheduled-start 'current-datetime) (let ((now (decode-time))) (list 'timestamp (list :type 'active :minute-start (nth 1 now) :hour-start (nth 2 now) :day-start (nth 3 now) :month-start (nth 4 now) :year-start (nth 5 now))))) ((or (and (eq org-icalendar-todo-unscheduled-start 'deadline-warning) dl) (and (eq org-icalendar-todo-unscheduled-start 'recurring-deadline-warning) dl-repeat-p)) (let ((dl-raw (org-element-property :raw-value dl))) (with-temp-buffer (insert dl-raw) (goto-char (point-min)) (org-timestamp-down-day (org-get-wdays dl-raw)) (org-element-timestamp-parser))))))) (concat "BEGIN:VTODO\n" "UID:TODO-" uid "\n" (org-icalendar-dtstamp) "\n" (when start (concat (org-icalendar-convert-timestamp start "DTSTART" nil timezone) "\n")) (when (and dl (not repeat-until)) (concat (org-icalendar-convert-timestamp dl "DUE" nil timezone) "\n")) ;; RRULE (cond ;; SCHEDULED, DEADLINE have different repeaters ((and dl-repeat-p (not (and (eq repeat-value (org-element-property :repeater-value dl)) (eq repeat-unit (org-element-property :repeater-unit dl))))) ;; TODO Implement via RDATE with changing DURATION (org-display-warning "Not yet implemented: \ different repeaters on SCHEDULED and DEADLINE. Skipping.") nil) ;; DEADLINE has repeater but SCHEDULED doesn't ((and dl-repeat-p (and sc (not sc-repeat-p))) ;; TODO SCHEDULED should only apply to first instance; ;; use RDATE with custom DURATION to implement that (org-display-warning "Not yet implemented: \ repeater on DEADLINE but not SCHEDULED. Skipping.") nil) ((or sc-repeat-p dl-repeat-p) (concat (org-icalendar--rrule repeat-unit repeat-value) ;; add UNTIL part to RRULE (when repeat-until (let* ((start-time (org-element-property :minute-start start)) ;; RFC5545 requires UTC iff DTSTART is not local time (local-time-p (and (not timezone) (equal org-icalendar-date-time-format ":%Y%m%dT%H%M%S"))) (encoded (org-encode-time 0 (or (org-element-property :minute-start repeat-until) 0) (or (org-element-property :hour-start repeat-until) 0) (org-element-property :day-start repeat-until) (org-element-property :month-start repeat-until) (org-element-property :year-start repeat-until)))) (concat ";UNTIL=" (cond ((not start-time) (format-time-string "%Y%m%d" encoded)) (local-time-p (format-time-string "%Y%m%dT%H%M%S" encoded)) ((format-time-string "%Y%m%dT%H%M%SZ" encoded t)))))) "\n"))) "SUMMARY:" summary "\n" (and (org-string-nw-p location) (format "LOCATION:%s\n" location)) (and (org-string-nw-p class) (format "CLASS:%s\n" class)) (and (org-string-nw-p description) (format "DESCRIPTION:%s\n" description)) "CATEGORIES:" categories "\n" "SEQUENCE:1\n" (format "PRIORITY:%d\n" (let ((pri (or (org-element-property :priority entry) org-priority-default))) (floor (- 9 (* 8. (/ (float (- org-priority-lowest pri)) (- org-priority-lowest org-priority-highest))))))) (format "STATUS:%s\n" (if (eq (org-element-property :todo-type entry) 'todo) "NEEDS-ACTION" "COMPLETED")) "END:VTODO\n"))) (defun org-icalendar--valarm (entry timestamp summary) "Create a VALARM component. ENTRY is the calendar entry triggering the alarm. TIMESTAMP is the start date-time of the entry. SUMMARY defines a short summary or subject for the task. Return VALARM component as a string, or nil if it isn't allowed." ;; Create a VALARM entry if the entry is timed. This is not very ;; general in that: ;; (a) only one alarm per entry is defined, ;; (b) only minutes are allowed for the trigger period ahead of the ;; start time, ;; (c) only a DISPLAY action is defined. [ESF] (let ((alarm-time (let ((warntime (org-element-property :APPT_WARNTIME entry))) (if warntime (string-to-number warntime) nil)))) (and (or (and alarm-time (> alarm-time 0)) (> org-icalendar-alarm-time 0) org-icalendar-force-alarm) (org-element-property :hour-start timestamp) (format "BEGIN:VALARM ACTION:DISPLAY DESCRIPTION:%s TRIGGER:-P0DT0H%dM0S END:VALARM\n" summary (cond ((and alarm-time org-icalendar-force-alarm) alarm-time) ((and alarm-time (not (zerop alarm-time))) alarm-time) (t org-icalendar-alarm-time)))))) ;;;; Template (defun org-icalendar-inner-template (contents _) "Return document body string after iCalendar conversion. CONTENTS is the transcoded contents string." contents) (defun org-icalendar-template (contents info) "Return complete document string after iCalendar conversion. CONTENTS is the transcoded contents string. INFO is a plist used as a communication channel." (org-icalendar--vcalendar ;; Name. (if (not (plist-get info :input-file)) (buffer-name (buffer-base-buffer)) (file-name-nondirectory (file-name-sans-extension (plist-get info :input-file)))) ;; Owner. (if (not (plist-get info :with-author)) "" (org-export-data (plist-get info :author) info)) ;; Timezone. (or (org-string-nw-p org-icalendar-timezone) (format-time-string "%Z")) ;; Description. (org-export-data (plist-get info :title) info) ;; TTL (plist-get info :icalendar-ttl) contents)) (defun org-icalendar--vcalendar (name owner tz description ttl contents) "Create a VCALENDAR component. NAME, OWNER, TZ, DESCRIPTION, TTL and CONTENTS are all strings giving, respectively, the name of the calendar, its owner, the timezone used, a short description, time to live (refresh period) and the other components included." (org-icalendar-fold-string (concat (format "BEGIN:VCALENDAR VERSION:2.0 X-WR-CALNAME:%s PRODID:-//%s//Emacs with Org mode//EN X-WR-TIMEZONE:%s X-WR-CALDESC:%s\n" (org-icalendar-cleanup-string name) (org-icalendar-cleanup-string owner) (org-icalendar-cleanup-string tz) (org-icalendar-cleanup-string description)) (when ttl (format "X-PUBLISHED-TTL:%s\n" (org-icalendar-cleanup-string ttl))) "CALSCALE:GREGORIAN\n" contents "END:VCALENDAR\n"))) ;;; Interactive Functions ;;;###autoload (defun org-icalendar-export-to-ics (&optional async subtreep visible-only body-only) "Export current buffer to an iCalendar file. If narrowing is active in the current buffer, only export its narrowed part. If a region is active, export that region. A non-nil optional argument ASYNC means the process should happen asynchronously. The resulting file should be accessible through the `org-export-stack' interface. When optional argument SUBTREEP is non-nil, export the sub-tree at point, extracting information from the headline properties first. When optional argument VISIBLE-ONLY is non-nil, don't export contents of hidden elements. When optional argument BODY-ONLY is non-nil, only write code between \"BEGIN:VCALENDAR\" and \"END:VCALENDAR\". Return ICS file name." (interactive) (let ((file (buffer-file-name (buffer-base-buffer)))) (when (and file org-icalendar-store-UID) (org-icalendar-create-uid file 'warn-user))) ;; Export part. Since this backend is backed up by `ascii', ensure ;; links will not be collected at the end of sections. (let ((outfile (org-export-output-file-name ".ics" subtreep))) (org-export-to-file 'icalendar outfile async subtreep visible-only body-only '(:ascii-charset utf-8 :ascii-links-to-notes nil) #'org-icalendar--post-process-file))) ;;;###autoload (defun org-icalendar-export-agenda-files (&optional async) "Export all agenda files to iCalendar files. When optional argument ASYNC is non-nil, export happens in an external process." (interactive) (if async ;; Asynchronous export is not interactive, so we will not call ;; `org-check-agenda-file'. Instead we remove any non-existent ;; agenda file from the list. (let ((files (cl-remove-if-not #'file-exists-p (org-agenda-files t)))) (org-export-async-start (lambda (results) (dolist (f results) (org-export-add-to-stack f 'icalendar))) `(let (output-files) (dolist (file ',files outputfiles) (with-current-buffer (org-get-agenda-file-buffer file) (push (expand-file-name (org-icalendar-export-to-ics)) output-files)))))) (let ((files (org-agenda-files t))) (org-agenda-prepare-buffers files) (unwind-protect (dolist (file files) (catch 'nextfile (org-check-agenda-file file) (with-current-buffer (org-get-agenda-file-buffer file) (condition-case err (org-icalendar-export-to-ics) (error (warn "Exporting %s to icalendar failed: %s" file (error-message-string err)) (signal (car err) (cdr err))))))) (org-release-buffers org-agenda-new-buffers))))) ;;;###autoload (defun org-icalendar-combine-agenda-files (&optional async) "Combine all agenda files into a single iCalendar file. A non-nil optional argument ASYNC means the process should happen asynchronously. The resulting file should be accessible through the `org-export-stack' interface. The file is stored under the name chosen in `org-icalendar-combined-agenda-file'." (interactive) (if async (let ((files (cl-remove-if-not #'file-exists-p (org-agenda-files t)))) (org-export-async-start (lambda (_) (org-export-add-to-stack (expand-file-name org-icalendar-combined-agenda-file) 'icalendar)) `(apply #'org-icalendar--combine-files ',files))) (apply #'org-icalendar--combine-files (org-agenda-files t)))) (defun org-icalendar-export-current-agenda (file) "Export current agenda view to an iCalendar FILE. This function assumes major mode for current buffer is `org-agenda-mode'." (let* ((org-export-use-babel) ;don't evaluate Babel blocks (contents (org-export-string-as (with-output-to-string (save-excursion (let ((p (point-min)) (seen nil)) ;prevent duplicates (while (setq p (next-single-property-change p 'org-hd-marker)) (let ((m (get-text-property p 'org-hd-marker))) (when (and m (not (member m seen))) (push m seen) (with-current-buffer (marker-buffer m) (org-with-wide-buffer (goto-char (marker-position m)) (princ (org-element-normalize-string (buffer-substring (point) (org-entry-end-position)))))))) (forward-line))))) 'icalendar t '(:ascii-charset utf-8 :ascii-links-to-notes nil :icalendar-include-todo all)))) (with-temp-file file (insert (org-icalendar--vcalendar org-icalendar-combined-name user-full-name (or (org-string-nw-p org-icalendar-timezone) (format-time-string "%Z")) org-icalendar-combined-description org-icalendar-ttl contents))) (org-icalendar--post-process-file file))) (defun org-icalendar--combine-files (&rest files) "Combine entries from multiple files into an iCalendar file. FILES is a list of files to build the calendar from." ;; At the end of the process, all buffers related to FILES are going ;; to be killed. Make sure to only kill the ones opened in the ;; process. (let ((org-agenda-new-buffers nil)) (unwind-protect (progn (with-temp-file org-icalendar-combined-agenda-file (insert (org-icalendar--vcalendar ;; Name. org-icalendar-combined-name ;; Owner. user-full-name ;; Timezone. (or (org-string-nw-p org-icalendar-timezone) (format-time-string "%Z")) ;; Description. org-icalendar-combined-description ;; TTL (Refresh period) org-icalendar-ttl ;; Contents. (concat ;; Agenda contents. (mapconcat (lambda (file) (catch 'nextfile (org-check-agenda-file file) (with-current-buffer (org-get-agenda-file-buffer file) ;; Create ID if necessary. (when org-icalendar-store-UID (org-icalendar-create-uid file t)) (org-export-as 'icalendar nil nil t '(:ascii-charset utf-8 :ascii-links-to-notes nil))))) files "") ;; BBDB anniversaries. (when (and org-icalendar-include-bbdb-anniversaries (require 'ol-bbdb nil t)) (with-output-to-string (org-bbdb-anniv-export-ical))))))) (org-icalendar--post-process-file org-icalendar-combined-agenda-file)) (org-release-buffers org-agenda-new-buffers)))) (provide 'ox-icalendar) ;; Local variables: ;; generated-autoload-file: "org-loaddefs.el" ;; End: ;;; ox-icalendar.el ends here org-mode-9.7.29+dfsg/lisp/ox-koma-letter.el000066400000000000000000001172471500430433700204260ustar00rootroot00000000000000;;; ox-koma-letter.el --- KOMA Scrlttr2 Backend for Org Export Engine -*- lexical-binding: t; -*- ;; Copyright (C) 2007-2025 Free Software Foundation, Inc. ;; Author: Nicolas Goaziou ;; Alan Schmitt ;; Viktor Rosenfeld ;; Rasmus Pank Roulund ;; Maintainer: Marco Wahl ;; Keywords: org, text, tex ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; ;; This library implements a KOMA Scrlttr2 backend, derived from the ;; LaTeX one. ;; ;; Depending on the desired output format, three commands are provided ;; for export: `org-koma-letter-export-as-latex' (temporary buffer), ;; `org-koma-letter-export-to-latex' ("tex" file) and ;; `org-koma-letter-export-to-pdf' ("pdf" file). ;; ;; On top of buffer keywords supported by `latex' backend (see ;; `org-latex-packages-alist'), this backend introduces the following ;; keywords: ;; - CLOSING: see `org-koma-letter-closing', ;; - FROM_ADDRESS: see `org-koma-letter-from-address', ;; - LCO: see `org-koma-letter-class-option-file', ;; - OPENING: see `org-koma-letter-opening', ;; - PHONE_NUMBER: see `org-koma-letter-phone-number', ;; - URL: see `org-koma-letter-url', ;; - FROM_LOGO: see `org-koma-letter-from-logo', ;; - SIGNATURE: see `org-koma-letter-signature', ;; - PLACE: see `org-koma-letter-place', ;; - LOCATION: see `org-koma-letter-location', ;; - TO_ADDRESS: If unspecified this is set to "\mbox{}". ;; ;; TO_ADDRESS, FROM_ADDRESS, LOCATION, CLOSING, and SIGNATURE can also ;; be specified using "special headings" with the special tags ;; specified in `org-koma-letter-special-tags-in-letter'. LaTeX line ;; breaks are not necessary for TO_ADDRESS, FROM_ADDRESS and LOCATION. ;; If both a headline and a keyword specify a to or from address the ;; value is determined in accordance with ;; `org-koma-letter-prefer-special-headings'. ;; ;; A number of OPTIONS settings can be set to change which contents is ;; exported. ;; - backaddress (see `org-koma-letter-use-backaddress') ;; - foldmarks (see `org-koma-letter-use-foldmarks') ;; - phone (see `org-koma-letter-use-phone') ;; - url (see `org-koma-letter-use-url') ;; - from-logo (see `org-koma-letter-use-from-logo') ;; - email (see `org-koma-letter-use-email') ;; - place (see `org-koma-letter-use-place') ;; - location (see `org-koma-letter-location') ;; - subject, a list of format options ;; (see `org-koma-letter-subject-format') ;; - after-closing-order, a list of the ordering of headings with ;; special tags after closing (see ;; `org-koma-letter-special-tags-after-closing') ;; - after-letter-order, as above, but after the end of the letter ;; (see `org-koma-letter-special-tags-after-letter'). ;; ;; The following variables works differently from the main LaTeX class ;; - AUTHOR: Default to user-full-name but may be disabled. ;; (See also `org-koma-letter-author'.) ;; - EMAIL: Same as AUTHOR. (See also `org-koma-letter-email'.) ;; ;; FROM_LOGO uses LaTeX markup. FROM_LOGO provides the ;; "includegraphics" command to tell LaTeX where to find the logo. ;; This command needs to know the logo's directory and file name. The ;; directory can either be relative or absolute, just as you would ;; expect. LaTeX can use three file types for the logo: PDF, JPEG, or ;; PNG. The logo can either include or exclude its extension, which ;; might surprise you. When you exclude its extension, LaTeX will ;; search the directory for the "best" quality graphics format. For ;; example if it finds both logo.pdf and logo.png then it will ;; identify the PDF as "better", and include "logo.pdf". This can be ;; useful, for example, when you are mocking up a logo in the PNG ;; raster format and then switch over to the higher quality PDF vector ;; format. When you include the file extension then LaTeX will ;; include it without searching for higher quality file types. ;; Whatever file type you choose, it will probably require a few ;; design iterations to get the best looking logo size for your ;; letter. Finally, the directory and file name are specified ;; *without* quotes. Here are some examples with commentary, in the ;; location of your letter, with a logo named "logo", to get you ;; started: ;; ;; Logo in the same directory: \includegraphics{logo} ;; or a sub-directory: \includegraphics{logos/production/logo} ;; ;; Logos specified using absolute paths on Linux or Windows: ;; ;; \includegraphics{~/correspondence/logo} ;; \includegraphics{~/correspondence/logos/production/logo} ;; \includegraphics{c:/you/correspondence/logo} ;; \includegraphics{c:/you/correspondence/logos/production/logo} ;; ;; Logos in the same directory where the "better" quality PDF will ;; be chosen over the JPG: ;; ;; \includegraphics{logo.pdf} ;; \includegraphics{logo.png} ;; ;; Headlines are in general ignored. However, headlines with special ;; tags can be used for specified contents like postscript (ps), ;; carbon copy (cc), enclosures (encl) and code to be inserted after ;; \end{letter} (after_letter). Specials tags are defined in ;; `org-koma-letter-special-tags-after-closing' and ;; `org-koma-letter-special-tags-after-letter'. Currently members of ;; `org-koma-letter-special-tags-after-closing' used as macros and the ;; content of the headline is the argument. ;; ;; Headlines with to and from may also be used rather than the keyword ;; approach described above. If both a keyword and a headline with ;; information is present precedence is determined by ;; `org-koma-letter-prefer-special-headings'. ;; ;; You need an appropriate association in `org-latex-classes' in order ;; to use the KOMA Scrlttr2 class. By default, a sparse scrlttr2 ;; class is provided: "default-koma-letter". You can also add you own ;; letter class. For instance: ;; ;; (add-to-list 'org-latex-classes ;; '("my-letter" ;; "\\documentclass\[% ;; DIV=14, ;; fontsize=12pt, ;; parskip=half, ;; subject=titled, ;; backaddress=false, ;; fromalign=left, ;; fromemail=true, ;; fromphone=true\]\{scrlttr2\} ;; \[DEFAULT-PACKAGES] ;; \[PACKAGES] ;; \[EXTRA]")) ;; ;; Then, in your Org document, be sure to require the proper class ;; with: ;; ;; #+LATEX_CLASS: my-letter ;; ;; Or by setting `org-koma-letter-default-class'. ;; ;; You may have to load (LaTeX) Babel as well, e.g., by adding ;; it to `org-latex-packages-alist', ;; ;; (add-to-list 'org-latex-packages-alist '("AUTO" "babel" nil)) ;;; Code: (require 'org-macs) (org-assert-version) (require 'cl-lib) (require 'ox-latex) ;; Install a default letter class. (unless (assoc "default-koma-letter" org-latex-classes) (add-to-list 'org-latex-classes '("default-koma-letter" "\\documentclass[11pt]{scrlttr2}"))) ;;; User-Configurable Variables (defgroup org-export-koma-letter nil "Options for exporting to KOMA scrlttr2 class in LaTeX export." :tag "Org Koma-Letter" :group 'org-export) (defcustom org-koma-letter-class-option-file "NF" "Letter Class Option File. This option can also be set with the LCO keyword." :type 'string) (defcustom org-koma-letter-author 'user-full-name "Sender's name. This variable defaults to calling the function `user-full-name' which just returns the current function `user-full-name'. Alternatively a string, nil or a function may be given. Functions must return a string. This option can also be set with the AUTHOR keyword." :type '(radio (function-item user-full-name) (string) (function) (const :tag "Do not export author" nil))) (defcustom org-koma-letter-email 'org-koma-letter-email "Sender's email address. This variable defaults to the value `org-koma-letter-email' which returns `user-mail-address'. Alternatively a string, nil or a function may be given. Functions must return a string. This option can also be set with the EMAIL keyword." :type '(radio (function-item org-koma-letter-email) (string) (function) (const :tag "Do not export email" nil))) (defcustom org-koma-letter-from-address "" "Sender's address, as a string. This option can also be set with one or more FROM_ADDRESS keywords." :type 'string) (defcustom org-koma-letter-phone-number "" "Sender's phone number, as a string. This option can also be set with the PHONE_NUMBER keyword." :type 'string) (defcustom org-koma-letter-url "" "Sender's URL, e. g., the URL of her homepage. This option can also be set with the URL keyword." :type 'string :safe #'stringp) (defcustom org-koma-letter-from-logo "" "Commands for inserting the sender's logo, e. g., \\includegraphics{logo}. This option can also be set with the FROM_LOGO keyword." :type 'string :safe #'stringp) (defcustom org-koma-letter-place "" "Place from which the letter is sent, as a string. This option can also be set with the PLACE keyword." :type 'string) (defcustom org-koma-letter-location "" "Sender's extension field, as a string. This option can also be set with the LOCATION keyword. Moreover, when: (1) Either `org-koma-letter-prefer-special-headings' is non-nil or there is no LOCATION keyword or the LOCATION keyword is empty; (2) the letter contains a headline with the special tag \"location\"; then the location will be set as the content of the location special heading. The location field is typically printed right of the address field (See Figure 4.9. in the English manual of 2015-10-03)." :type 'string) (defcustom org-koma-letter-opening "" "Letter's opening, as a string. This option can also be set with the OPENING keyword. Moreover, when: (1) Either `org-koma-letter-prefer-special-headings' is non-nil or the CLOSING keyword is empty (2) `org-koma-letter-headline-is-opening-maybe' is non-nil; (3) the letter contains a headline without a special tag (e.g. \"to\" or \"ps\"); then the opening will be implicitly set as the untagged headline title." :type 'string) (defcustom org-koma-letter-closing "" "Letter's closing, as a string. This option can also be set with the CLOSING keyword. Moreover, when: (1) Either `org-koma-letter-prefer-special-headings' is non-nil or the CLOSING keyword is empty; (2) `org-koma-letter-headline-is-opening-maybe' is non-nil; (3) the letter contains a headline with the special tag \"closing\"; then the opening will be set as the title of the closing special heading title." :type 'string) (defcustom org-koma-letter-signature "" "Signature, as a string. This option can also be set with the SIGNATURE keyword. Moreover, when: (1) Either `org-koma-letter-prefer-special-headings' is non-nil or there is no CLOSING keyword or the CLOSING keyword is empty; (2) `org-koma-letter-headline-is-opening-maybe' is non-nil; (3) the letter contains a headline with the special tag \"closing\"; then the signature will be set as the content of the closing special heading. Note if the content is empty the signature will not be set." :type 'string) (defcustom org-koma-letter-prefer-special-headings nil "Non-nil means prefer headlines over keywords for TO and FROM. This option can also be set with the OPTIONS keyword, e.g.: \"special-headings:t\"." :type 'boolean) (defcustom org-koma-letter-subject-format t "Non-nil means include the subject. Support formatting options. When t, insert a subject using default options. When nil, do not insert a subject at all. It can also be a list of symbols among the following ones: `afteropening' Subject after opening `beforeopening' Subject before opening `centered' Subject centered `left' Subject left-justified `right' Subject right-justified `titled' Add title/description to subject `underlined' Set subject underlined `untitled' Do not add title/description to subject Please refer to the KOMA-script manual (Table 4.16. in the English manual of 2012-07-22). This option can also be set with the OPTIONS keyword, e.g.: \"subject:(underlined centered)\"." :type '(choice (const :tag "No export" nil) (const :tag "Default options" t) (set :tag "Configure options" (const :tag "Subject after opening" afteropening) (const :tag "Subject before opening" beforeopening) (const :tag "Subject centered" centered) (const :tag "Subject left-justified" left) (const :tag "Subject right-justified" right) (const :tag "Add title or description to subject" underlined) (const :tag "Set subject underlined" titled) (const :tag "Do not add title or description to subject" untitled)))) (defcustom org-koma-letter-use-backaddress nil "Non-nil prints return address in line above to address. This option can also be set with the OPTIONS keyword, e.g.: \"backaddress:t\"." :type 'boolean) (defcustom org-koma-letter-use-foldmarks t "Configure appearance of folding marks. When t, activate default folding marks. When nil, do not insert folding marks at all. It can also be a list of symbols among the following ones: `B' Activate upper horizontal mark on left paper edge `b' Deactivate upper horizontal mark on left paper edge `H' Activate all horizontal marks on left paper edge `h' Deactivate all horizontal marks on left paper edge `L' Activate left vertical mark on upper paper edge `l' Deactivate left vertical mark on upper paper edge `M' Activate middle horizontal mark on left paper edge `m' Deactivate middle horizontal mark on left paper edge `P' Activate punch or center mark on left paper edge `p' Deactivate punch or center mark on left paper edge `T' Activate lower horizontal mark on left paper edge t Deactivate lower horizontal mark on left paper edge `V' Activate all vertical marks on upper paper edge `v' Deactivate all vertical marks on upper paper edge This option can also be set with the OPTIONS keyword, e.g.: \"foldmarks:(b l m t)\"." :type '(choice (const :tag "Activate default folding marks" t) (const :tag "Deactivate folding marks" nil) (set :tag "Configure folding marks" (const :tag "Activate upper horizontal mark on left paper edge" B) (const :tag "Deactivate upper horizontal mark on left paper edge" b) (const :tag "Activate all horizontal marks on left paper edge" H) (const :tag "Deactivate all horizontal marks on left paper edge" h) (const :tag "Activate left vertical mark on upper paper edge" L) (const :tag "Deactivate left vertical mark on upper paper edge" l) (const :tag "Activate middle horizontal mark on left paper edge" M) (const :tag "Deactivate middle horizontal mark on left paper edge" m) (const :tag "Activate punch or center mark on left paper edge" P) (const :tag "Deactivate punch or center mark on left paper edge" p) (const :tag "Activate lower horizontal mark on left paper edge" T) (const :tag "Deactivate lower horizontal mark on left paper edge" t) (const :tag "Activate all vertical marks on upper paper edge" V) (const :tag "Deactivate all vertical marks on upper paper edge" v)))) (defcustom org-koma-letter-use-phone nil "Non-nil prints sender's phone number. This option can also be set with the OPTIONS keyword, e.g.: \"phone:t\"." :type 'boolean) (defcustom org-koma-letter-use-url nil "Non-nil prints sender's URL. This option can also be set with the OPTIONS keyword, e.g.: \"url:t\"." :type 'boolean :safe #'booleanp) (defcustom org-koma-letter-use-from-logo nil "Non-nil prints sender's FROM_LOGO. This option can also be set with the OPTIONS keyword, e.g.: \"from-logo:t\"." :type 'boolean :safe #'booleanp) (defcustom org-koma-letter-use-email nil "Non-nil prints sender's email address. This option can also be set with the OPTIONS keyword, e.g.: \"email:t\"." :type 'boolean) (defcustom org-koma-letter-use-place t "Non-nil prints the letter's place next to the date. This option can also be set with the OPTIONS keyword, e.g.: \"place:nil\"." :type 'boolean) (defcustom org-koma-letter-default-class "default-koma-letter" "Default class for `org-koma-letter'. The value must be a member of `org-latex-classes'." :type 'string) (defcustom org-koma-letter-headline-is-opening-maybe t "Non-nil means a headline may be used as an opening and closing. See also `org-koma-letter-opening' and `org-koma-letter-closing'." :type 'boolean) (defcustom org-koma-letter-prefer-subject nil "Non-nil means title should be interpreted as subject if subject is missing. This option can also be set with the OPTIONS keyword, e.g. \"title-subject:t\"." :type 'boolean) (defconst org-koma-letter-special-tags-in-letter '(to from closing location) "Header tags related to the letter itself.") (defconst org-koma-letter-special-tags-after-closing '(after_closing ps encl cc) "Header tags to be inserted in the letter after closing.") (defconst org-koma-letter-special-tags-as-macro '(ps encl cc) "Header tags to be inserted as macros.") (defconst org-koma-letter-special-tags-after-letter '(after_letter) "Header tags to be inserted after the letter.") (defvar org-koma-letter-special-contents nil "Holds special content temporarily.") ;;; Define Backend (org-export-define-derived-backend 'koma-letter 'latex :options-alist '((:latex-class "LATEX_CLASS" nil org-koma-letter-default-class t) (:lco "LCO" nil org-koma-letter-class-option-file) (:author "AUTHOR" nil (org-koma-letter--get-value org-koma-letter-author) parse) (:author-changed-in-buffer-p "AUTHOR" nil nil t) (:from-address "FROM_ADDRESS" nil org-koma-letter-from-address newline) (:phone-number "PHONE_NUMBER" nil org-koma-letter-phone-number) (:url "URL" nil org-koma-letter-url) (:from-logo "FROM_LOGO" nil org-koma-letter-from-logo) (:email "EMAIL" nil (org-koma-letter--get-value org-koma-letter-email) t) (:to-address "TO_ADDRESS" nil nil newline) (:place "PLACE" nil org-koma-letter-place) (:location "LOCATION" nil org-koma-letter-location) (:subject "SUBJECT" nil nil parse) (:opening "OPENING" nil org-koma-letter-opening parse) (:closing "CLOSING" nil org-koma-letter-closing parse) (:signature "SIGNATURE" nil org-koma-letter-signature newline) (:special-headings nil "special-headings" org-koma-letter-prefer-special-headings) (:special-tags-as-macro nil nil org-koma-letter-special-tags-as-macro) (:special-tags-in-letter nil nil org-koma-letter-special-tags-in-letter) (:special-tags-after-closing nil "after-closing-order" org-koma-letter-special-tags-after-closing) (:special-tags-after-letter nil "after-letter-order" org-koma-letter-special-tags-after-letter) (:with-backaddress nil "backaddress" org-koma-letter-use-backaddress) (:with-email nil "email" org-koma-letter-use-email) (:with-foldmarks nil "foldmarks" org-koma-letter-use-foldmarks) (:with-phone nil "phone" org-koma-letter-use-phone) (:with-url nil "url" org-koma-letter-use-url) (:with-from-logo nil "from-logo" org-koma-letter-use-from-logo) (:with-place nil "place" org-koma-letter-use-place) (:with-subject nil "subject" org-koma-letter-subject-format) (:with-title-as-subject nil "title-subject" org-koma-letter-prefer-subject) (:with-headline-opening nil nil org-koma-letter-headline-is-opening-maybe) ;; Special properties non-nil when a setting happened in buffer. ;; They are used to prioritize in-buffer settings over "lco" ;; files. See `org-koma-letter-template'. (:inbuffer-author "AUTHOR" nil 'koma-letter:empty) (:inbuffer-from "FROM" nil 'koma-letter:empty) (:inbuffer-email "EMAIL" nil 'koma-letter:empty) (:inbuffer-phone-number "PHONE_NUMBER" nil 'koma-letter:empty) (:inbuffer-url "URL" nil 'koma-letter:empty) (:inbuffer-from-logo "FROM_LOGO" nil 'koma-letter:empty) (:inbuffer-place "PLACE" nil 'koma-letter:empty) (:inbuffer-location "LOCATION" nil 'koma-letter:empty) (:inbuffer-signature "SIGNATURE" nil 'koma-letter:empty) (:inbuffer-with-backaddress nil "backaddress" 'koma-letter:empty) (:inbuffer-with-email nil "email" 'koma-letter:empty) (:inbuffer-with-foldmarks nil "foldmarks" 'koma-letter:empty) (:inbuffer-with-phone nil "phone" 'koma-letter:empty) (:inbuffer-with-url nil "url" 'koma-letter:empty) (:inbuffer-with-from-logo nil "from-logo" 'koma-letter:empty) (:inbuffer-with-place nil "place" 'koma-letter:empty)) :translate-alist '((export-block . org-koma-letter-export-block) (export-snippet . org-koma-letter-export-snippet) (headline . org-koma-letter-headline) (keyword . org-koma-letter-keyword) (template . org-koma-letter-template)) :menu-entry '(?k "Export with KOMA Scrlttr2" ((?L "As LaTeX buffer" org-koma-letter-export-as-latex) (?l "As LaTeX file" org-koma-letter-export-to-latex) (?p "As PDF file" org-koma-letter-export-to-pdf) (?o "As PDF file and open" (lambda (a s v b) (if a (org-koma-letter-export-to-pdf t s v b) (org-open-file (org-koma-letter-export-to-pdf nil s v b)))))))) ;;; Helper functions (defun org-koma-letter-email () "Return the current `user-mail-address'." user-mail-address) ;; The following is taken from/inspired by ox-grof.el ;; Thanks, Luis! (defun org-koma-letter--get-tagged-contents (key) "Get contents from a headline tagged with KEY. The contents is stored in `org-koma-letter-special-contents'." (let ((value (cdr (assoc-string (org-koma-letter--get-value key) org-koma-letter-special-contents)))) (when value (org-string-nw-p (org-trim value))))) (defun org-koma-letter--get-value (value) "Turn value into a string whenever possible. Determines if VALUE is nil, a string, a function or a symbol and return a string or nil." (when value (cond ((stringp value) value) ((functionp value) (funcall value)) ((symbolp value) (symbol-name value)) (t value)))) (defun org-koma-letter--special-contents-inline (keywords info) "Process KEYWORDS members of `org-koma-letter-special-contents'. KEYWORDS is a list of symbols. Return them as a string to be formatted. INFO is the information plist possibly holding :special-tags-as-macro property. See `org-koma-letter-special-tags-as-macro'. The function is used for inserting content of special headings such as the one tagged with PS." (mapconcat (lambda (keyword) (let* ((name (org-koma-letter--get-value keyword)) (value (org-koma-letter--get-tagged-contents name)) (macrop (memq keyword (plist-get info :special-tags-as-macro)))) (cond ((not value) nil) (macrop (format "\\%s{%s}\n" name value)) (t value)))) keywords "\n")) (defun org-koma-letter--add-latex-newlines (string) "Replace regular newlines with LaTeX newlines (i.e. `\\\\') in STRING. Return a new string." (let ((str (org-trim string))) (when (org-string-nw-p str) (replace-regexp-in-string "\n" "\\\\\\\\\n" str)))) ;;; Transcode Functions ;;;; Export Block (defun org-koma-letter-export-block (export-block _contents _info) "Transcode an EXPORT-BLOCK element into KOMA Scrlttr2 code. CONTENTS is nil. INFO is a plist used as a communication channel." (when (member (org-element-property :type export-block) '("KOMA-LETTER" "LATEX")) (org-remove-indentation (org-element-property :value export-block)))) ;;;; Export Snippet (defun org-koma-letter-export-snippet (export-snippet _contents _info) "Transcode an EXPORT-SNIPPET object into KOMA Scrlttr2 code. CONTENTS is nil. INFO is a plist used as a communication channel." (when (memq (org-export-snippet-backend export-snippet) '(latex koma-letter)) (org-element-property :value export-snippet))) ;;;; Keyword (defun org-koma-letter-keyword (keyword contents info) "Transcode a KEYWORD element into KOMA Scrlttr2 code. CONTENTS is nil. INFO is a plist used as a communication channel." (let ((key (org-element-property :key keyword)) (value (org-element-property :value keyword))) ;; Handle specifically KOMA-LETTER keywords. Otherwise, fallback ;; to `latex' backend. (if (equal key "KOMA-LETTER") value (org-export-with-backend 'latex keyword contents info)))) ;; Headline (defun org-koma-letter-headline (headline contents info) "Transcode a HEADLINE element from Org to LaTeX. CONTENTS holds the contents of the headline. INFO is a plist holding contextual information. Note that if a headline is tagged with a tag from `org-koma-letter-special-tags' it will not be exported, but stored in `org-koma-letter-special-contents' and included at the appropriate place." (let ((special-tag (org-koma-letter--special-tag headline info))) (if (not special-tag) contents (push (cons special-tag contents) org-koma-letter-special-contents) ""))) (defun org-koma-letter--special-tag (headline info) "Non-nil if HEADLINE is a special headline. INFO is a plist holding contextual information. Return first special tag headline." (let ((special-tags (append (plist-get info :special-tags-in-letter) (plist-get info :special-tags-after-closing) (plist-get info :special-tags-after-letter)))) (cl-some (lambda (tag) (and (assoc-string tag special-tags) tag)) (org-export-get-tags headline info)))) (defun org-koma-letter--keyword-or-headline (plist-key pred info) "Return the correct version of opening or closing. PLIST-KEY should be a key in info, typically :opening or :closing. PRED is a predicate run on headline to determine which title to use which takes two arguments, a headline element and an info plist. INFO is a plist holding contextual information. Return the preferred candidate for the exported of PLIST-KEY." (let* ((keyword-candidate (plist-get info plist-key)) (headline-candidate (when (and (plist-get info :with-headline-opening) (or (plist-get info :special-headings) (not keyword-candidate))) (org-element-map (plist-get info :parse-tree) 'headline (lambda (h) (and (funcall pred h info) (org-element-property :title h))) info t)))) (org-export-data (or headline-candidate keyword-candidate "") info))) ;;;; Template (defun org-koma-letter-template (contents info) "Return complete document string after KOMA Scrlttr2 conversion. CONTENTS is the transcoded contents string. INFO is a plist holding export options." (concat ;; Timestamp. (and (plist-get info :time-stamp-file) (format-time-string "%% Created %Y-%m-%d %a %H:%M\n")) ;; LaTeX compiler (org-latex--insert-compiler info) ;; Document class and packages. (org-latex-make-preamble info) ;; Settings. They can come from three locations, in increasing ;; order of precedence: global variables, LCO files and in-buffer ;; settings. Thus, we first insert settings coming from global ;; variables, then we insert LCO files, and, eventually, we insert ;; settings coming from buffer keywords. (org-koma-letter--build-settings 'global info) (mapconcat (lambda (file) (format "\\LoadLetterOption{%s}\n" file)) (split-string (or (plist-get info :lco) "")) "") (org-koma-letter--build-settings 'buffer info) ;; Date. (format "\\date{%s}\n" (org-export-data (org-export-get-date info) info)) ;; Hyperref, document start, and subject and title. (let* ((with-subject (plist-get info :with-subject)) (with-title (plist-get info :with-title)) (title-as-subject (and with-subject (plist-get info :with-title-as-subject))) (subject* (org-string-nw-p (org-export-data (plist-get info :subject) info))) (title* (and with-title (org-string-nw-p (org-export-data (plist-get info :title) info)))) (subject (cond ((not with-subject) nil) (title-as-subject (or subject* title*)) (t subject*))) (title (cond ((not with-title) nil) (title-as-subject (and subject* title*)) (t title*))) (hyperref-template (plist-get info :latex-hyperref-template)) (spec (append (list (cons ?t (or title subject ""))) (org-latex--format-spec info)))) (concat (when (and with-subject (not (eq with-subject t))) (format "\\KOMAoption{subject}{%s}\n" (if (symbolp with-subject) with-subject (mapconcat #'symbol-name with-subject ",")))) ;; Hyperref. (and (stringp hyperref-template) (format-spec hyperref-template spec)) ;; Document start. "\\begin{document}\n\n" ;; Subject and title. (when subject (format "\\setkomavar{subject}{%s}\n" subject)) (when title (format "\\setkomavar{title}{%s}\n" title)) (when (or (org-string-nw-p title) (org-string-nw-p subject)) "\n"))) ;; Letter start. (let ((keyword-val (plist-get info :to-address)) (heading-val (org-koma-letter--get-tagged-contents 'to))) (format "\\begin{letter}{%%\n%s}\n\n" (org-koma-letter--add-latex-newlines (or (if (plist-get info :special-headings) (or heading-val keyword-val) (or keyword-val heading-val)) "\\mbox{}")))) ;; Opening. (format "\\opening{%s}\n\n" (org-koma-letter--keyword-or-headline :opening (lambda (h i) (not (org-koma-letter--special-tag h i))) info)) ;; Letter body. contents ;; Closing. (format "\\closing{%s}\n" (org-koma-letter--keyword-or-headline :closing (lambda (h i) (let ((special-tag (org-koma-letter--special-tag h i))) (and special-tag (string= "closing" special-tag)))) info)) (org-koma-letter--special-contents-inline (plist-get info :special-tags-after-closing) info) ;; Letter end. "\n\\end{letter}\n" (org-koma-letter--special-contents-inline (plist-get info :special-tags-after-letter) info) ;; Document end. "\n\\end{document}")) (defun org-koma-letter--build-settings (scope info) "Build settings string according to type. SCOPE is either `global' or `buffer'. INFO is a plist used as a communication channel." (let* ((check-scope ;; Non-nil value when SETTING was defined in SCOPE. (lambda (setting) (let ((property (intern (format ":inbuffer-%s" setting)))) (if (eq scope 'global) (eq (plist-get info property) 'koma-letter:empty) (not (eq (plist-get info property) 'koma-letter:empty)))))) (heading-or-key-value (lambda (heading key &optional scoped) (let* ((heading-val (org-koma-letter--get-tagged-contents heading)) (key-val (org-string-nw-p (plist-get info key))) (scopedp (funcall check-scope (or scoped heading)))) (and (or (and key-val scopedp) heading-val) (not (and (eq scope 'global) heading-val)) (if scopedp key-val heading-val)))))) (concat ;; Name. (let ((author (plist-get info :author))) (and author (funcall check-scope 'author) (format "\\setkomavar{fromname}{%s}\n" (org-export-data author info)))) ;; From. (let ((from (funcall heading-or-key-value 'from :from-address))) (and from (format "\\setkomavar{fromaddress}{%s}\n" (org-koma-letter--add-latex-newlines from)))) ;; Email. (let ((email (plist-get info :email))) (and email (funcall check-scope 'email) (format "\\setkomavar{fromemail}{%s}\n" email))) (and (funcall check-scope 'with-email) (format "\\KOMAoption{fromemail}{%s}\n" (if (plist-get info :with-email) "true" "false"))) ;; Phone number. (let ((phone-number (plist-get info :phone-number))) (and (org-string-nw-p phone-number) (funcall check-scope 'phone-number) (format "\\setkomavar{fromphone}{%s}\n" phone-number))) (and (funcall check-scope 'with-phone) (format "\\KOMAoption{fromphone}{%s}\n" (if (plist-get info :with-phone) "true" "false"))) ;; URL (let ((url (plist-get info :url))) (and (org-string-nw-p url) (funcall check-scope 'url) (format "\\setkomavar{fromurl}{%s}\n" url))) (and (funcall check-scope 'with-url) (format "\\KOMAoption{fromurl}{%s}\n" (if (plist-get info :with-url) "true" "false"))) ;; From Logo (let ((from-logo (plist-get info :from-logo))) (and (org-string-nw-p from-logo) (funcall check-scope 'from-logo) (format "\\setkomavar{fromlogo}{%s}\n" from-logo))) (and (funcall check-scope 'with-from-logo) (format "\\KOMAoption{fromlogo}{%s}\n" (if (plist-get info :with-from-logo) "true" "false"))) ;; Signature. (let* ((heading-val (and (plist-get info :with-headline-opening) (pcase (org-koma-letter--get-tagged-contents 'closing) ((and (pred org-string-nw-p) closing) (org-trim closing)) (_ nil)))) (signature (org-string-nw-p (plist-get info :signature))) (signature-scope (funcall check-scope 'signature))) (and (or (and signature signature-scope) heading-val) (not (and (eq scope 'global) heading-val)) (format "\\setkomavar{signature}{%s}\n" (if signature-scope signature heading-val)))) ;; Back address. (and (funcall check-scope 'with-backaddress) (format "\\KOMAoption{backaddress}{%s}\n" (if (plist-get info :with-backaddress) "true" "false"))) ;; Place. (let ((with-place-set (funcall check-scope 'with-place)) (place-set (funcall check-scope 'place))) (and (or (and with-place-set place-set) (and (eq scope 'buffer) (or with-place-set place-set))) (format "\\setkomavar{place}{%s}\n" (if (plist-get info :with-place) (plist-get info :place) "")))) ;; Location. (let ((location (funcall heading-or-key-value 'location :location))) (and location (format "\\setkomavar{location}{%s}\n" location))) ;; Folding marks. (and (funcall check-scope 'with-foldmarks) (let ((foldmarks (plist-get info :with-foldmarks))) (cond ((consp foldmarks) (format "\\KOMAoptions{foldmarks=true,foldmarks=%s}\n" (mapconcat #'symbol-name foldmarks ""))) (foldmarks "\\KOMAoptions{foldmarks=true}\n") (t "\\KOMAoptions{foldmarks=false}\n"))))))) ;;; Commands ;;;###autoload (defun org-koma-letter-export-as-latex (&optional async subtreep visible-only body-only ext-plist) "Export current buffer as a KOMA Scrlttr2 letter. If narrowing is active in the current buffer, only export its narrowed part. If a region is active, export that region. A non-nil optional argument ASYNC means the process should happen asynchronously. The resulting buffer should be accessible through the `org-export-stack' interface. When optional argument SUBTREEP is non-nil, export the sub-tree at point, extracting information from the headline properties first. When optional argument VISIBLE-ONLY is non-nil, don't export contents of hidden elements. When optional argument BODY-ONLY is non-nil, only write code between \"\\begin{letter}\" and \"\\end{letter}\". EXT-PLIST, when provided, is a property list with external parameters overriding Org default settings, but still inferior to file-local settings. Export is done in a buffer named \"*Org KOMA-LETTER Export*\". It will be displayed if `org-export-show-temporary-export-buffer' is non-nil." (interactive) (let (org-koma-letter-special-contents) (org-export-to-buffer 'koma-letter "*Org KOMA-LETTER Export*" async subtreep visible-only body-only ext-plist (if (fboundp 'major-mode-remap) (major-mode-remap 'latex-mode) #'LaTeX-mode)))) ;;;###autoload (defun org-koma-letter-export-to-latex (&optional async subtreep visible-only body-only ext-plist) "Export current buffer as a KOMA Scrlttr2 letter (tex). If narrowing is active in the current buffer, only export its narrowed part. If a region is active, export that region. A non-nil optional argument ASYNC means the process should happen asynchronously. The resulting file should be accessible through the `org-export-stack' interface. When optional argument SUBTREEP is non-nil, export the sub-tree at point, extracting information from the headline properties first. When optional argument VISIBLE-ONLY is non-nil, don't export contents of hidden elements. When optional argument BODY-ONLY is non-nil, only write code between \"\\begin{letter}\" and \"\\end{letter}\". EXT-PLIST, when provided, is a property list with external parameters overriding Org default settings, but still inferior to file-local settings. When optional argument PUB-DIR is set, use it as the publishing directory. Return output file's name." (interactive) (let ((outfile (org-export-output-file-name ".tex" subtreep)) (org-koma-letter-special-contents)) (org-export-to-file 'koma-letter outfile async subtreep visible-only body-only ext-plist))) ;;;###autoload (defun org-koma-letter-export-to-pdf (&optional async subtreep visible-only body-only ext-plist) "Export current buffer as a KOMA Scrlttr2 letter (pdf). If narrowing is active in the current buffer, only export its narrowed part. If a region is active, export that region. A non-nil optional argument ASYNC means the process should happen asynchronously. The resulting file should be accessible through the `org-export-stack' interface. When optional argument SUBTREEP is non-nil, export the sub-tree at point, extracting information from the headline properties first. When optional argument VISIBLE-ONLY is non-nil, don't export contents of hidden elements. When optional argument BODY-ONLY is non-nil, only write code between \"\\begin{letter}\" and \"\\end{letter}\". EXT-PLIST, when provided, is a property list with external parameters overriding Org default settings, but still inferior to file-local settings. Return PDF file's name." (interactive) (let ((file (org-export-output-file-name ".tex" subtreep)) (org-koma-letter-special-contents)) (org-export-to-file 'koma-letter file async subtreep visible-only body-only ext-plist #'org-latex-compile))) (provide 'ox-koma-letter) ;;; ox-koma-letter.el ends here org-mode-9.7.29+dfsg/lisp/ox-latex.el000066400000000000000000005375621500430433700173250ustar00rootroot00000000000000;;; ox-latex.el --- LaTeX Backend for Org Export Engine -*- lexical-binding: t; -*- ;; Copyright (C) 2011-2025 Free Software Foundation, Inc. ;; Author: Nicolas Goaziou ;; Maintainer: Daniel Fleischer ;; Keywords: outlines, hypermedia, calendar, text ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; ;; See Org manual for details. ;;; Code: (require 'org-macs) (org-assert-version) (require 'cl-lib) (require 'ox) (require 'ox-publish) ;;; Function Declarations (defvar org-latex-default-packages-alist) (defvar org-latex-packages-alist) (defvar orgtbl-exp-regexp) (declare-function engrave-faces-latex-gen-preamble "ext:engrave-faces-latex") (declare-function engrave-faces-latex-buffer "ext:engrave-faces-latex") (declare-function engrave-faces-latex-gen-preamble-line "ext:engrave-faces-latex") (declare-function engrave-faces-get-theme "ext:engrave-faces") (defvar engrave-faces-latex-output-style) (defvar engrave-faces-current-preset-style) (defvar engrave-faces-latex-mathescape) ;;; Define Backend (org-export-define-backend 'latex '((bold . org-latex-bold) (center-block . org-latex-center-block) (clock . org-latex-clock) (code . org-latex-code) (drawer . org-latex-drawer) (dynamic-block . org-latex-dynamic-block) (entity . org-latex-entity) (example-block . org-latex-example-block) (export-block . org-latex-export-block) (export-snippet . org-latex-export-snippet) (fixed-width . org-latex-fixed-width) (footnote-definition . org-latex-footnote-definition) (footnote-reference . org-latex-footnote-reference) (headline . org-latex-headline) (horizontal-rule . org-latex-horizontal-rule) (inline-src-block . org-latex-inline-src-block) (inlinetask . org-latex-inlinetask) (italic . org-latex-italic) (item . org-latex-item) (keyword . org-latex-keyword) (latex-environment . org-latex-latex-environment) (latex-fragment . org-latex-latex-fragment) (line-break . org-latex-line-break) (link . org-latex-link) (node-property . org-latex-node-property) (paragraph . org-latex-paragraph) (plain-list . org-latex-plain-list) (plain-text . org-latex-plain-text) (planning . org-latex-planning) (property-drawer . org-latex-property-drawer) (quote-block . org-latex-quote-block) (radio-target . org-latex-radio-target) (section . org-latex-section) (special-block . org-latex-special-block) (src-block . org-latex-src-block) (statistics-cookie . org-latex-statistics-cookie) (strike-through . org-latex-strike-through) (subscript . org-latex-subscript) (superscript . org-latex-superscript) (table . org-latex-table) (table-cell . org-latex-table-cell) (table-row . org-latex-table-row) (target . org-latex-target) (template . org-latex-template) (timestamp . org-latex-timestamp) (underline . org-latex-underline) (verbatim . org-latex-verbatim) (verse-block . org-latex-verse-block) ;; Pseudo objects and elements. (latex-math-block . org-latex-math-block) (latex-matrices . org-latex-matrices)) :menu-entry '(?l "Export to LaTeX" ((?L "As LaTeX buffer" org-latex-export-as-latex) (?l "As LaTeX file" org-latex-export-to-latex) (?p "As PDF file" org-latex-export-to-pdf) (?o "As PDF file and open" (lambda (a s v b) (if a (org-latex-export-to-pdf t s v b) (org-open-file (org-latex-export-to-pdf nil s v b))))))) :filters-alist '((:filter-options . org-latex-math-block-options-filter) (:filter-paragraph . org-latex-clean-invalid-line-breaks) (:filter-parse-tree org-latex-math-block-tree-filter org-latex-matrices-tree-filter org-latex-image-link-filter) (:filter-verse-block . org-latex-clean-invalid-line-breaks)) :options-alist '((:latex-class "LATEX_CLASS" nil org-latex-default-class t) (:latex-class-options "LATEX_CLASS_OPTIONS" nil nil t) (:latex-header "LATEX_HEADER" nil nil newline) (:latex-header-extra "LATEX_HEADER_EXTRA" nil nil newline) (:description "DESCRIPTION" nil nil parse) (:keywords "KEYWORDS" nil nil parse) (:subtitle "SUBTITLE" nil nil parse) ;; Other variables. (:latex-active-timestamp-format nil nil org-latex-active-timestamp-format) (:latex-caption-above nil nil org-latex-caption-above) (:latex-classes nil nil org-latex-classes) (:latex-default-figure-position nil nil org-latex-default-figure-position) (:latex-default-table-environment nil nil org-latex-default-table-environment) (:latex-default-quote-environment nil nil org-latex-default-quote-environment) (:latex-default-table-mode nil nil org-latex-default-table-mode) (:latex-default-footnote-command "LATEX_FOOTNOTE_COMMAND" nil org-latex-default-footnote-command) (:latex-diary-timestamp-format nil nil org-latex-diary-timestamp-format) (:latex-engraved-options nil nil org-latex-engraved-options) (:latex-engraved-preamble nil nil org-latex-engraved-preamble) (:latex-engraved-theme "LATEX_ENGRAVED_THEME" nil org-latex-engraved-theme) (:latex-footnote-defined-format nil nil org-latex-footnote-defined-format) (:latex-footnote-separator nil nil org-latex-footnote-separator) (:latex-format-drawer-function nil nil org-latex-format-drawer-function) (:latex-format-headline-function nil nil org-latex-format-headline-function) (:latex-format-inlinetask-function nil nil org-latex-format-inlinetask-function) (:latex-hyperref-template nil nil org-latex-hyperref-template t) (:latex-image-default-scale nil nil org-latex-image-default-scale) (:latex-image-default-height nil nil org-latex-image-default-height) (:latex-image-default-option nil nil org-latex-image-default-option) (:latex-image-default-width nil nil org-latex-image-default-width) (:latex-images-centered nil nil org-latex-images-centered) (:latex-inactive-timestamp-format nil nil org-latex-inactive-timestamp-format) (:latex-inline-image-rules nil nil org-latex-inline-image-rules) (:latex-link-with-unknown-path-format nil nil org-latex-link-with-unknown-path-format) (:latex-src-block-backend nil nil org-latex-src-block-backend) (:latex-listings-langs nil nil org-latex-listings-langs) (:latex-listings-options nil nil org-latex-listings-options) (:latex-listings-src-omit-language nil nil org-latex-listings-src-omit-language) (:latex-minted-langs nil nil org-latex-minted-langs) (:latex-minted-options nil nil org-latex-minted-options) (:latex-prefer-user-labels nil nil org-latex-prefer-user-labels) (:latex-subtitle-format nil nil org-latex-subtitle-format) (:latex-subtitle-separate nil nil org-latex-subtitle-separate) (:latex-table-scientific-notation nil nil org-latex-table-scientific-notation) (:latex-tables-booktabs nil nil org-latex-tables-booktabs) (:latex-tables-centered nil nil org-latex-tables-centered) (:latex-text-markup-alist nil nil org-latex-text-markup-alist) (:latex-title-command nil nil org-latex-title-command) (:latex-toc-command nil nil org-latex-toc-command) (:latex-compiler "LATEX_COMPILER" nil org-latex-compiler) ;; Redefine regular options. (:date "DATE" nil "\\today" parse))) ;;; Internal Variables (defconst org-latex-language-alist (let ((de-default-plist '(:babel "ngerman" :babel-ini-alt "german" :polyglossia "german" :polyglossia-variant "german" :lang-name "German" :script "latin" :script-tag "latn")) (zh-default-plist '(:babel-ini-only "chinese" :polyglossia "chinese" :polyglossia-variant "simplified" :lang-name "Chinese Simplified" :script "hans" :script-tag "hans"))) `(("af" :babel "afrikaans" :polyglossia "afrikaans" :lang-name "Afrikaans" :script "latin" :script-tag "latn") ("am" :babel-ini-only "amharic" :polyglossia "amharic" :lang-name "Amharic" :script "ethiopic" :script-tag "ethi") ("ar" :babel-ini-only "arabic" :polyglossia "arabic" :lang-name "Arabic" :script "arabic" :script-tag "arab") ("ast" :babel-ini-only "asturian" :polyglossia "asturian" :lang-name "Asturian" :script "latin" :script-tag "latn") ("bg" :babel "bulgarian" :polyglossia "bulgarian" :lang-name "Bulgarian" :script "cyrillic" :script-tag "cyrl") ("bn" :babel-ini-only "bengali" :polyglossia "bengali" :lang-name "Bengali" :script "bengali" :script-tag: "beng") ("bo" :babel-ini-only "tibetan" :polyglossia "tibetan" :lang-name "Tibetan" :script "tibetan" :script-tag "tib") ("br" :babel "breton" :polyglossia "breton" :lang-name "Breton" :script "latin" :script-tag "latn") ("ca" :babel "catalan" :polyglossia "catalan" :lang-name "Catalan" :script "latin" :script-tag "latn") ("cop" :babel-ini-only "coptic" :polyglossia "coptic" :lang-name "Coptic" :script "coptic" :script-tag "copt") ("cs" :babel "czech" :polyglossia "czech" :lang-name "Czech" :script "latin" :script-tag "latn") ("cy" :babel "welsh" :polyglossia "welsh" :lang-name "Welsh" :script "latin" :script-tag "latn") ("da" :babel "danish" :polyglossia "danish" :lang-name "Danish" :script "latin" :script-tag "latn") ("de" ,@de-default-plist) ("de-de" ,@de-default-plist) ("de-at" :babel "naustrian" :babel-ini-alt "german-austria" :polyglossia "german" :polyglossia-variant "austrian" :lang-name "German" :script "latin" :script-tag "latn") ("dsb" :babel "lowersorbian" :babel-ini-alt "lsorbian" :polyglossia "sorbian" :polyglossia-variant "lower" :lang-name "Lower Sorbian" :script "latin" :script-tag "latn") ("dv" :polyglossia "divehi" :lang-name "Dhivehi" :script "latin" :script-tag "latn") ("el" :babel "greek" :polyglossia "greek" :lang-name "Greek" :script "greek" :script-tag "grek") ("el-polyton" :babel "polutonikogreek" :babel-ini-alt "polytonicgreek" :polyglossia "greek" :polyglossia-variant "polytonic" :lang-name "Polytonic Greek" :script "greek" :script-tag "grek") ("grc" :babel "greek.ancient" :babel-ini-alt "ancientgreek" :polyglossia "greek" :polyglossia-variant "ancient" :lang-name "Ancient Greek" :script "greek" :script-tag "grek") ("en" :babel "english" :polyglossia "english" :polyglossia-variant "usmax" :lang-name "English" :script "latin" :script-tag "latn") ("en-au" :babel "australian" :polyglossia "english" :polyglossia-variant "australian" :lang-name "English" :script "latin" :script-tag "latn") ("en-ca" :babel "canadian" :polyglossia "english" :polyglossia-variant "canadian" :lang-name "English" :script "latin" :script-tag "latn") ("en-gb" :babel "british" :polyglossia "english" :polyglossia-variant "uk" :lang-name "English" :script "latin" :script-tag "latn") ("en-nz" :babel "newzealand" :polyglossia "english" :polyglossia-variant "newzealand" :lang-name "English" :script "latin" :script-tag "latn") ("en-us" :babel "american" :polyglossia "english" :polyglossia-variant "usmax" :lang-name "English" :script "latin" :script-tag "latn") ("eo" :babel "esperanto" :polyglossia "esperanto" :lang-name "Esperanto" :script "latin" :script-tag "latn") ("es" :babel "spanish" :polyglossia "spanish" :lang-name "Spanish" :script "latin" :script-tag "latn") ("es-mx" :babel "spanishmx" :polyglossia "spanish" :polyglossia-variant "mexican" :lang-name "Spanish" :script "latin" :script-tag "latn") ("et" :babel "estonian" :polyglossia "estonian" :lang-name "Estonian" :script "latin" :script-tag "latn") ("eu" :babel "basque" :polyglossia "basque" :lang-name "Basque" :script "latin" :script-tag "latn") ("fa" :babel "persian" :polyglossia "persian" :lang-name "Persian" :script "arabic" :script-tag "arab") ("fi" :babel "finnish" :polyglossia "finnish" :lang-name "Finnish" :script "latin" :script-tag "latn") ("fr" :babel "french" :polyglossia "french" :lang-name "French" :script "latin" :script-tag "latn") ("fr-ca" :babel "canadien" :babel-ini-alt "canadian" :polyglossia "french" :polyglossia-variant "canadian" :lang-name "French" :script "latin" :script-tag "latn") ("fur" :babel "friulian" :polyglossia "friulian" :lang-name "Friulian" :script "latin" :script-tag "latn") ("ga" :babel "irish" :polyglossia "gaelic" :polyglossia-variant "irish" :lang-name "Irish Gaelic" :script "latin" :script-tag "latn") ("gd" :babel "scottish" :polyglossia "gaelic" :polyglossia-variant "scottish" :lang-name "Scottish Gaelic" :script "latin" :script-tag "latn") ("gl" :babel "galician" :polyglossia "galician" :lang-name "Galician" :script "latin" :script-tag "latn") ("he" :babel "hebrew" :polyglossia "hebrew" :lang-name "Hebrew" :script "hebrew" :script-tag "hebr") ("hi" :babel "hindi" :polyglossia "hindi" :lang-name "Hindi" :script "devanagari" :script-tag "deva") ("hr" :babel "croatian" :polyglossia "croatian" :lang-name "Croatian" :script "latin" :script-tag "latn") ("hsb" :babel "uppersorbian" :polyglossia "sorbian" :polyglossia-variant "upper" :lang-name "Upper Sorbian" :script "latin" :script-tag "latn") ("hu" :babel "magyar" :polyglossia "magyar" :lang-name "Magyar" :script "latin" :script-tag "latn") ("hy" :babel-ini-only "armenian" :polyglossia "armenian" :lang-name "Armenian" :script "armenian" :script-tag "armn") ("ia" :babel "interlingua" :polyglossia "interlingua" :lang-name "Interlingua" :script "latin" :script-tag "latn") ("id" :babel "indonesian" :polyglossia "malay" :polyglossia-variant "indonesian" :lang-name "Indonesian" :script "latin" :script-tag "latn") ("is" :babel "icelandic" :polyglossia "icelandic" :lang-name "Icelandic" :script "latin" :script-tag "latn") ("it" :babel "italian" :polyglossia "italian" :lang-name "Italian" :script "latin" :script-tag "latn") ("kn" :babel-ini-only "kannada" :polyglossia "kannada" :lang-name "Kannada" :script "kannada" :script-tag "knda") ("la" :babel "latin" :polyglossia "latin" :lang-name "Latin" :script "latin" :script-tag "latn") ("la-classic" :babel "classiclatin" :polyglossia "latin" :polyglossia-variant "classic" :lang-name "Classic Latin" :script "latin" :script-tag "latn") ("la-medieval" :babel "medievallatin" :polyglossia "latin" :polyglossia-variant "medieval" :lang-name "Medieval Latin" :script "latin" :script-tag "latn") ("la-ecclesiastic" :babel "ecclesiasticlatin" :polyglossia "latin" :polyglossia-variant "ecclesiastic" :lang-name "Ecclesiastic Latin" :script "latin" :script-tag "latn") ("lo" :babel-ini-only "lao" :polyglossia "lao" :lang-name "Lao" :script "lao" :script-tag "lao") ("lt" :babel "lithuanian" :polyglossia "lithuanian" :lang-name "Lithuanian" :script "latin" :script-tag "latn") ("lv" :babel "latvian" :polyglossia "latvian" :lang-name "Latvian" :script "latin" :script-tag "latn") ("ml" :babel-ini-only "malayalam" :polyglossia "malayalam" :lang-name "Malayalam" :script "malayalam" :script-tag "mlym") ("mr" :babel-ini-only "marathi" :polyglossia "marathi" :lang-name "Marathi" :script "devanagari" :script-tag "deva") ("ms" :babel "malay" :polyglossia "malay" :polyglossia-variant "malaysian" :lang-name "Malay" :script "latin" :script-tag "latn") ("nb" :babel "norsk" :polyglossia "norwegian" :polyglossia-variant "bokmal" :lang-name "Norwegian Bokmål" :script "latin" :script-tag "latn") ("nl" :babel "dutch" :polyglossia "dutch" :lang-name "Dutch" :script "latin" :script-tag "latn") ("nn" :babel "nynorsk" :polyglossia "norwegian" :polyglossia-variant "nynorsk" :lang-name "Norwegian Nynorsk" :script "latin" :script-tag "latn") ("no" :babel "norsk" :polyglossia "norsk" :lang-name "Norwegian" :script "latin" :script-tag "latn") ("oc" :babel "occitan" :polyglossia "occitan" :lang-name "Occitan" :script "latin" :script-tag "latn") ("pl" :babel "polish" :polyglossia "polish" :lang-name "Polish" :script "latin" :script-tag "latn") ("pms" :babel "piedmontese" :polyglossia "piedmontese" :lang-name "Piedmontese" :script "latin" :script-tag "latn") ("pt" :babel "portuges" :polyglossia "portuges" :lang-name "Portuges" :script "latin" :script-tag "latn") ("pt-br" :babel "brazilian" :polyglossia "brazilian" :lang-name "Portuges" :script "latin" :script-tag "latn") ("rm" :babel-ini-only "romansh" :polyglossia "romansh" :lang-name "Romansh" :script "latin" :script-tag "latn") ("ro" :babel "romanian" :polyglossia "romanian" :lang-name "Romanian" :script "latin" :script-tag "latn") ("ru" :babel "russian" :polyglossia "russian" :lang-name "Russian" :script "cyrillic" :script-tag "cyrl") ("sa" :babel-ini-only "sanskrit" :polyglossia "sanskrit" :lang-name "Sanskrit" :script "devanagari" :script-tag "deva") ("sk" :babel "slovak" :polyglossia "slovak" :lang-name "Slovak" :script "latin" :script-tag "latn") ("sl" :babel "slovene" :polyglossia "slovene" :lang-name "Slovene" :script "latin" :script-tag "latn") ("sq" :babel "albanian" :polyglossia "albanian" :lang-name "Albanian" :script "latin" :script-tag "latn") ("sr" :babel "serbian" :polyglossia "serbian" :lang-name "Serbian" :script "latin" :script-tag "latn") ("sr-cyrl" :babel-ini-only "serbian-cyrl" :polyglossia "serbian" :lang-name "Serbian" :script "cyrillic" :script-tag "cyrl") ("sr-latn" :babel-ini-only "serbian-latin" :polyglossia "serbian" :lang-name "Serbian" :script "latin" :script-tag "latn") ("sv" :babel "swedish" :polyglossia "swedish" :lang-name "Swedish" :script "latin" :script-tag "latn") ("syr" :babel-ini-only "syriac" :polyglossia "syriac" :lang-name "Syriac" :script "syriac" :script-tag "syrc") ("ta" :babel-ini-only "tamil" :polyglossia "tamil" :lang-name "Tamil" :script "tamil" :script-tag "taml") ("te" :babel-ini-only "telugu" :polyglossia "telugu" :lang-name "Telugu" :script "telugu" :script-tag "telu") ("th" :babel "thai" :polyglossia "thai" :lang-name "Thai" :script "thai" :script-tag "thai") ("tk" :babel "turkmen" :polyglossia "turkmen" :lang-name "Turkmen" :script "latin" :script-tag "latn") ("tr" :babel "turkish" :polyglossia "turkish" :lang-name "Turkish" :script "latin" :script-tag "latn") ("uk" :babel "ukrainian" :polyglossia "ukrainian" :lang-name "Ukrainian" :script "cyrillic" :script-tag "cyrl") ("ur" :babel-ini-only "urdu" :polyglossia "urdu" :lang-name "Urdu" :script "arabic" :script-tag "arab") ("vi" :babel "vietnamese" :polyglossia "vietnamese" :lang-name "Vietnamese" :script "latin" :script-tag "latn") ("zh" ,@zh-default-plist) ("zh-cn" ,@zh-default-plist) ("zh-tw" :babel-ini-only "chinese-traditional" :polyglossia "chinese" :polyglossia-variant "traditional" :lang-name "Chinese Traditional" :script "hant" :script-tag "hant"))) "Alist between language code and its properties for LaTeX export. In each element of the list car is always the language code and cdr is a property list. Valid keywords for this list can be: - `:babel' the name of the language loaded by the Babel LaTeX package - `:polyglossia' the name of the language loaded by the Polyglossia LaTeX package - `:babel-ini-only' the name of the language loaded by Babel exclusively through the new ini files method. See `http://mirrors.ctan.org/macros/latex/required/babel/base/babel.pdf' - `:babel-ini-alt' an alternative language name when it is loaded using ini files - `:polyglossia-variant' the language variant loaded by Polyglossia - `:lang-name' the actual name of the language - `:script' the script name - `:script-tag' the script otf tag.") (defconst org-latex-table-matrix-macros '(("bordermatrix" . "\\cr") ("qbordermatrix" . "\\cr") ("kbordermatrix" . "\\\\")) "Alist between matrix macros and their row ending.") (defconst org-latex-math-environments-re (format "\\`[ \t]*\\\\begin{%s\\*?}" (regexp-opt '("equation" "eqnarray" "math" "displaymath" "align" "gather" "multline" "flalign" "alignat" "xalignat" "xxalignat" "subequations" ;; breqn "dmath" "dseries" "dgroup" "darray" ;; empheq "empheq"))) "Regexp of LaTeX math environments.") ;;; User Configurable Variables (defgroup org-export-latex nil "Options for exporting Org mode files to LaTeX." :tag "Org Export LaTeX" :group 'org-export) ;;;; Generic (defcustom org-latex-caption-above '(table) "When non-nil, place caption string at the beginning of elements. Otherwise, place it near the end. When value is a list of symbols, put caption above selected elements only. Allowed symbols are: `image', `table', `src-block' and `special-block'." :group 'org-export-latex :version "26.1" :package-version '(Org . "8.3") :type '(choice (const :tag "For all elements" t) (const :tag "For no element" nil) (set :tag "For the following elements only" :greedy t (const :tag "Images" image) (const :tag "Tables" table) (const :tag "Source code" src-block) (const :tag "Special blocks" special-block)))) (defcustom org-latex-prefer-user-labels nil "Use user-provided labels instead of internal ones when non-nil. When this variable is non-nil, Org will use the value of CUSTOM_ID property, NAME keyword or Org target as the key for the \\label commands generated. By default, Org generates its own internal labels during LaTeX export. This process ensures that the \\label keys are unique and valid, but it means the keys are not available in advance of the export process. Setting this variable gives you control over how Org generates labels during LaTeX export, so that you may know their keys in advance. One reason to do this is that it allows you to refer to various elements using a single label both in Org's link syntax and in embedded LaTeX code. For example, when this variable is non-nil, a headline like this: ** Some section :PROPERTIES: :CUSTOM_ID: sec:foo :END: This is section [[#sec:foo]]. #+BEGIN_EXPORT latex And this is still section \\ref{sec:foo}. #+END_EXPORT will be exported to LaTeX as: \\subsection{Some section} \\label{sec:foo} This is section \\ref{sec:foo}. And this is still section \\ref{sec:foo}. A non-default value of `org-latex-reference-command' will change the command (\\ref by default) used to create label references. Note, however, that setting this variable introduces a limitation on the possible values for CUSTOM_ID and NAME. When this variable is non-nil, Org passes their value to \\label unchanged. You are responsible for ensuring that the value is a valid LaTeX \\label key, and that no other \\label commands with the same key appear elsewhere in your document. (Keys may contain letters, numbers, and the following punctuation: `_' `.' `-' `:'.) There are no such limitations on CUSTOM_ID and NAME when this variable is nil. For headlines that do not define the CUSTOM_ID property or elements without a NAME, Org will continue to use its default labeling scheme to generate labels and resolve links into proper references." :group 'org-export-latex :type 'boolean :version "26.1" :package-version '(Org . "8.3") :safe #'booleanp) (defcustom org-latex-reference-command "\\ref{%s}" "Format string that takes a reference to produce a LaTeX reference command. The reference is a label such as sec:intro. A format string of \"\\ref{%s}\" produces numbered references and will always work. It may be desirable to make use of a package such as hyperref or cleveref and then change the format string to \"\\autoref{%s}\" or \"\\cref{%s}\" for example." :group 'org-export-latex :type 'string :package-version '(Org . "9.5")) ;;;; Preamble (defcustom org-latex-default-class "article" "The default LaTeX class." :group 'org-export-latex :type '(string :tag "LaTeX class")) (defcustom org-latex-classes '(("article" "\\documentclass[11pt]{article}" ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}")) ("report" "\\documentclass[11pt]{report}" ("\\part{%s}" . "\\part*{%s}") ("\\chapter{%s}" . "\\chapter*{%s}") ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}")) ("book" "\\documentclass[11pt]{book}" ("\\part{%s}" . "\\part*{%s}") ("\\chapter{%s}" . "\\chapter*{%s}") ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}"))) "Alist of LaTeX classes and associated header and structure. If #+LATEX_CLASS is set in the buffer, use its value and the associated information. Here is the structure of each cell: (class-name header-string (numbered-section . unnumbered-section) ...) The header string ----------------- The HEADER-STRING is the header that will be inserted into the LaTeX file. It should contain the \\documentclass macro, and anything else that is needed for this setup. To this header, the following commands will be added: - Calls to \\usepackage for all packages mentioned in the variables `org-latex-default-packages-alist' and `org-latex-packages-alist'. Thus, your header definitions should avoid to also request these packages. - Lines specified via \"#+LATEX_HEADER:\" and \"#+LATEX_HEADER_EXTRA:\" keywords. If you need more control about the sequence in which the header is built up, or if you want to exclude one of these building blocks for a particular class, you can use the following macro-like placeholders. [DEFAULT-PACKAGES] \\usepackage statements for default packages [NO-DEFAULT-PACKAGES] do not include any of the default packages [PACKAGES] \\usepackage statements for packages [NO-PACKAGES] do not include the packages [EXTRA] the stuff from #+LATEX_HEADER(_EXTRA) [NO-EXTRA] do not include #+LATEX_HEADER(_EXTRA) stuff So a header like \\documentclass{article} [NO-DEFAULT-PACKAGES] [EXTRA] \\providecommand{\\alert}[1]{\\textbf{#1}} [PACKAGES] will omit the default packages, and will include the #+LATEX_HEADER and #+LATEX_HEADER_EXTRA lines, then have a call to \\providecommand, and then place \\usepackage commands based on the content of `org-latex-packages-alist'. If your header, `org-latex-default-packages-alist' or `org-latex-packages-alist' inserts \"\\usepackage[AUTO]{inputenc}\", AUTO will automatically be replaced with a coding system derived from `buffer-file-coding-system'. See also the variable `org-latex-inputenc-alist' for a way to influence this mechanism. Likewise, if your header contains \"\\usepackage[AUTO]{babel}\" or \"\\usepackage[AUTO]{polyglossia}\", AUTO will be replaced with the language related to the language code specified by `org-export-default-language'. Note that constructions such as \"\\usepackage[french,AUTO,english]{babel}\" are permitted. For Polyglossia the language will be set via the macros \"\\setmainlanguage\" and \"\\setotherlanguage\". See also `org-latex-guess-babel-language' and `org-latex-guess-polyglossia-language'. The sectioning structure ------------------------ The sectioning structure of the class is given by the elements following the header string. For each sectioning level, a number of strings is specified. A %s formatter is mandatory in each section string and will be replaced by the title of the section. Instead of a cons cell (numbered . unnumbered), you can also provide a list of 2 or 4 elements, (numbered-open numbered-close) or (numbered-open numbered-close unnumbered-open unnumbered-close) providing opening and closing strings for a LaTeX environment that should represent the document section. The opening clause should have a %s to represent the section title. Instead of a list of sectioning commands, you can also specify a function name. That function will be called with two parameters, the (reduced) level of the headline, and a predicate non-nil when the headline should be numbered. It must return a format string in which the section title will be added." :group 'org-export-latex :type '(repeat (list (string :tag "LaTeX class") (string :tag "LaTeX header") (repeat :tag "Levels" :inline t (choice (cons :tag "Heading" (string :tag " numbered") (string :tag "unnumbered")) (list :tag "Environment" (string :tag "Opening (numbered)") (string :tag "Closing (numbered)") (string :tag "Opening (unnumbered)") (string :tag "Closing (unnumbered)")) (function :tag "Hook computing sectioning")))))) (defcustom org-latex-inputenc-alist nil "Alist of inputenc coding system names, and what should really be used. For example, adding an entry (\"utf8\" . \"utf8x\") will cause \\usepackage[utf8x]{inputenc} to be used for buffers that are written as utf8 files." :group 'org-export-latex :type '(repeat (cons (string :tag "Derived from buffer") (string :tag "Use this instead")))) (defcustom org-latex-title-command "\\maketitle" "The command used to insert the title just after \\begin{document}. This format string may contain these elements: %a for AUTHOR keyword %t for TITLE keyword %s for SUBTITLE keyword %k for KEYWORDS line %d for DESCRIPTION line %c for CREATOR line %l for Language keyword %L for capitalized language keyword %D for DATE keyword If you need to use a \"%\" character, you need to escape it like that: \"%%\". Setting :latex-title-command in publishing projects will take precedence over this variable." :group 'org-export-latex :type '(string :tag "Format string")) (defcustom org-latex-subtitle-format "\\\\\\medskip\n\\large %s" "Format string used for transcoded subtitle. The format string should have at most one \"%s\"-expression, which is replaced with the subtitle." :group 'org-export-latex :version "26.1" :package-version '(Org . "8.3") :type '(string :tag "Format string")) (defcustom org-latex-subtitle-separate nil "Non-nil means the subtitle is not typeset as part of title." :group 'org-export-latex :version "26.1" :package-version '(Org . "8.3") :type 'boolean) (defcustom org-latex-toc-command "\\tableofcontents\n\n" "LaTeX command to set the table of contents, list of figures, etc. This command only applies to the table of contents generated with the toc:t, toc:1, toc:2, toc:3, ... options, not to those generated with the #+TOC keyword." :group 'org-export-latex :type 'string) (defcustom org-latex-hyperref-template "\\hypersetup{\n pdfauthor={%a},\n pdftitle={%t},\n pdfkeywords={%k}, pdfsubject={%d},\n pdfcreator={%c}, \n pdflang={%L}}\n" "Template for hyperref package options. This format string may contain these elements: %a for AUTHOR keyword %t for TITLE keyword %s for SUBTITLE keyword %k for KEYWORDS line %d for DESCRIPTION line %c for CREATOR line %l for Language keyword %L for capitalized language keyword %D for DATE keyword If you need to use a \"%\" character, you need to escape it like that: \"%%\". As a special case, a nil value prevents template from being inserted. Setting :latex-hyperref-template in publishing projects will take precedence over this variable." :group 'org-export-latex :version "26.1" :package-version '(Org . "8.3") :type '(choice (const :tag "No template" nil) (string :tag "Format string"))) ;;;; Headline (defcustom org-latex-format-headline-function 'org-latex-format-headline-default-function "Function for formatting the headline's text. This function will be called with six arguments: TODO the todo keyword (string or nil) TODO-TYPE the type of todo (symbol: `todo', `done', nil) PRIORITY the priority of the headline (integer or nil) TEXT the main headline text (string) TAGS the tags (list of strings or nil) INFO the export options (plist) The function result will be used in the section format string." :group 'org-export-latex :version "24.4" :package-version '(Org . "8.0") :type 'function) ;;;; Footnotes (defcustom org-latex-default-footnote-command "\\footnote{%s%s}" "Default command used to insert footnotes. Customize this command if the LaTeX class provides a different command like \"\\sidenote{%s%s}\" that you want to use. The value will be passed as an argument to `format' as the following (format org-latex-default-footnote-command footnote-description footnote-label)" :group 'org-export-latex :package-version '(Org . "9.7") :type 'string) (defcustom org-latex-footnote-separator "\\textsuperscript{,}\\," "Text used to separate footnotes." :group 'org-export-latex :type 'string) (defcustom org-latex-footnote-defined-format "\\textsuperscript{\\ref{%s}}" "Format string used to format reference to footnote already defined. %s will be replaced by the label of the referred footnote." :group 'org-export-latex :type '(choice (const :tag "Use plain superscript (default)" "\\textsuperscript{\\ref{%s}}") (const :tag "Use Memoir/KOMA-Script footref" "\\footref{%s}") (string :tag "Other format string")) :version "26.1" :package-version '(Org . "9.0")) ;;;; Timestamps (defcustom org-latex-active-timestamp-format "\\textit{%s}" "A printf format string to be applied to active timestamps." :group 'org-export-latex :type 'string) (defcustom org-latex-inactive-timestamp-format "\\textit{%s}" "A printf format string to be applied to inactive timestamps." :group 'org-export-latex :type 'string) (defcustom org-latex-diary-timestamp-format "\\textit{%s}" "A printf format string to be applied to diary timestamps." :group 'org-export-latex :type 'string) ;;;; Links (defcustom org-latex-images-centered t "When non-nil, images are centered." :group 'org-export-latex :version "26.1" :package-version '(Org . "9.0") :type 'boolean :safe #'booleanp) (defcustom org-latex-image-default-option "" "Default option for images." :group 'org-export-latex :version "24.4" :package-version '(Org . "8.0") :type 'string) (defcustom org-latex-image-default-width ".9\\linewidth" "Default width for images. This value will not be used if a height is provided." :group 'org-export-latex :version "24.4" :package-version '(Org . "8.0") :type 'string) (defcustom org-latex-image-default-scale "" "Default scale for images. This value will not be used if a width or a scale is provided, or if the image is wrapped within a \"wrapfigure\" environment. Scale overrides width and height." :group 'org-export-latex :package-version '(Org . "9.3") :type 'string) (defcustom org-latex-image-default-height "" "Default height for images. This value will not be used if a width is provided, or if the image is wrapped within a \"figure\" or \"wrapfigure\" environment." :group 'org-export-latex :version "24.4" :package-version '(Org . "8.0") :type 'string) (defcustom org-latex-default-figure-position "htbp" "Default position for LaTeX figures." :group 'org-export-latex :type 'string :version "26.1" :package-version '(Org . "9.0")) (defcustom org-latex-inline-image-rules `(("file" . ,(rx "." (or "pdf" "jpeg" "jpg" "png" "ps" "eps" "tikz" "pgf" "svg") eos)) ("https" . ,(rx "." (or "jpeg" "jpg" "png" "ps" "eps" "tikz" "pgf" "svg") eos))) "Rules characterizing image files that can be inlined into LaTeX. A rule consists in an association whose key is the type of link to consider, and value is a regexp that will be matched against link's path. Note that, by default, the image extension *actually* allowed depend on the way the LaTeX file is processed. When used with pdflatex, pdf, jpg and png images are OK. When processing through dvi to Postscript, only ps and eps are allowed. The default we use here encompasses both." :group 'org-export-latex :package-version '(Org . "9.6") :type '(alist :key-type (string :tag "Type") :value-type (regexp :tag "Path"))) (defcustom org-latex-link-with-unknown-path-format "\\texttt{%s}" "Format string for links with unknown path type." :group 'org-export-latex :type 'string) ;;;; Tables (defcustom org-latex-default-table-environment "tabular" "Default environment used to build tables." :group 'org-export-latex :version "24.4" :package-version '(Org . "8.0") :type 'string) (defcustom org-latex-default-quote-environment "quote" "Default environment used to `quote' blocks." :group 'org-export-latex :package-version '(Org . "9.5") :type 'string) (defcustom org-latex-default-table-mode 'table "Default mode for tables. Value can be a symbol among: `table' Regular LaTeX table. `math' In this mode, every cell is considered as being in math mode and the complete table will be wrapped within a math environment. It is particularly useful to write matrices. `inline-math' This mode is almost the same as `math', but the math environment will be inlined. `verbatim' The table is exported as it appears in the Org buffer, within a verbatim environment. This value can be overridden locally with, i.e. \":mode math\" in LaTeX attributes. When modifying this variable, it may be useful to change `org-latex-default-table-environment' accordingly." :group 'org-export-latex :version "24.4" :package-version '(Org . "8.0") :type '(choice (const :tag "Table" table) (const :tag "Matrix" math) (const :tag "Inline matrix" inline-math) (const :tag "Verbatim" verbatim)) :safe (lambda (s) (memq s '(table math inline-math verbatim)))) (defcustom org-latex-tables-centered t "When non-nil, tables are exported in a center environment." :group 'org-export-latex :type 'boolean :safe #'booleanp) (defcustom org-latex-tables-booktabs nil "When non-nil, display tables in a formal \"booktabs\" style. This option assumes that the \"booktabs\" package is properly loaded in the header of the document. This value can be ignored locally with \":booktabs t\" and \":booktabs nil\" LaTeX attributes." :group 'org-export-latex :version "24.4" :package-version '(Org . "8.0") :type 'boolean :safe #'booleanp) (defcustom org-latex-table-scientific-notation nil "Format string to display numbers in scientific notation. The format should have \"%s\" twice, for mantissa and exponent \(i.e., \"%s\\\\times10^{%s}\"). When nil, no transformation is made." :group 'org-export-latex :version "24.4" :package-version '(Org . "8.0") :type '(choice (string :tag "Format string") (const :tag "No formatting" nil))) ;;;; Text markup (defcustom org-latex-text-markup-alist '((bold . "\\textbf{%s}") (code . protectedtexttt) (italic . "\\emph{%s}") (strike-through . "\\sout{%s}") (underline . "\\uline{%s}") (verbatim . protectedtexttt)) "Alist of LaTeX expressions to convert text markup. The key must be a symbol among `bold', `code', `italic', `strike-through', `underline' and `verbatim'. The value is a formatting string to wrap fontified text with. Value can also be set to the following symbols: `verb' and `protectedtexttt'. For the former, Org will use \"\\verb\" to create a format string and select a delimiter character that isn't in the string. For the latter, Org will use \"\\texttt\" to typeset and try to protect special characters. If no association can be found for a given markup, text will be returned as-is." :group 'org-export-latex :version "26.1" :package-version '(Org . "8.3") :type 'alist :options '(bold code italic strike-through underline verbatim)) ;;;; Drawers (defcustom org-latex-format-drawer-function (lambda (_ contents) contents) "Function called to format a drawer in LaTeX code. The function must accept two parameters: NAME the drawer name, like \"LOGBOOK\" CONTENTS the contents of the drawer. The function should return the string to be exported. The default function simply returns the value of CONTENTS." :group 'org-export-latex :version "26.1" :package-version '(Org . "8.3") :type 'function) ;;;; Inlinetasks (defcustom org-latex-format-inlinetask-function 'org-latex-format-inlinetask-default-function "Function called to format an inlinetask in LaTeX code. The function must accept seven parameters: TODO the todo keyword (string or nil) TODO-TYPE the todo type (symbol: `todo', `done', nil) PRIORITY the inlinetask priority (integer or nil) NAME the inlinetask name (string) TAGS the inlinetask tags (list of strings or nil) CONTENTS the contents of the inlinetask (string or nil) INFO the export options (plist) The function should return the string to be exported." :group 'org-export-latex :type 'function :version "26.1" :package-version '(Org . "8.3")) ;; Src blocks (defcustom org-latex-src-block-backend 'verbatim "Backend used to generate source code listings. This sets the behavior for fontifying source code, possibly even with color. There are four implementations of this functionality you may choose from (ordered from least to most capable): 1. Verbatim 2. Listings 3. Minted 4. Engraved The first two options provide basic syntax highlighting (listings), or none at all (verbatim). When using listings, you also need to make use of LaTeX package \"listings\". The \"color\" LaTeX package is also needed if you would like color too. These can simply be added to `org-latex-packages-alist', using customize or something like: (require \\='ox-latex) (add-to-list \\='org-latex-packages-alist \\='(\"\" \"listings\")) (add-to-list \\='org-latex-packages-alist \\='(\"\" \"color\")) There are two further options for more comprehensive fontification. The first can be set with, (setq org-latex-src-block-backend \\='minted) which causes source code to be exported using the LaTeX package minted as opposed to listings. If you want to use minted, you need to add the minted package to `org-latex-packages-alist', for example using customize, or with (require \\='ox-latex) (add-to-list \\='org-latex-packages-alist \\='(\"newfloat\" \"minted\")) In addition, it is necessary to install pygments \(URL `https://pygments.org>'), and to configure the variable `org-latex-pdf-process' so that the -shell-escape option is passed to pdflatex. The minted choice has possible repercussions on the preview of latex fragments (see `org-preview-latex-fragment'). If you run into previewing problems, please consult URL `https://orgmode.org/worg/org-tutorials/org-latex-preview.html'. The most comprehensive option can be set with, (setq org-latex-src-block-backend \\='engraved) which causes source code to be run through `engrave-faces-latex-buffer', which generates colorings using Emacs's font-lock information. This requires the Emacs package engrave-faces (available from GNU ELPA), and the LaTeX package fvextra be installed. The styling of the engraved result can be customized with `org-latex-engraved-preamble' and `org-latex-engraved-options'. The default preamble also uses the LaTeX package tcolorbox in addition to fvextra." :group 'org-export-latex :package-version '(Org . "9.6") :type '(choice (const :tag "Use listings" listings) (const :tag "Use minted" minted) (const :tag "Use engrave-faces-latex" engraved) (const :tag "Export verbatim" verbatim)) :safe (lambda (s) (memq s '(listings minted engraved verbatim)))) (defcustom org-latex-listings-langs '((emacs-lisp "Lisp") (lisp "Lisp") (clojure "Lisp") (c "C") (cc "C++") (fortran "fortran") (perl "Perl") (cperl "Perl") (python "Python") (ruby "Ruby") (html "HTML") (xml "XML") (tex "TeX") (latex "[LaTeX]TeX") (shell-script "bash") (gnuplot "Gnuplot") (ocaml "[Objective]Caml") (caml "Caml") (sql "SQL") (sqlite "sql") (makefile "make") (R "r")) "Alist mapping languages to their listing language counterpart. The key is a symbol, the major mode symbol without the \"-mode\". The value is the string that should be inserted as the language parameter for the listings package. If the mode name and the listings name are the same, the language does not need an entry in this list - but it does not hurt if it is present." :group 'org-export-latex :version "26.1" :package-version '(Org . "8.3") :type '(repeat (list (symbol :tag "Major mode ") (string :tag "Listings language")))) (defcustom org-latex-listings-src-omit-language nil "Discard src block language parameter in listings. Set this option to t to omit the \"language=\" in the parameters to \"lstlisting\" environments when exporting an src block. This is necessary, for example, when the \"fancyvrb\" package is used instead of \"listings\": #+LATEX_HEADER: \\RequirePackage{fancyvrb} #+LATEX_HEADER: \\DefineVerbatimEnvironment{verbatim}{Verbatim}{...} #+LATEX_HEADER: \\DefineVerbatimEnvironment{lstlisting}{Verbatim}{...}" :group 'org-export-latex :package-version '(Org . "9.7") :type 'boolean :safe #'booleanp) (defcustom org-latex-listings-options nil "Association list of options for the latex listings package. These options are supplied as a comma-separated list to the \\lstlisting command. Each element of the association list should be a list or cons cell containing two strings: the name of the option, and the value. For example, (setq org-latex-listings-options \\='((\"basicstyle\" \"\\\\small\") (\"keywordstyle\" \"\\\\color{black}\\\\bfseries\\\\underbar\"))) ; or (setq org-latex-listings-options \\='((\"basicstyle\" . \"\\\\small\") (\"keywordstyle\" . \"\\\\color{black}\\\\bfseries\\\\underbar\"))) will typeset the code in a small size font with underlined, bold black keywords. Note that the same options will be applied to blocks of all languages. If you need block-specific options, you may use the following syntax: #+ATTR_LATEX: :options key1=value1,key2=value2 #+BEGIN_SRC ... #+END_SRC" :group 'org-export-latex :type '(repeat (list (string :tag "Listings option name ") (string :tag "Listings option value")))) (defcustom org-latex-minted-langs '((emacs-lisp "common-lisp") (cc "c++") (cperl "perl") (shell-script "bash") (caml "ocaml")) "Alist mapping languages to their minted language counterpart. The key is a symbol, the major mode symbol without the \"-mode\". The value is the string that should be inserted as the language parameter for the minted package. If the mode name and the listings name are the same, the language does not need an entry in this list - but it does not hurt if it is present. Note that minted uses all lower case for language identifiers, and that the full list of language identifiers can be obtained with: pygmentize -L lexers" :group 'org-export-latex :type '(repeat (list (symbol :tag "Major mode ") (string :tag "Minted language")))) (defcustom org-latex-minted-options nil "Association list of options for the latex minted package. These options are supplied within square brackets in \\begin{minted} environments. Each element of the alist should be a list or cons cell containing two strings: the name of the option, and the value. For example, (setq org-latex-minted-options \\='((\"bgcolor\" \"bg\") (\"frame\" \"lines\"))) ; or (setq org-latex-minted-options \\='((\"bgcolor\" . \"bg\") (\"frame\" . \"lines\"))) will result in source blocks being exported with \\begin{minted}[bgcolor=bg,frame=lines]{} as the start of the minted environment. Note that the same options will be applied to blocks of all languages. If you need block-specific options, you may use the following syntax: #+ATTR_LATEX: :options key1=value1,key2=value2 #+BEGIN_SRC ... #+END_SRC" :group 'org-export-latex :type '(repeat (list (string :tag "Minted option name ") (string :tag "Minted option value")))) (defcustom org-latex-custom-lang-environments nil "Alist mapping languages to language-specific LaTeX environments. It is used during export of source blocks by the listings and minted LaTeX packages. The environment may be a simple string, composed of only letters and numbers. In this case, the string is directly the name of the LaTeX environment to use. The environment may also be a format string. In this case the format string will be directly exported. This format string may contain these elements: %s for the formatted source %c for the caption %f for the float attribute %l for an appropriate label %o for the LaTeX attributes For example, (setq org-latex-custom-lang-environments \\='((python \"pythoncode\") (ocaml \"\\\\begin{listing} \\\\begin{minted}[%o]{ocaml} %s\\\\end{minted} \\\\caption{%c} \\\\label{%l}\"))) would have the effect that if Org encounters a Python source block during LaTeX export it will produce \\begin{pythoncode} \\end{pythoncode} and if Org encounters an Ocaml source block during LaTeX export it will produce \\begin{listing} \\begin{minted}[]{ocaml} \\end{minted} \\caption{} \\label{
EOF $script = <<'EOF'; EOF while ($page = shift) { system "mv $page $page.orig"; open IN,"<$page.orig" or die "Cannot read from $page.orig\n"; undef $/; $all = ; close IN; $all =~ s//$&\n$script/; $all =~ s/^/\n$contents/m; open OUT,">$page" or die "Cannot write to $page\n"; print OUT $all; close OUT; system "rm $page.orig"; } org-mode-9.7.29+dfsg/mk/manfull.pl000077500000000000000000000021551500430433700166610ustar00rootroot00000000000000#!/usr/bin/perl $failures = 0; while ($page = shift) { system "mv $page $page.orig"; open IN,"<$page.orig" or die "Cannot read from $page.orig\n"; open OUT,">$page" or die "Cannot write to $page\n"; $toc = undef; while () { if (//) { print OUT; print OUT ''; } elsif (/
/) { print OUT; print OUT '

This is the official manual for the latest Org mode release.

'; } elsif (/

Table of Contents<\/h2>|

/) { print OUT; print OUT 'https://orgmode.org
'; $toc = 1; } elsif (/<\/div>/ and $toc) { print OUT "

"; $toc = 0; } else { print OUT; } } system "rm $page.orig"; if (!defined($toc) || $toc != 0) { ++$failures; print STDERR "Patching of $page failed\n"; } } $failures == 0 or die "Patching of $failures files failed\n"; org-mode-9.7.29+dfsg/mk/mansplit.pl000077500000000000000000000063211500430433700170510ustar00rootroot00000000000000#!/usr/bin/perl # Work on the files that are created by makeinfo for html output # split into many small files. # This will walk though the files listed on the command line, install # Sebastian Rose's key reader and add a small top-level-only table # of contents that will be placed into a special region and visible # in all subfiles. The small contents is a constant and has to be updated # by hand, currently. $contents = <

This is the official manual for the latest Org-mode release.

Table of Contents

EOF $script = <<'EOF'; EOF while ($page = shift) { system "mv $page $page.orig"; open IN,"<$page.orig" or die "Cannot read from $page.orig\n"; undef $/; $all = ; close IN; $all =~ s//$&\n$script/; $all =~ s/^/\n$contents/m; open OUT,">$page" or die "Cannot write to $page\n"; print OUT $all; close OUT; system "rm $page.orig"; } org-mode-9.7.29+dfsg/mk/org-fixup.el000066400000000000000000000206331500430433700171260ustar00rootroot00000000000000;;; org-fixup.el --- make life easier for folks without GNU make -*- lexical-binding: t; -*- ;; ;; Author: Achim Gratz ;; Keywords: orgmode ;; URL: https://orgmode.org ;; ;; This file is not part of GNU Emacs. ;; ;; GNU Emacs 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, or (at your option) ;; any later version. ;; GNU Emacs 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 GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;; Commentary: (require 'org-compat "org-compat.el") (defun org-make-manual () "Generate the Texinfo file out of the Org manual." (require 'ox-texinfo) (find-file "../doc/org-manual.org") (let ((org-confirm-babel-evaluate nil) ;; We do not want to search local user files when building manuals. (org-id-track-globally nil)) (org-texinfo-export-to-texinfo))) (defun org-make-guide () "Generate the Texinfo file out of the Org guide." (require 'ox-texinfo) (find-file "../doc/org-guide.org") (let ((org-confirm-babel-evaluate nil) ;; We do not want to search local user files when building manuals. (org-id-track-globally nil)) (org-texinfo-export-to-texinfo))) (make-obsolete 'org-make-manuals "use org-make-manual and org-make-guide." "9.6") (defun org-make-manuals () "Generate the Texinfo files out of Org manuals." (require 'ox-texinfo) (dolist (manual '("../doc/org-manual.org" "../doc/org-guide.org")) (find-file manual) (let ((org-confirm-babel-evaluate nil) ;; We do not want to search local user files when building manuals. (org-id-track-globally nil)) (org-texinfo-export-to-texinfo)))) (defun org-make-org-version (org-release org-git-version) "Make the file org-version.el in the current directory. This function is internally used by the build system and should be used by foreign build systems or installers to produce this file in the installation directory of Org mode. Org will not work correctly if this file is not present (except directly from the Git work tree)." (with-temp-buffer (insert "\ ;;; org-version.el --- autogenerated file, do not edit -*- lexical-binding: t -*- ;; ;;; Code: ;;;\#\#\#autoload \(defun org-release () \"The release version of Org. Inserted by installing Org mode or when a release is made.\" (let ((org-release \"" org-release "\")) org-release)) ;;;\#\#\#autoload \(defun org-git-version () \"The Git version of Org mode. Inserted by installing Org or when a release is made.\" (let ((org-git-version \"" org-git-version "\")) org-git-version)) \f\n\(provide 'org-version\) \f\n;; Local Variables:\n;; version-control: never ;; no-byte-compile: t ;; coding: utf-8\n;; End:\n;;; org-version.el ends here\n") (let ((inhibit-read-only t)) (write-file "org-version.el")))) (defun org-make-org-loaddefs () "Make the file org-loaddefs.el in the current directory. This function is internally used by the build system and should be used by foreign build systems or installers to produce this file in the installation directory of Org mode. Org will not work correctly if this file is not up-to-date." (let ((outfile "org-loaddefs.el")) (if (fboundp 'loaddefs-generate) ; FIXME: Emacs >= 29 (loaddefs-generate default-directory (expand-file-name outfile)) (require 'autoload) (with-temp-buffer (set-visited-file-name outfile) (insert ";;; org-loaddefs.el --- autogenerated file, do not edit\n;;\n;;; Code:\n") (let ((files (directory-files default-directory nil "^\\(org\\|ob\\|ox\\|ol\\|oc\\)\\(-.*\\)?\\.el$"))) (mapc (lambda (f) (generate-file-autoloads f)) files)) (insert "\f\n(provide 'org-loaddefs)\n") (insert "\f\n;; Local Variables:\n;; version-control: never\n") (insert ";; no-byte-compile: t\n;; no-update-autoloads: t\n") (insert ";; coding: utf-8\n;; End:\n;;; org-loaddefs.el ends here\n") (let ((inhibit-read-only t)) (save-buffer)))))) (defun org-make-autoloads (&optional compile force) "Make the files org-loaddefs.el and org-version.el in the install directory. Finds the install directory by looking for library \"org\". Optionally byte-compile lisp files in the install directory or force re-compilation. This function is provided for easier manual install when the build system can't be used." (let ((origin default-directory) (dirlisp (org-find-library-dir "org"))) (unwind-protect (progn (cd dirlisp) (org-fixup) (org-make-org-version (org-release) (org-git-version)) (org-make-org-loaddefs) (when compile (byte-recompile-directory dirlisp 0 force))) (cd origin)))) (defun org-make-autoloads-compile () "Call org-make-autoloads with compile argument. Convenience function for easier invocation from command line." (org-make-autoloads 'compile nil)) (defun org-make-autoloads-compile-force () "Call org-make-autoloads with compile force arguments. Convenience function for easier invocation from command line." (org-make-autoloads 'compile 'force)) ;; Internal functions (defun org-make-local-mk () "Internal function for the build system." (let ((default "mk/default.mk") (local "local.mk")) (unwind-protect (with-temp-buffer (insert-file-contents default) (goto-char (point-min)) (when (search-forward "-8<-" nil t) (forward-line 1) (delete-region (point-min) (point))) (when (search-forward "->8-" nil t) (forward-line 0) (delete-region (point) (point-max))) (goto-char (point-min)) (insert " # Remove \"oldorg:\" to switch to \"all\" as the default target. # Change \"oldorg:\" to an existing target to make that target the default, # or define your own target here to become the default target. oldorg: # do what the old Makefile did by default. ##---------------------------------------------------------------------- ") (goto-char (point-max)) (insert "\ # See default.mk for further configuration options. ") (let ((inhibit-read-only t)) (write-file local))) nil))) (defun org-make-letterformat (a4name lettername) "Internal function for the build system." (unwind-protect (with-temp-buffer (insert-file-contents a4name) (goto-char (point-min)) (while (search-forward "\\pdflayout=(0l)" nil t) (replace-match "\\pdflayout=(1l)" nil t)) (let ((inhibit-read-only t)) (write-file lettername))) nil)) ;; redefine version functions (defmacro org-fixup () (let* ((origin default-directory) (dirlisp (org-find-library-dir "org")) (dirorg (concat dirlisp "../" )) (dirgit (concat dirorg ".git/" )) (org-version "N/A-fixup") (org-git-version "N/A-fixup !!check installation!!")) (if (and (boundp 'org-fake-release) (stringp org-fake-release) (boundp 'org-fake-git-version) (stringp org-fake-git-version)) (setq org-version org-fake-release org-git-version org-fake-git-version) (if (load (concat dirlisp "org-version.el") 'noerror 'nomessage 'nosuffix) (setq org-version (org-release) org-git-version (org-git-version)) (when (and (file-exists-p dirgit) (executable-find "git")) (unwind-protect (progn (cd dirorg) (let ((git6 (substring (shell-command-to-string "git describe --abbrev=6 HEAD") 0 -1)) (git0 (substring (shell-command-to-string "git describe --abbrev=0 HEAD") 0 -1)) (gitd (string-match "\\S-" (shell-command-to-string "git status -uno --porcelain")))) (setq org-git-version (concat git6 (when gitd ".dirty") "-git")) (if (string-match "^release_" git0) (setq org-version (substring git0 8)) (setq org-version git0)))) (cd origin))))) (message "org-fixup.el: redefined Org version") `(progn (defun org-release () ,org-version) (defun org-git-version () ,org-git-version)))) (provide 'org-fixup) ;; Local Variables: ;; no-byte-compile: t ;; coding: utf-8 ;; End: ;;; org-fixup.el ends here org-mode-9.7.29+dfsg/mk/orgcard2txt.pl000077500000000000000000000065741500430433700174770ustar00rootroot00000000000000# orgcard2txt.pl - a script to generate orgcard.txt from orgcard.tex # Copyright (C) 2010, 2013 Osamu OKANO # # Version: 0.1 # # 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 . # # Usage: # ====== # perl orgcard2txt.pl orgcard.tex > orgcard.txt use strict; use warnings; sub rep_esc{ my $s = shift @_; $s =~ s/\\kbd\{([^}]+)\}/$1/g; $s =~ s/\$\^([0-9])\$/[$1]/g; $s =~ s/\\rm //g; $s =~ s/\\\///g; $s =~ s/\\\^\{\}/^/g; $s =~ s/\\}/}/g; $s =~ s/\\\{/{/g; $s =~ s/\\\#/#/g; $s =~ s/\\\^/^/g; $s =~ s/\\\%/%/g; $s =~ s/\\\_/_/g; $s =~ s/\\\&/&/g; $s =~ s/\\\$/\$/g; $s =~ s/\$\\leftrightarrow\$/<->/g; $s =~ s/\$\\pm 1\$/±1/g; $s =~ s/``\{\\tt ([^}]+)}''/`$1'/g; return $s; } my $page=0; my $orgversionnumber; open(IN,"org-version.tex"); while(){ last if(/\f/); $orgversionnumber = $1 if /\\def\\orgversionnumber\{([^}]+)}/; } close(IN); print <){ if(/\f/){ $page = $page + 1; next; } next if($page != 1); next if(/^%/); next if /Org Mode Reference Card \([12]\/2\)/; next if /\\centerline\{\(for version \\orgversionnumber\)}/; next if /\(for version \)/; next if /\\newcolumn/; next if /\\copyrightnotice/; next if /\\bye/; next if /\\title\{([^}]+)}/; chomp; # print "b:$_\n"; s/([^\\])\%.+$/$1/; # print "a:$_\n"; if (/\\section\{(.+)}/){ my $sec = rep_esc($1); print "================================================================================\n"; print "$sec\n"; print "================================================================================\n"; next; } if (/{\\bf (.+)}/){ my $bf = rep_esc($1); print "--------------------------------------------------------------------------------\n"; print "$bf\n"; print "--------------------------------------------------------------------------------\n"; next; } if (/^{\\it (.+)}/){ my $it = rep_esc($1); print "--------------------------------------------------------------------------------\n"; print "$it\n"; print "--------------------------------------------------------------------------------\n"; next; } if(/^\\key\{(.+)}\s*$/||/^\\metax\{(.+)}\s*$/){ my ($k,$v) = split(/}\{/,$1); my $k2 = &rep_esc($k); my $v2 = &rep_esc($v); # print "$k2\t$v2\n"; ($key,$value)=($k2,$v2); write; next; } my $line = rep_esc($_); $line =~ s/{\\it ([^}]+)}/$1/g; $line =~ s/{\\tt ([^}]+)}/$1/g; print "$line\n"; } close(IN); org-mode-9.7.29+dfsg/mk/server.mk000066400000000000000000000036031500430433700165210ustar00rootroot00000000000000#---------------------------------------------------------------- # This file is used to upload the Org documentation to the server #---------------------------------------------------------------- .PHONY: helpserver \ doc-up \ upload \ tagwarn version help helpserver:: $(info ) $(info Maintenance) $(info ===========) $(info upload - clean up, populate the server with documentation) helpserver:: @echo "" #---------------------------------------------------------------------- SERVROOT ?= upload SERVERMK ?= true # or just any value at all, really #---------------------------------------------------------------------- release: cleanall info pdf card tagwarn PKG_TAG = $(shell date +%Y%m%d) PKG_DOC = "Outline-based notes management and organizer" PKG_REQ = "" # marmalade chokes on explicit "nil" tagwarn: $(if $(filter-out $(ORGVERSION), $(GITVERSION)), \ $(info ======================================================) \ $(info = =) \ $(info = A release should only be made from a revision that =) \ $(info = has an annotated tag! =) \ $(info = =) \ $(info ======================================================)) version: @echo ORGVERSION=$(ORGVERSION) GITVERSION=$(GITVERSION)$(ORGDIST) @echo "ORGVERSION ?= $(ORGVERSION)" > mk/version.mk @echo "GITVERSION ?= $(GITVERSION)" >> mk/version.mk doc-up: info pdf card html $(MAKE) -C doc manual guide $(CP) doc/org.html $(SERVROOT) $(CP) doc/orgcard.pdf $(SERVROOT) $(CP) doc/orgcard_letter.pdf $(SERVROOT) $(CP) doc/org.pdf $(SERVROOT) $(CP) doc/orgguide.html $(SERVROOT) $(CP) doc/orgguide.pdf $(SERVROOT) $(CP) doc/manual/* $(SERVROOT)/manual $(CP) doc/guide/* $(SERVROOT)/guide upload: cleanall doc-up org-mode-9.7.29+dfsg/mk/targets.mk000066400000000000000000000113451500430433700166660ustar00rootroot00000000000000.EXPORT_ALL_VARIABLES: # Additional distribution files DISTFILES_extra= Makefile etc LISPDIRS = lisp OTHERDIRS = doc etc CLEANDIRS = testing mk SUBDIRS = $(OTHERDIRS) $(LISPDIRS) INSTSUB = $(SUBDIRS:%=install-%) ORG_MAKE_DOC ?= info html pdf ifneq ($(wildcard .git),) # Use the org.el header. ORGVERSION := $(patsubst %-dev,%,$(shell $(BATCH) --eval "(require 'lisp-mnt)" \ --visit lisp/org.el --eval '(princ (lm-header "version"))')) GITVERSION := $(shell git describe --match release\* --abbrev=6 HEAD 2>/dev/null || echo "${ORGVERSION}-$(shell git describe --match release\* --abbrev=6 --always HEAD)") GITSTATUS := $(shell git status -uno --porcelain) else -include mk/version.mk GITVERSION ?= N/A ORGVERSION ?= N/A endif DATE := $(shell date +%Y-%m-%d) YEAR := $(shell date +%Y) ifneq ($(GITSTATUS),) GITVERSION := $(GITVERSION:.dirty=).dirty endif .PHONY: all oldorg update update2 up0 up1 up2 single native $(SUBDIRS) \ check test install $(INSTSUB) \ info html pdf card refcard doc docs \ autoloads cleanall clean $(CLEANDIRS:%=clean%) \ clean-install cleanelc cleandirs \ cleanlisp cleandoc cleandocs cleantest \ compile compile-dirty uncompiled \ config config-test config-exe config-all config-eol config-version \ vanilla repro CONF_BASE = EMACS DESTDIR ORGCM ORG_MAKE_DOC CONF_DEST = lispdir infodir datadir testdir CONF_TEST = BTEST_PRE BTEST_POST BTEST_OB_LANGUAGES BTEST_EXTRA BTEST_RE CONF_EXEC = CP MKDIR RM RMR FIND CHMOD SUDO PDFTEX TEXI2PDF TEXI2HTML MAKEINFO INSTALL_INFO CONF_CALL = BATCH BATCHL ELC ELN ELCDIR NOBATCH BTEST MAKE_LOCAL_MK MAKE_ORG_INSTALL MAKE_ORG_VERSION config-eol:: EOL = \# config-eol:: config-all config config-all:: $(info ) $(info ========= Emacs executable and Installation paths) $(foreach var,$(CONF_BASE),$(info $(var) = $($(var))$(EOL))) $(foreach var,$(CONF_DEST),$(info $(var) = $(DESTDIR)$($(var))$(EOL))) config-test config-all:: $(info ) $(info ========= Test configuration) $(foreach var,$(CONF_TEST),$(info $(var) = $($(var))$(EOL))) config-exe config-all:: $(info ) $(info ========= Executables used by make) $(foreach var,$(CONF_EXEC),$(info $(var) = $($(var))$(EOL))) config-cmd config-all:: $(info ) $(info ========= Commands used by make) $(foreach var,$(CONF_CALL),$(info $(var) = $($(var))$(EOL))) config config-test config-exe config-all config-version:: $(info ========= Org version) $(info make: Org mode version $(ORGVERSION) ($(GITVERSION) => $(lispdir))) @echo "" oldorg: compile info # what the old makefile did when no target was specified uncompiled: | cleanlisp autoloads # for developing refcard: card update update2:: | up0 all single: ORGCM=single single: compile native: ORGCM=native native: compile .PRECIOUS: local.mk local.mk: $(info ======================================================) $(info = Invoke "make help" for a synopsis of make targets. =) $(info = Created a default local.mk template. =) $(info = Setting "oldorg" as the default target. =) $(info = Please adapt local.mk to your local setup! =) $(info ======================================================) -@$(MAKE_LOCAL_MK) all compile:: $(foreach dir, doc lisp, $(MAKE) -C $(dir) clean;) compile compile-dirty:: $(MAKE) -C lisp $@ all clean-install:: $(foreach dir, $(SUBDIRS), $(MAKE) -C $(dir) $@;) vanilla: -@$(NOBATCH) & check test:: compile check test test-dirty:: -$(MKDIR) $(testdir) TMPDIR=$(testdir) $(BTEST) ifeq ($(TEST_NO_AUTOCLEAN),) # define this variable to leave $(testdir) around for inspection $(MAKE) cleantest endif up0 up1 up2:: git checkout $(GIT_BRANCH) git remote update git pull up1 up2:: all $(MAKE) test-dirty up2 update2:: $(SUDO) $(MAKE) install install: $(INSTSUB) install-info: install-doc doc docs: $(ORG_MAKE_DOC) info html pdf card: $(MAKE) -C doc $@ $(INSTSUB): $(MAKE) -C $(@:install-%=%) install autoloads: lisp $(MAKE) -C $< $@ repro: | cleanall autoloads -@$(REPRO) & cleandirs: $(foreach dir, $(SUBDIRS), $(MAKE) -C $(dir) cleanall;) clean: cleanlisp cleandoc cleanall: cleandirs cleantest -$(FIND) . \( -name \*~ -o -name \*# -o -name .#\* \) -exec $(RM) {} + -$(FIND) $(CLEANDIRS) \( -name \*~ -o -name \*.elc \) -exec $(RM) {} + $(CLEANDIRS:%=clean%): -$(FIND) $(@:clean%=%) \( -name \*~ -o -name \*.elc \) -exec $(RM) {} + cleanelc: $(MAKE) -C lisp $@ cleanlisp cleandoc: $(MAKE) -C $(@:clean%=%) clean cleandocs: $(MAKE) -C doc clean -$(FIND) doc -name \*~ -exec $(RM) {} \; cleantest: # git-annex creates non-writable directories so that the files within # them can't be removed; if rm fails, try to recover by making all # directories writable -$(RMR) $(testdir) || { \ $(FIND) $(testdir) -type d -exec $(CHMOD) u+w {} + && \ $(RMR) $(testdir) ; \ } org-mode-9.7.29+dfsg/testing/000077500000000000000000000000001500430433700157265ustar00rootroot00000000000000org-mode-9.7.29+dfsg/testing/.gitignore000066400000000000000000000002201500430433700177100ustar00rootroot00000000000000# in case anyone wants to keep ert in the testing directory, e.g., for # old versions of Emacs ert .org-test-id-locations .test-org-id-locationsorg-mode-9.7.29+dfsg/testing/README000066400000000000000000000126731500430433700166170ustar00rootroot00000000000000# -*- mode:org -*- #+TITLE: Org mode Testing #+PROPERTY: header-args:emacs-lisp :results silent * Dependencies The only dependency is [[http://www.emacswiki.org/emacs/ErtTestLibrary][ERT]] the Emacs testing library which ships with Emacs24. If you are running an older version of Emacs and don't already have ERT installed it can be installed from its old [[https://github.com/ohler/ert][git repository]]. * Non-interactive batch testing from the command line The simplest way to run the Org mode test suite is from the command line with the following invocation. Note that the paths below are relative to the base of the Org mode directory. Also note that many of the current tests uses babel evaluation... #+BEGIN_SRC sh :dir (expand-file-name "..") # For Emacs earlier than 24, add -L /path/to/ert emacs -Q --batch \ -L lisp/ -L testing/ -L testing/lisp -l lisp/org.el \ -l lisp/org-id.el -l testing/org-test.el \ --eval "(progn (org-reload) (setq org-confirm-babel-evaluate nil) \ (org-babel-do-load-languages 'org-babel-load-languages \ '((emacs-lisp . t) (shell . t) (org . t))))" \ -f org-test-run-batch-tests #+END_SRC The options in the above command are explained below. | -Q | ignores any personal configuration ensuring a vanilla Emacs instance is used | | --batch | runs Emacs in "batch" mode with no gui and termination after execution | | -l | loads Org mode and the Org mode test suite defined in testing/org-test.el | | --eval | reloads Org mode and allows evaluation of code blocks by the tests | | -f | actually runs the tests using the `org-test-run-batch-tests' function | * Trigger the tests with 'make' ** Recompile all Target ~test~ can be used to trigger a test run. The tests start after cleaning up and recompilation. #+BEGIN_SRC sh :dir (expand-file-name "..") :results silent make test #+END_SRC See ../mk/default.mk for details. ** Test dirty The 'dirty' targets are for recompiling without cleaning and rebuilding everything. This usually speeds up the recompilation considerably. Note that this speed up comes to the price of possibly weird errors due to the unclean build. The dirty target for testing is called ~test-dirty~. #+BEGIN_SRC sh :dir (expand-file-name "..") :results silent make test-dirty #+END_SRC ** Select tests by regexp Variable ~BTEST_RE~ can be set to limit the tests which are performed. ~BTEST_RE~ is interpreted as regexp. Example: #+begin_src shell make BTEST_RE='test-.*-inlinetask' test-dirty #+end_src yields #+begin_example ... selected tests: test-.*-inlinetask Running 2 tests (2017-12-28 15:04:45+0100) passed 1/2 test-org-export/handle-inlinetasks passed 2/2 test-org-inlinetask/goto-end Ran 2 tests, 2 results as expected (2017-12-28 15:04:45+0100) ... #+end_example * Interactive testing from within Emacs To run the Org mode test suite from a current Emacs instance simply load and run the test suite with the following commands. 1) First load the test suite. #+BEGIN_SRC emacs-lisp :var here=(buffer-file-name) (add-to-list 'load-path (file-name-directory here)) (require 'org-test) #+END_SRC 2) Load required Babel languages #+BEGIN_SRC emacs-lisp (org-babel-do-load-languages 'org-babel-load-languages (and (mapc (lambda (lang) (add-to-list 'org-babel-load-languages (cons lang t))) '(emacs-lisp shell org)) org-babel-load-languages)) #+END_SRC 3) Then run the test suite. Babel evaluation confirmation is disabled and ~C-c C-c~ is enabled while running the tests. #+BEGIN_SRC emacs-lisp (let (org-babel-no-eval-on-ctrl-c-ctrl-c org-confirm-babel-evaluate) (org-test-run-all-tests)) #+END_SRC When a test fails, run it interactively and investigate the problem in the ERT results buffer. To run one test: Use this as a demo example of a failing test #+BEGIN_SRC emacs-lisp (ert-deftest test-org/org-link-encode-ascii-character-demo-of-fail () (should (string= "%5B" ; Expecting %5B is correct. (org-link-encode "["))) (should (string= "%5C" ; Expecting %5C is wrong, %5D correct. (org-link-encode "]")))) #+END_SRC or evaluate the ~ert-deftest form~ of the test you want to run. Then ~M-x ert RET test-org/org-link-encode-ascii-character-demo-of-fail RET~. When not visible yet switch to the ERT results buffer named ~*ert*~. When a test failed the ERT results buffer shows the details of the first ~should~ that failed. See ~(info "(ert)Running Tests Interactively")~ on how to re-run, start the debugger etc. To run several tests: ~M-x ert RET "" RET~. To run all tests of a single test file: ~M-x ert-delete-all-tests RET~ and confirm. ~M-x load-file RET testing/lisp/.el RET M-x ert RET t RET~. Consider to set #+BEGIN_SRC emacs-lisp (setq pp-escape-newlines nil) #+END_SRC before running the test when looking at ~should~ in the ERT results buffer. Especially when using ~l~ to look at passed test results and possibly missing an appropriate setting of ~pp-escape-newlines~ made only temporarily for the running time of the test as e. g. tests using ~org-test-table-target-expect-tblfm~ do. * Troubleshooting - If the variable ~org-babel-no-eval-on-ctrl-c-ctrl-c~ is non-nil then it will result in some test failure, as there are tests which rely on this behavior. org-mode-9.7.29+dfsg/testing/examples/000077500000000000000000000000001500430433700175445ustar00rootroot00000000000000org-mode-9.7.29+dfsg/testing/examples/Basic.bib000066400000000000000000000011201500430433700212350ustar00rootroot00000000000000@book{friends, title = {{{LaTeX}} and Friends}, author = {van Dongen, M.R.C.}, date = {2012}, location = {Berlin}, publisher = {Springer}, doi = {10.1007/978-3-642-23816-1}, isbn = {9783642238161} } @InCollection{Geyer2011, author = {Geyer, Charles J}, title = {{Introduction to Markov\plus Chain Monte Carlo}}, year = 2011, booktitle = {{Handbook of Markov Chain Monte Carlo}}, editor = {Brooks, Steve and Gelman, Andrew and Jones, Galin and Meng, Xiao-Li}, publisher = {CRC press}, pages = 45, } org-mode-9.7.29+dfsg/testing/examples/agenda-file.org000066400000000000000000000010571500430433700224140ustar00rootroot00000000000000* Test Agenda <2017-03-10 Fri> * test agenda SCHEDULED: <2017-07-19 Wed> ** subnote * test code 216bc1ff1d862e78183e38ee9a4da504919b9878 <2019-01-08 Tue> * test agenda non-scheduled #+begin_src org SCHEDULED: <2022-01-03 Mon> #+end_src * colon scheduled entry : SCHEDULED: <2022-01-03 Mon> * begin_example scheduled entry #+begin_example SCHEDULED: <2022-01-03 Mon> #+end_example * test timestamp inside properties :PROPERTIES: :CREATED: <2022-03-22 Tue> :END: * test sexp timestamp inside properties :PROPERTIES: :CREATED: <%%(diary-date 03 25 2022)> :END: org-mode-9.7.29+dfsg/testing/examples/agenda-file2.org000066400000000000000000000003111500430433700224660ustar00rootroot00000000000000* TODO one SCHEDULED: <2024-01-17 Wed 09:30-10:00> * TODO two SCHEDULED: <2024-01-17 Wed 10:00-12:30> * TODO three SCHEDULED: <2024-01-17 Wed 13:00-15:00> * TODO four SCHEDULED: <2024-01-17 Wed 19:00> org-mode-9.7.29+dfsg/testing/examples/agenda-search.org000066400000000000000000000002141500430433700227340ustar00rootroot00000000000000* Test heading with text foo * Test heading with inlinetask foo ****************** inline text inside inlinetask ****************** END bar org-mode-9.7.29+dfsg/testing/examples/att1/000077500000000000000000000000001500430433700204155ustar00rootroot00000000000000org-mode-9.7.29+dfsg/testing/examples/att1/fileA000066400000000000000000000000161500430433700213550ustar00rootroot00000000000000Text in fileA org-mode-9.7.29+dfsg/testing/examples/att1/fileB000066400000000000000000000000161500430433700213560ustar00rootroot00000000000000Text in fileB org-mode-9.7.29+dfsg/testing/examples/att2/000077500000000000000000000000001500430433700204165ustar00rootroot00000000000000org-mode-9.7.29+dfsg/testing/examples/att2/fileC000066400000000000000000000000151500430433700213570ustar00rootroot00000000000000Text in fileCorg-mode-9.7.29+dfsg/testing/examples/att2/fileD000066400000000000000000000000161500430433700213610ustar00rootroot00000000000000text in fileD org-mode-9.7.29+dfsg/testing/examples/attachments.org000066400000000000000000000007061500430433700225730ustar00rootroot00000000000000#+TITLE: Org attach testfile Used to test and verify the functionality of org-attach. * H1 :PROPERTIES: :DIR: att1 :END: A link to one attachment: [[attachment:fileA]] ** H1.1 A link to another attachment: [[attachment:fileB]] ** H1.2 :PROPERTIES: :DIR: att2 :END: * H2 :PROPERTIES: :ID: abcd123 :END: * H3 :PROPERTIES: :DIR: att1 :ID: abcd1234 :END: ** H3.1 :PROPERTIES: :ID: abcd12345 :END: org-mode-9.7.29+dfsg/testing/examples/babel-dangerous.org000066400000000000000000000005131500430433700233060ustar00rootroot00000000000000#+Title: dangerous code block examples which should be isolated #+OPTIONS: ^:nil * no default value for vars :PROPERTIES: :ID: f2df5ba6-75fa-4e6b-8441-65ed84963627 :END: There is no default value assigned to =x= variable. This is not permitted anymore. #+name: carre #+begin_src python :var x return x*x #+end_src org-mode-9.7.29+dfsg/testing/examples/babel.el000066400000000000000000000003031500430433700211270ustar00rootroot00000000000000;; -*- lexical-binding: t; -*- (string-match-p "^#[[:digit:]]+$" "#123") ;; [[id:73115FB0-6565-442B-BB95-50195A499EF4][detangle:1]] ;; detangle changes ;; linked content to detangle:1 ends here org-mode-9.7.29+dfsg/testing/examples/babel.org000066400000000000000000000276511500430433700213350ustar00rootroot00000000000000#+Title: a collection of examples for Babel tests #+OPTIONS: ^:nil * =:noweb= header argument expansion :PROPERTIES: :ID: eb1f6498-5bd9-45e0-9c56-50717053e7b7 :END: #+name: noweb-example #+begin_src emacs-lisp :results silent :exports code (message "expanded1") #+end_src #+name: noweb-example2 #+begin_src emacs-lisp :results silent (message "expanded2") #+end_src #+begin_src emacs-lisp :noweb yes :results silent ;; noweb-1-yes-start <> #+end_src #+begin_src emacs-lisp :noweb no :results silent ;; noweb-no-start <> #+end_src #+begin_src emacs-lisp :noweb yes :results silent ;; noweb-2-yes-start <> #+end_src #+begin_src emacs-lisp :noweb tangle :results silent ;; noweb-tangle-start <> <> #+end_src * =:noweb= header argument expansion using :exports results :PROPERTIES: :ID: 8701beb4-13d9-468c-997a-8e63e8b66f8d :END: #+name: noweb-example #+begin_src emacs-lisp :exports results (message "expanded1") #+end_src #+name: noweb-example2 #+begin_src emacs-lisp :exports results (message "expanded2") #+end_src #+begin_src emacs-lisp :noweb yes :exports results ;; noweb-1-yes-start <> #+end_src #+begin_src emacs-lisp :noweb no :exports code ;; noweb-no-start <> #+end_src #+begin_src emacs-lisp :noweb yes :exports results ;; noweb-2-yes-start <> #+end_src #+begin_src emacs-lisp :noweb tangle :exports code <> <> #+end_src * excessive id links on tangling :PROPERTIES: :ID: ef06fd7f-012b-4fde-87a2-2ae91504ea7e :END: ** no, don't give me an ID #+begin_src emacs-lisp :tangle no (message "not to be tangled") #+end_src ** yes, I'd love an ID :PROPERTIES: :ID: ae7b55ca-9ef2-4d30-bd48-da30e35fd0f3 :END: #+begin_src emacs-lisp :tangle no (message "for tangling") #+end_src * simple named code block :PROPERTIES: :ID: 0d82b52d-1bb9-4916-816b-2c67c8108dbb :END: #+name: i-have-a-name #+begin_src emacs-lisp 42 #+end_src #+name: : 42 #+name: i-have-a-name : 42 * Pascal's Triangle -- exports both test :PROPERTIES: :ID: 92518f2a-a46a-4205-a3ab-bcce1008a4bb :END: #+name: pascals-triangle #+begin_src emacs-lisp :var n=5 :exports both (defun pascals-triangle (n) (if (= n 0) (list (list 1)) (let* ((prev-triangle (pascals-triangle (- n 1))) (prev-row (car (reverse prev-triangle)))) (append prev-triangle (list (cl-map 'list #'+ (append prev-row '(0)) (append '(0) prev-row))))))) (pascals-triangle n) #+end_src * executing an lob call line :PROPERTIES: :header-args: :results silent :ID: fab7e291-fde6-45fc-bf6e-a485b8bca2f0 :END: #+call: echo(input="testing") #+call: echo(input="testing") :results vector #+call: echo[:var input="testing"]() #+call: echo[:var input="testing"]() :results vector #+call: echo("testing") #+call: echo("testing") :results vector This is an inline call call_echo(input="testing") embedded in prose. This is an inline call call_echo(input="testing")[:results vector] embedded in prose. #+call: lob-minus(8, 4) call_echo("testing") call_concat(1,2,3) #+name: concat #+begin_src emacs-lisp :var a=0 :var b=0 :var c=0 (format "%S%S%S" a b c) #+end_src * exporting an lob call line :PROPERTIES: :ID: 72ddeed3-2d17-4c7f-8192-a575d535d3fc :END: #+name: double #+begin_src emacs-lisp :var it=0 (* 2 it) #+end_src The following exports as a normal call line #+call: double(it=0) Now here is an inline call call_double(it=1) stuck in the middle of some prose. This one should not be exported =call_double(it=2)= because it is quoted. Finally this next one should export, even though it starts a line call_double(it=3) because sometimes inline blocks fold with a paragraph. And, a call with raw results call_double(4)[:results raw] should not have quoted results. The following 2*5=call_double(5) should export even when prefixed by an = sign. * inline source block :PROPERTIES: :ID: 54cb8dc3-298c-4883-a933-029b3c9d4b18 :END: Here is one in the middle src_sh{echo 1} of a line. Here is one at the end of a line. src_sh{echo 2} src_sh{echo 3} Here is one at the beginning of a line. * exported inline source block :PROPERTIES: :ID: cd54fc88-1b6b-45b6-8511-4d8fa7fc8076 :header-args: :exports code :END: Here is one in the middle src_sh{echo 1} of a line. Here is one at the end of a line. src_sh{echo 2} src_sh{echo 3} Here is one at the beginning of a line. Here is one that is also evaluated: src_sh[:exports both]{echo 4} * mixed blocks with exports both :PROPERTIES: :ID: 5daa4d03-e3ea-46b7-b093-62c1b7632df3 :END: #+name: a-list - a - b - c #+begin_src emacs-lisp :exports both "code block results" #+end_src #+begin_src emacs-lisp :var lst=a-list :results list :exports both (reverse lst) #+end_src * using the =:noweb-ref= header argument :PROPERTIES: :ID: 54d68d4b-1544-4745-85ab-4f03b3cbd8a0 :header-args: :noweb-sep "" :END: #+begin_src sh :tangle yes :noweb yes :shebang "#!/bin/sh" <> #+end_src ** query all mounted disks #+begin_src sh :noweb-ref fullest-disk df #+end_src ** strip the header row #+begin_src sh :noweb-ref fullest-disk |sed '1d' #+end_src ** sort by the percent full #+begin_src sh :noweb-ref fullest-disk |awk '{print $5 " " $6}'|sort -n |tail -1 #+end_src ** extract the mount point #+begin_src sh :noweb-ref fullest-disk |awk '{print $2}' #+end_src * resolving sub-trees as references :PROPERTIES: :ID: 2409e8ba-7b5f-4678-8888-e48aa02d8cb4 :header-args: :results silent :END: #+begin_src emacs-lisp :var text=d4faa7b3-072b-4dcf-813c-dd7141c633f3 (length text) #+end_src #+begin_src org :noweb yes <> <> #+end_src ** simple subtree with custom ID :PROPERTIES: :CUSTOM_ID: simple-subtree :END: this is simple ** simple subtree with global ID :PROPERTIES: :ID: d4faa7b3-072b-4dcf-813c-dd7141c633f3 :END: has length 14 * exporting a code block with a name :PROPERTIES: :ID: b02ddd8a-eeb8-42ab-8664-8a759e6f43d9 :END: exporting a code block with a name #+name: qux #+begin_src sh :foo "baz" echo bar #+end_src * noweb no-export and exports both :PROPERTIES: :ID: 8a820f6c-7980-43db-8a24-0710d33729c9 :END: Weird interaction. here is one block #+name: noweb-no-export-and-exports-both-1 #+BEGIN_SRC sh :exports none echo 1 #+END_SRC and another #+BEGIN_SRC sh :noweb no-export :exports both # I am inside the code block <> #+END_SRC * in order evaluation on export :PROPERTIES: :header-args: :exports results :ID: 96cc7073-97ec-4556-87cf-1f9bffafd317 :END: First. #+name: foo-for-order-of-evaluation #+begin_src emacs-lisp :var it=1 (push it *evaluation-collector*) #+end_src Second #+begin_src emacs-lisp (push 2 *evaluation-collector*) #+end_src Third src_emacs-lisp{(car (push 3 *evaluation-collector*))} Fourth #+call: foo-for-order-of-evaluation(4) Fifth #+begin_src emacs-lisp (push 5 *evaluation-collector*) #+end_src * exporting more than just results from a call line :PROPERTIES: :ID: bec63a04-491e-4caa-97f5-108f3020365c :END: Here is a call line with more than just the results exported. #+call: double(8) * strip noweb references on export :PROPERTIES: :ID: 8e7bd234-99b2-4b14-8cd6-53945e409775 :END: #+name: strip-export-1 #+BEGIN_SRC sh :exports none i="10" #+END_SRC #+BEGIN_SRC sh :noweb strip-export :exports code :results silent <> echo "1$i" #+END_SRC * use case of reading entry properties :PROPERTIES: :ID: cc5fbc20-bca5-437a-a7b8-2b4d7a03f820 :END: Use case checked and documented with this test: During their evaluation the source blocks read values from properties from the entry where the call has been made unless the value is overridden with the optional argument of the caller. ** section :PROPERTIES: :a: 1 :c: 3 :END: Note: Just export of a property can be done with a macro: {{{property(a)}}}. #+NAME: src_block_location_shell-sect-call #+CALL: src_block_location_shell() #+NAME: src_block_location_elisp-sect-call #+CALL: src_block_location_elisp() - sect inline call_src_block_location_shell()[:results raw] - sect inline call_src_block_location_elisp()[:results raw] *** subsection :PROPERTIES: :b: 2 :c: 4 :END: #+NAME: src_block_location_shell-sub0-call #+CALL: src_block_location_shell() #+NAME: src_block_location_elisp-sub0-call #+CALL: src_block_location_elisp() - sub0 inline call_src_block_location_shell()[:results raw] - sub0 inline call_src_block_location_elisp()[:results raw] #+NAME: src_block_location_shell-sub1-call #+CALL: src_block_location_shell(c=5, e=6) #+NAME: src_block_location_elisp-sub1-call #+CALL: src_block_location_elisp(c=5, e=6) - sub1 inline call_src_block_location_shell(c=5, e=6)[:results raw] - sub1 inline call_src_block_location_elisp(c=5, e=6)[:results raw] **** function definition comments for ":var": - The "or" is to deal with a property not present. - The t is to get property inheritance. #+NAME: src_block_location_shell #+HEADER: :var a=(or (org-entry-get org-babel-current-src-block-location "a" t) "0") #+HEADER: :var b=(or (org-entry-get org-babel-current-src-block-location "b" t) "0") #+HEADER: :var c=(or (org-entry-get org-babel-current-src-block-location "c" t) "0") #+HEADER: :var d=(or (org-entry-get org-babel-current-src-block-location "d" t) "0") #+HEADER: :var e=(or (org-entry-get org-babel-current-src-block-location "e" t) "0") #+BEGIN_SRC sh :shebang #!/bin/sh :exports results :results verbatim printf "shell a:$a, b:$b, c:$c, d:$d, e:$e" #+END_SRC #+RESULTS: src_block_location_shell #+NAME: src_block_location_elisp #+HEADER: :var a='nil #+HEADER: :var b='nil #+HEADER: :var c='nil #+HEADER: :var d='nil #+HEADER: :var e='nil #+BEGIN_SRC emacs-lisp :exports results (setq ;; - The first `or' together with ":var ='nil" is to check for ;; a value bound from an optional call argument, in the examples ;; here: c=5, e=6 ;; - The second `or' is to deal with a property not present ;; - The t is to get property inheritance a (or a (string-to-number (or (org-entry-get org-babel-current-src-block-location "a" t) "0"))) b (or b (string-to-number (or (org-entry-get org-babel-current-src-block-location "b" t) "0"))) c (or c (string-to-number (or (org-entry-get org-babel-current-src-block-location "c" t) "0"))) d (or d (string-to-number (or (org-entry-get org-babel-current-src-block-location "e" t) "0"))) e (or e (string-to-number (or (org-entry-get org-babel-current-src-block-location "d" t) "0")))) (format "elisp a:%d, b:%d, c:%d, d:%d, e:%d" a b c d e) #+END_SRC * =:file-ext= and =:output-dir= header args :PROPERTIES: :ID: 93573e1d-6486-442e-b6d0-3fedbdc37c9b :END: #+name: file-ext-basic #+BEGIN_SRC emacs-lisp :file-ext txt nil #+END_SRC #+name: file-ext-dir-relative #+BEGIN_SRC emacs-lisp :file-ext txt :output-dir foo nil #+END_SRC #+name: file-ext-dir-relative-slash #+BEGIN_SRC emacs-lisp :file-ext txt :output-dir foo/ nil #+END_SRC #+name: file-ext-dir-absolute #+BEGIN_SRC emacs-lisp :file-ext txt :output-dir /tmp nil #+END_SRC #+name: file-ext-file-wins #+BEGIN_SRC emacs-lisp :file-ext txt :file foo.bar nil #+END_SRC #+name: output-dir-and-file #+BEGIN_SRC emacs-lisp :output-dir xxx :file foo.bar nil #+END_SRC * detangle ** false positive The =[[= causes a false positive which ~org-babel-detangle~ should handle properly #+begin_src emacs-lisp :tangle yes (string-match-p "^#[[:digit:]]+$" "#123") #+end_src ** linked content to detangle :PROPERTIES: :ID: 73115FB0-6565-442B-BB95-50195A499EF4 :END: #+begin_src emacs-lisp :tangle yes :comments link #+end_src org-mode-9.7.29+dfsg/testing/examples/data/000077500000000000000000000000001500430433700204555ustar00rootroot00000000000000org-mode-9.7.29+dfsg/testing/examples/data/ab/000077500000000000000000000000001500430433700210375ustar00rootroot00000000000000org-mode-9.7.29+dfsg/testing/examples/data/ab/cd123/000077500000000000000000000000001500430433700216535ustar00rootroot00000000000000org-mode-9.7.29+dfsg/testing/examples/data/ab/cd123/fileE000066400000000000000000000000131500430433700226140ustar00rootroot00000000000000peek-a-boo org-mode-9.7.29+dfsg/testing/examples/diary-file000066400000000000000000000000771500430433700215200ustar00rootroot000000000000002019-01-08 test code: f0bcf0cd8bad93c1451bb6e1b2aaedef5cce7cbb org-mode-9.7.29+dfsg/testing/examples/images/000077500000000000000000000000001500430433700210115ustar00rootroot00000000000000org-mode-9.7.29+dfsg/testing/examples/images/Org mode logo mono-color.png000077500000000000000000000165431500430433700261550ustar00rootroot00000000000000PNG  IHDRbKGD pHYs  tIME. !UdIDATxyt\?ZUe-,6`ݱ q$$3$lN9t2=9IB XI$n KY%0^[e-,Y[~W,Kv-d9QT~r(G9Qr]$G0h9g*"R$L/q"+޳s¤ By\$@4/@G π8J Ct ]W"_RHH$hhhȁ/?PJIa胞;u:_VS""¡Cr~.d'm9@ <tx<~w??t"r}8΋mY;sݲ,\MU|ƈ%@@O:tyS;6K^S.MMMi]c[aV\(k.d"]o9F?[L)^d2z">a>D$`:}+P#4 S (f1IR.!PP6U ,c`n\a8GpRwW_J> 8sC`gU@ʃ$fPD,|q5jp]}]˚9P( la' &Dz!f`5`eY5vd^Zj#j"wS@0B W;,+hƹI_Kw`{nh6AAEǁ~|O)Րtvv&Mh2#%u ?Q=Arc T"F17iYxA_xoUUCx|8r&?Jr&= 뗿e3T`pp׌Ahƺ=k;ē0芹''ᚖanţ*UNGeee~ l=yuZZi@MNw-B)52B`2L7Ru= QDzJc>K&Vb0;u`ܣ;VO\oroxGW-` gҎC}#JYD6>|߄/]yљ_Ā/cF nvnOejX'vQjohxz{0-O?ӢZi[ƑQ~d& NQ7;WUU.I3{#^?G8RRAO0m/ލ^okaYM_ F F#cD>y l`K X ߿L)EM1 RDdB5Y+Q%b"Fgsm%*MO0%(YmQ >$Iӑ_LR Q %C^[݁ζ2OM1[z}׬vR5гRlG_-"g}˄ش񅍷qd~pu~lM\\>-|o͞Or#v D²m: jD="NxE[g{5da[e텥5w䙞d8H2k%MݺO_e- ʽ:ߐ,PVECx0p$ ڤ^~e¢v pԛWs}`sz.;-%˚yx;{>@ŽW$h:vD"6+?8-]#uu?a==Ԛ볪trW$+໮ˢjq#s+*Xs%TT|nd VP90P v"GgZXU# b݊mp\r xc$L@Q`z}i)WZd1t;$||S.yyZd@)+ƍ|m㧘[Q㺙KiO?(p]t0!dzPOEIɘVuw+,~I߻k˂S07cR~^no|6ӛ(-,L֠7atQᚷ\qӃi@0^˷ns5*7mb_T2"gB`@Ǧo>[ZF6\E`C; fu~>sށ!Lmv|y$apHn ,Ex\l/@4g|:c=e$ {1Rцu/r;tXXA,B(%[oMmm`+%=0(`Qb;c*L,Yy\?wVj7Lc"u de1"Bo8kf43D.^ۃ+24 8{q faKo^xaW_7ZX`@o8Lck;d[Ûxi>74E$aeQXP'BUW#u,"t-ċ`bpyWSPVJt ]x{ܶi@eǂ8JiwN),"Q\PY_UE߇wёnqPƬQ.?Ye ɼ#Ee://Jq|O 9OwƲ,zj~ joi\*?CO(?ϮŔ ֲ s,?-"b˼0pk+2*fcd>Zz1WӺg6@'V_AOo\RASi%0Mt, K@F7xl@,:Dц@ͪUԮzěbIJހjRTFMO szhtr|ti. E P=ni\o2qj`*U"~&cl PIvE %q@HYBO0ë́׀_(hM (8TT,t(d(['+kLӣ:Ęe+̨9'-`sR|=$߀"/#+l+_s:?\VE(eIgam.K;9*ߟ7amZOnd#au$,}Uys"AjLɱf=O [JdcE0)]n(XZ{F1!`<649(JNxL'T8m 'O}ER{)$l/b"]beMx2:v٣0CL't}M%?%~ץlܮ۔ZT'bx}X(<\,8ƱlB+(5qs.w9N,D( PwFD)\8_P!>'IA,BQ$<>z0=0Zd2=)f AlD=dMfkخv K۞364u*@+m$[N_7>kMM?\?(7'G0B)hjg"OcFlT0k=34)ɢ"mI+mgAz:mZ<*\ۃRI =A-j~gVM0;ޖ|^gggn*X-t3;N\L,*[+ckfޛvz d!cՎ!+(՟UuI|}iA+Eˉ+$2Gߍ1(E_ %tΆƸz|*Km|cRIE}A8D|x™A@2A`}a_0S2uLh0ixʡ?41Ċ#=Z a~'GgLc ߟ1'I^"F0#O&8R(W,ⶇC.2Q˶i|?q?1|)ە3{~= 6L27,%e24f/*s۶#|)x;1luqBgQM+bHCϰ*]}9A'v&?|АȈ3սUd6AoțC;6>SG`?ű@bcvznY|/bX+нc'eQ_2}1DPUKҲwOy2h zll$mcw®67"j-0z&;?E9ƍ=2҃.Vvj"tL1*!tsot>fڂRʊ(ڀBГCٝWH8db/";eުUy>~r{7M\Y^(uݡKqm]u1 X"dƇFO!S mo&mT٠ fQ,_yy/"D=?o*9n{4o>tc;6Qf ߾|ΟWLS\}& KTNqnGwL6ݿ$ù=uPOs}=W""z:13;/x&~:j\e CR;tXvu uαmA)Ip"Fϛuˁ?-$A=G:@7]k6ܛ81wlbE NSGL>9WozJ:jW_b.cf\ȃȉiAO8 nˁ?홠yWN"eug//]B޽9?imܦc{6zj0OKJGDYY3_):x0#@Ko/i4xEuU Ķ=# s7e?Ouo +׽:m|:Jj7l>IJ_O ?]`ρ=x =Rq>֭9/J3v;1> #k&m[q6(dp]^ܖ'u]V :+ErU-f?Sgoz8>8<+%9gEnArusx`fE3OR/Jͼ;I$RQr(G9 aIENDB`org-mode-9.7.29+dfsg/testing/examples/include.html000066400000000000000000000000151500430433700220510ustar00rootroot00000000000000

HTML!

org-mode-9.7.29+dfsg/testing/examples/include.org000066400000000000000000000005771500430433700217110ustar00rootroot00000000000000Small Org file with an include keyword. #+BEGIN_SRC emacs-lisp :exports results (+ 2 1) #+END_SRC #+INCLUDE: "include2.org" * Heading body * Another heading :PROPERTIES: :CUSTOM_ID: ah :END: 1 2 3 * A headline with a table :PROPERTIES: :CUSTOM_ID: ht :END: #+CAPTION: a table #+NAME: tbl | 1 | * drawer-headline :PROPERTIES: :CUSTOM_ID: dh :END: :LOGBOOK: drawer :END: content org-mode-9.7.29+dfsg/testing/examples/include2.org000066400000000000000000000000111500430433700217520ustar00rootroot00000000000000Success! org-mode-9.7.29+dfsg/testing/examples/link-in-heading.org000066400000000000000000000003571500430433700232200ustar00rootroot00000000000000this file has a link in it's heading, which can cause problems * [[http://www.example.com][example]] what a weird heading... #+begin_src emacs-lisp ;; a8b1d111-eca8-49f0-8930-56d4f0875155 (message "my heading has a link") #+end_src org-mode-9.7.29+dfsg/testing/examples/links.org000066400000000000000000000010751500430433700214000ustar00rootroot00000000000000#+TITLE: Testing various links types * Plain links - https://orgmode.org - [[https://orgmode.org][Org mode website]] - mailto:bzg@gnu.org * Links to files - file:///home/ - [[file:normal.org][normal.org]] - [[file:normal.org::3][normal.org (third line)]] - file:normal.org::example - file:normal.org::* top - id:eaefc396-8943-4666-be6a-d5a1dbb05480 * External links :PROPERTIES: :ID: eaefc396-8943-4666-be6a-d5a1dbb05480 :END: - info:Org - [[info:org:External links]] - [[shell:ls -l]] - elisp:org-agenda - [[elisp:(find-file-other-frame "normal.org")]] org-mode-9.7.29+dfsg/testing/examples/macro-templates.org000066400000000000000000000000711500430433700233500ustar00rootroot00000000000000#+TITLE: Macro templates #+MACRO: included-macro success org-mode-9.7.29+dfsg/testing/examples/no-heading.org000066400000000000000000000004071500430433700222670ustar00rootroot00000000000000This is an example file for use by the Org mode tests. This file is special because it has no headings, which can be erroneously assumed by some code. #+begin_src emacs-lisp :tangle no ;; 94839181-184f-4ff4-a72f-94214df6f5ba (message "I am code") #+end_src org-mode-9.7.29+dfsg/testing/examples/normal.org000066400000000000000000000011351500430433700215450ustar00rootroot00000000000000#+TITLE: Example file #+OPTIONS: num:nil ^:nil #+STARTUP: hideblocks This is an example file for use by the Org mode tests. * top ** code block :PROPERTIES: :header-args: :tangle yes :CUSTOM_ID: code-block-section :END: Here are a couple of code blocks. #+begin_src emacs-lisp :tangle no ;; 94839181-184f-4ff4-a72f-94214df6f5ba (message "I am code") #+end_src * accumulating properties in drawers :PROPERTIES: :header-args+: :var bar=2 :header-args: :var foo=1 :ID: 75282ba2-f77a-4309-a970-e87c149fe125 :END: #+begin_src emacs-lisp :results silent (list bar foo) #+end_src org-mode-9.7.29+dfsg/testing/examples/ob-C-test.org000066400000000000000000000077131500430433700220220ustar00rootroot00000000000000#+Title: a collection of examples for ob-C tests #+OPTIONS: ^:nil * Simple tests :PROPERTIES: :ID: fa6db330-e960-4ea2-ac67-94bb845b8577 :END: #+source: simple #+begin_src cpp :includes "" :results silent std::cout << 42; return 0; #+end_src #+source: simple #+begin_src cpp :includes :results silent std::cout << 42; return 0; #+end_src #+source: simple #+begin_src D :results silent writefln ("%s", 42); #+end_src #+source: integer_var #+begin_src cpp :var q=12 :includes "" :results silent std::cout << q; return 0; #+end_src #+source: integer_var #+begin_src D :var q=12 :results silent writefln ("%s", q); #+end_src #+source: two_var #+begin_src cpp :var q=12 :var p=10 :includes "" :results silent std::cout << p+q; return 0; #+end_src #+source: two_var #+begin_src D :var q=12 :var p=10 :results silent writefln ("%s", p+q); #+end_src #+source: string_var #+begin_src cpp :var q="word" :includes '( ) :results silent std::cout << q << ' ' << std::strlen(q); return 0; #+end_src #+source: string_var #+begin_src D :var q="word" :results silent writefln ("%s %s", q, q.length); #+end_src #+source: define #+begin_src cpp :defines N 42 :includes "" :results silent std::cout << N; return 0; #+end_src * Array :PROPERTIES: :ID: 2df1ab83-3fa3-462a-a1f3-3aef6044a874 :END: #+source: array #+begin_src cpp :includes "" :results vector :results silent for (int i=1; i<3; i++) { std::cout << i << '\n'; } return 0; #+end_src #+source: array #+begin_src D :results vector :results silent foreach (i; 1..3) writefln ("%s", i); #+end_src * Matrix :PROPERTIES: :ID: cc65d6b3-8e8e-4f9c-94cd-f5a00cdeceb5 :END: #+name: C-matrix | 1 | 2 | | 3 | 4 | #+source: list_var #+begin_src cpp :var a='("abc" "def") :includes "" :results silent std::cout << a[0] << a[1] << sizeof(a)/sizeof(*a) << '\n'; #+end_src #+source: list_var #+begin_src D :var a='("abc" "def") :results silent writefln ("%s%s%s", a[0], a[1], a.length); #+end_src #+source: vector_var #+begin_src cpp :var a='[1 2] :includes "" :results silent std::cout << a[0] << a[1] << sizeof(a)/sizeof(*a) << '\n'; #+end_src #+source: vector_var #+begin_src D :var a='[1 2] :results silent writefln ("%s%s%s", a[0], a[1], a.length); #+end_src #+source: list_list_var #+begin_src cpp :var q=C-matrix :includes "" :results silent std::cout << q[0][0] << ' ' << q[1][0] << '\n' << q[0][1] << ' ' << q[1][1] << '\n'; // transpose #+end_src #+source: list_list_var #+begin_src D :var q=C-matrix :results silent writefln ("%s %s", q[0][0], q[1][0]); writefln ("%s %s", q[0][1], q[1][1]); // transpose #+end_src * Inhomogeneous table :PROPERTIES: :ID: e112bc2e-419a-4890-99c2-7ac4779531cc :END: #+name: tinomogen | day | quty | |-----------+------| | monday | 34 | | tuesday | 41 | | wednesday | 56 | | thursday | 17 | | friday | 12 | | saturday | 7 | | sunday | 4 | #+source: inhomogeneous_table #+begin_src cpp :var tinomogen=tinomogen :results silent :includes int main() { int i, j; for (i=0; i :results output verbatim raw std::cout << "\"line 1\"\n"; std::cout << "\"line 2\"\n"; std::cout << "\"line 3\"\n"; #+end_src org-mode-9.7.29+dfsg/testing/examples/ob-awk-test.in000066400000000000000000000000401500430433700222230ustar00rootroot00000000000000 # an input file for awk test 15org-mode-9.7.29+dfsg/testing/examples/ob-awk-test.org000066400000000000000000000014751500430433700224210ustar00rootroot00000000000000#+Title: a collection of examples for ob-awk tests #+OPTIONS: ^:nil * Simple tests :PROPERTIES: :ID: 9e998b2a-3581-43fe-b26d-07d3c507b86a :END: Run without input stream #+begin_src awk :output silent :results silent BEGIN { print 42 } #+end_src Use a code block output as an input #+begin_src awk :stdin genseq :results silent { print 42+$1 } #+end_src Use input file #+name: genfile #+begin_src awk :in-file ob-awk-test.in :results silent $0~/[\t]*#/{ # skip comments next } { print $1*10 } #+end_src #+name: awk-table-input | a | b | c | #+begin_src awk :var a=awk-table-input BEGIN{ print a; } #+end_src * Input data generators A code block to generate input stream #+name: genseq #+begin_src emacs-lisp :results silent (print "1") #+end_src org-mode-9.7.29+dfsg/testing/examples/ob-fortran-test.org000066400000000000000000000045151500430433700233100ustar00rootroot00000000000000#+Title: a collection of examples for ob-fortran tests #+OPTIONS: ^:nil * simple programs :PROPERTIES: :ID: 459384e8-1797-4f11-867e-dde0473ea7cc :END: #+name: hello #+begin_src fortran :results silent print *, 'Hello world' #+end_src #+name: fortran_parameter #+begin_src fortran :results silent integer, parameter :: i = 10 write (*, '(i2)') i #+end_src * variable resolution :PROPERTIES: :ID: d8d1dfd3-5f0c-48fe-b55d-777997e02242 :END: #+begin_src fortran :var N = 15 :results silent write (*, '(i2)') N #+end_src Define for preprocessed fortran #+begin_src fortran :defines N 42 :results silent implicit none write (*, '(i2)') N #+end_src #+begin_src fortran :var s="word" :results silent write (*, '(a4)') s #+end_src * arrays :PROPERTIES: :ID: c28569d9-04ce-4cad-ab81-1ea29f691465 :END: Real array as input #+begin_src fortran :var s='(1.0 2.0 3.0) :results silent write (*, '(3f5.2)'), s #+end_src #+name: test_tbl | 1.0 | | 2.0 | #+begin_src fortran :var s=test_tbl :results silent write (*, '(2f5.2)'), s #+end_src * matrix :PROPERTIES: :ID: 3f73ab19-d25a-428d-8c26-e8c6aa933976 :END: Real matrix as input #+name: fortran-input-matrix1 | 0.0 | 42.0 | | 0.0 | 0.0 | | 0.0 | 0.0 | #+name: fortran-input-matrix2 | 0.0 | 0.0 | 0.0 | | 0.0 | 0.0 | 42.0 | #+begin_src fortran :var s=fortran-input-matrix1 :results silent write (*, '(i2)'), nint(s(1,2)) #+end_src #+begin_src fortran :var s=fortran-input-matrix2 :results silent write (*, '(i2)'), nint(s(2,3)) #+end_src * failing :PROPERTIES: :ID: 891ead4a-f87a-473c-9ae0-1cf348bcd04f :END: Should fail (TODO: add input variables for the case with explicit program statement) #+begin_src fortran :var s="word" :results silent program ex print *, "output of ex program" end program ex #+end_src Fails to compile (TODO: error check in ob-fortran.el) #+begin_src fortran :var s='(1 ()) :results silent print *, s #+end_src Should fail to compile with gfortran #+begin_src fortran :flags --std=f95 --pedantic-error :results silent program ex integer*8 :: i end program ex #+end_src * programs input parameters :PROPERTIES: :ID: 2d5330ea-9934-4737-9ed6-e1d3dae2dfa4 :END: Pass parameters to the program #+begin_src fortran :cmdline "23" :results silent character(len=255) :: cmd call get_command_argument(1, cmd) write (*,*) trim(cmd) #+end_src org-mode-9.7.29+dfsg/testing/examples/ob-header-arg-defaults.org000066400000000000000000000121061500430433700244570ustar00rootroot00000000000000#+TITLE: Tests for default header arguments to Babel source blocks #+OPTIONS: ^:nil #+PROPERTY: header-args :var t1="gh1" t2="gh2_clobbered" #+PROPERTY: header-args+ :var t4="gh4" t2="gh2" :var end=9 #+PROPERTY: header-args:emacs-lisp :var t1="ge1" t4="ge4_clobbered" #+PROPERTY: header-args:emacs-lisp+ :var t4="ge4" :var t5="ge5" #+PROPERTY: header-args:emacs-lisp+ :results silent :noweb yes #+NAME: showvar #+BEGIN_SRC emacs-lisp :execute no (mapconcat (lambda (n) (let* ((n (string (+ 48 n))) (p (intern (concat "t" n)))) (if (boundp p) (eval p) (concat "--" n)))) (number-sequence 1 end) "/") #+END_SRC * Global property :PROPERTIES: :ID: 3fdadb69-5d15-411e-aad0-f7860cdd7816 :END: | Global | t1 | t2 | t3 | t4 | t5 | t6 | t7 | t8 | t9 | |------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----| | header-args | gh1 | gh2 | --- | gh4 | --- | --- | --- | --- | --- | | header-args:emacs-lisp | ge1 | --- | --- | ge4 | ge5 | --- | --- | --- | --- | |------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----| | Result | ge1 | gh2 | --3 | ge4 | ge5 | --6 | --7 | --8 | --9 | #+CALL: showvar() :results silent #+BEGIN_SRC emacs-lisp :var end=7 <> #+END_SRC * Tree property ** Overwrite :PROPERTIES: :ID: a9cdfeda-9f31-4bb5-b694-2cf452f07dfd :header-args: :var t7="th7" :header-args:emacs-lisp: :var t8="te8" :header-args:emacs-lisp+: :results silent :noweb yes :var end=9 :END: | Global | t1 | t2 | t3 | t4 | t5 | t6 | t7 | t8 | t9 | |------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----| | header-args | gh1 | gh2 | --- | gh4 | --- | --- | --- | --- | --- | | header-args:emacs-lisp | ge1 | --- | --- | ge4 | ge5 | --- | --- | --- | --- | |------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----| | Tree | t1 | t2 | t3 | t4 | t5 | t6 | t7 | t8 | t9 | |------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----| | header-args | --- | --- | --- | --- | --- | --- | th7 | --- | --- | | header-args:emacs-lisp | --- | --- | --- | --- | --- | --- | --- | te8 | --- | |------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----| | Result #+CALL | ge1 | gh2 | --3 | ge4 | ge5 | --6 | th7 | te8 | --9 | | Result noweb | --1 | --2 | --3 | --4 | --5 | --6 | th7 | te8 | --9 | #+CALL: showvar() :results silent #+BEGIN_SRC emacs-lisp <> #+END_SRC ** Accumulate :PROPERTIES: :ID: 1d97d258-fd50-4107-a095-e4625bffc57b :header-args+: :var t2="th2" t3="th3" :header-args:emacs-lisp+: :var t5="te5" end=8 :END: | Global | t1 | t2 | t3 | t4 | t5 | t6 | t7 | t8 | t9 | |-------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----| | header-args | gh1 | gh2 | --- | gh4 | --- | --- | --- | --- | --- | | header-args:emacs-lisp | ge1 | --- | --- | ge4 | ge5 | --- | --- | --- | --- | |-------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----| | Tree | t1 | t2 | t3 | t4 | t5 | t6 | t7 | t8 | t9 | |-------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----| | header-args+ | --- | th2 | th3 | --- | --- | --- | --- | --- | --- | | header-args:emacs-lisp+ | --- | --- | --- | --- | te5 | --- | --- | --- | --- | |-------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----| | Result #+CALL | ge1 | th2 | th3 | ge4 | te5 | --6 | --7 | --8 | --9 | | Result noweb | ge1 | th2 | th3 | ge4 | te5 | --6 | --7 | --8 | --9 | #+CALL: showvar(end=6) :results silent #+BEGIN_SRC emacs-lisp <> #+END_SRC ** Complex :PROPERTIES: :ID: fa0e912d-d9b4-47b0-9f9e-1cbb39f7cbc2 :header-args+: :var t2="th2" :header-args:emacs-lisp: :var t5="te5" end=7 :header-args:emacs-lisp+: :results silent :noweb yes :var end=9 :END: | Global | t1 | t2 | t3 | t4 | t5 | t6 | t7 | t8 | t9 | |------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----| | header-args | gh1 | gh2 | --- | gh4 | --- | --- | --- | --- | --- | | header-args:emacs-lisp | ge1 | --- | --- | ge4 | ge5 | --- | --- | --- | --- | |------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----| | Tree | t1 | t2 | t3 | t4 | t5 | t6 | t7 | t8 | t9 | |------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----| | header-args+ | --- | th2 | --- | --- | --- | --- | --- | --- | --- | | header-args:emacs-lisp | --- | --- | --- | --- | te5 | --- | --- | --- | --- | |------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----| | Result #+CALL | gh1 | th2 | go3 | gh4 | te5 | --6 | --7 | --8 | --9 | | Result noweb | gh1 | th2 | --3 | gh4 | te5 | --6 | --7 | --8 | --9 | #+CALL: showvar(end=6) :results silent #+BEGIN_SRC emacs-lisp <> #+END_SRC org-mode-9.7.29+dfsg/testing/examples/ob-lilypond-broken.ly000066400000000000000000000003611500430433700236200ustar00rootroot00000000000000line 1 line 2 line 3 line 4 line 5 line 6 line 7 line 8 line 9 line 10 line 11 line 12 line 13 line 14 line 15 line 16 line 17 line 18 line 19 line 20 line 21 line 22 line 23 line 24 line 25 line 26 line 27 line 28 line 29 line 30 org-mode-9.7.29+dfsg/testing/examples/ob-lilypond-broken.org000066400000000000000000000005451500430433700237670ustar00rootroot00000000000000* Faulty lilypond org file for test purposes (do not adjust) line 2 line 3 line 4 line 5 line 6 line 7 line 8 line 9 line 10 line 11 line 12 line 13 line 14 line 15 line 16 line 17 line 18 line 19 line 20 line 21 line 22 line 23 line 24 line 25 line 26 line 27 line 28 line 29 line 30 line 31 line 32 line 33 line 34 line 35 line 36 line 37 line 38 line 39 org-mode-9.7.29+dfsg/testing/examples/ob-lilypond-test.error000066400000000000000000000006741500430433700240330ustar00rootroot00000000000000Processing `xxx' Parsing... /path/to/tangled/file/test.ly:25:0: error: syntax error, unexpected \score, expecting '=' \score { Interpreting music... [8][16][24][32] Preprocessing graphical objects... Interpreting music... MIDI output to `xxx' Finding the ideal number of pages... Fitting music on 2 or 3 pages... Drawing systems... Layout output to `xxx' Converting to `xxx'... error: failed files: "/Path/to/tangled/file/test.ly/example.ly" org-mode-9.7.29+dfsg/testing/examples/ob-lilypond-test.ly000066400000000000000000000014151500430433700233200ustar00rootroot00000000000000 % [[file:~/.emacs.d/martyn/martyn/ob-lilypond/test/test-build/test.org::*LilyPond%2520Version][LilyPond-Version:1]] \version "2.12.3" % LilyPond-Version:1 ends here % [[file:~/.emacs.d/martyn/martyn/ob-lilypond/test/test-build/test.org::*lilypond%2520block%2520for%2520test%2520purposes][lilypond-block-for-test-purposes:1]] \score { \relative c' { c8 d e f g a b c | b a g f e d c4 | } % lilypond-block-for-test-purposes:1 ends here % [[file:~/.emacs.d/martyn/martyn/ob-lilypond/test/test-build/test.org::*lilypond%2520block%2520for%2520test%2520purposes][lilypond-block-for-test-purposes:2]] \layout { } \midi { \context { \Score tempoWholesPerMinute = #(ly:make-moment 150 4) } } } % lilypond-block-for-test-purposes:2 ends here org-mode-9.7.29+dfsg/testing/examples/ob-lilypond-test.org000066400000000000000000000007641500430433700234710ustar00rootroot00000000000000* Test org lilypond file This is a simple file for test purposes ** LilyPond Version #+begin_src lilypond \version "2.12.3" #+end_src ** DONE lilypond block for test purposes #+begin_src lilypond \score { \relative c' { c8 d e f g a b c | b a g f e d c4 | } #+end_src #+begin_src lilypond \layout { } \midi { \context { \Score tempoWholesPerMinute = #(ly:make-moment 150 4) } } } #+end_src org-mode-9.7.29+dfsg/testing/examples/ob-maxima-test.org000066400000000000000000000063521500430433700231120ustar00rootroot00000000000000#+Title: a collection of examples for ob-maxima tests #+OPTIONS: ^:nil * Simple tests :PROPERTIES: :ID: b5842ed4-8e8b-4b18-a1c9-cef006b6a6c8 :END: #+begin_src maxima :var s=4 :results silent print(s); #+end_src Pass a string #+begin_src maxima :var fun="sin(x)" :var q=2 :results silent print(diff(fun, x, q))$ #+end_src * Graphic output Graphic output #+begin_src maxima :var a=0.5 :results graphics :file maxima-test-sin.png plot2d(sin(a*x), [x, 0, 2*%pi])$ #+end_src #+begin_src maxima :results graphics :file maxima-test-3d.png plot3d (2^(-u^2 + v^2), [u, -3, 3], [v, -2, 2])$ #+end_src ** Use the ~draw~ package This test exercises the ~:graphics-pkg~ header argument. #+name: ob-maxima/draw #+begin_src maxima :var a=0.5 :results graphics file :file ./maxima-test-cos.png :graphics-pkg draw draw2d(explicit(cos(a*x), x, -%pi, %pi))$ #+end_src * Output to a file Output to a file #+begin_src maxima :file maxima-test-ouput.out for i:1 thru 10 do print(i)$ #+end_src * List input :PROPERTIES: :ID: b5561c6a-73cd-453a-ba5e-62ad84844de6 :END: Simple list as an input #+begin_src maxima :var a=(list 1 2 3) :results silent :results verbatim print(a)$ #+end_src #+begin_src maxima :var a=(list 1 (list 1 2) 3) :results silent :results verbatim print(a+1); #+end_src * Table input :PROPERTIES: :ID: 400ee228-6b12-44fd-8097-7986f0f0db43 :END: #+name: test_tbl_col | 1.0 | | 2.0 | #+name: test_tbl_row | 1.0 | 2.0 | #+begin_src maxima :var s=test_tbl_col :results silent :results verbatim print(s+1.0); #+end_src #+begin_src maxima :var s=test_tbl_row :results silent :results verbatim print(s+1.0); #+end_src Matrix #+name: test_tbl_mtr | 1.0 | 1.0 | #+begin_src maxima :var s=test_tbl_mtr :results silent :results verbatim ms: apply(matrix, s); print(ms); #+end_src * Construct a table from the output :PROPERTIES: :ID: cc158527-b867-4b1d-8ae0-b8c713a90fd7 :END: #+begin_src maxima :results silent with_stdout("/dev/null", load(numericalio))$ m: genmatrix (lambda([i,j], i+j-1), 3, 3)$ write_data(m, "/dev/stdout")$ #+end_src * LaTeX output #+begin_src maxima :exports both :results latex :results verbatim assume(x>0); tex(ratsimp(diff(%e^(a*x), x))); #+end_src #+results: #+BEGIN_LaTeX $$a\,e^{a\,x}$$ #+END_LaTeX * Batch :PROPERTIES: :header-args:maxima: :exports both :results verbatim :batch batch :END: Exercise the ~:batch~ header argument. These tests are also defined in ~testing/lisp/test-ob-maxima.el~. The test name is name of the ~ert~ test. #+name: ob-maxima/batch+verbatim #+begin_src maxima (assume(z>0), integrate(exp(-t)*t^z, t, 0, inf)); #+end_src #+name: ob-maxima/batch+verbatim+quiet #+begin_src maxima :cmdline --quiet (assume(z>0), integrate(exp(-t)*t^z, t, 0, inf)); #+end_src #+name: ob-maxima/batch+verbatim+:lisp #+begin_src maxima :cmdline --quiet :lisp #$(assume(z>0),integrate(exp(-t)*t^z, t, 0, inf));#$ #+end_src #+name: ob-maxima/batch+verbatim+empty-string #+begin_src maxima :cmdline --quiet ""; #+end_src #+name: ob-maxima/batch+verbatim+whitespace-string #+begin_src maxima :cmdline --quiet " "; #+end_src #+name: ob-maxima/batch+verbatim+syntax-error #+begin_src maxima :cmdline --quiet ; #+end_src #+name: ob-maxima/batch+verbatim+eof-error #+begin_src maxima :cmdline --quiet x: #+end_src org-mode-9.7.29+dfsg/testing/examples/ob-octave-test.org000066400000000000000000000030641500430433700231140ustar00rootroot00000000000000#+Title: a collection of examples for ob-octave tests #+OPTIONS: ^:nil * Simple tests :PROPERTIES: :ID: 54dcd61d-cf6c-4d7a-b9e5-854953c8a753 :END: Number output #+begin_src octave :exports results :results silent ans = 10 #+end_src Array output #+begin_src octave :exports results :results silent ans = 1:4' #+end_src * Input tests :PROPERTIES: :ID: cc2d82bb-2ac0-45be-a0c8-d1463b86a3ba :END: Input an integer variable #+begin_src octave :exports results :results silent :var s=42 ans = s #+end_src Input an array #+begin_src octave :exports results :results silent :var s='(1.0 2.0 3.0) ans = s #+end_src Input a matrix #+begin_src octave :exports results :results silent :var s='((1 2) (3 4)) ans = s #+end_src Input a string #+begin_src octave :exports results :results silent :var s="test" ans = s(1:2) #+end_src Input elisp nil #+begin_src octave :exports results :results silent :var s='nil ans = s #+end_src * Graphical tests Graphics file. This test is performed by =ob-octave/graphics-file= in =testing/lisp/test-ob-octave.el=. #+begin_src octave :results file graphics :file sombrero.png sombrero; #+end_src Graphics file in a session. This test is performed by =ob-octave/graphics-file-session= in =testing/lisp/test-ob-octave.el=. #+begin_src octave :session :results graphics file :file sombrero.png sombrero; #+end_src Graphics file with a space in name. This test is performed by =ob-octave/graphics-file-space= in =testing/lisp/test-ob-octave.el=. #+begin_src octave :results graphics file :file sombrero hat.png sombrero; #+end_src org-mode-9.7.29+dfsg/testing/examples/ob-screen-test.org000066400000000000000000000002121500430433700231020ustar00rootroot00000000000000#+Title: a collection of examples for ob-screen tests #+begin_src screen :session create-tmpdir mkdir -p $TMPDIR cd $TMPDIR #+end_src org-mode-9.7.29+dfsg/testing/examples/ob-sed-test.org000066400000000000000000000015201500430433700224010ustar00rootroot00000000000000#+PROPERTY: results silent scalar #+Title: a collection of examples for ob-sed tests * Test simple execution of sed script :PROPERTIES: :ID: C7E7CA6A-2601-42C9-B534-4102D62E458D :END: #+NAME: ex1 #+BEGIN_EXAMPLE An example sentence. #+END_EXAMPLE #+BEGIN_SRC sed :stdin ex1 s/n example/ processed/ 2 d #+END_SRC * Test :in-file header argument :PROPERTIES: :ID: 54EC49AA-FE9F-4D58-812E-00FC87FAF562 :END: #+BEGIN_SRC sed :in-file test1.txt s/test/tested/ #+END_SRC * Test :cmd-line header argument :PROPERTIES: :ID: E3C6A8BA-39FF-4840-BA8E-90D5C4365AB1 :END: /Note:/ Use =-i.backup= instead of just =-i= because the BSD =sed=, unlike the GNU =sed=, requires =-i =. #+BEGIN_SRC sed :in-file test2.txt :cmd-line "-i.backup" s/test/tested again/ #+END_SRC org-mode-9.7.29+dfsg/testing/examples/org-exp.org000066400000000000000000000004511500430433700216360ustar00rootroot00000000000000#+Title: a collection of examples for export tests #+OPTIONS: ^:nil * stripping commas from within blocks on export :PROPERTIES: :ID: 76d3a083-67fa-4506-a41d-837cc48158b5 :END: The following commas should not be removed. #+begin_src r a <- c(1 , 2 , 3) #+end_src org-mode-9.7.29+dfsg/testing/examples/property-inheritance.org000066400000000000000000000006011500430433700244250ustar00rootroot00000000000000:PROPERTIES: :header-args: :var foo=1 :header-args+: :var bar=2 :END: #+begin_src emacs-lisp (+ foo bar) #+end_src * overwriting a file-wide property :PROPERTIES: :header-args: :var foo=7 :END: #+begin_src emacs-lisp foo #+end_src * appending to a file-wide property :PROPERTIES: :header-args+: :var baz=3 :END: #+begin_src emacs-lisp (+ foo bar baz) #+end_src org-mode-9.7.29+dfsg/testing/examples/pub-symlink/000077500000000000000000000000001500430433700220165ustar00rootroot00000000000000org-mode-9.7.29+dfsg/testing/examples/pub-symlink/link.org000066400000000000000000000000101500430433700234530ustar00rootroot00000000000000Symlink org-mode-9.7.29+dfsg/testing/examples/pub/000077500000000000000000000000001500430433700203325ustar00rootroot00000000000000org-mode-9.7.29+dfsg/testing/examples/pub/a.org000066400000000000000000000001571500430433700212660ustar00rootroot00000000000000#+title: A #+date: <2014-03-04 Tue> * Headline1 :PROPERTIES: :CUSTOM_ID: a1 :END: [[file:b.org::*Headline1]] org-mode-9.7.29+dfsg/testing/examples/pub/b.org000066400000000000000000000001501500430433700212600ustar00rootroot00000000000000#+title: b #+date: <2012-03-29 Thu> * Headline1 :PROPERTIES: :CUSTOM_ID: b1 :END: [[file:a.org::#a1]] org-mode-9.7.29+dfsg/testing/examples/pub/file.txt000066400000000000000000000000051500430433700220050ustar00rootroot00000000000000Text org-mode-9.7.29+dfsg/testing/examples/pub/link000077700000000000000000000000001500430433700236152../pub-symlinkustar00rootroot00000000000000org-mode-9.7.29+dfsg/testing/examples/pub/noextension000066400000000000000000000000141500430433700226210ustar00rootroot00000000000000No extensionorg-mode-9.7.29+dfsg/testing/examples/pub/sub/000077500000000000000000000000001500430433700211235ustar00rootroot00000000000000org-mode-9.7.29+dfsg/testing/examples/pub/sub/c.org000066400000000000000000000000441500430433700220540ustar00rootroot00000000000000#+title: C #+date: <2013-03-20 Wed> org-mode-9.7.29+dfsg/testing/examples/setupfile.org000066400000000000000000000000431500430433700222520ustar00rootroot00000000000000#+SETUPFILE: subdir/setupfile2.org org-mode-9.7.29+dfsg/testing/examples/setupfile3.org000066400000000000000000000001441500430433700223370ustar00rootroot00000000000000#+BIND: variable value #+DESCRIPTION: l2 #+LANGUAGE: en #+SELECT_TAGS: b #+TITLE: b #+PROPERTY: a 1 org-mode-9.7.29+dfsg/testing/examples/sub-bib/000077500000000000000000000000001500430433700210675ustar00rootroot00000000000000org-mode-9.7.29+dfsg/testing/examples/sub-bib/include-global-bib.org000066400000000000000000000000311500430433700252050ustar00rootroot00000000000000#+bibliography: /foo.bib org-mode-9.7.29+dfsg/testing/examples/sub-bib/include-relative-bib.org000066400000000000000000000000321500430433700255610ustar00rootroot00000000000000#+bibliography: ./foo.bib org-mode-9.7.29+dfsg/testing/examples/subdir/000077500000000000000000000000001500430433700210345ustar00rootroot00000000000000org-mode-9.7.29+dfsg/testing/examples/subdir/setupfile2.org000066400000000000000000000000371500430433700236270ustar00rootroot00000000000000#+SETUPFILE: ../setupfile3.org org-mode-9.7.29+dfsg/testing/lisp/000077500000000000000000000000001500430433700166755ustar00rootroot00000000000000org-mode-9.7.29+dfsg/testing/lisp/test-duplicates-detector.el000066400000000000000000000233301500430433700241410ustar00rootroot00000000000000;;; test-duplicates-detector.el --- Tests for finding duplicates in Org tests -*- lexical-binding: t; -*- ;; Copyright (C) 2023 Ilya Chernyshov ;; Authors: Ilya Chernyshov ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;; ;;; Commentary: ;; Unit tests that check for duplicate forms and tests in all Org test files. ;; Forms are considered duplicate if they: ;; 1. are `equal-including-properties', ;; 2. have the same nesting path, ;; 3. either are `should-' macros or have `should-' macros inside. ;; To ignore a form or a group of forms, wrap them in ;; `org-test-ignore-duplicate'. ;; `ert-deftest' are considered duplicate if their body are ;; `equal-including-properties.' When comparing, the docstrings are not taken ;; into account. ;;; Code: (require 'org-test "../testing/org-test") ;;;; Variables (defvar test-duplicates-progn-forms '( progn prog1 let dolist dotimes org-test-with-temp-text org-test-with-temp-text-in-file org-test-at-id org-test-ignore-duplicate) "List of forms equivalent to `progn'. Immediate children inside these are not checked for duplicates.") (defvar test-duplicates-detector-file-path (expand-file-name "test-duplicates-detector.el" (expand-file-name "lisp" org-test-dir))) (defvar test-duplicates-detector-files (remove test-duplicates-detector-file-path (directory-files (expand-file-name "lisp" org-test-dir) t "\\.el$"))) (defvar test-duplicates-detector-duplicate-forms nil "A list where each element is either: ((file test-name [(form-1 . numerical-order) (form-2 . numerical-order) ...]) (dup-form-1 . (numerical-order [numerical-order ...])) [ (dup-form-2 . (numerical-order [numerical-order ...])) (dup-form-3 . (numerical-order [numerical-order ...])) ...]) or (test-1-symbol . duplicate-of-test-1-symbol) Where (file test-name [(form-1 . numerical-order) (form-2 . numerical-order) ...]) is a path to duplicates. For example, the path for the duplicates in the following test: test-file.el (ertdeftest test-name () \"Docstring.\" (let ((var-1 \"value\")) (when var-1 (should-not (equal 2 (some-func \"string\" \"x\" nil))) (some-func \"string\" \"x=2\") (should-not (equal 2 (some-func \"string\" \"x\" nil))) (some-func \"string\" \"x=2\")))) would look like this: (\"/absolute/path/to/test-file.el\" test-name (let . 4) (when . 2)) And the records about the duplicates would look like this: ((should-not (equal 2 (some-func \"string\" \"x\" nil))) 4 2)") (defvar test-duplicates-detector-forms nil "Nested alist of found forms and paths to them (not filtered).") ;;;; Macros (defmacro org-test-ignore-duplicate (&rest body) "Eval BODY forms sequentially and return value of last one. The macro's body will be ignored by `test-duplicates-detector.el' tests to skip duplicate forms inside the body." (declare (indent 0)) `(progn ,@body)) ;;;; ERT tests (ert-deftest test-org-tests/find-duplicates () "Try to find duplicate forms and ert-deftests in FILES." (should-not (test-duplicates-detector--find-duplicates test-duplicates-detector-files))) ;;;; Auxiliary functions (defun test-duplicates-detector--find-duplicates (files) "Try to find duplicate forms and ert-deftests in FILES. Duplicate forms will be written to `test-duplicates-detector-duplicate-forms'. `message' paths to them in a human-readable format." (setq test-duplicates-detector-forms nil) (let (found-deftests duplicate-tests) (dolist (file files) (with-current-buffer (find-file-noselect file) (save-excursion (goto-char (point-min)) (while (search-forward "(ert-deftest" nil t) (goto-char (match-beginning 0)) (let (deftest test-name) (ignore-errors (while (setq deftest (read (current-buffer))) (setq test-name (cadr deftest)) (when (eq (car deftest) 'ert-deftest) (if-let* ((f (seq-find (lambda (x) (equal-including-properties ;; if cadddr is a docstring (if (stringp (cadddr deftest)) (cddddr deftest) (cdddr deftest)) (if (stringp (cadddr x)) (cddddr x) (cdddr x)))) found-deftests))) (push (cons test-name (cadr f)) duplicate-tests) (push deftest found-deftests) (test-duplicates-detector--search-forms-recursively deftest (list file test-name))))))))))) (setq test-duplicates-detector-duplicate-forms (seq-filter #'cdr (mapcar (lambda (file) (cons (car file) (seq-filter (lambda (x) (and (caddr x) (seq-intersection '(should-not should should-error) (flatten-list (car x))))) (cdr file)))) test-duplicates-detector-forms))) (when test-duplicates-detector-duplicate-forms (message "Found duplicates (To ignore the duplicate forms, wrap them in `org-test-ignore-duplicate'): %s" (mapconcat (lambda (path) (let* ((file (file-relative-name (caar path))) (test-name (symbol-name (cadar path))) (string-path (append (list file test-name) (mapcar (lambda (x) (symbol-name (car x))) (cddar path)))) (indent -1) (print-level 3)) (concat (mapconcat (lambda (x) (concat (make-string (* (setq indent (1+ indent)) 2) ? ) x "\n")) string-path "") (mapconcat (lambda (x) (format "%s%S: %d times\n" (make-string (* indent 2) ? ) (car x) (length (cdr x)))) (cdr path) "")))) test-duplicates-detector-duplicate-forms ""))) (when duplicate-tests (message "Duplicate ERT tests found:\n%s\n" (mapconcat (lambda (x) (format "%S" x)) duplicate-tests "\n"))) (append test-duplicates-detector-duplicate-forms duplicate-tests))) (defun test-duplicates-detector--search-forms-recursively (form form-path) "Search for forms recursively in FORM. FORM-PATH is list of the form: (\"file-path\" ert-test-symbol (symbol-1 . sexp-order-1) (symbol-2 . sexp-order-2)) Write each form to `test-duplicates-detector-forms'" (let ((idx 0)) (dolist (sub-form form) (when (consp sub-form) (unless (memq (car-safe form) test-duplicates-progn-forms) (push idx (alist-get sub-form (alist-get form-path test-duplicates-detector-forms nil nil #'equal) nil nil #'equal-including-properties))) (unless (memq (car sub-form) '(should-not should should-error)) (test-duplicates-detector--search-forms-recursively sub-form (append form-path (list (cons (car sub-form) idx)))))) (cl-incf idx)))) ;;;; Testing the detector itself (ert-deftest test-org-tests/test-duplicates-detector-testing-find-duplicates () "Test `test-duplicates-detector--find-duplicates'." (should (equal (test-duplicates-detector--find-duplicates (list test-duplicates-detector-file-path)) `(((,test-duplicates-detector-file-path test-org-tests/test-with-nested-duplicates) ((let ((var "string")) (should (message "123 %s" var))) 6 4)) ((,test-duplicates-detector-file-path test-org-tests/test-with-duplicates-at-root) ((should (message "123")) 6 4)) (test-org-tests/duplicate-test-2 . test-org-tests/duplicate-test-1))))) ;;;;; Tests with duplicate forms (ert-deftest test-org-tests/test-with-duplicates-at-root () "Test with duplicates at the root." (should (message "123")) (format "%s" "string") (should (message "123"))) (ert-deftest test-org-tests/test-with-nested-duplicates () "Test with nested duplicates." (let ((var "string")) (should (message "123 %s" var))) (format "%s" "string") (let ((var "string")) (should (message "123 %s" var))) (format "%s" "string")) ;;;;; Tests without duplicates (ert-deftest test-org-tests/test-without-duplicates-1 () "Test without duplicates." (let ((var-1 "asd")) (concat "string" var-1)) (should (let ((var-1 "asd")) (concat "string" var-1)))) (ert-deftest test-org-tests/test-without-duplicates-2 () "Test without duplicates. Equal `should' macros, but different nesting paths." (let ((var "string")) (should (format "123 %s" "asd"))) (+ 5 6 9) (should (format "123 %s" "asd"))) ;;;;; Duplicate deftests (maybe different names, but same body) (ert-deftest test-org-tests/duplicate-test-1 () "Docstring of duplicate-test-1." (let ((var 99)) (+ 5 6 9 var) (should (format "123 %s" "asd"))) (should (format "123 %s" "asd"))) (ert-deftest test-org-tests/duplicate-test-2 () "Docstring of duplicate-test-2." (let ((var 99)) (+ 5 6 9 var) (should (format "123 %s" "asd"))) (should (format "123 %s" "asd"))) (provide 'test-duplicates-detector) ;; Local Variables: ;; outline-regexp: "\\(;\\{3,\\} \\)" ;; End: ;;; test-duplicates-detector.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-C.el000066400000000000000000000170151500430433700207600ustar00rootroot00000000000000;;; test-ob-C.el --- tests for ob-C.el -*- lexical-binding: t; -*- ;; Copyright (c) 2010-2014, 2019 Sergey Litvinov, Thierry Banel ;; Authors: Sergey Litvinov, Thierry Banel ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (unless (featurep 'ob-C) (signal 'missing-test-dependency "Support for C code blocks")) (ert-deftest ob-C/simple-program () "Hello world program." (if (executable-find org-babel-C++-compiler) (org-test-at-id "fa6db330-e960-4ea2-ac67-94bb845b8577" (org-babel-next-src-block 1) (should (= 42 (org-babel-execute-src-block)))))) (ert-deftest ob-C/symbol-include () "Hello world program with unquoted :includes." (if (executable-find org-babel-C++-compiler) (org-test-at-id "fa6db330-e960-4ea2-ac67-94bb845b8577" (org-babel-next-src-block 2) (should (= 42 (org-babel-execute-src-block)))))) (ert-deftest ob-D/simple-program () "Hello world program." (if (executable-find org-babel-D-compiler) (org-test-at-id "fa6db330-e960-4ea2-ac67-94bb845b8577" (org-babel-next-src-block 3) (should (= 42 (org-babel-execute-src-block)))))) (ert-deftest ob-C/integer-var () "Test of an integer variable." (if (executable-find org-babel-C++-compiler) (org-test-at-id "fa6db330-e960-4ea2-ac67-94bb845b8577" (org-babel-next-src-block 4) (should (= 12 (org-babel-execute-src-block)))))) (ert-deftest ob-D/integer-var () "Test of an integer variable." (if (executable-find org-babel-D-compiler) (org-test-at-id "fa6db330-e960-4ea2-ac67-94bb845b8577" (org-babel-next-src-block 5) (should (= 12 (org-babel-execute-src-block)))))) (ert-deftest ob-C/two-integer-var () "Test of two input variables" (if (executable-find org-babel-C++-compiler) (org-test-at-id "fa6db330-e960-4ea2-ac67-94bb845b8577" (org-babel-next-src-block 6) (should (= 22 (org-babel-execute-src-block)))))) (ert-deftest ob-D/two-integer-var () "Test of two input variables" (if (executable-find org-babel-D-compiler) (org-test-at-id "fa6db330-e960-4ea2-ac67-94bb845b8577" (org-babel-next-src-block 7) (should (= 22 (org-babel-execute-src-block)))))) (ert-deftest ob-C/string-var () "Test of a string input variable" (if (executable-find org-babel-C++-compiler) (org-test-at-id "fa6db330-e960-4ea2-ac67-94bb845b8577" (org-babel-next-src-block 8) (should (equal "word 4" (org-babel-execute-src-block)))))) (ert-deftest ob-D/string-var () "Test of a string input variable" (if (executable-find org-babel-D-compiler) (org-test-at-id "fa6db330-e960-4ea2-ac67-94bb845b8577" (org-babel-next-src-block 9) (should (equal "word 4" (org-babel-execute-src-block)))))) (ert-deftest ob-C/preprocessor () "Test of a string variable" (if (executable-find org-babel-C++-compiler) (org-test-at-id "fa6db330-e960-4ea2-ac67-94bb845b8577" (org-babel-next-src-block 10) (should (= 42 (org-babel-execute-src-block)))))) (ert-deftest ob-C/float-var () "Test that floats are passed without unnecessary rounding." (if (executable-find org-babel-C++-compiler) (org-test-with-temp-text "#+source: float_var #+begin_src cpp :var x=1.123456789012345678 :includes \"\" :results silent double y = 1.123456789012345678; std::cout << (x == y); #+end_src" (should (= 1 (org-babel-execute-src-block)))))) (ert-deftest ob-C/table () "Test of a table output" (if (executable-find org-babel-C++-compiler) (org-test-at-id "2df1ab83-3fa3-462a-a1f3-3aef6044a874" (org-babel-next-src-block 1) (should (equal '((1) (2)) (org-babel-execute-src-block)))))) (ert-deftest ob-D/table () "Test of a table output" (if (executable-find org-babel-D-compiler) (org-test-at-id "2df1ab83-3fa3-462a-a1f3-3aef6044a874" (org-babel-next-src-block 2) (should (equal '((1) (2)) (org-babel-execute-src-block)))))) (ert-deftest ob-C/list-var () "Test of a list input variable" (if (executable-find org-babel-C++-compiler) (org-test-at-id "cc65d6b3-8e8e-4f9c-94cd-f5a00cdeceb5" (org-babel-next-src-block 1) (should (string= "abcdef2" (org-babel-execute-src-block)))))) (ert-deftest ob-D/list-var () "Test of a list input variable" (if (executable-find org-babel-D-compiler) (org-test-at-id "cc65d6b3-8e8e-4f9c-94cd-f5a00cdeceb5" (org-babel-next-src-block 2) (should (string= "abcdef2" (org-babel-execute-src-block)))))) (ert-deftest ob-C/vector-var () "Test of a vector input variable" (if (executable-find org-babel-C++-compiler) (org-test-at-id "cc65d6b3-8e8e-4f9c-94cd-f5a00cdeceb5" (org-babel-next-src-block 3) (should (equal 122 (org-babel-execute-src-block)))))) (ert-deftest ob-D/vector-var () "Test of a vector input variable" (if (executable-find org-babel-D-compiler) (org-test-at-id "cc65d6b3-8e8e-4f9c-94cd-f5a00cdeceb5" (org-babel-next-src-block 4) (should (equal 122 (org-babel-execute-src-block)))))) (ert-deftest ob-C/list-list-var () "Test of a list list input variable" (if (executable-find org-babel-C++-compiler) (org-test-at-id "cc65d6b3-8e8e-4f9c-94cd-f5a00cdeceb5" (org-babel-next-src-block 5) (should (equal '((1 3) (2 4)) (org-babel-execute-src-block)))))) (ert-deftest ob-D/list-list-var () "Test of a list list input variable" (if (executable-find org-babel-D-compiler) (org-test-at-id "cc65d6b3-8e8e-4f9c-94cd-f5a00cdeceb5" (org-babel-next-src-block 6) (should (equal '((1 3) (2 4)) (org-babel-execute-src-block)))))) (ert-deftest ob-C/inhomogeneous_table () "Test inhomogeneous input table" (if (executable-find org-babel-C++-compiler) (org-test-at-id "e112bc2e-419a-4890-99c2-7ac4779531cc" (org-babel-next-src-block 1) (should (equal '(("monday" 34) ("tuesday" 41) ("wednesday" 56) ("thursday" 17) ("friday" 12) ("saturday" 7) ("sunday" 4) ("Friday" "friday")) (org-babel-execute-src-block)))))) (ert-deftest ob-D/inhomogeneous_table () "Test inhomogeneous input table" (if (executable-find org-babel-D-compiler) (org-test-at-id "e112bc2e-419a-4890-99c2-7ac4779531cc" (org-babel-next-src-block 2) (should (equal '(("monday" 34) ("tuesday" 41) ("wednesday" 56) ("thursday" 17) ("friday" 12) ("saturday" 7) ("sunday" 4) ("Friday" "friday")) (org-babel-execute-src-block)))))) (ert-deftest ob-C/ouput-doublequotes () "Double quotes not swallowed in raw output" (if (executable-find org-babel-C++-compiler) (org-test-at-id "9386490b-4063-4400-842c-4a634edbedf5" (org-babel-next-src-block 1) (should (equal "\"line 1\"\n\"line 2\"\n\"line 3\"\n" (org-babel-execute-src-block)))))) (provide 'test-ob-C) ;;; test-ob-C.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-R.el000066400000000000000000000275471500430433700210120ustar00rootroot00000000000000;;; test-ob-R.el --- tests for ob-R.el -*- lexical-binding: t; -*- ;; Copyright (c) 2011-2014, 2019 Eric Schulte ;; Authors: Eric Schulte ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (org-test-for-executable "R") (require 'ob-core) (unless (featurep 'ess) (signal 'missing-test-dependency "ESS")) (defvar ess-ask-for-ess-directory) (defvar ess-history-file) (defvar ess-r-post-run-hook) (declare-function ess-command "ext:ess-inf" (cmd &optional out-buffer sleep no-prompt-check wait proc proc force-redisplay timeout)) (declare-function ess-calculate-width "ext:ess-inf" (opt)) (unless (featurep 'ob-R) (signal 'missing-test-dependency "Support for R code blocks")) (ert-deftest test-ob-R/simple-session () (let (ess-ask-for-ess-directory ess-history-file) (org-test-with-temp-text "#+begin_src R :session R\n paste(\"Yep!\")\n#+end_src\n" (should (string= "Yep!" (org-babel-execute-src-block)))))) (ert-deftest test-ob-R/colnames-yes-header-argument () (org-test-with-temp-text "#+name: eg | col | |-----| | a | | b | #+header: :colnames yes #+header: :var x = eg #+begin_src R x #+end_src" (org-babel-next-src-block) (should (equal '(("col") hline ("a") ("b")) (org-babel-execute-src-block))))) (ert-deftest test-ob-R/colnames-nil-header-argument () (org-test-with-temp-text "#+name: eg | col | |-----| | a | | b | #+header: :colnames nil #+header: :var x = eg #+begin_src R x #+end_src" (org-babel-next-src-block) (should (equal '(("col") hline ("a") ("b")) (org-babel-execute-src-block))))) (ert-deftest test-ob-R/colnames-no-header-argument () (org-test-with-temp-text "#+name: eg | col | |-----| | a | | b | #+header: :colnames no #+header: :var x = eg #+begin_src R x #+end_src" (org-babel-next-src-block) (should (equal '(("col") ("a") ("b")) (org-babel-execute-src-block))))) (ert-deftest test-ob-R/results-file () (let (ess-ask-for-ess-directory ess-history-file) (org-test-with-temp-text "#+NAME: TESTSRC #+BEGIN_SRC R :results file a <- file.path(\"junk\", \"test.org\") a #+END_SRC" (goto-char (point-min)) (org-babel-execute-maybe) (org-babel-goto-named-result "TESTSRC") (forward-line 1) (should (string= "[[file:junk/test.org]]" (buffer-substring-no-properties (point-at-bol) (point-at-eol)))) (goto-char (point-min)) (forward-line 1) (insert "#+header: :session\n") (goto-char (point-min)) (org-babel-execute-maybe) (org-babel-goto-named-result "TESTSRC") (forward-line 1) (should (string= "[[file:junk/test.org]]" (buffer-substring-no-properties (point-at-bol) (point-at-eol))))))) (ert-deftest test-ob-r/output-with-<> () "make sure angle brackets are well formatted" (let (ess-ask-for-ess-directory ess-history-file) (should (string="[1] \" \" [1] \"one three\" [1] \"end35\" " (org-test-with-temp-text "#+begin_src R :results output print(\" \") print(\"one three\") print(\"end35\") #+end_src " (org-babel-execute-src-block)) )))) ;; (ert-deftest test-ob-r/output-with-error () ;; "make sure angle brackets are well formatted" ;; (let (ess-ask-for-ess-directory ess-history-file) ;; (should (string="Error in print(1/a) : object 'a' not found" ;; (org-test-with-temp-text "#+begin_src R :results output ;; print(1/a) ;; #+end_src ;; " ;; (org-babel-execute-src-block)) ;; )))) (ert-deftest test-ob-R/output-nonprinted () (let (ess-ask-for-ess-directory ess-history-file) (org-test-with-temp-text "#+begin_src R :results output 4.0 * 3.5 log(10) log10(10) \(3 + 1) * 5 3^-1 1/0 #+end_src" (should (string= "[1] 14\n[1] 2.302585\n[1] 1\n[1] 20\n[1] 0.3333333\n[1] Inf\n" (org-babel-execute-src-block)))))) (ert-deftest test-ob-r/NA-blank () "For :results value, NAs should be empty" (let (ess-ask-for-ess-directory ess-history-file) (should (equal '(("A" "B") hline ("" 1) (1 2) (1 "") (1 4) (1 4)) (org-test-with-temp-text "#+BEGIN_SRC R :results value :colnames yes data.frame(A=c(NA,1,1,1,1),B=c(1,2,NA,4,4)) #+end_src" (org-babel-execute-src-block)))))) (ert-deftest ob-session-async-R-simple-session-async-value () (let (ess-ask-for-ess-directory ess-history-file (org-babel-temporary-directory "/tmp") (org-confirm-babel-evaluate nil)) (org-test-with-temp-text "#+begin_src R :session R :async yes\n Sys.sleep(.1)\n paste(\"Yep!\")\n#+end_src\n" (should (let ((expected "Yep!")) (and (not (string= expected (org-babel-execute-src-block))) (string= expected (progn (sleep-for 0.200) (goto-char (org-babel-where-is-src-block-result)) (org-babel-read-result))))))))) (ert-deftest ob-session-async-R-simple-session-async-output () (let (ess-ask-for-ess-directory ess-history-file (org-babel-temporary-directory "/tmp") (org-confirm-babel-evaluate nil) ;; Workaround for Emacs 27. See https://orgmode.org/list/87ilduqrem.fsf@localhost (ess-r-post-run-hook (lambda () (ess-command (ess-calculate-width 9999))))) (org-test-with-temp-text "#+begin_src R :session R :results output :async yes\n Sys.sleep(.1)\n 1:5\n#+end_src\n" (should (let ((expected "[1] 1 2 3 4 5")) (and (not (string= expected (org-babel-execute-src-block))) (string= expected (progn (sleep-for 0.200) (goto-char (org-babel-where-is-src-block-result)) (org-babel-read-result))))))))) (ert-deftest ob-session-async-R-named-output () (let (ess-ask-for-ess-directory ess-history-file (org-babel-temporary-directory "/tmp") org-confirm-babel-evaluate (src-block "#+begin_src R :async :session R :results output\n 1:5\n#+end_src") (results-before "\n\n#+NAME: foobar\n#+RESULTS:\n: [1] 1") (results-after "\n\n#+NAME: foobar\n#+RESULTS:\n: [1] 1 2 3 4 5\n") ;; Workaround for Emacs 27. See https://orgmode.org/list/87ilduqrem.fsf@localhost (ess-r-post-run-hook (lambda () (ess-command (ess-calculate-width 9999))))) (org-test-with-temp-text (concat src-block results-before) (should (progn (org-babel-execute-src-block) (sleep-for 0.200) (string= (concat src-block results-after) (buffer-string))))))) (ert-deftest ob-session-async-R-named-value () (let (ess-ask-for-ess-directory ess-history-file org-confirm-babel-evaluate (org-babel-temporary-directory "/tmp") (src-block "#+begin_src R :async :session R :results value\n paste(\"Yep!\")\n#+end_src") (results-before "\n\n#+NAME: foobar\n#+RESULTS:\n: [1] 1") (results-after "\n\n#+NAME: foobar\n#+RESULTS:\n: Yep!\n")) (org-test-with-temp-text (concat src-block results-before) (should (progn (org-babel-execute-src-block) (sleep-for 0.200) (string= (concat src-block results-after) (buffer-string))))))) (ert-deftest ob-session-async-R-output-drawer () (let (ess-ask-for-ess-directory ess-history-file org-confirm-babel-evaluate (org-babel-temporary-directory "/tmp") (src-block "#+begin_src R :async :session R :results output drawer\n 1:5\n#+end_src") (result "\n\n#+RESULTS:\n:results:\n[1] 1 2 3 4 5\n:end:\n") ;; Workaround for Emacs 27. See https://orgmode.org/list/87ilduqrem.fsf@localhost (ess-r-post-run-hook (lambda () (ess-command (ess-calculate-width 9999))))) (org-test-with-temp-text src-block (should (progn (org-babel-execute-src-block) (sleep-for 0.200) (string= (concat src-block result) (buffer-string))))))) (ert-deftest ob-session-async-R-value-drawer () (let (ess-ask-for-ess-directory ess-history-file org-confirm-babel-evaluate (org-babel-temporary-directory "/tmp") (src-block "#+begin_src R :async :session R :results value drawer\n 1:3\n#+end_src") (result "\n\n#+RESULTS:\n:results:\n1\n2\n3\n:end:\n")) (org-test-with-temp-text src-block (should (progn (org-babel-execute-src-block) (sleep-for 0.200) (string= (concat src-block result) (buffer-string))))))) ; add test for :result output (ert-deftest ob-session-R-result-output () (let (ess-ask-for-ess-directory ess-history-file org-confirm-babel-evaluate (org-babel-temporary-directory "/tmp") (src-block "#+begin_src R :session R :results output \n 1:3\n#+end_src") (result "\n\n#+RESULTS:\n: [1] 1 2 3\n" )) (org-test-with-temp-text src-block (should (progn (org-babel-execute-src-block) (sleep-for 0.200) (string= (concat src-block result) (buffer-string))))))) (ert-deftest ob-session-R-result-value () (let (ess-ask-for-ess-directory ess-history-file org-confirm-babel-evaluate (org-babel-temporary-directory "/tmp")) (org-test-with-temp-text "#+begin_src R :session R :results value \n 1:50\n#+end_src" (should (equal (number-sequence 1 50) (mapcar #'car (org-babel-execute-src-block))))))) ;; test for printing of (nested) list (ert-deftest ob-R-nested-list () "List are printed as the first column of a table and nested lists are ignored" (let (ess-ask-for-ess-directory ess-history-file org-confirm-babel-evaluate (org-babel-temporary-directory "/tmp") (text " #+NAME: example-list - simple - not - nested - list #+BEGIN_SRC R :var x=example-list x #+END_SRC ") (result " #+RESULTS: | simple | | list | ")) (org-test-with-temp-text-in-file text (goto-char (point-min)) (org-babel-next-src-block) (should (progn (org-babel-execute-src-block) (sleep-for 0.200) (string= (concat text result) (buffer-string))))))) (ert-deftest test-ob-R/async-prompt-filter () "Test that async evaluation doesn't remove spurious prompts and leading indentation." (let* (ess-ask-for-ess-directory ess-history-file org-confirm-babel-evaluate (session-name "*R:test-ob-R/session-async-results*") (kill-buffer-query-functions nil) (start-time (current-time)) (wait-time (time-add start-time 3)) uuid-placeholder) (org-test-with-temp-text (concat "#+begin_src R :session " session-name " :async t :results output table(c('ab','ab','c',NA,NA), useNA='always') #+end_src") (setq uuid-placeholder (org-trim (org-babel-execute-src-block))) (catch 'too-long (while (string-match uuid-placeholder (buffer-string)) (progn (sleep-for 0.01) (when (time-less-p wait-time (current-time)) (throw 'too-long (ert-fail "Took too long to get result from callback")))))) (search-forward "#+results") (beginning-of-line 2) (when (should (re-search-forward "\ :\\([ ]+ab\\)[ ]+c[ ]+[ ]* :\\([ ]+2\\)[ ]+1[ ]+2")) (should (equal (length (match-string 1)) (length (match-string 2)))) (kill-buffer session-name))))) (provide 'test-ob-R) ;;; test-ob-R.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-awk.el000066400000000000000000000035501500430433700213570ustar00rootroot00000000000000;;; test-ob-awk.el --- tests for ob-awk.el -*- lexical-binding: t; -*- ;; Copyright (c) 2010-2014, 2019 Sergey Litvinov ;; Authors: Sergey Litvinov ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (org-test-for-executable "awk") (unless (featurep 'ob-awk) (signal 'missing-test-dependency "Support for Awk code blocks")) (ert-deftest ob-awk/input-none () "Test with no input file" (org-test-at-id "9e998b2a-3581-43fe-b26d-07d3c507b86a" (org-babel-next-src-block) (should (= 42 (org-babel-execute-src-block))))) (ert-deftest ob-awk/input-src-block-1 () "Test a code block as an input" (org-test-at-id "9e998b2a-3581-43fe-b26d-07d3c507b86a" (org-babel-next-src-block 2) (should (= 43 (org-babel-execute-src-block))))) (ert-deftest ob-awk/input-src-block-2 () "Test a code block as an input" (org-test-at-id "9e998b2a-3581-43fe-b26d-07d3c507b86a" (org-babel-next-src-block 3) (should (= 150 (org-babel-execute-src-block))))) (ert-deftest ob-awk/tabular-input () "Test a code block as an input" (org-test-at-id "9e998b2a-3581-43fe-b26d-07d3c507b86a" (org-babel-next-src-block 4) (should (equal '(("a" "b" "c")) (org-babel-execute-src-block))))) (provide 'test-ob-awk) ;;; test-ob-awk.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-calc.el000066400000000000000000000076151500430433700215050ustar00rootroot00000000000000;;; test-ob-calc.el --- tests for ob-calc.el -*- lexical-binding: t; -*- ;; Copyright (C) 2024 Visuwesh ;; Author: Visuwesh ;; 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 . ;;; Code: (require 'ob-calc) (unless (featurep 'ob-calc) (signal 'missing-test-dependency "Support for Calc code blocks")) (ert-deftest ob-calc/simple-program-mult () "Test of simple multiplication." (org-test-with-temp-text "\ #+BEGIN_SRC calc :results silent 1 * 2 #+END_SRC" (should (equal "2" (org-babel-execute-src-block))))) (ert-deftest ob-calc/simple-program-arith () "Test of simple arithmetic." (org-test-with-temp-text "\ #+BEGIN_SRC calc :results silent 12 + 16 - 1 #+END_SRC" (should (equal "27" (org-babel-execute-src-block))))) (ert-deftest ob-calc/float-var () "Test of floating variable." (org-test-with-temp-text "\ #+BEGIN_SRC calc :results silent :var x=2.0 1/x #+END_SRC" (should (equal "0.5" (org-babel-execute-src-block))))) (ert-deftest ob-calc/simple-program-symbolic () "Test of simple symbolic algebra." (org-test-with-temp-text "\ #+BEGIN_SRC calc :results silent inv(a) #+END_SRC" (should (equal "1 / a" (org-babel-execute-src-block))))) (ert-deftest ob-calc/matrix-inversion () "Test of a matrix inversion." (org-test-with-temp-text "\ #+NAME: ob-calc-table-1 | 1 | 2 | 3 | | 5 | 6 | 7 | | 9 | 14 | 11 | #+BEGIN_SRC calc :results silent :var a=ob-calc-table-1 inv(a) #+END_SRC " (should (equal "[[-1, 0.625, -0.125], [0.25, -0.5, 0.25], [0.5, 0.125, -0.125]]" (let ((calc-float-format '(float 0))) ;; ;; Make sure that older Calc buffers are not present. (save-current-buffer (when (ignore-errors (calc-select-buffer)) (kill-buffer))) ;; Now, let-bound `calc-float-format' will take ;; effect. (org-babel-execute-src-block)))))) (ert-deftest ob-calc/matrix-algebra () "Test of simple matrix algebra." (org-test-with-temp-text "\ #+NAME: ob-calc-table-2 | 1 | 2 | 3 | 4 | 5 | #+BEGIN_SRC calc :results silent :var a=ob-calc-table-2 a*2 - 2 #+END_SRC" (should (equal "[0, 2, 4, 6, 8]" (org-babel-execute-src-block))))) (ert-deftest ob-calc/matrix-mean () "Test of simple mean of a vector." (org-test-with-temp-text "\ #+NAME: ob-calc-table-2 | 1 | 2 | 3 | 4 | 5 | #+BEGIN_SRC calc :results silent :var a=ob-calc-table-2 vmean(a) #+END_SRC" (should (equal "3" (org-babel-execute-src-block))))) (ert-deftest ob-calc/matrix-correct-conv-column () "Test of conversion of column table to Calc format." (org-test-with-temp-text "\ #+NAME: ob-calc-table-3 | 1 | | 2 | | 3 | #+BEGIN_SRC calc :results silent :var a=ob-calc-table-3 a #+END_SRC" (should (equal "[[1], [2], [3]]" (org-babel-execute-src-block))))) (ert-deftest ob-calc/matrix-correct-conv-row () "Test of conversion of row table to Calc format." (org-test-with-temp-text "\ #+NAME: ob-calc-table-2 | 1 | 2 | 3 | 4 | 5 | #+BEGIN_SRC calc :results silent :var a=ob-calc-table-2 a #+END_SRC" (should (equal "[1, 2, 3, 4, 5]" (org-babel-execute-src-block))))) (provide 'test-ob-calc) ;;; test-ob-calc.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-clojure.el000066400000000000000000000021461500430433700222400ustar00rootroot00000000000000;;; test-ob-clojure.el -*- lexical-binding: t; -*- ;; Copyright (c) 2018-2025 Free Software Foundation, Inc. ;; Authors: stardiviner ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Comments: ;; Org tests for ob-clojure.el live here ;;; Code: (unless (featurep 'ob-clojure) (signal 'missing-test-dependency "Support for Clojure code blocks")) ;; FIXME: The old tests where totally off. We need to write new tests. (provide 'test-ob-clojure) ;;; test-ob-clojure.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-comint.el000066400000000000000000000060621500430433700220670ustar00rootroot00000000000000;;; test-ob-comint.el -*- lexical-binding: t; -*- ;; Copyright (c) 2024 Matthew Trzcinski ;; Authors: Matthew Trzcinski ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Comment: ;; See testing/README for how to run tests. ;;; Requirements: ;;; Code: (ert-deftest test-org-babel-comint/prompt-filter-removes-prompt () "Test that prompt is actually removed." (let* ((prompt "org_babel_sh_prompt> ") (results "org_babel_sh_prompt> echo 'ob_comint_async_shell_start_d78ac49f-dc8a-4c39-827c-c93225484d59' # print message echo \"hello world\" echo 'ob_comint_async_shell_end_d78ac49f-dc8a-4c39-827c-c93225484d59' ob_comint_async_shell_start_d78ac49f-dc8a-4c39-827c-c93225484d59 org_babel_sh_prompt> org_babel_sh_prompt> \"hello world\" org_babel_sh_prompt> ob_comint_async_shell_end_d78ac49f-dc8a-4c39-827c-c93225484d59 org_babel_sh_prompt> ")) (should (string= (org-trim (string-join (mapcar #'org-trim (org-babel-comint--prompt-filter results prompt)) "\n") "\n") "echo 'ob_comint_async_shell_start_d78ac49f-dc8a-4c39-827c-c93225484d59' # print message echo \"hello world\" echo 'ob_comint_async_shell_end_d78ac49f-dc8a-4c39-827c-c93225484d59' ob_comint_async_shell_start_d78ac49f-dc8a-4c39-827c-c93225484d59 \"hello world\" ob_comint_async_shell_end_d78ac49f-dc8a-4c39-827c-c93225484d59")))) (ert-deftest test-org-babel-comint/echo-filter-removes-echo () "Test that echo is actually removed." (let* ((echo "echo 'ob_comint_async_shell_start_d78ac49f-dc8a-4c39-827c-c93225484d59' # print message echo \"hello world\" echo 'ob_comint_async_shell_end_d78ac49f-dc8a-4c39-827c-c93225484d59'") (result "org_babel_sh_prompt> echo 'ob_comint_async_shell_start_d78ac49f-dc8a-4c39-827c-c93225484d59' # print message echo \"hello world\" echo 'ob_comint_async_shell_end_d78ac49f-dc8a-4c39-827c-c93225484d59' ob_comint_async_shell_start_d78ac49f-dc8a-4c39-827c-c93225484d59 org_babel_sh_prompt> org_babel_sh_prompt> \"hello world\" org_babel_sh_prompt> ob_comint_async_shell_end_d78ac49f-dc8a-4c39-827c-c93225484d59 org_babel_sh_prompt> ")) (should (string= (org-babel-comint--echo-filter result echo) "\nob_comint_async_shell_start_d78ac49f-dc8a-4c39-827c-c93225484d59 org_babel_sh_prompt> org_babel_sh_prompt> \"hello world\" org_babel_sh_prompt> ob_comint_async_shell_end_d78ac49f-dc8a-4c39-827c-c93225484d59 org_babel_sh_prompt> ")))) (provide 'test-ob-comint) ;;; test-ob-comint.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-emacs-lisp.el000066400000000000000000000124711500430433700226340ustar00rootroot00000000000000;;; test-ob-emacs-lisp.el -*- lexical-binding: t; -*- ;; Copyright (c) 2012-2025 Free Software Foundation, Inc. ;; Authors: Eric Schulte, Martyn Jago ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Comments: ;; Org tests for ob-emacs-lisp.el live here ;;; Code: (ert-deftest ob-emacs-lisp/commented-last-block-line-no-var () (org-test-with-temp-text-in-file " #+begin_src emacs-lisp ;; #+end_src" (org-babel-next-src-block) (org-babel-execute-maybe) (should (re-search-forward "results:" nil t)) (forward-line) (should (string= "" (buffer-substring-no-properties (point-at-bol) (point-at-eol))))) (org-test-with-temp-text-in-file " #+begin_src emacs-lisp \"some text\";; #+end_src" (org-babel-next-src-block) (org-babel-execute-maybe) (should (re-search-forward "results:" nil t)) (forward-line) (should (string= ": some text" (buffer-substring-no-properties (point-at-bol) (point-at-eol)))))) (ert-deftest ob-emacs-lisp/commented-last-block-line-with-var () (org-test-with-temp-text-in-file " #+begin_src emacs-lisp :var a=1 ;; #+end_src" (org-babel-next-src-block) (org-babel-execute-maybe) (re-search-forward "results" nil t) (forward-line) (should (string= "" (buffer-substring-no-properties (point-at-bol) (point-at-eol)))))) (ert-deftest ob-emacs-lisp/commented-last-block-line () (should (string= ": 2" (org-test-with-temp-text-in-file " #+begin_src emacs-lisp :var a=2 2;; #+end_src" (org-babel-next-src-block) (org-babel-execute-maybe) (re-search-forward "results" nil t) (buffer-substring-no-properties (line-beginning-position 2) (line-end-position 2)))))) (ert-deftest ob-emacs-lisp/dynamic-lexical-execute () (cl-flet ((execute (text) (org-test-with-temp-text-in-file text (org-babel-next-src-block) (org-babel-execute-maybe) (re-search-forward "results" nil t) (re-search-forward ": " nil t) (buffer-substring-no-properties (point) (point-at-eol))))) (should (string= "dynamic" (execute " #+begin_src emacs-lisp :lexical no :results verbatim \(let ((x 'dynamic)) (funcall (let ((x 'lexical)) (lambda () x)))) #+end_src"))) (should (string= "lexical" (execute " #+begin_src emacs-lisp :lexical yes :results verbatim \(let ((x 'dynamic)) (funcall (let ((x 'lexical)) (lambda () x)))) #+end_src"))) (defvar ob-emacs--x) (should (string= "dynamic" (let ((ob-emacs--x 'dynamic)) (execute " #+begin_src emacs-lisp :lexical no :results verbatim ob-emacs--x #+end_src")))) (should (string= "lexical" (let ((ob-emacs--x 'dynamic)) (execute " #+begin_src emacs-lisp :lexical '((ob-emacs--x . lexical)) :results verbatim ob-emacs--x #+end_src")))) ;; Src block execution uses `eval'. As of 2019-02-26, `eval' does ;; not dynamically bind `lexical-binding' to the value of its ;; LEXICAL parameter. Hence, (eval 'lexical-binding LEXICAL) ;; evaluates to the same value that just `lexical-binding' ;; evaluates to, even if LEXICAL is different. So tests like the ;; following do not work here: ;; ;; (should (string= "t" (execute " ;; #+begin_src emacs-lisp :lexical yes :results verbatim ;; lexical-binding ;; #+end_src"))) ;; ;; However, the corresponding test in ;; `ob-emacs-lisp/dynamic-lexical-edit' does work. )) (ert-deftest ob-emacs-lisp/dynamic-lexical-edit () (cl-flet ((execute (text) (org-test-with-temp-text-in-file text (org-babel-next-src-block) (org-edit-src-code) (goto-char (point-max)) (prog1 (eval-last-sexp 0) (org-edit-src-exit))))) (should (eq 'dynamic (execute " #+begin_src emacs-lisp :lexical no :results verbatim \(let ((x 'dynamic)) (funcall (let ((x 'lexical)) (lambda () x)))) #+end_src"))) (should (eq 'lexical (execute " #+begin_src emacs-lisp :lexical yes :results verbatim \(let ((x 'dynamic)) (funcall (let ((x 'lexical)) (lambda () x)))) #+end_src"))) (defvar ob-emacs--x) (should (eq 'dynamic (let ((ob-emacs--x 'dynamic)) (execute " #+begin_src emacs-lisp :lexical no :results verbatim ob-emacs--x #+end_src")))) (should (eq 'lexical (let ((ob-emacs--x 'dynamic)) (execute " #+begin_src emacs-lisp :lexical '((ob-emacs--x . lexical)) :results verbatim ob-emacs--x #+end_src")))) (should (equal nil (execute " #+begin_src emacs-lisp :lexical no :results verbatim lexical-binding #+end_src"))) (should (equal t (execute " #+begin_src emacs-lisp :lexical yes :results verbatim lexical-binding #+end_src"))) (should (equal '((x . 0)) (execute " #+begin_src emacs-lisp :lexical '((x . 0)) :results verbatim lexical-binding #+end_src"))))) (provide 'test-ob-emacs-lisp) ;;; test-ob-emacs-lisp.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-eshell.el000066400000000000000000000043461500430433700220550ustar00rootroot00000000000000;;; test-ob-eshell.el -*- lexical-binding: t; -*- ;; Copyright (c) 2018 stardiviner ;; Authors: stardiviner ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Comment: ;; Template test file for Org tests ;;; Code: (unless (featurep 'ob-eshell) (signal 'missing-test-dependency "Support for Eshell code blocks")) (ert-deftest ob-eshell/execute () "Test ob-eshell execute." (should (string= (org-test-with-temp-text "#+begin_src eshell echo 2 #+end_src" (org-babel-execute-src-block) (goto-char (org-babel-where-is-src-block-result)) (forward-line) (buffer-substring-no-properties (point) (line-end-position))) ": 2"))) (ert-deftest ob-eshell/variables-assignment () "Test ob-eshell variables assignment." (should (string= (org-test-with-temp-text "#+begin_src eshell :var hi=\"hello, world\" echo $hi #+end_src" (org-babel-execute-src-block) (goto-char (org-babel-where-is-src-block-result)) (forward-line) (buffer-substring-no-properties (point) (line-end-position))) ": hello, world"))) (ert-deftest ob-eshell/session () "Test ob-eshell session." (should (string= (org-test-with-temp-text "#+begin_src eshell :session (setq hi \"hello, world\") #+end_src #+begin_src eshell :session echo $hi #+end_src" (org-babel-execute-src-block) (org-babel-next-src-block) (org-babel-execute-src-block) (goto-char (org-babel-where-is-src-block-result)) (forward-line) (buffer-substring-no-properties (point) (line-end-position))) ": hello, world"))) (provide 'test-ob-eshell) ;;; test-ob-eshell.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-exp.el000066400000000000000000000516351500430433700214000ustar00rootroot00000000000000;;; test-ob-exp.el -*- lexical-binding: t; -*- ;; Copyright (c) 2010-2015, 2019 Eric Schulte ;; Authors: Eric Schulte ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Comments: ;; Template test file for Org tests ;;; Code: (require 'ob-exp) (require 'org-src) (require 'org-test "../testing/org-test") (defmacro org-test-with-expanded-babel-code (&rest body) "Execute BODY while in a buffer with all Babel code evaluated. Current buffer is a copy of the original buffer." `(let ((string (org-with-wide-buffer (buffer-string))) (narrowing (list (point-min) (point-max))) (org-export-use-babel t)) (with-temp-buffer (org-mode) (insert string) (apply #'narrow-to-region narrowing) (org-babel-exp-process-buffer) (goto-char (point-min)) (progn ,@body)))) (ert-deftest test-ob-exp/org-babel-exp-src-blocks/w-no-headers () "Testing export without any headlines in the Org mode file." (require 'ox-ascii) (let ((text-file (concat (file-name-sans-extension org-test-no-heading-file) ".txt"))) (when (file-exists-p text-file) (delete-file text-file)) (org-test-in-example-file org-test-no-heading-file ;; Export the file to HTML. (org-export-to-file 'ascii text-file)) ;; should create a ".txt" file (should (file-exists-p text-file)) ;; should not create a file with "::" appended to its name (should-not (file-exists-p (concat org-test-no-heading-file "::"))) (when (file-exists-p text-file) (delete-file text-file)))) (ert-deftest test-ob-exp/org-babel-exp-src-blocks/w-no-file () "Testing export from buffers which are not visiting any file." (require 'ox-ascii) (let ((name (generate-new-buffer-name "*Org ASCII Export*"))) (org-test-in-example-file nil (org-export-to-buffer 'ascii name nil nil nil t)) ;; Should create a new buffer. (should (buffer-live-p (get-buffer name))) ;; Should contain the content of the buffer. (with-current-buffer (get-buffer name) (should (string-match (regexp-quote org-test-file-ob-anchor) (buffer-string)))) (when (get-buffer name) (kill-buffer name)))) (ert-deftest test-ob-exp/org-babel-exp-src-blocks/w-no-headers2 () "Testing export without any headlines in the Org file." (let ((html-file (concat (file-name-sans-extension org-test-link-in-heading-file) ".html"))) (when (file-exists-p html-file) (delete-file html-file)) (org-test-in-example-file org-test-link-in-heading-file ;; export the file to html (org-export-to-file 'html html-file)) ;; should create a .html file (should (file-exists-p html-file)) ;; should not create a file with "::" appended to its name (should-not (file-exists-p (concat org-test-link-in-heading-file "::"))) (when (file-exists-p html-file) (delete-file html-file)))) (ert-deftest ob-exp/noweb-on-export () "Noweb header arguments export correctly. - yes expand on both export and tangle - no expand on neither export or tangle - tangle expand on only tangle not export" (should (equal '("(message \"expanded1\")" "(message \"expanded2\")" ";; noweb-1-yes-start (message \"expanded1\")" ";; noweb-no-start <>" ";; noweb-2-yes-start (message \"expanded2\")" ";; noweb-tangle-start <> <>") (org-test-at-id "eb1f6498-5bd9-45e0-9c56-50717053e7b7" (org-narrow-to-subtree) (org-element-map (org-test-with-expanded-babel-code (org-element-parse-buffer)) 'src-block (lambda (src) (org-trim (org-element-property :value src)))))))) (ert-deftest ob-exp/noweb-on-export-with-exports-results () "Noweb header arguments export correctly using :exports results. - yes expand on both export and tangle - no expand on neither export or tangle - tangle expand on only tangle not export" (should (equal '(";; noweb-no-start <>" "<> <>") (org-test-at-id "8701beb4-13d9-468c-997a-8e63e8b66f8d" (org-narrow-to-subtree) (org-element-map (org-test-with-expanded-babel-code (org-element-parse-buffer)) 'src-block (lambda (src) (org-trim (org-element-property :value src)))))))) (ert-deftest ob-exp/exports-both () "Test the \":exports both\" header argument. The code block evaluation should create both a code block and a table." (org-test-at-id "92518f2a-a46a-4205-a3ab-bcce1008a4bb" (org-narrow-to-subtree) (let ((tree (org-test-with-expanded-babel-code (org-element-parse-buffer)))) (should (and (org-element-map tree 'src-block 'identity) (org-element-map tree 'table 'identity)))))) (ert-deftest ob-exp/mixed-blocks-with-exports-both () (should (equal '(property-drawer plain-list src-block fixed-width src-block plain-list) (org-test-at-id "5daa4d03-e3ea-46b7-b093-62c1b7632df3" (org-narrow-to-subtree) (mapcar #'org-element-type (org-element-map (org-test-with-expanded-babel-code (org-element-parse-buffer 'greater-element)) 'section 'org-element-contents nil t)))))) (ert-deftest ob-exp/export-with-name () (should (string-match "=qux=" (let ((org-babel-exp-code-template "=%name=\n#+BEGIN_SRC %lang%flags\nbody\n#+END_SRC")) (org-test-at-id "b02ddd8a-eeb8-42ab-8664-8a759e6f43d9" (org-narrow-to-subtree) (org-test-with-expanded-babel-code (buffer-string))))))) (ert-deftest ob-exp/export-with-header-argument () (let ((org-babel-exp-code-template " | header | value | |---------+----------| | foo | %foo | | results | %results | #+BEGIN_SRC %lang%flags\nbody\n#+END_SRC")) (org-test-at-id "b02ddd8a-eeb8-42ab-8664-8a759e6f43d9" (org-narrow-to-subtree) (org-test-with-expanded-babel-code (should (string-match "baz" (buffer-string))) (should (string-match "replace" (buffer-string))))))) (ert-deftest ob-exp/noweb-no-export-and-exports-both () (should (string-match "<>" (org-test-at-id "8a820f6c-7980-43db-8a24-0710d33729c9" (org-narrow-to-subtree) (org-test-with-expanded-babel-code (org-element-map (org-element-parse-buffer) 'src-block (lambda (src-block) (org-element-property :value src-block)) nil t)))))) (ert-deftest ob-exp/evaluate-all-executables-in-order () (defvar *evaluation-collector*) (should (equal '(5 4 3 2 1) (let ((org-export-use-babel t) *evaluation-collector*) (org-test-at-id "96cc7073-97ec-4556-87cf-1f9bffafd317" (org-narrow-to-subtree) (buffer-string) (org-test-with-expanded-babel-code *evaluation-collector*)))))) (ert-deftest ob-exp/exports-inline () (should (string-match (regexp-quote "Here is one in the middle {{{results(=1=)}}} of a line. Here is one at the end of a line. {{{results(=2=)}}} {{{results(=3=)}}} Here is one at the beginning of a line.") (org-test-at-id "54cb8dc3-298c-4883-a933-029b3c9d4b18" (org-narrow-to-subtree) (let ((org-babel-inline-result-wrap "=%s=")) (org-test-with-expanded-babel-code (buffer-string))))))) (ert-deftest ob-exp/exports-inline-code () (should (equal "src_emacs-lisp[ :exports code]{(+ 1 1)}" (org-test-with-temp-text "src_emacs-lisp[:exports code]{(+ 1 1)}" (let ((org-babel-inline-result-wrap "=%s=") (org-export-use-babel t)) (org-babel-exp-process-buffer)) (buffer-string)))) (should (equal "src_emacs-lisp[ :exports code]{(+ 1 1)}" (org-test-with-temp-text "src_emacs-lisp[ :exports code ]{(+ 1 1)}" (let ((org-babel-inline-result-wrap "=%s=") (org-export-use-babel t)) (org-babel-exp-process-buffer)) (buffer-string)))) ;; Do not escape characters in inline source blocks. (should (equal "src_c[ :exports code]{*a}" (org-test-with-temp-text "src_c[ :exports code ]{*a}" (let ((org-babel-inline-result-wrap "=%s=") (org-export-use-babel t)) (org-babel-exp-process-buffer)) (buffer-string)))) (should (equal "src_emacs-lisp[ :exports both]{(+ 1 1)} {{{results(=2=)}}}" (org-test-with-temp-text "src_emacs-lisp[:exports both]{(+ 1 1)}" (let ((org-babel-inline-result-wrap "=%s=") (org-export-use-babel t)) (org-babel-exp-process-buffer)) (buffer-string)))) (should (equal "{{{results(=2=)}}}" (org-test-with-temp-text "src_emacs-lisp[:exports results :results scalar]{(+ 1 1)}" (let ((org-babel-inline-result-wrap "=%s=") (org-export-use-babel t)) (org-babel-exp-process-buffer)) (buffer-string)))) (should (equal "foosrc_emacs-lisp[:exports code]{(+ 1 1)}" (org-test-with-temp-text "foosrc_emacs-lisp[:exports code]{(+ 1 1)}" (let ((org-babel-inline-result-wrap "=%s=") (org-export-use-babel t)) (org-babel-exp-process-buffer)) (buffer-string)))) (should (let ((text "src_emacs lisp{(+ 1 1)}")) (string-match (regexp-quote text) (org-test-with-temp-text text (let ((org-babel-inline-result-wrap "=%s=") (org-export-use-babel t)) (org-babel-exp-process-buffer)) (buffer-string))))) (should (string-match (replace-regexp-in-string "\\\\\\[]{" "\\(?:\\[]\\)?{" ;accept both src_sh[]{...} or src_sh{...} (regexp-quote "Here is one in the middle src_sh[]{echo 1} of a line. Here is one at the end of a line. src_sh[]{echo 2} src_sh[]{echo 3} Here is one at the beginning of a line. Here is one that is also evaluated: src_sh[ :exports both]{echo 4} {{{results(=4=)}}}") nil t) (org-test-at-id "cd54fc88-1b6b-45b6-8511-4d8fa7fc8076" (org-narrow-to-subtree) (let ((org-babel-inline-result-wrap "=%s=") (org-export-use-babel t)) (org-test-with-expanded-babel-code (buffer-string))))))) (ert-deftest ob-exp/exports-inline-code-double-eval () "Based on default header arguments for inline code blocks (:exports results), the resulting code block `src_emacs-lisp{2}' should also be evaluated." (let ((org-babel-inline-result-wrap "=%s=") (org-export-use-babel t)) (should (string-match "\\`{{{results(src_emacs-lisp\\[\\]{2})}}}$" (org-test-with-temp-text "src_emacs-lisp[:exports results :results code]{(+ 1 1)}" (org-babel-exp-process-buffer) (buffer-string)))))) (ert-deftest ob-exp/exports-inline-code-eval-code-once () "Ibid above, except that the resulting inline code block should not be evaluated." (let ((org-export-use-babel t)) (should (string-match "{{{results(src_emacs-lisp\\(?:\\[[: a-zA-Z]+]\\)?{2})}}}$" (org-test-with-temp-text (concat "src_emacs-lisp[:exports results :results code " ":results_switches \":exports code\"]{(+ 1 1)}") (org-babel-exp-process-buffer) (buffer-string)))))) (ert-deftest ob-exp/exports-inline-code-double-eval-exports-both () (let ((org-export-use-babel t)) (should (string-match (concat "\\`src_emacs-lisp\\(?:\\[.+?]\\)?{(\\+ 1 1)} " "{{{results(src_emacs-lisp\\[ :exports code\\]{2})}}}$") (org-test-with-temp-text (concat "src_emacs-lisp[:exports both :results code " ":results_switches \":exports code\"]{(+ 1 1)}") (org-babel-exp-process-buffer) (buffer-string)))))) (ert-deftest ob-exp/export-call-line-information () (org-test-at-id "bec63a04-491e-4caa-97f5-108f3020365c" (org-narrow-to-subtree) (let ((org-babel-exp-call-line-template "\n: call: %line special-token")) (org-test-with-expanded-babel-code (should (string-match "double" (buffer-string))) (should (string-match "16" (buffer-string))) (should (string-match "special-token" (buffer-string))))))) (ert-deftest ob-exp/noweb-strip-export-ensure-strips () (org-test-at-id "8e7bd234-99b2-4b14-8cd6-53945e409775" (org-narrow-to-subtree) (org-babel-next-src-block 2) (should (= 110 (org-babel-execute-src-block))) (let ((result (org-test-with-expanded-babel-code (buffer-string)))) (should-not (string-match (regexp-quote "<>") result)) (should-not (string-match (regexp-quote "i=\"10\"") result))))) (ert-deftest ob-exp/use-case-of-reading-entry-properties () (org-test-at-id "cc5fbc20-bca5-437a-a7b8-2b4d7a03f820" (org-narrow-to-subtree) (let* ((case-fold-search nil) (result (org-test-with-expanded-babel-code (buffer-string))) (sect "a:1, b:0, c:3, d:0, e:0") (sub0 "a:1, b:2, c:4, d:0, e:0") (sub1 "a:1, b:2, c:5, d:0, e:6") (func sub0)) ;; entry "section" (should (string-match (concat "_shell-sect-call\n: shell " sect "\n") result)) (should (string-match (concat "_elisp-sect-call\n: elisp " sect "\n") result)) (should (string-match (concat "\n- sect inline shell " sect "\n") result)) (should (string-match (concat "\n- sect inline elisp " sect "\n") result)) ;; entry "subsection", call without arguments (should (string-match (concat "_shell-sub0-call\n: shell " sub0 "\n") result)) (should (string-match (concat "_elisp-sub0-call\n: elisp " sub0 "\n") result)) (should (string-match (concat "\n- sub0 inline shell " sub0 "\n") result)) (should (string-match (concat "\n- sub0 inline elisp " sub0 "\n") result)) ;; entry "subsection", call with arguments (should (string-match (concat "_shell-sub1-call\n: shell " sub1 "\n") result)) (should (string-match (concat "_elisp-sub1-call\n: elisp " sub1 "\n") result)) (should (string-match (concat "\n- sub1 inline shell " sub1 "\n") result)) (should (string-match (concat "\n- sub1 inline elisp " sub1 "\n") result)) ;; entry "function definition" (should (string-match (concat "_location_shell\n: shell " func "\n") result)) (should (string-match (concat "_location_elisp\n: elisp " func "\n") result))))) (ert-deftest ob-exp/export-from-a-temp-buffer () (let ((org-export-use-babel t)) (org-test-with-temp-text " #+Title: exporting from a temporary buffer #+name: foo #+BEGIN_SRC emacs-lisp :foo #+END_SRC #+name: bar #+BEGIN_SRC emacs-lisp :bar #+END_SRC #+BEGIN_SRC emacs-lisp :var foo=foo :noweb yes :exports results (list foo <>) #+END_SRC " (let* ((ascii (org-export-as 'ascii))) (should (string-match (regexp-quote " :foo :bar \n") ascii)))))) (ert-deftest ob-export/export-with-results-before-block () "Test export when results are inserted before source block." (let ((org-export-use-babel t)) (should (equal "#+RESULTS: src1 : 2 #+NAME: src1 #+begin_src emacs-lisp :exports both \(+ 1 1) #+end_src" (org-test-with-temp-text "#+RESULTS: src1 #+NAME: src1 #+BEGIN_SRC emacs-lisp :exports both \(+ 1 1) #+END_SRC" (org-babel-exp-process-buffer) (org-trim (org-no-properties (buffer-string)))))))) (ert-deftest ob-export/export-src-block-with-switches () "Test exporting a source block with switches." (should (string-match "\\`#\\+BEGIN_SRC emacs-lisp -n -r$" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp -n -r\n\(+ 1 1)\n#+END_SRC" (org-babel-exp-process-buffer) (buffer-string))))) (ert-deftest ob-export/export-src-block-with-flags () "Test exporting a source block with a flag." (should (string-match "\\`#\\+BEGIN_SRC emacs-lisp :flags -some-flag$" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp :flags -some-flag\n\(+ 1 1)\n#+END_SRC" (org-babel-exp-process-buffer) (buffer-string))))) (ert-deftest ob-export/export-and-indentation () "Test indentation of evaluated source blocks during export." ;; No indentation. (should (string-match "^t" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n t\n#+END_SRC" (let ((indent-tabs-mode t) (tab-width 1) (org-src-preserve-indentation nil)) (org-babel-exp-process-buffer) (buffer-string))))) ;; Preserve indentation with "-i" flag. (should (string-match "^ t" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp -i\n t\n#+END_SRC" (let ((indent-tabs-mode t) (tab-width 1)) (org-babel-exp-process-buffer) (buffer-string))))) ;; Preserve indentation with a non-nil ;; `org-src-preserve-indentation'. (should (string-match "^ t" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n t\n#+END_SRC" (let ((indent-tabs-mode t) (tab-width 1) (org-src-preserve-indentation t)) (org-babel-exp-process-buffer) (buffer-string)))))) (ert-deftest ob-export/export-under-commented-headline () "Test evaluation of code blocks under COMMENT headings." (let ((org-export-use-babel t) (org-babel-inline-result-wrap "=%s=")) ;; Do not eval block in a commented headline. (should (string-match ": 2" (org-test-with-temp-text "* Headline #+BEGIN_SRC emacs-lisp :exports results \(+ 1 1) #+END_SRC" (org-babel-exp-process-buffer) (buffer-string)))) (should-not (string-match ": 2" (org-test-with-temp-text "* COMMENT Headline #+BEGIN_SRC emacs-lisp :exports results \(+ 1 1) #+END_SRC" (org-babel-exp-process-buffer) (buffer-string)))) ;; Do not eval inline blocks either. (should (string-match "=2=" (org-test-with-temp-text "* Headline src_emacs-lisp{(+ 1 1)}" (org-babel-exp-process-buffer) (buffer-string)))) (should-not (string-match "=2=" (org-test-with-temp-text "* COMMENT Headline src_emacs-lisp{(+ 1 1)}" (org-babel-exp-process-buffer) (buffer-string)))) ;; Also check parent headlines. (should-not (string-match ": 2" (org-test-with-temp-text " * COMMENT Headline ** Children #+BEGIN_SRC emacs-lisp :exports results \(+ 1 1) #+END_SRC" (org-babel-exp-process-buffer) (buffer-string)))))) (ert-deftest ob-export/reference-in-post-header () "Test references in :post header during export." (should (org-test-with-temp-text " #+NAME: foo #+BEGIN_SRC emacs-lisp :exports none :var bar=\"baz\" (concat \"bar\" bar) #+END_SRC #+NAME: nofun #+BEGIN_SRC emacs-lisp :exports results :post foo(\"nofun\") #+END_SRC" (org-babel-exp-process-buffer) t))) (ert-deftest ob-export/babel-evaluate () "Test `org-export-use-babel' effect." ;; When nil, no Babel code is executed. (should-not (string-match-p "2" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp :exports results\n(+ 1 1)\n#+END_SRC" (let ((org-export-use-babel nil)) (org-babel-exp-process-buffer)) (buffer-string)))) (should-not (string-match-p "2" (org-test-with-temp-text "src_emacs-lisp{(+ 1 1)}" (let ((org-export-use-babel nil)) (org-babel-exp-process-buffer)) (buffer-string)))) ;; When non-nil, all Babel code types are executed. (should (string-match-p "2" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp :exports results\n(+ 1 1)\n#+END_SRC" (let ((org-export-use-babel t)) (org-babel-exp-process-buffer)) (buffer-string)))) (should (string-match-p "2" (org-test-with-temp-text "src_emacs-lisp{(+ 1 1)}" (let ((org-export-use-babel t)) (org-babel-exp-process-buffer)) (buffer-string))))) (ert-deftest ob-export/body-with-coderef () "Test exporting a code block with coderefs." (should (equal "#+begin_src emacs-lisp :exports code\n0 (ref:foo)\n#+end_src" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp :exports code\n0 (ref:foo)\n#+END_SRC" (let ((org-export-use-babel t) (org-coderef-label-format "(ref:foo)")) (org-babel-exp-process-buffer)) (buffer-string)))) (should (equal "#+begin_src emacs-lisp -l \"r:%s\" -lisp :exports code\n1 r:foo\n#+end_src" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp -l \"r:%s\" -lisp :exports code\n1 r:foo\n#+END_SRC" (let ((org-export-use-babel t)) (org-babel-exp-process-buffer)) (buffer-string))))) (ert-deftest ob-exp/src-block-with-affiliated-keyword () "Test exporting a code block with affiliated keywords." ;; Pathological case: affiliated keyword matches inline source block ;; syntax. (should (equal "#+name: call_foo\n#+begin_src emacs-lisp\n42\n#+end_src" (org-test-with-temp-text "#+name: call_foo\n#+BEGIN_SRC emacs-lisp\n42\n#+END_SRC" (let ((org-export-use-babel t)) (org-babel-exp-process-buffer)) (buffer-string))))) (ert-deftest ob-exp/unknown-call-reference () "Test exporting with a call that references an unknown name." (should-error (org-test-with-temp-text "call_foo()" (let ((org-export-use-babel t)) (org-babel-exp-process-buffer))) :type 'user-error)) (provide 'test-ob-exp) ;;; test-ob-exp.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-fortran.el000066400000000000000000000101171500430433700222450ustar00rootroot00000000000000;;; test-ob-fortran.el --- tests for ob-fortran.el -*- lexical-binding: t; -*- ;; Copyright (c) 2010-2014, 2019 Sergey Litvinov ;; Authors: Sergey Litvinov ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (org-test-for-executable "gfortran") (unless (featurep 'ob-fortran) (signal 'missing-test-dependency "Support for Fortran code blocks")) (ert-deftest ob-fortran/simple-program () "Test of hello world program." (org-test-at-id "459384e8-1797-4f11-867e-dde0473ea7cc" (org-babel-next-src-block) (should (equal "Hello world" (org-babel-execute-src-block))))) (ert-deftest ob-fortran/fortran-var-program () "Test a fortran variable" (org-test-at-id "459384e8-1797-4f11-867e-dde0473ea7cc" (org-babel-next-src-block 2) (should (= 10 (org-babel-execute-src-block))))) (ert-deftest ob-fortran/input-var () "Test :var" (org-test-at-id "d8d1dfd3-5f0c-48fe-b55d-777997e02242" (org-babel-next-src-block) (should (= 15 (org-babel-execute-src-block))))) (ert-deftest ob-fortran/preprocessor-var () "Test preprocessed fortran" (org-test-at-id "d8d1dfd3-5f0c-48fe-b55d-777997e02242" (org-babel-next-src-block 2) (should (= 42 (org-babel-execute-src-block))))) (ert-deftest ob-fortran/character-var () "Test string input" (org-test-at-id "d8d1dfd3-5f0c-48fe-b55d-777997e02242" (org-babel-next-src-block 3) (should (equal "word" (org-babel-execute-src-block))))) (ert-deftest ob-fortran/list-var () "Test real array input" (org-test-at-id "c28569d9-04ce-4cad-ab81-1ea29f691465" (org-babel-next-src-block) (should (equal "1.00 2.00 3.00" (org-babel-execute-src-block))))) (ert-deftest ob-fortran/list-var-from-table () "Test real array from a table" (org-test-at-id "c28569d9-04ce-4cad-ab81-1ea29f691465" (org-babel-next-src-block 2) (should (equal "1.00 2.00" (org-babel-execute-src-block))))) (ert-deftest ob-fortran/list-matrix-from-table1 () "Test real matrix from a table" (org-test-at-id "3f73ab19-d25a-428d-8c26-e8c6aa933976" (org-babel-next-src-block 1) (should (= 42 (org-babel-execute-src-block))))) (ert-deftest ob-fortran/list-matrix-from-table2 () "Test real matrix from a table" (org-test-at-id "3f73ab19-d25a-428d-8c26-e8c6aa933976" (org-babel-next-src-block 2) (should (= 42 (org-babel-execute-src-block))))) (ert-deftest ob-fortran/no-variables-with-main () "Test :var with explicit 'program'" (org-test-at-id "891ead4a-f87a-473c-9ae0-1cf348bcd04f" (org-babel-next-src-block) (should-error (org-babel-execute-src-block)) :type 'error)) (ert-deftest ob-fortran/wrong-list () "Test wrong input list" (org-test-at-id "891ead4a-f87a-473c-9ae0-1cf348bcd04f" (org-babel-next-src-block 2) (org-babel-execute-src-block) (when (should (buffer-live-p (get-buffer org-babel-error-buffer-name))) (kill-buffer org-babel-error-buffer-name)) :type 'error)) (ert-deftest ob-fortran/compiler-flags () "Test compiler's flags" (org-test-at-id "891ead4a-f87a-473c-9ae0-1cf348bcd04f" (org-babel-next-src-block 3) (org-babel-execute-src-block) (when (should (buffer-live-p (get-buffer org-babel-error-buffer-name))) (kill-buffer org-babel-error-buffer-name)) :type 'error)) (ert-deftest ob-fortran/command-arguments () "Test real array from a table" (org-test-at-id "2d5330ea-9934-4737-9ed6-e1d3dae2dfa4" (org-babel-next-src-block) (should (= 23 (org-babel-execute-src-block))))) (provide 'test-ob-fortran) ;;; test-ob-fortran.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-haskell-ghci.el000066400000000000000000000325161500430433700231340ustar00rootroot00000000000000;;; test-ob-haskell-ghci.el --- tests for ob-haskell.el GHCi -*- lexical-binding: t; -*- ;; Copyright (c) 2023-2025 Free Software Foundation, Inc. ;; Authors: Bruno BARBIER ;; This file is part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; ;;;; Useful references ;; ;; - https://orgmode.org/worg/org-contrib/babel/languages/lang-compat.html ;; - GHCi manual: https://downloads.haskell.org/ghc/latest/docs/users_guide/ghci.html ;;; Code: ;; (require 'org-test "../testing/org-test") (org-test-for-executable "ghci") (unless (featurep 'haskell-mode) (signal 'missing-test-dependency "haskell-mode")) (unless (featurep 'haskell) (signal 'missing-test-dependency "haskell")) ;;; Helpers ;; (defun test-ob-haskell-ghci-checking-buffers (todo) "Check some buffer related invariants.." (when (get-buffer "*haskell*") (error "A buffer named '*haskell*' exists. Can't safely test haskell blocks")) (prog1 (funcall todo) (when-let* ((hb (get-buffer "*haskell*"))) ;; We created a "*haskell*" buffer. That shouldn't happen. (error "'ob-haskell' created a buffer named '*haskell*'")))) (defun test-ob-haskell-ghci (args content &optional preamble unprotected) "Execute the code block CONTENT in a new GHCi session; return the result. Add ARGS to the code block argument line. Insert PREAMBLE before the code block. When UNPROTECTED is non-nil, check pre/post conditions." (when (listp content) (setq content (string-join content "\n"))) (unless preamble (setq preamble "")) (let ((todo (lambda () (prog1 (org-test-with-temp-text (concat preamble "\n" "#+begin_src haskell :compile no " args "\n" "" content "\n#+end_src") (org-babel-execute-src-block)))))) (if unprotected (funcall todo) (test-ob-haskell-ghci-checking-buffers todo)))) ;;; Tests ;;;; Hello Worlds. ;; (ert-deftest ob-haskell/hello-world-value-pure () (should (equal "Hello World!" (test-ob-haskell-ghci "" "\"Hello World!\"")))) (ert-deftest ob-haskell/hello-world-value-IO () (should (equal "Hello World!" (test-ob-haskell-ghci "" "return \"Hello World!\"")))) (ert-deftest ob-haskell/hello-world-output () (should (equal "Hello World!" (test-ob-haskell-ghci ":results output" "putStrLn \"Hello World!\"")))) (ert-deftest ob-haskell/hello-world-output-nothing () ;; GHCi prints the value on standard output. So, the last value is part of the output. (should (equal "Hello World!" (test-ob-haskell-ghci ":results output" "return \"Hello World!\"")))) (ert-deftest ob-haskell/hello-world-output-multilines () (should (equal "Hello World!" (test-ob-haskell-ghci ":results output" " :{ main :: IO () main = putStrLn \"Hello World!\" :} main ")))) ;;;; Sessions ;; (ert-deftest ob-haskell/sessions-must-not-share-variables () "Sessions must not share variables." (test-ob-haskell-ghci ":session s1" "x=2" nil) (should (equal 2 (test-ob-haskell-ghci ":session s1" "x" nil))) (test-ob-haskell-ghci ":session s2" "x=3" nil) (should-not (equal 3 (test-ob-haskell-ghci ":session s1" "x" nil))) ) (ert-deftest ob-haskell/session-named-none-means-one-shot-sessions () "When no session, use a new session. \"none\" is a special name that means `no session'." (test-ob-haskell-ghci ":session none" "x=2" nil) (should-not (equal 2 (test-ob-haskell-ghci ":session \"none\"" "x" nil)))) (ert-deftest ob-haskell/reuse-variables-in-same-session () "Reuse variables between blocks using the same session." (test-ob-haskell-ghci ":session s1" "x=2" nil) (should (equal 2 (test-ob-haskell-ghci ":session s1" "x")))) (ert-deftest ob-haskell/may-use-the-*haskell*-session () "The user may use the special *haskell* buffer." (when (get-buffer "*haskell*") (error "A buffer named '*haskell*' exists. Can't run this test")) (unwind-protect (progn (test-ob-haskell-ghci ":session *haskell*" "x=2" nil :unprotected) (should (equal 2 (test-ob-haskell-ghci ":session *haskell*" "x" nil :unprotected)))) (with-current-buffer "*haskell*" (let ((kill-buffer-query-functions nil) (kill-buffer-hook nil)) (kill-buffer "*haskell*"))))) ;;;; Values ;; (ert-deftest ob-haskell/value-is-the-last-expression () "Return the value of the last expression." (should (equal 3 (test-ob-haskell-ghci "" '("1" "1+1" "1+1+1")))) (should (equal 3 (test-ob-haskell-ghci "" '("x=1" "y=1+1" "x+y"))))) (ert-deftest ob-haskell/value-is-the-last-expression-2 () "Return the value of the last expression." (should (equal 7 (test-ob-haskell-ghci "" " putStrLn \"a string\" return \"useless\" 3+4 ")))) (ert-deftest ob-haskell/eval-numbers () "Evaluation of numbers." (should (equal 7 (test-ob-haskell-ghci "" "7"))) (should (equal 7.5 (test-ob-haskell-ghci "" "7.5"))) (should (equal 10.0 (test-ob-haskell-ghci "" "10::Double"))) (should (equal 10 (test-ob-haskell-ghci "" "10::Int")))) (ert-deftest ob-haskell/eval-strings () "Evaluation of strings." (should (equal "a string" (test-ob-haskell-ghci "" "\"a string\"")))) ;;;; Output without EOL ;; (ert-deftest ob-haskell/output-without-eol-1 () "Cannot get output from incomplete lines, when entered line by line." :expected-result :failed (should (equal "123" (test-ob-haskell-ghci ":results output" " putStr(\"1\") putStr(\"2\") putStr(\"3\") putStr(\"\\n\") ")))) (ert-deftest ob-haskell/output-without-eol-2 () "Incomplete output lines are OK when using a multiline block." (should (equal "123" (test-ob-haskell-ghci ":results output" " :{ do putStr(\"1\") putStr(\"2\") putStr(\"3\") putStr(\"\\n\") :} ")))) (ert-deftest ob-haskell/output-without-eol-3 () "Incomplete output lines are OK on one line." (should (equal "123" (test-ob-haskell-ghci ":results output" " do { putStr(\"1\"); putStr(\"2\"); putStr(\"3\"); putStr(\"\\n\") } ")))) ;;;; Local variables (ert-deftest ob-haskell/let-one-line () "Local definitions on one line." (should (equal 6 (test-ob-haskell-ghci "" "let { x=2; y=3 } in x*y")))) (ert-deftest ob-haskell/let-multilines-1 () "Local definitions on multiple lines." (should (equal 6 (test-ob-haskell-ghci "" " :{ let { x=2 ; y=3 } in x*y :} ")))) (ert-deftest ob-haskell/let-multilines-2 () "Local definitions on multiple lines, relying on indentation." (should (equal 6 (test-ob-haskell-ghci "" " :{ let x=2 y=3 in x*y :} ")))) ;;;; Declarations with multiple lines. (ert-deftest ob-haskell/decl-multilines-1 () "A multiline declaration, then use it." (should (equal 3 (test-ob-haskell-ghci "" " :{ let length' [] = 0 length' (_:l) = 1 + length' l :} length' [1,2,3] ")))) (ert-deftest ob-haskell/decl-multilines-2 () "A multiline declaration, then use it." (should (equal 5 (test-ob-haskell-ghci "" " :{ length' :: [a] -> Int length' [] = 0 length' (_:l) = 1 + length' l :} length' [1..5] ")))) (ert-deftest ob-haskell/primes () "From haskell.org.""" (should (equal '(2 3 5 7 11 13 17 19 23 29) (test-ob-haskell-ghci "" " :{ primes = filterPrime [2..] where filterPrime (p:xs) = p : filterPrime [x | x <- xs, x `mod` p /= 0] :} take 10 primes ")))) ;;;; Lists ;; (ert-deftest ob-haskell/a-simple-list () "Evaluation of list of values." (should (equal '(1 2 3) (test-ob-haskell-ghci "" "[1,2,3]")))) (ert-deftest ob-haskell/2D-lists () "Evaluation of nested lists into a table." (should (equal '((1 2 3) (4 5 6)) (test-ob-haskell-ghci "" "[[1..3], [4..6]]")))) (ert-deftest ob-haskell/2D-lists-multilines () "Evaluation of nested lists into a table, as multilines." (should (equal '((1 2 3) (4 5 6) (7 8 9)) (test-ob-haskell-ghci "" " :{ [ [1..3] , [4..6] , [7..9] ] :} ")))) ;;;; Tuples ;; (ert-deftest ob-haskell/a-simple-tuple () "Evaluation of tuple of values." (should (equal '(1 2 3) (test-ob-haskell-ghci "" "(1,2,3)")))) (ert-deftest ob-haskell/2D-tuples () "Evaluation of nested tuples into a table." (should (equal '((1 2 3) (4 5 6)) (test-ob-haskell-ghci "" "((1,2,3), (4,5,6))")))) (ert-deftest ob-haskell/2D-tuples-multilines () "Evaluation of nested tuples into a table, as multilines." (should (equal '((1 2 3) (4 5 6) (7 8 9)) (test-ob-haskell-ghci "" " :{ ( (1,2,3) , (4,5,6) , (7,8,9) ) :} ")))) ;;;; Data tables ;; (ert-deftest ob-haskell/int-table-data () "From worg: int-table-data." (should (equal 10 (test-ob-haskell-ghci ":var t=int-table-data" "sum [sum r | r <- t]" "#+name: int-table-data | 1 | 2 | | 3 | 4 |")))) (ert-deftest ob-haskell/float-table-data () "From worg: float-table-data." (should (equal 11.0 (test-ob-haskell-ghci ":var t=float-table-data" "sum [sum r | r <- t]" "#+name: float-table-data | 1.1 | 2.2 | | 3.3 | 4.4 |")))) (ert-deftest ob-haskell/string-table-data () "From worg: string-table-data." (should (equal "abcd" (test-ob-haskell-ghci ":var t=string-table-data" "concat [concat r | r <- t]" "#+name: string-table-data | a | b | | c | d |")))) ;;;; Reuse results ;; (ert-deftest ob-haskell/reuse-table () "Reusing a computed tables." (should (equal 78 (test-ob-haskell-ghci ":var t=a-table" "sum [sum r | r <- t]" "#+name: a-table #+begin_src haskell [ [x..x+2] | x <- [1,4 .. 12] ] #+end_src ")))) ;;;; Not defined errors ;; (ert-deftest ob-haskell/not-defined () "Evaluation of undefined variables." :expected-result :failed (should-error (test-ob-haskell-ghci "" "notDefined :: IO Int"))) (ert-deftest ob-haskell/not-defined-then-defined-1 () "Evaluation of undefined variables. This is a valid haskell source, but, invalid when entered one line at a time in GHCi." :expected-result :failed (should-error (test-ob-haskell-ghci "" " v :: Int v = 4 "))) (ert-deftest ob-haskell/not-defined-then-defined-1-fixed () "Like not-defined-then-defined-1, but using the mutiline marks." (let ((r (test-ob-haskell-ghci "" " :{ v :: Int v = 4 :} "))) (should (eq nil r)))) (ert-deftest ob-haskell/not-defined-then-defined-1-fixed-2 () "Like not-defined-then-defined-1, but using one line." (should (eq nil (test-ob-haskell-ghci "" "v = 4 :: Int")))) (ert-deftest ob-haskell/not-defined-then-defined-2 () "Evaluation of undefined variables, followed by a correct one." ;; ghci output is: ;; | :2:1-4: error: ;; | • Variable not in scope: main :: IO () ;; | • Perhaps you meant ‘min’ (imported from Prelude) ;; | Hello, World! ;; and ob-haskell just reports the last line "Hello, World!". (should (string-match "Variable not in scope" (test-ob-haskell-ghci ":results output" " main :: IO () main = putStrLn \"Hello, World!\" main ")))) ;;;; Imports ;; (ert-deftest ob-haskell/import () "Import and use library." (should (equal 65 (test-ob-haskell-ghci "" " import Data.IORef r <- newIORef 65 readIORef r ")))) (ert-deftest ob-haskell/import-with-vars () "Import and use library with vars." (should (equal 65 (test-ob-haskell-ghci ":var x=65" " import Data.IORef r <- newIORef x readIORef r ")))) ;;;; What is the result? ;; (ert-deftest ob-haskell/results-value-1 () "Don't confuse output and values: nothing." (should (equal nil (test-ob-haskell-ghci ":results value" "return ()")))) (ert-deftest ob-haskell/results-value-2 () "Don't confuse output and values: a list." (should (equal '(1 2) (test-ob-haskell-ghci ":results value" "return [1,2]")))) (ert-deftest ob-haskell/results-value-3 () "Don't confuse output and values: nothing." (should (equal nil (test-ob-haskell-ghci ":results value" "putStrLn \"3\"")))) (ert-deftest ob-haskell/results-value-4 () "Don't confuse output and values: nothing." (should (equal nil (test-ob-haskell-ghci ":results value" " putStrLn \"3\" return () ")))) ;;;; GHCi commands ;; (ert-deftest ob-haskell/ghci-type () "The ghci meta command ':type'." (should (equal "n :: Int" (test-ob-haskell-ghci ":results output" "let n=3::Int\n:type n")))) (ert-deftest ob-haskell/ghci-info () "The ghci meta command ':info' ." (should (string-match-p "repeat :: a -> \\[a\\][ \t]+-- Defined in ‘GHC.List’" (test-ob-haskell-ghci ":results output" ":info repeat")))) (provide 'test-ob-haskell-ghci) ;;; test-ob-haskell-ghci.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-header-arg-defaults.el000066400000000000000000000061061500430433700244010ustar00rootroot00000000000000;;; test-ob-header-arg-defaults.el --- tests for default header args from properties -*- lexical-binding: t; -*- ;; Copyright (c) 2013, 2014, 2019 Achim Gratz ;; Authors: Achim Gratz ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (ert-deftest test-ob-header-arg-defaults/global/call () (org-test-at-id "3fdadb69-5d15-411e-aad0-f7860cdd7816" (org-babel-next-src-block 1) (forward-line -1) (should (equal "ge1/gh2/--3/ge4/ge5/--6/--7/--8/--9" (org-babel-execute-src-block nil (org-babel-lob-get-info)))))) (ert-deftest test-ob-header-arg-defaults/global/noweb () (org-test-at-id "3fdadb69-5d15-411e-aad0-f7860cdd7816" (org-babel-next-src-block 1) (should (equal "ge1/gh2/--3/ge4/ge5/--6/--7" (org-babel-execute-src-block))))) (ert-deftest test-ob-header-arg-defaults/tree/overwrite/call () (should (equal "ge1/gh2/--3/ge4/ge5/--6/th7/te8/--9" (org-test-at-id "a9cdfeda-9f31-4bb5-b694-2cf452f07dfd" (org-babel-next-src-block 1) (forward-line -1) (org-babel-execute-src-block nil (org-babel-lob-get-info)))))) (ert-deftest test-ob-header-arg-defaults/tree/overwrite/noweb () (should (equal "--1/--2/--3/--4/--5/--6/th7/te8/--9" (org-test-at-id "a9cdfeda-9f31-4bb5-b694-2cf452f07dfd" (org-babel-next-src-block 1) (org-babel-execute-src-block))))) (ert-deftest test-ob-header-arg-defaults/tree/accumulate/call () (should (equal "ge1/th2/th3/ge4/te5/--6" (org-test-at-id "1d97d258-fd50-4107-a095-e4625bffc57b" (org-babel-next-src-block 1) (forward-line -1) (org-babel-execute-src-block nil (org-babel-lob-get-info)))))) (ert-deftest test-ob-header-arg-defaults/tree/accumulate/noweb () (should (equal "ge1/th2/th3/ge4/te5/--6/--7/--8" (org-test-at-id "1d97d258-fd50-4107-a095-e4625bffc57b" (org-babel-next-src-block 1) (org-babel-execute-src-block))))) (ert-deftest test-ob-header-arg-defaults/tree/complex/call () (should (equal "gh1/th2/--3/gh4/te5/--6" (org-test-at-id "fa0e912d-d9b4-47b0-9f9e-1cbb39f7cbc2" (org-babel-next-src-block 1) (forward-line -1) (org-babel-execute-src-block nil (org-babel-lob-get-info)))))) (ert-deftest test-ob-header-arg-defaults/tree/complex/noweb () (should (equal "gh1/th2/--3/gh4/te5/--6/--7/--8/--9" (org-test-at-id "fa0e912d-d9b4-47b0-9f9e-1cbb39f7cbc2" (org-babel-next-src-block 1) (org-babel-execute-src-block))))) (provide 'test-ob-header-arg-defaults) ;;; test-ob-header-arg-defaults.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-java.el000066400000000000000000000515251500430433700215230ustar00rootroot00000000000000;;; test-ob-java.el --- tests for ob-java.el -*- lexical-binding: t; -*- ;; Copyright (c) 2020-2025 Free Software Foundation, Inc. ;; Authors: Eric Schulte ;; Dan Davison ;; Maintainer: Ian Martins ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (require 'org-test "../testing/org-test") (require 'ob-core) ;; ob-java is needed for linter tests as well. org-lint relies on ;; default header arg value. (unless (featurep 'ob-java) (signal 'missing-test-dependency "Support for java code blocks")) ;;; No Java required (ert-deftest ob-java/lint-header-args-buffer () ;; Test that the Org linter accepts every supported Java source ;; block header argument at the buffer level. (org-test-with-temp-text " #+property: header-args:java+ :dir /tmp #+property: header-args:java+ :classname com.example.Example #+property: header-args:java+ :imports com.example.OtherExample #+property: header-args:java+ :cmpflag -classpath .:/tmp/example/ #+property: header-args:java+ :cmdline -classpath .:/tmp/example/ #+property: header-args:java+ :cmdarg -verbose" (should-not (org-lint '(wrong-header-argument))))) (ert-deftest ob-java/lint-header-args-heading () ;; Test that the Org linter accepts every supported Java source ;; block header argument at the heading level. (org-test-with-temp-text " * Test :PROPERTIES: :header-args:java+: :dir /tmp :header-args:java+: :classname com.example.Example :header-args:java+: :imports com.example.OtherExample :header-args:java+: :cmpflag -classpath .:/tmp/example/ :header-args:java+: :cmdline -classpath .:/tmp/example/ :header-args:java+: :cmdarg -verbose :END:" (should-not (org-lint '(wrong-header-argument))))) (ert-deftest ob-java/lint-header-args-block () ;; Test that the Org linter accepts every supported Java source ;; block header argument at the block level. (org-test-with-temp-text " #+header: :dir /tmp #+header: :classname com.example.Example #+header: :imports com.example.OtherExample #+header: :cmpflag -classpath .:/tmp/example/ #+header: :cmdline -classpath .:/tmp/example/ #+header: :cmdarg -verbose #+begin_src java #+end_src" (should-not (org-lint '(wrong-header-argument))))) ;;; Java required (org-test-for-executable "java") (org-test-for-executable "javac") ; simple tests (ert-deftest ob-java/simple () "Hello world program that writes output. Also tests that ob-java defaults to scripting mode." (org-test-with-temp-text "#+begin_src java :dir 'nil :results silent System.out.print(42); #+end_src" (should (string= "42" (org-babel-execute-src-block))))) (ert-deftest ob-java/simple-with-bracket () "Hello world program that outputs an open square bracket." (org-test-with-temp-text "#+begin_src java :dir 'nil :results output silent System.out.print(\"[42\"); #+end_src" (should (string= "[42" (org-babel-execute-src-block))))) (ert-deftest ob-java/simple-with-quote () "Hello world program that writes quotes." (org-test-with-temp-text "#+begin_src java :dir 'nil :results output silent System.out.print(\"\\\"42\\\"\"); #+end_src" (should (string= "\"42\"" (org-babel-execute-src-block))))) (ert-deftest ob-java/simple-return-int () "Hello world program that returns an int value." (org-test-with-temp-text "#+begin_src java :dir 'nil :results value silent return 42; #+end_src" (should (eq 42 (org-babel-execute-src-block))))) (ert-deftest ob-java/simple-return-float () "Hello world program that returns a float value." (org-test-with-temp-text "#+begin_src java :dir 'nil :results value silent return 42.0; #+end_src" (should (equal 42.0 (org-babel-execute-src-block))))) (ert-deftest ob-java/simple-return-string () "Hello world program that returns a string value." (org-test-with-temp-text "#+begin_src java :dir 'nil :results value silent return \"forty two\"; #+end_src" (should (string= "forty two" (org-babel-execute-src-block))))) (ert-deftest ob-java/simple-with-main () "Hello world program that defines a main function." (org-test-with-temp-text "#+begin_src java :dir 'nil :results output silent public static void main(String[] args) { System.out.print(42); } #+end_src" (should (string= "42" (org-babel-execute-src-block))))) (ert-deftest ob-java/simple-with-two-methods () "Hello world program with two methods and no class." (org-test-with-temp-text "#+begin_src java :dir 'nil :results output silent public static void main(String[] args) { System.out.print(foo()); } public static int foo() { return 42; } #+end_src" (should (string= "42" (org-babel-execute-src-block))))) (ert-deftest ob-java/simple-with-no-main () "Hello world program with no main method. Babel adds a dummy one so it can run without error." (org-test-with-temp-text "#+begin_src java :dir 'nil :results output silent public static int foo() { return 42; } #+end_src" (should (string= "success" (org-babel-execute-src-block))))) (ert-deftest ob-java/simple-with-main-args-array () "Hello world program that defines a main function with the square brackets after `args'." (org-test-with-temp-text "#+begin_src java :dir 'nil :results output silent public static void main(String args[]) { System.out.print(42); } #+end_src" (should (string= "42" (org-babel-execute-src-block))))) (ert-deftest ob-java/simple-with-main-whitespace () "Hello world program that defines a main function with the square brackets after `args'." (org-test-with-temp-text "#+begin_src java :dir 'nil :results output silent public static void main ( String args [] ) { System.out.print(42); } #+end_src" (should (string= "42" (org-babel-execute-src-block))))) (ert-deftest ob-java/simple-with-class () "Hello world program that defines a class." (org-test-with-temp-text "#+begin_src java :dir 'nil :results output silent public class Simple { public static void main(String[] args) { System.out.print(42); } } #+end_src" (should (string= "42" (org-babel-execute-src-block))))) (ert-deftest ob-java/simple-with-non-public-class () "Hello world program that defines a non-public class." (org-test-with-temp-text "#+begin_src java :dir 'nil :results output silent class Simple { public static void main(String[] args) { System.out.print(42); } } #+end_src" (should (string= "42" (org-babel-execute-src-block))))) (ert-deftest ob-java/simple-with-class-and-package () "Hello world program that defines a class and package." (org-test-with-temp-text "#+begin_src java :dir 'nil :results output silent package pkg; public class Simple { public static void main(String[] args) { System.out.print(42); } } #+end_src" (should (string= "42" (org-babel-execute-src-block))))) (ert-deftest ob-java/simple-with-class-attr () "Hello world program with class header attribute." (org-test-with-temp-text "#+begin_src java :dir 'nil :results output silent :classname Simple public static void main(String[] args) { System.out.print(42); } #+end_src" (should (string= "42" (org-babel-execute-src-block))))) (ert-deftest ob-java/simple-with-class-attr-with-package () "Hello world program with class attr with package." (org-test-with-temp-text "#+begin_src java :dir 'nil :results output silent :classname pkg.Simple public static void main(String[] args) { System.out.print(42); } #+end_src" (should (string= "42" (org-babel-execute-src-block))))) (ert-deftest ob-java/one-arg () "Command line arg." (org-test-with-temp-text "#+begin_src java :dir 'nil :results output silent :cmdargs \"fortytwo\" System.out.print(args[0]); #+end_src" (should (string= "fortytwo" (org-babel-execute-src-block))))) (ert-deftest ob-java/args-quoted-string () "Two command line args, first contains a space." (org-test-with-temp-text "#+begin_src java :dir 'nil :results output silent :cmdargs \"\\\"forty two\\\" 42\" System.out.println(args[0]); System.out.println(args[1]); #+end_src" (should (string= "forty two\n42\n" (org-babel-execute-src-block))))) ;; var tests (ert-deftest ob-java/integer-var () "Read and write an integer variable." (org-test-with-temp-text "#+begin_src java :dir 'nil :var a=42 :results output silent System.out.print(a); #+end_src" (should (string= "42" (org-babel-execute-src-block))))) (ert-deftest ob-java/var-with-main () "Read and write an integer variable, with main function provided." (org-test-with-temp-text "#+begin_src java :dir 'nil :var a=42 :results output silent public static void main(String[] args) { System.out.print(a); } #+end_src" (should (string= "42" (org-babel-execute-src-block))))) (ert-deftest ob-java/var-with-class () "Read and write an integer variable, with class provided." (org-test-with-temp-text "#+begin_src java :dir 'nil :var a=42 :results output silent public class Main { public static void main(String[] args) { System.out.print(a); } } #+end_src" (should (string= "42" (org-babel-execute-src-block))))) (ert-deftest ob-java/var-with-class-and-package () "Read and write an integer variable, with class and package provided." (org-test-with-temp-text "#+begin_src java :dir 'nil :var a=42 :results output silent package pkg; public class Main { public static void main(String[] args) { System.out.print(a); } } #+end_src" (should (string= "42" (org-babel-execute-src-block))))) (ert-deftest ob-java/var-with-class-and-hanging-curlies () "Read and write an integer variable, with class with hanging curlies." (org-test-with-temp-text "#+begin_src java :dir 'nil :var a=42 :results output silent public class Main { public static void main(String[] args) { System.out.print(a); } } #+end_src" (should (string= "42" (org-babel-execute-src-block))))) (ert-deftest ob-java/two-vars () "Read two integer variables, combine and write them." (org-test-with-temp-text "#+begin_src java :dir 'nil :var a=21 b=2 :results output silent System.out.print(a*b); #+end_src" (should (string= "42" (org-babel-execute-src-block))))) (ert-deftest ob-java/string-var () "Read and write a string variable." (org-test-with-temp-text "#+begin_src java :dir 'nil :var a=\"forty two\" :results output silent System.out.print(String.format(\"%s, len=%d\", a, a.length())); #+end_src" (should (string= "forty two, len=9" (org-babel-execute-src-block))))) (ert-deftest ob-java/multiline-string-var () "Java doesn't support multiline string literals, so this errors." (org-test-with-temp-text "#+begin_src java :dir 'nil :var a=\"forty\ntwo\" :results output silent System.out.print(String.format(\"%s, len=%d\", a, a.length())); #+end_src" (should-error (org-babel-execute-src-block))) :type 'error) ;; return list (ert-deftest ob-java/return-vector-using-list () "Return a vector using a list." (org-test-with-temp-text "#+begin_src java :dir 'nil :results value vector silent import java.util.List; import java.util.Arrays; List> a = Arrays.asList(Arrays.asList(4), Arrays.asList(2)); return a; #+end_src" (should (equal '((4) (2)) (org-babel-execute-src-block))))) (ert-deftest ob-java/return-vector-using-array () "Return a vector using an array." (org-test-with-temp-text "#+begin_src java :dir 'nil :results value vector silent Integer[][] a = {{4}, {2}}; return a; #+end_src" (should (equal '((4) (2)) (org-babel-execute-src-block))))) (ert-deftest ob-java/read-return-list () "Read and return a list." (org-test-with-temp-text "#+begin_src java :dir 'nil :var a=java_list :results value silent import java.util.List; import java.util.Arrays; List b = Arrays.asList(a.get(0), a.get(1)); return b; #+end_src #+name: java_list - forty - two" (should (equal '("forty" "two") (org-babel-execute-src-block))))) (ert-deftest ob-java/read-list-return-array () "Read a list and return an array." (org-test-with-temp-text "#+begin_src java :dir 'nil :var a=java_list :results value silent String[] b = {a.get(0), a.get(1)}; return b; #+end_src #+name: java_list - forty - two" (should (equal '("forty" "two") (org-babel-execute-src-block))))) (ert-deftest ob-java/read-return-list-with-package () "Return a vector." (org-test-with-temp-text "#+begin_src java :dir 'nil :var a=java_list :results value silent package pkg; import java.util.List; import java.util.Arrays; List b = Arrays.asList(a.get(0), a.get(1)); return b; #+end_src #+name: java_list - forty - two" (should (equal '("forty" "two") (org-babel-execute-src-block))))) (ert-deftest ob-java/output-list-with-spaces () "Return a vector." (org-test-with-temp-text "#+begin_src java :dir 'nil :results output list raw silent System.out.println(\"forty two\"); System.out.println(\"forty two\"); #+end_src" (should (equal "forty two\nforty two\n" (org-babel-execute-src-block))))) ;; list vars (ert-deftest ob-java/list-var () "Read and write a list variable." (org-test-with-temp-text "#+begin_src java :dir 'nil :var a='(\"forty\" \"two\") :results value silent import java.util.List; List b = a; return b; #+end_src" (should (equal '("forty" "two") (org-babel-execute-src-block))))) (ert-deftest ob-java/vector-var () "Read and write a vector variable." (org-test-with-temp-text "#+begin_src java :dir 'nil :var a='[\"forty\" \"two\"] :results value silent import java.util.List; List b = a; return b; #+end_src" (should (equal '("forty" "two") (org-babel-execute-src-block))))) (ert-deftest ob-java/matrix-var () "Read and write matrix variable." (org-test-with-temp-text "#+begin_src java :dir 'nil :var a=java_matrix :results value silent import java.util.List; import java.util.Arrays; List> b = Arrays.asList(Arrays.asList(a.get(0).get(0), a.get(1).get(0)), Arrays.asList(a.get(0).get(1), a.get(1).get(1))); return b; // transpose #+end_src #+name: java_matrix | 2 | 1 | | 4 | 2 |" (should (equal '((2 4) (1 2)) (org-babel-execute-src-block))))) (ert-deftest ob-java/matrix-var-with-header () "Read matrix variable and write it with header." (org-test-with-temp-text "#+begin_src java :dir 'nil :var a=java_matrix :results value table silent import java.util.List; import java.util.Arrays; List b = Arrays.asList(Arrays.asList(\"col1\", \"col2\"), null, Arrays.asList(a.get(0).get(0), a.get(1).get(0)), Arrays.asList(a.get(0).get(1), a.get(1).get(1))); return b; // transpose #+end_src #+name: java_matrix | 2 | 1 | | 4 | 2 |" (should (equal '(("col1" "col2") hline (2 4) (1 2)) (org-babel-execute-src-block))))) ;; output table (ert-deftest ob-java/output-table-with-header () "Write a table that includes a header." (org-test-with-temp-text "#+begin_src java :dir 'nil :var a=java_matrix :results output raw table silent System.out.println(\"|col1|col2|\"); System.out.println(\"|-\"); for (int ii=0; ii b = Arrays.asList(Arrays.asList(a.get(0).get(0), Integer.parseInt(a.get(0).get(1))*2), Arrays.asList(a.get(1).get(0), Integer.parseInt(a.get(1).get(1))*2)); return b; #+end_src #+name: java_table | string | number | |--------+--------| | forty | 2 | | two | 1 |" (should (equal '(("forty" 4) ("two" 2)) (org-babel-execute-src-block))))) ;; imports (ert-deftest ob-java/import_library () "Import a standard java library." (org-test-with-temp-text "#+begin_src java :dir 'nil :results output silent :imports java.util.Base64 byte[] encoded = Base64.getEncoder().encode(\"42\".getBytes()); String decoded = new String(Base64.getDecoder().decode(encoded)); System.out.print(String.format(\"encoded=%s, decoded=%s\", new String(encoded), decoded)); #+end_src" (should (string= "encoded=NDI=, decoded=42" (org-babel-execute-src-block))))) (ert-deftest ob-java/import_library_inline () "Import a standard java library." (org-test-with-temp-text "#+begin_src java :dir 'nil :results output silent import java.util.Base64; byte[] encoded = Base64.getEncoder().encode(\"42\".getBytes()); String decoded = new String(Base64.getDecoder().decode(encoded)); System.out.print(String.format(\"encoded=%s, decoded=%s\", new String(encoded), decoded)); #+end_src" (should (string= "encoded=NDI=, decoded=42" (org-babel-execute-src-block))))) ;; tangle (ert-deftest ob-java/tangle () "Tangle a source block." (org-test-with-temp-text-in-file "#+begin_src java :dir 'nil :tangle \"Tangle.java\" :results value :classname Tangle return \"tangled\"; #+end_src" (should (string= "public class Tangle { public static void main(String[] args) { return \"tangled\"; } } " (unwind-protect (progn (org-babel-tangle) (with-temp-buffer (insert-file-contents "Tangle.java") (untabify (point-min) (point-max)) (buffer-string))) (delete-file "Tangle.java")))))) (ert-deftest ob-java/tangle-with-package () "Tangle a source block." (org-test-with-temp-text-in-file "#+begin_src java :dir 'nil :tangle \"tangle/Tangle.java\" :results value :classname tangle.Tangle return \"tangled\"; #+end_src" (should (string= "package tangle; public class Tangle { public static void main(String[] args) { return \"tangled\"; } } " (unwind-protect (progn (make-directory "tangle") (org-babel-tangle) (with-temp-buffer (insert-file-contents "tangle/Tangle.java") (untabify (point-min) (point-max)) (buffer-string))) (delete-file "tangle/Tangle.java") (delete-directory "tangle")))))) ;; specify output dir (ert-deftest ob-java/simple-dir () "Hello world program that writes output." (org-test-with-temp-text (format "#+begin_src java :dir %s :results output silent System.out.print(42); #+end_src" temporary-file-directory) (should (string= "42" (unwind-protect (org-babel-execute-src-block) (delete-file (concat (file-name-as-directory temporary-file-directory) "Main.java")) (delete-file (concat (file-name-as-directory temporary-file-directory) "Main.class"))))))) (ert-deftest ob-java/simple-dir-with-package () "Hello world program that writes output." (org-test-with-temp-text (format "#+begin_src java :dir %s :results output silent package pkg; public class Main { public static void main(String[] args) { System.out.print(42); } } #+end_src" temporary-file-directory) (should (string= "42" (unwind-protect (org-babel-execute-src-block) (delete-file (concat (file-name-as-directory temporary-file-directory) "pkg/Main.java")) (delete-file (concat (file-name-as-directory temporary-file-directory) "pkg/Main.class")) (delete-directory (concat (file-name-as-directory temporary-file-directory) "pkg"))))))) (provide 'test-ob-java) ;;; test-ob-java.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-julia.el000066400000000000000000000160671500430433700217100ustar00rootroot00000000000000;;; test-ob-julia.el --- tests for ob-julia.el -*- lexical-binding: t; -*- ;; Copyright (c) 2011-2014, 2019, 2021 Eric Schulte ;; Authors: Pedro Bruel, based on test-ob-python.el by Eric Schulte ;; Maintainer: Pedro Bruel ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (org-test-for-executable "julia") (require 'ob-core) (unless (featurep 'ob-julia) (signal 'missing-test-dependency "Support for julia code blocks")) (unless (featurep 'ess) (signal 'missing-test-dependency "ESS")) (ert-deftest test-ob-julia/colnames-yes-header-argument () (should (equal '(("col") hline ("a") ("b")) (org-test-with-temp-text "#+name: eg | col | |-----| | a | | b | #+header: :colnames yes #+header: :var x = eg #+begin_src julia return x #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-julia/colnames-yes-header-argument-again () (should (equal '(("a") hline ("b*") ("c*")) (org-test-with-temp-text "#+name: less-cols | a | |---| | b | | c | #+header: :colnames yes #+begin_src julia :var tab=less-cols return [[val + '*' for val in row] for row in tab] #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-julia/colnames-nil-header-argument () (should (equal '(("col") hline ("a") ("b")) (org-test-with-temp-text "#+name: eg | col | |-----| | a | | b | #+header: :colnames nil #+header: :var x = eg #+begin_src julia return x #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-julia/colnames-no-header-argument-again () (should (equal '(("a*") ("b*") ("c*")) (org-test-with-temp-text "#+name: less-cols | a | |---| | b | | c | #+header: :colnames no #+begin_src julia :var tab=less-cols return [[val + '*' for val in row] for row in tab] #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-julia/colnames-no-header-argument () (should (equal '(("col") ("a") ("b")) (org-test-with-temp-text "#+name: eg | col | |-----| | a | | b | #+header: :colnames no #+header: :var x = eg #+begin_src julia return x #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-julia/session-multiline () (should (equal "20" (org-test-with-temp-text "#+begin_src julia :session :results output foo = 0 for _ in range(10): foo += 1 foo += 1 print(foo) #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-julia/insert-necessary-blank-line-when-sending-code-to-interpreter () (should (equal 2 (org-test-with-temp-text "#+begin_src julia :session :results value if True: 1 2 #+end_src" ;; Previously, while adding `:session' to a normal code ;; block, also need to add extra blank lines to end ;; indent block or indicate logical sections. Now, the ;; `org-babel-julia-evaluate-session' can do it ;; automatically: ;; ;; >>> if True: ;; >>> 1 ;; >>> ;; >>> 2 (org-babel-execute-maybe) (org-babel-execute-src-block))))) (ert-deftest test-ob-julia/if-else-block () (should (equal "success" (org-test-with-temp-text "#+begin_src julia :session :results value value = 'failure' if False: pass else: value = 'success' value #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-julia/indent-block-with-blank-lines () (should (equal 20 (org-test-with-temp-text "#+begin_src julia :session :results value foo = 0 for i in range(10): foo += 1 foo += 1 foo #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-julia/assign-underscore () (should (equal "success" (org-test-with-temp-text "#+begin_src julia :session :results value _ = 'failure' 'success' #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-julia/multiline-var () (should (equal "a\nb\nc" (org-test-with-temp-text "#+begin_src julia :var text=\"a\\nb\\nc\" return text #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-julia/multiline-str () (should (equal "a\nb\nc" (org-test-with-temp-text "#+begin_src julia text=\"a\\nb\\nc\" return text #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-julia/header-var-assignment () (should (equal "success" (org-test-with-temp-text "#+begin_src julia :var text=\"failure\" text text=\"success\" return text #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-julia/session-value-sleep () (should (equal "success" (org-test-with-temp-text "#+begin_src julia :session :results value import time time.sleep(.1) 'success' #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-julia/async-simple-session-output () (let ((org-babel-temporary-directory temporary-file-directory) (org-confirm-babel-evaluate nil)) (org-test-with-temp-text "#+begin_src julia :session :async yes :results output import time time.sleep(.1) print('Yep!') #+end_src\n" (should (let ((expected "Yep!")) (and (not (string= expected (org-babel-execute-src-block))) (string= expected (progn (sleep-for 0.200) (goto-char (org-babel-where-is-src-block-result)) (org-babel-read-result))))))))) (ert-deftest test-ob-julia/async-named-output () (let (org-confirm-babel-evaluate (org-babel-temporary-directory temporary-file-directory) (src-block "#+begin_src julia :async :session :results output print(\"Yep!\") #+end_src") (results-before " #+NAME: foobar #+RESULTS: : Nope!") (results-after " #+NAME: foobar #+RESULTS: : Yep! ")) (org-test-with-temp-text (concat src-block results-before) (should (progn (org-babel-execute-src-block) (sleep-for 0.200) (string= (concat src-block results-after) (buffer-string))))))) (ert-deftest test-ob-julia/async-output-drawer () (let (org-confirm-babel-evaluate (org-babel-temporary-directory temporary-file-directory) (src-block "#+begin_src julia :async :session :results output drawer print(list(range(3))) #+end_src") (result " #+RESULTS: :results: [0, 1, 2] :end: ")) (org-test-with-temp-text src-block (should (progn (org-babel-execute-src-block) (sleep-for 0.200) (string= (concat src-block result) (buffer-string))))))) (provide 'test-ob-julia) ;;; test-ob-julia.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-lilypond.el000066400000000000000000000364031500430433700224320ustar00rootroot00000000000000;;; test-ob-lilypond.el --- tests for ob-lilypond.el -*- lexical-binding: t; -*- ;; Copyright (c) 2010-2014, 2019 Martyn Jago ;; Authors: Martyn Jago ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (require 'ob-lilypond) (with-current-buffer (get-buffer-create "test-ob-lilypond.el") (setq org-babel-lilypond-here (file-name-directory (or load-file-name (buffer-file-name))))) (ert-deftest ob-lilypond/feature-provision () (should (featurep 'ob-lilypond))) (ert-deftest ob-lilypond/org-babel-tangle-lang-exts () (let ((found nil) (list org-babel-tangle-lang-exts)) (while list (when (equal (car list) '("LilyPond" . "ly")) (setq found t)) (setq list (cdr list))) (should found))) (ert-deftest ob-lilypond/org-babel-prep-session:lilypond () (should-error (org-babel-prep-session:lilypond nil nil)) :type 'error) (ert-deftest ob-lilypond/ly-compile-lilyfile () (cl-letf (((symbol-function 'call-process) 'list) (org-babel-lilypond-gen-png nil) (org-babel-lilypond-gen-html nil) (org-babel-lilypond-use-eps nil) (org-babel-lilypond-gen-svg nil)) (should (equal `(,org-babel-lilypond-ly-command ;program nil ;infile "*lilypond*" ;buffer display ,@(when org-babel-lilypond-gen-png '("--png")) ;&rest... ,@(when org-babel-lilypond-gen-html '("--html")) ,@(when org-babel-lilypond-gen-pdf '("--pdf")) ,@(when org-babel-lilypond-use-eps '("-dbackend=eps")) ,@(when org-babel-lilypond-gen-svg '("-dbackend=svg")) "--output=test-file" "test-file.ly") (org-babel-lilypond-compile-lilyfile "test-file.ly"))))) (ert-deftest ob-lilypond/ly-compile-post-tangle () (should (boundp 'org-babel-lilypond-compile-post-tangle))) (ert-deftest ob-lilypond/ly-display-pdf-post-tangle () (should (boundp 'org-babel-lilypond-display-pdf-post-tangle))) (ert-deftest ob-lilypond/ly-play-midi-post-tangle () (should (boundp 'org-babel-lilypond-play-midi-post-tangle))) (ert-deftest ob-lilypond/ly-command-ly/bound () (should (boundp 'org-babel-lilypond-ly-command))) (ert-deftest ob-lilypond/ly-command-ly/stringp () (should (stringp org-babel-lilypond-ly-command))) (ert-deftest ob-lilypond/ly-command-pdf/bound () (should (boundp 'org-babel-lilypond-pdf-command))) (ert-deftest ob-lilypond/ly-command-pdf/stringp () (should (stringp org-babel-lilypond-pdf-command))) (ert-deftest ob-lilypond/ly-command-midi/bound () (should (boundp 'org-babel-lilypond-midi-command))) (ert-deftest ob-lilypond/ly-command-midi/stringp () (should (stringp org-babel-lilypond-midi-command))) (ert-deftest ob-lilypond/ly-commands/darwin () (let ((system-type 'darwin) org-babel-lilypond-ly-command org-babel-lilypond-pdf-command org-babel-lilypond-midi-command) (custom-reevaluate-setting 'org-babel-lilypond-commands) (should (equal (list org-babel-lilypond-ly-command org-babel-lilypond-pdf-command org-babel-lilypond-midi-command) (list "/Applications/lilypond.app/Contents/Resources/bin/lilypond" "open" "open")))) (custom-reevaluate-setting 'org-babel-lilypond-commands)) (ert-deftest ob-lilypond/ly-commands/windows-nt () (let ((system-type 'windows-nt) org-babel-lilypond-ly-command org-babel-lilypond-pdf-command org-babel-lilypond-midi-command) (custom-reevaluate-setting 'org-babel-lilypond-commands) (should (equal (list org-babel-lilypond-ly-command org-babel-lilypond-pdf-command org-babel-lilypond-midi-command) (list "lilypond" "" "")))) (custom-reevaluate-setting 'org-babel-lilypond-commands)) (ert-deftest ob-lilypond/ly-commands/other () (let ((system-type 'other) org-babel-lilypond-ly-command org-babel-lilypond-pdf-command org-babel-lilypond-midi-command) (custom-reevaluate-setting 'org-babel-lilypond-commands) (should (equal (list org-babel-lilypond-ly-command org-babel-lilypond-pdf-command org-babel-lilypond-midi-command) (list "lilypond" "xdg-open" "xdg-open")))) (custom-reevaluate-setting 'org-babel-lilypond-commands)) (ert-deftest ob-lilypond/ly-gen-png () (should (boundp 'org-babel-lilypond-gen-png))) (ert-deftest ob-lilypond/ly-gen-svg () (should (boundp 'org-babel-lilypond-gen-svg))) (ert-deftest ob-lilypond/ly-gen-html () (should (boundp 'org-babel-lilypond-gen-html))) (ert-deftest ob-lilypond/ly-gen-pdf () (should (boundp 'org-babel-lilypond-gen-pdf))) (ert-deftest ob-lilypond/use-eps () (should (boundp 'org-babel-lilypond-use-eps))) (ert-deftest ob-lilypond/ly-arrange-mode () (should (boundp 'org-babel-lilypond-arrange-mode))) ;; (ert-deftest ob-lilypond/org-babel-default-header-args:lilypond () ;; (should (equal '((:tangle . "yes") ;; (:noweb . "yes") ;; (:results . "silent") ;; (:comments . "yes")) ;; org-babel-default-header-args:lilypond))) ;;TODO finish... (ert-deftest ob-lilypond/org-babel-expand-body:lilypond () (should (equal "This is a test" (org-babel-expand-body:lilypond "This is a test" ())))) ;;TODO (ert-deftest org-babel-lilypond-test-org-babel-execute:lilypond ()) (ert-deftest ob-lilypond/ly-check-for-compile-error () (set-buffer (get-buffer-create "*lilypond*")) (erase-buffer) (should (not (org-babel-lilypond-check-for-compile-error nil t))) (insert-file-contents (concat org-babel-lilypond-here "../examples/ob-lilypond-test.error") nil nil nil t) (goto-char (point-min)) (should (org-babel-lilypond-check-for-compile-error nil t)) (kill-buffer "*lilypond*")) (ert-deftest ob-lilypond/ly-process-compile-error () (find-file-other-window (concat org-babel-lilypond-here "../examples/ob-lilypond-broken.org")) (set-buffer (get-buffer-create "*lilypond*")) (insert-file-contents (concat org-babel-lilypond-here "../examples/ob-lilypond-test.error") nil nil nil t) (goto-char (point-min)) (search-forward "error:" nil t) (should-error (org-babel-lilypond-process-compile-error (concat org-babel-lilypond-here "../examples/ob-lilypond-broken.ly")) :type 'error) (set-buffer "ob-lilypond-broken.org") (should (equal 238 (point))) (exchange-point-and-mark) (should (equal (+ 238 (length "line 25")) (point))) (kill-buffer "*lilypond*") (kill-buffer "ob-lilypond-broken.org")) (ert-deftest ob-lilypond/ly-mark-error-line () (let ((file-name (concat org-babel-lilypond-here "../examples/ob-lilypond-broken.org")) (expected-point-min 198) (expected-point-max 205) (line "line 20")) (find-file-other-window file-name) (org-babel-lilypond-mark-error-line file-name line) (should (equal expected-point-min (point))) (exchange-point-and-mark) (should (= expected-point-max (point))) (kill-buffer (file-name-nondirectory file-name)))) (ert-deftest ob-lilypond/ly-parse-line-num () (with-temp-buffer (insert-file-contents (concat org-babel-lilypond-here "../examples/ob-lilypond-test.error") nil nil nil t) (goto-char (point-min)) (search-forward "error:") (should (equal 25 (org-babel-lilypond-parse-line-num (current-buffer)))))) (ert-deftest ob-lilypond/ly-parse-error-line () (let ((org-babel-lilypond-file (concat org-babel-lilypond-here "../examples/ob-lilypond-broken.ly"))) (should (equal "line 20" (org-babel-lilypond-parse-error-line org-babel-lilypond-file 20))) (should (not (org-babel-lilypond-parse-error-line org-babel-lilypond-file 0))))) (ert-deftest ob-lilypond/ly-attempt-to-open-pdf () (let ((post-tangle org-babel-lilypond-display-pdf-post-tangle) (org-babel-lilypond-file (concat org-babel-lilypond-here "../examples/ob-lilypond-test.ly")) (pdf-file (concat org-babel-lilypond-here "../examples/ob-lilypond-test.pdf"))) (setq org-babel-lilypond-display-pdf-post-tangle t) (when (not (file-exists-p pdf-file)) (set-buffer (get-buffer-create (file-name-nondirectory pdf-file))) (write-file pdf-file)) (should (equal (concat org-babel-lilypond-pdf-command " " pdf-file) (org-babel-lilypond-attempt-to-open-pdf org-babel-lilypond-file t))) (delete-file pdf-file) (kill-buffer (file-name-nondirectory pdf-file)) (should (string-prefix-p "No pdf file generated" (org-babel-lilypond-attempt-to-open-pdf pdf-file))) (setq org-babel-lilypond-display-pdf-post-tangle post-tangle))) (ert-deftest ob-lilypond/ly-attempt-to-play-midi () (let ((post-tangle org-babel-lilypond-play-midi-post-tangle) (org-babel-lilypond-file (concat org-babel-lilypond-here "../examples/ob-lilypond-test.ly")) (midi-file (concat org-babel-lilypond-here "../examples/ob-lilypond-test.midi"))) (setq org-babel-lilypond-play-midi-post-tangle t) (when (not (file-exists-p midi-file)) (set-buffer (get-buffer-create (file-name-nondirectory midi-file))) (write-file midi-file)) (should (equal (concat org-babel-lilypond-midi-command " " midi-file) (org-babel-lilypond-attempt-to-play-midi org-babel-lilypond-file t))) (delete-file midi-file) (kill-buffer (file-name-nondirectory midi-file)) (should (string-prefix-p "No midi file generated" (org-babel-lilypond-attempt-to-play-midi midi-file))) (setq org-babel-lilypond-play-midi-post-tangle post-tangle))) (ert-deftest ob-lilypond/ly-toggle-midi-play-toggles-flag () (if org-babel-lilypond-play-midi-post-tangle (progn (org-babel-lilypond-toggle-midi-play) (should (not org-babel-lilypond-play-midi-post-tangle)) (org-babel-lilypond-toggle-midi-play) (should org-babel-lilypond-play-midi-post-tangle)) (org-babel-lilypond-toggle-midi-play) (should org-babel-lilypond-play-midi-post-tangle) (org-babel-lilypond-toggle-midi-play) (should (not org-babel-lilypond-play-midi-post-tangle)))) (ert-deftest ob-lilypond/ly-toggle-pdf-display-toggles-flag () (if org-babel-lilypond-display-pdf-post-tangle (progn (org-babel-lilypond-toggle-pdf-display) (should (not org-babel-lilypond-display-pdf-post-tangle)) (org-babel-lilypond-toggle-pdf-display) (should org-babel-lilypond-display-pdf-post-tangle)) (org-babel-lilypond-toggle-pdf-display) (should org-babel-lilypond-display-pdf-post-tangle) (org-babel-lilypond-toggle-pdf-display) (should (not org-babel-lilypond-display-pdf-post-tangle)))) (ert-deftest ob-lilypond/ly-toggle-pdf-generation-toggles-flag () (if org-babel-lilypond-gen-pdf (progn (org-babel-lilypond-toggle-pdf-generation) (should (not org-babel-lilypond-gen-pdf)) (org-babel-lilypond-toggle-pdf-generation) (should org-babel-lilypond-gen-pdf)) (org-babel-lilypond-toggle-pdf-generation) (should org-babel-lilypond-gen-pdf) (org-babel-lilypond-toggle-pdf-generation) (should (not org-babel-lilypond-gen-pdf)))) (ert-deftest ob-lilypond/ly-toggle-arrange-mode () (if org-babel-lilypond-arrange-mode (progn (org-babel-lilypond-toggle-arrange-mode) (should (not org-babel-lilypond-arrange-mode)) (org-babel-lilypond-toggle-arrange-mode) (should org-babel-lilypond-arrange-mode)) (org-babel-lilypond-toggle-arrange-mode) (should org-babel-lilypond-arrange-mode) (org-babel-lilypond-toggle-arrange-mode) (should (not org-babel-lilypond-arrange-mode)))) (ert-deftest ob-lilypond/ly-toggle-png-generation-toggles-flag () (if org-babel-lilypond-gen-png (progn (org-babel-lilypond-toggle-png-generation) (should (not org-babel-lilypond-gen-png)) (org-babel-lilypond-toggle-png-generation) (should org-babel-lilypond-gen-png)) (org-babel-lilypond-toggle-png-generation) (should org-babel-lilypond-gen-png) (org-babel-lilypond-toggle-png-generation) (should (not org-babel-lilypond-gen-png)))) (ert-deftest ob-lilypond/ly-toggle-html-generation-toggles-flag () (if org-babel-lilypond-gen-html (progn (org-babel-lilypond-toggle-html-generation) (should (not org-babel-lilypond-gen-html)) (org-babel-lilypond-toggle-html-generation) (should org-babel-lilypond-gen-html)) (org-babel-lilypond-toggle-html-generation) (should org-babel-lilypond-gen-html) (org-babel-lilypond-toggle-html-generation) (should (not org-babel-lilypond-gen-html)))) (ert-deftest ob-lilypond/ly-switch-extension-with-extensions () (should (equal "test-name.xyz" (org-babel-lilypond-switch-extension "test-name" ".xyz"))) (should (equal "test-name.xyz" (org-babel-lilypond-switch-extension "test-name.abc" ".xyz"))) (should (equal "test-name" (org-babel-lilypond-switch-extension "test-name.abc" "")))) (ert-deftest ob-lilypond/ly-switch-extension-with-paths () (should (equal "/some/path/to/test-name.xyz" (org-babel-lilypond-switch-extension "/some/path/to/test-name" ".xyz")))) (ert-deftest ob-lilypond/ly-get-header-args () (should (equal '((:tangle . "yes") (:noweb . "yes") (:results . "silent") (:cache . "yes") (:comments . "yes")) (org-babel-lilypond-set-header-args t))) (should (equal '((:results . "file") (:exports . "results")) (org-babel-lilypond-set-header-args nil)))) (ert-deftest ob-lilypond/ly-set-header-args () (org-babel-lilypond-set-header-args t) (should (equal '((:tangle . "yes") (:noweb . "yes") (:results . "silent") (:cache . "yes") (:comments . "yes")) org-babel-default-header-args:lilypond)) (org-babel-lilypond-set-header-args nil) (should (equal '((:results . "file") (:exports . "results")) org-babel-default-header-args:lilypond))) (provide 'test-ob-lilypond) ;;; test-ob-lilypond.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-lob.el000066400000000000000000000205111500430433700213450ustar00rootroot00000000000000;;; test-ob-lob.el --- test for ob-lob.el -*- lexical-binding: t; -*- ;; Copyright (c) 2010-2015, 2019 Eric Schulte ;; Authors: Eric Schulte ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . (eval-and-compile (require 'cl-lib)) (require 'ob-lob) ;;; Tests (org-babel-lob-ingest (expand-file-name "library-of-babel.org" (expand-file-name "doc" (expand-file-name ".." (expand-file-name ".." (file-name-directory (or load-file-name buffer-file-name))))))) (ert-deftest test-ob-lob/ingest () "Test the ingestion of an Org file." (should (< 0 (org-babel-lob-ingest (expand-file-name "babel.org" org-test-example-dir))))) (ert-deftest test-ob-lob/call-with-header-arguments () "Test the evaluation of a library of babel #+call: line." (cl-letf (((symbol-function 'org-babel-insert-result) (symbol-function 'ignore))) (let ((org-babel-library-of-babel (org-test-with-temp-text-in-file " #+name: echo #+begin_src emacs-lisp :var input=\"echo'd\" input #+end_src #+name: lob-minus #+begin_src emacs-lisp :var a=0 :var b=0 (- a b) #+end_src" (org-babel-lob-ingest) org-babel-library-of-babel))) (org-test-at-id "fab7e291-fde6-45fc-bf6e-a485b8bca2f0" (move-beginning-of-line 1) (forward-line 6) (message (buffer-substring (point-at-bol) (point-at-eol))) (should (string= "testing" (org-babel-execute-src-block nil (org-babel-lob-get-info)))) (forward-line 1) (should (string= "testing" (caar (org-babel-execute-src-block nil (org-babel-lob-get-info))))) (forward-line 1) (should (string= "testing" (org-babel-execute-src-block nil (org-babel-lob-get-info)))) (forward-line 1) (should (string= "testing" (caar (org-babel-execute-src-block nil (org-babel-lob-get-info))))) (forward-line 1) (should (string= "testing" (org-babel-execute-src-block nil (org-babel-lob-get-info)))) (forward-line 1) (should (string= "testing" (caar (org-babel-execute-src-block nil (org-babel-lob-get-info))))) (forward-line 1) (beginning-of-line) (forward-char 27) (should (string= "testing" (org-babel-execute-src-block nil (org-babel-lob-get-info)))) (forward-line 1) (beginning-of-line) (forward-char 27) (should (string= "testing" (caar (org-babel-execute-src-block nil (org-babel-lob-get-info))))) (forward-line 1) (beginning-of-line) (should (= 4 (org-babel-execute-src-block nil (org-babel-lob-get-info)))) (forward-line 1) (should (string= "testing" (org-babel-execute-src-block nil (org-babel-lob-get-info)))) (forward-line 1) (should (string= "123" (org-babel-execute-src-block nil (org-babel-lob-get-info)))))))) (ert-deftest test-ob-lob/export-lob-lines () "Test the export of a variety of library babel call lines." (let ((org-babel-inline-result-wrap "=%s=") (org-export-use-babel t)) (org-test-at-id "72ddeed3-2d17-4c7f-8192-a575d535d3fc" (org-narrow-to-subtree) (let ((string (org-with-wide-buffer (buffer-string))) (narrowing (list (point-min) (point-max)))) (with-temp-buffer (org-mode) (insert string) (apply #'narrow-to-region narrowing) (org-babel-exp-process-buffer) (message (buffer-string)) (goto-char (point-min)) (should (re-search-forward "^: 0" nil t)) (should (re-search-forward "call {{{results(=2=)}}} stuck" nil t)) (should (re-search-forward "exported =call_double(it=2)= because" nil t)) (should (re-search-forward "^{{{results(=6=)}}} because" nil t)) (should (re-search-forward "results 8 should" nil t)) (should (re-search-forward "following 2\\*5={{{results(=10=)}}} should" nil t))))))) (ert-deftest test-ob-lob/do-not-eval-lob-lines-in-example-blocks-on-export () (require 'ox) (org-test-with-temp-text-in-file " for export #+begin_example #+call: rubbish() #+end_example" (should (progn (org-babel-exp-process-buffer) t)))) (defvar temporary-value-for-test) (ert-deftest test-ob-lob/caching-call-line () (let ((temporary-value-for-test 0)) (org-test-with-temp-text " #+name: call-line-caching-example #+begin_src emacs-lisp :var bar=\"baz\" (setq temporary-value-for-test (+ 1 temporary-value-for-test)) #+end_src #+call: call-line-caching-example(\"qux\") :cache yes " ;; first execution should flip value to t (should (eq (org-babel-execute-src-block nil (org-babel-lob-get-info)) 1)) ;; if cached, second evaluation will retain the t value (should (eq (org-babel-execute-src-block nil (org-babel-lob-get-info)) 1))))) (ert-deftest test-ob-lob/named-caching-call-line () (let ((temporary-value-for-test 0)) (org-test-with-temp-text " #+name: call-line-caching-example #+begin_src emacs-lisp :var bar=\"baz\" (setq temporary-value-for-test (+ 1 temporary-value-for-test)) #+end_src #+name: call-line-caching-called #+call: call-line-caching-example(\"qux\") :cache yes " ;; first execution should flip value to t (should (eq (org-babel-execute-src-block nil (org-babel-lob-get-info)) 1)) ;; if cached, second evaluation will retain the t value (should (eq (org-babel-execute-src-block nil (org-babel-lob-get-info)) 1))))) (ert-deftest test-ob-lob/assignment-with-newline () "Test call lines with an argument containing a newline character." (should (equal " foo" (org-test-with-temp-text " #+name: test-newline #+begin_src emacs-lisp :var x=\"a\" 'foo #+end_src call_test-newline[:eval yes :results raw](\"a\nb\")" (org-babel-execute-src-block nil (org-babel-lob-get-info)) (buffer-substring (point) (point-max))))) (should (equal " bar" (org-test-with-temp-text " #+name: test-newline #+begin_src emacs-lisp :var x=\"a\" 'bar #+end_src call_test-newline[:eval yes :results raw]('(1\n2))" (org-babel-execute-src-block nil (org-babel-lob-get-info)) (buffer-substring (point) (point-max)))))) (ert-deftest test-ob-lob/external-reference-syntax () "Test external reference syntax for Babel calls." (should (= 2 (org-test-with-temp-text-in-file "#+name: foo\n#+begin_src emacs-lisp\n(+ 1 1)\n#+end_src" (let ((file (buffer-file-name))) (org-test-with-temp-text (format "#+call: %s:foo()" file) (org-babel-execute-src-block nil (org-babel-lob-get-info)))))))) (ert-deftest test-ob-lob/call-with-indirection () "Test calling code with indirection." (should (= 2 (org-test-with-temp-text " #+name: foo #+begin_src emacs-lisp \(+ 1 1) #+end_src #+name: bar #+call: foo() #+call: bar()" (org-babel-execute-src-block nil (org-babel-lob-get-info))))) (should (= 10 (org-test-with-temp-text " #+name: foo #+begin_src emacs-lisp :var x=1 \(* 2 x) #+end_src #+name: bar #+call: foo(x=3) #+call: bar(x=5)" (org-babel-execute-src-block nil (org-babel-lob-get-info))))) (should (= 6 (org-test-with-temp-text " #+name: foo #+begin_src emacs-lisp :var x=1 \(* 2 x) #+end_src #+name: bar #+call: foo(x=3) #+call: bar()" (org-babel-execute-src-block nil (org-babel-lob-get-info)))))) (ert-deftest test-ob-lob/confirm-evaluate () "Test confirmation when exporting lob calls." ;; With the default `org-confirm-babel-evaluate' of t, the caller is ;; queried one time. (should (= 1 (let ((org-export-use-babel t) (org-confirm-babel-evaluate t) (confirm-evaluate-calls 0)) (cl-letf (((symbol-function 'yes-or-no-p) (lambda (&rest _ignore) (cl-incf confirm-evaluate-calls) t))) (org-test-with-temp-text " #+name: foo #+begin_src emacs-lisp nil #+end_src #+call: foo()" (let ((string (buffer-string))) (with-temp-buffer (org-mode) (insert string) (org-babel-exp-process-buffer) confirm-evaluate-calls)))))))) (provide 'test-ob-lob) ;;; test-ob-lob.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-lua.el000066400000000000000000000110401500430433700213470ustar00rootroot00000000000000;;; test-ob-lua.el --- tests for ob-lua.el -*- lexical-binding: t; -*- ;; Copyright (c) 2016, 2019 Thibault Marin ;; Authors: Thibault Marin ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (unless (featurep 'ob-lua) (signal 'missing-test-dependency "Support for Lua code blocks")) (ert-deftest test-ob-lua/simple-value () "Test associative array return by value." (should (= 2 (org-test-with-temp-text "#+name: eg | a | 1 | | b | 2 | #+header: :results value #+header: :var x = eg #+begin_src lua return x['b'] #+end_src" (org-babel-next-src-block) (org-babel-execute-src-block))))) (ert-deftest test-ob-lua/simple-output () "Test text output from table." (should (equal "result: c\n" (org-test-with-temp-text "#+name: eg | a | b | c | d | #+header: :results output #+header: :var x = eg #+begin_src lua print('result: ' .. x[1][3]) #+end_src" (org-babel-next-src-block) (org-babel-execute-src-block))))) (ert-deftest test-ob-lua/colnames-yes-header-argument () "Test table passing with `colnames' header." (should (equal "a" (org-test-with-temp-text "#+name: eg | col | |-----| | a | | b | #+header: :colnames yes #+header: :var x = eg #+begin_src lua return x[1] #+end_src" (org-babel-next-src-block) (org-babel-execute-src-block))))) (ert-deftest test-ob-lua/colnames-yes-header-argument-pp () "Test table passing with `colnames' header and `pp' option." (should (equal "a = 12\nb = 13" (org-test-with-temp-text "#+name: eg | col | val | |-----+-----| | a | 12 | | b | 13 | #+header: :results value pp #+header: :colnames yes #+header: :var x = eg #+begin_src lua return x #+end_src" (org-babel-next-src-block) (org-babel-execute-src-block))))) (ert-deftest test-ob-lua/colnames-nil-header-argument () "Test table with `colnames' set to `nil'." (should (equal "1 = a\n2 = b" (org-test-with-temp-text "#+name: eg | col | |-----| | a | | b | #+header: :colnames nil #+header: :var x = eg #+header: :results value pp #+begin_src lua return x #+end_src" (org-babel-next-src-block) (org-babel-execute-src-block))))) (ert-deftest test-ob-lua/colnames-no-header-argument () "Test table passing without `colnames'." (should (equal "1 = col\n2 = a\n3 = b" (org-test-with-temp-text "#+name: eg | col | |-----| | a | | b | #+header: :colnames no #+header: :var x = eg #+header: :results value pp #+begin_src lua return x #+end_src" (org-babel-next-src-block) (org-babel-execute-src-block))))) (ert-deftest test-ob-lua/types () "Test returning different types." (should (equal "nil" (org-test-with-temp-text "src_lua{return nil}" (org-babel-execute-src-block)))) (should (equal "true" (org-test-with-temp-text "src_lua{return true}" (org-babel-execute-src-block)))) (should (equal "false" (org-test-with-temp-text "src_lua{return false}" (org-babel-execute-src-block)))) (should (equal 1 (org-test-with-temp-text "src_lua{return 1}" (org-babel-execute-src-block)))) (should (equal "hello world" (org-test-with-temp-text "src_lua{return 'hello world'}" (org-babel-execute-src-block)))) (should (equal 0 (string-match "table: 0x[0-9A-F]+" (org-test-with-temp-text "src_lua{return {}}" (org-babel-execute-src-block)))))) (ert-deftest test-ob-lua/multiple-values () "Test returning multiple values." (should (equal "1, 2, 3" (org-test-with-temp-text "src_lua{return 1, 2, 3}" (org-babel-execute-src-block)))) (should (equal "1|2|3" (let ((org-babel-lua-multiple-values-separator "|")) (org-test-with-temp-text "src_lua{return 1, 2, 3}" (org-babel-execute-src-block)))))) (provide 'test-ob-lua) ;;; test-ob-lua.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-maxima.el000066400000000000000000000145011500430433700220470ustar00rootroot00000000000000;;; test-ob-maxima.el --- tests for ob-maxima.el -*- lexical-binding: t; -*- ;; Copyright (c) 2010-2014, 2019 Sergey Litvinov ;; Authors: Sergey Litvinov ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . (org-test-for-executable "maxima") (unless (featurep 'ob-maxima) (signal 'missing-test-dependency "Support for Maxima code blocks")) (ert-deftest ob-maxima/integer-input () "Test of integer input" (org-test-at-id "b5842ed4-8e8b-4b18-a1c9-cef006b6a6c8" (org-babel-next-src-block) (should (equal 4 (org-babel-execute-src-block))))) (ert-deftest ob-maxima/string-input () "Test of string input" (org-test-at-id "b5842ed4-8e8b-4b18-a1c9-cef006b6a6c8" (org-babel-next-src-block 2) (should (equal "- sin(x)" (org-babel-execute-src-block))))) (ert-deftest ob-maxima/simple-list-input () "Test of flat list input" (org-test-at-id "b5561c6a-73cd-453a-ba5e-62ad84844de6" (org-babel-next-src-block) (should (equal "[1, 2, 3] " (org-babel-execute-src-block))))) (ert-deftest ob-maxima/list-input () "Test of list input" (org-test-at-id "b5561c6a-73cd-453a-ba5e-62ad84844de6" (org-babel-next-src-block 2) (should (equal "[2, [2, 3], 4] " (org-babel-execute-src-block))))) (ert-deftest ob-maxima/table-input1 () "Test of table input" (org-test-at-id "400ee228-6b12-44fd-8097-7986f0f0db43" (org-babel-next-src-block) (should (equal "[[2.0], [3.0]] " (org-babel-execute-src-block))))) (ert-deftest ob-maxima/table-input2 () "Test of table input" (org-test-at-id "400ee228-6b12-44fd-8097-7986f0f0db43" (org-babel-next-src-block 2) (should (equal "[[2.0, 3.0]] " (org-babel-execute-src-block))))) (ert-deftest ob-maxima/matrix-output () "Test of table output" (org-test-at-id "cc158527-b867-4b1d-8ae0-b8c713a90fd7" (org-babel-next-src-block) (should (equal '((1 2 3) (2 3 4) (3 4 5)) (org-babel-execute-src-block))))) ;; 6 tests to test the :batch header argument (ert-deftest ob-maxima/batch+verbatim () "Exercise the `:batch' header argument. Since `--very-quiet' is set, the input and output are printed without labels." (org-test-with-temp-text (format "#+begin_src maxima :results verbatim :batch batch (assume(z>0), integrate(exp(-t)*t^z, t, 0, inf)); #+end_src") (should (equal (org-babel-execute-src-block) "(assume(z > 0),integrate(exp(-t)*t^z,t,0,inf))\n gamma(z + 1)")))) (ert-deftest ob-maxima/batch+verbatim+quiet () "Exercise the `:batch' header argument. Since `--quiet' is set, the input and output are printed with labels." (org-test-with-temp-text (format "#+name: ob-maxima/batch+verbatim #+begin_src maxima :results verbatim :batch batch :cmdline --quiet (assume(z>0), integrate(exp(-t)*t^z, t, 0, inf)); #+end_src") (should (equal (org-babel-execute-src-block) "(%i1) (assume(z > 0),integrate(exp(-t)*t^z,t,0,inf))\n(%o1) gamma(z + 1)")))) (ert-deftest ob-maxima/batch+verbatim+:lisp () "Exercise the `:batch' header argument with `:lisp' reader. Since `--quiet' is set, the output is printed (as a lisp form)." (org-test-with-temp-text (format "#+name: ob-maxima/batch+verbatim+:lisp #+begin_src maxima :results verbatim :batch batch :cmdline --quiet :lisp #$(assume(z>0),integrate(exp(-t)*t^z, t, 0, inf));#$ #+end_src ") (should (equal (org-babel-execute-src-block) "((%GAMMA SIMP) ((MPLUS SIMP) 1 $Z))")))) (ert-deftest ob-maxima/batch+verbatim+empty-string-vq () "Exercise the `:batch' header argument with empty string input. Since `--very-quiet' is set, the output is printed." (org-test-with-temp-text (format "#+name: ob-maxima/batch+verbatim+empty-string-vq #+begin_src maxima :results verbatim :batch batch :cmdline --very-quiet \"\"; #+end_src ") (should (equal (org-babel-execute-src-block) "\"\"\n ")))) (ert-deftest ob-maxima/batch+verbatim+empty-string () "Exercise the `:batch' header argument with empty string input. Since `--quiet' is set, the input and output are printed with labels." (org-test-with-temp-text (format "#+name: ob-maxima/batch+verbatim+empty-string #+begin_src maxima :results verbatim :batch batch :cmdline --quiet \"\"; #+end_src ") (should (equal (org-babel-execute-src-block) "(%i1) \"\"\n(%o1) ")))) (ert-deftest ob-maxima/batch+verbatim+whitespace-string () "Exercise the `:batch' header argument with whitespace input. Since `--quiet' is set, the input and output are printed with labels." (org-test-with-temp-text (format "#+name: ob-maxima/batch+verbatim+whitespace-string #+begin_src maxima :results verbatim :batch batch :cmdline --quiet \" \"; #+end_src ") (should (equal (org-babel-execute-src-block) "(%i1) \" \"\n(%o1) ")))) (ert-deftest ob-maxima/batch+verbatim+syntax-error () "Exercise the `:batch' header argument with syntax error. Send empty input line to Maxima." (org-test-with-temp-text (format "#+name: ob-maxima/batch+verbatim+syntax-error #+begin_src maxima :results verbatim :batch batch :cmdline --quiet ; #+end_src ") (should (string-match "incorrect syntax: Premature termination of input at ;\\." (org-babel-execute-src-block))))) (ert-deftest ob-maxima/batch+verbatim+eof-error () "Exercise the `:batch' header argument with syntax error. Send an incomplete expression to Maxima." (org-test-with-temp-text (format "#+name: ob-maxima/batch+verbatim+eof-error #+begin_src maxima :results verbatim :batch batch :cmdline --quiet x: #+end_src ") (should (string-match "end of file while scanning expression\\." (org-babel-execute-src-block))))) (provide 'test-ob-maxima) ;;; test-ob-maxima.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-octave.el000066400000000000000000000122561500430433700220610ustar00rootroot00000000000000;;; test-ob-octave.el --- tests for ob-octave.el -*- lexical-binding: t; -*- ;; Copyright (c) 2010-2014, 2019 Sergey Litvinov ;; Authors: Sergey Litvinov ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . (org-test-for-executable "octave") (unless (featurep 'ob-octave) (signal 'missing-test-dependency "Support for Octave code blocks")) (ert-deftest ob-octave/input-none () "Number output" (org-test-at-id "54dcd61d-cf6c-4d7a-b9e5-854953c8a753" (org-babel-next-src-block) (should (= 10 (org-babel-execute-src-block))))) (ert-deftest ob-octave/output-vector () "Vector output" (org-test-at-id "54dcd61d-cf6c-4d7a-b9e5-854953c8a753" (org-babel-next-src-block 2) (should (equal '((1 2 3 4)) (org-babel-execute-src-block))))) (ert-deftest ob-octave/input-variable () "Input variable" (org-test-at-id "cc2d82bb-2ac0-45be-a0c8-d1463b86a3ba" (org-babel-next-src-block) (should (= 42 (org-babel-execute-src-block))))) (ert-deftest ob-octave/input-array () "Input an array" (org-test-at-id "cc2d82bb-2ac0-45be-a0c8-d1463b86a3ba" (org-babel-next-src-block 2) (should (equal '((1 2 3)) (org-babel-execute-src-block))))) (ert-deftest ob-octave/input-matrix () "Input a matrix" (org-test-at-id "cc2d82bb-2ac0-45be-a0c8-d1463b86a3ba" (org-babel-next-src-block 3) (should (equal '((1 2) (3 4)) (org-babel-execute-src-block))))) (ert-deftest ob-octave/input-string () "Input a string" (org-test-at-id "cc2d82bb-2ac0-45be-a0c8-d1463b86a3ba" (org-babel-next-src-block 4) (should (equal "te" (org-babel-execute-src-block))))) (ert-deftest ob-octave/input-nil () "Input elisp nil" (org-test-at-id "cc2d82bb-2ac0-45be-a0c8-d1463b86a3ba" (org-babel-next-src-block 5) (should (equal nil (org-babel-execute-src-block))))) (ert-deftest ob-octave/graphics-file () "Graphics file. Test that link is correctly inserted and graphics file is created (and not empty). Clean-up side-effects." ;; In case a prior test left the Error Output buffer hanging around. (when (get-buffer "*Org-Babel Error Output*") (kill-buffer "*Org-Babel Error Output*")) (let ((file (make-temp-file "test-ob-octave-" nil ".png"))) (unwind-protect (org-test-with-temp-text (format "#+begin_src octave :results file graphics :file %s sombrero; #+end_src" file) (org-babel-execute-src-block) (should (search-forward (format "[[file:%s]]" file) nil nil)) (should (file-readable-p file))) ;; clean-up (delete-file file) (when (get-buffer "*Org-Babel Error Output*") (kill-buffer "*Org-Babel Error Output*"))))) (ert-deftest ob-octave/graphics-file-session () "Graphics file in a session. Test that session is started in *Inferior Octave* buffer, link is correctly inserted and graphics file is created (and not empty). Clean-up side-effects." (let ((file (make-temp-file "test-ob-octave-" nil ".png"))) (unwind-protect (org-test-with-temp-text (format "#+begin_src octave :session :results file graphics :file %s crash_dumps_octave_core(0); sombrero; #+end_src" file) (org-babel-execute-src-block) (should (get-buffer "*Inferior Octave*")) (should (search-forward (format "[[file:%s]]" file) nil nil)) (should (file-readable-p file))) ;; clean-up (delete-file file) (let (kill-buffer-query-functions kill-buffer-hook) (kill-buffer "*Inferior Octave*")) (when (get-buffer "*Org-Babel Error Output*") (kill-buffer "*Org-Babel Error Output*"))))) (ert-deftest ob-octave/graphics-file-space () "Graphics file with a space in filename. Test that session is started in *Inferior Octave* buffer, link is correctly inserted and graphics file is created (and not empty). Clean-up side-effects." (let ((file (make-temp-file "test ob octave-" nil ".png"))) (unwind-protect (org-test-with-temp-text (format "#+begin_src octave :results file graphics :file %s sombrero; #+end_src" file) (org-babel-execute-src-block) (should (search-forward (format "[[file:%s]]" file) nil nil)) (should (file-readable-p file))) ;; clean-up (delete-file file) (when (get-buffer "*Org-Babel Error Output*") (kill-buffer "*Org-Babel Error Output*"))))) (ert-deftest ob-octave/session-multiline () "Test multiline session input." (dotimes (_ 3) (org-test-with-temp-text "#+begin_src octave :session oct2 :results output x = 1; x = 1; x = 1 #+end_src" (should (equal "x = 1" (org-babel-execute-src-block)))))) (provide 'test-ob-octave) org-mode-9.7.29+dfsg/testing/lisp/test-ob-perl.el000066400000000000000000000043711500430433700215410ustar00rootroot00000000000000;;; test-ob-perl.el --- tests for ob-perl.el -*- lexical-binding: t; -*- ;; Copyright (c) 2013, 2014, 2019 Achim Gratz ;; Authors: Achim Gratz ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (org-test-for-executable "perl") (unless (featurep 'ob-perl) (signal 'missing-test-dependency "Support for perl code blocks")) (ert-deftest test-ob-perl/simple-output () (org-test-with-temp-text " #+header: :results output #+begin_src perl print qq(Hi Mom!$/I'm home.); #+end_src" (org-babel-next-src-block) (should (equal "Hi Mom!\nI'm home." (org-babel-execute-src-block))))) (ert-deftest test-ob-perl/simple-value () (org-test-with-temp-text " #+header: :results value #+begin_src perl qq(Hi Mom!$/I'm home.); #+end_src" (org-babel-next-src-block) (should (equal '(("Hi Mom!") ("I'm home.")) (org-babel-execute-src-block))))) (ert-deftest test-ob-perl/table-passthrough-colnames-nil () (org-test-with-temp-text "#+name: eg | col1 | col2 | |------+------| | a | 1 | | b | 2.0 | #+header: :colnames nil #+header: :var x = eg #+begin_src perl #+end_src" (org-babel-next-src-block) (should (equal '(("col1" "col2") hline ("a" 1) ("b" 2.0)) (org-babel-execute-src-block))))) (ert-deftest test-ob-perl/table-passthrough-colnames-no () (org-test-with-temp-text "#+name: eg | col1 | col2 | |------+------| | a | 1 | | b | 2.0 | #+header: :colnames no #+header: :var x = eg #+begin_src perl #+end_src" (org-babel-next-src-block) (should (equal '(("col1" "col2") ("a" 1) ("b" 2.0)) (org-babel-execute-src-block))))) (provide 'test-ob-perl) ;;; test-ob-perl.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-plantuml.el000066400000000000000000000041001500430433700224210ustar00rootroot00000000000000;;; test-ob-plantuml.el --- tests for ob-plantuml.el -*- lexical-binding: t; -*- ;; Copyright (c) 2016, 2019 Thibault Marin ;; Authors: Thibault Marin ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (require 'ob-plantuml) (ert-deftest test-ob-plantuml/single-var () "Test file output with input variable." (should (string= "@startuml !define CLASSNAME test_class class CLASSNAME @enduml" (let ((org-plantuml-jar-path nil)) (org-test-with-temp-text "#+name: variable_value : test_class #+header: :file tmp.puml #+header: :var CLASSNAME=variable_value #+begin_src plantuml class CLASSNAME #+end_src" (org-babel-next-src-block) (let ((src-block-info (cdr (org-babel-get-src-block-info)))) (org-babel-plantuml-make-body (car src-block-info) (car (cdr src-block-info))))))))) (ert-deftest test-ob-plantuml/prologue () "Test file output with prologue." (should (string= "@startuml skinparam classBackgroundColor #FF0000 class test_class @enduml" (let ((org-plantuml-jar-path nil)) (org-test-with-temp-text "#+header: :file tmp.puml #+header: :prologue skinparam classBackgroundColor #FF0000 #+begin_src plantuml class test_class #+end_src" (org-babel-next-src-block) (let ((src-block-info (cdr (org-babel-get-src-block-info)))) (org-babel-plantuml-make-body (car src-block-info) (car (cdr src-block-info))))))))) (provide 'test-ob-plantuml) ;;; test-ob-plantuml.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-python.el000066400000000000000000000244761500430433700221300ustar00rootroot00000000000000;;; test-ob-python.el --- tests for ob-python.el -*- lexical-binding: t; -*- ;; Copyright (c) 2011-2014, 2019 Eric Schulte ;; Authors: Eric Schulte ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (org-test-for-executable "python") (require 'ob-core) (unless (featurep 'ob-python) (signal 'missing-test-dependency "Support for Python code blocks")) (ert-deftest test-ob-python/colnames-yes-header-argument () (should (equal '(("col") hline ("a") ("b")) (org-test-with-temp-text "#+name: eg | col | |-----| | a | | b | #+header: :colnames yes #+header: :var x = eg #+begin_src python return x #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-python/colnames-yes-header-argument-again () (should (equal '(("a") hline ("b*") ("c*")) (org-test-with-temp-text "#+name: less-cols | a | |---| | b | | c | #+header: :colnames yes #+begin_src python :var tab=less-cols return [[val + '*' for val in row] for row in tab] #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-python/colnames-nil-header-argument () (should (equal '(("col") hline ("a") ("b")) (org-test-with-temp-text "#+name: eg | col | |-----| | a | | b | #+header: :colnames nil #+header: :var x = eg #+begin_src python return x #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-python/colnames-no-header-argument-again () (should (equal '(("a*") ("b*") ("c*")) (org-test-with-temp-text "#+name: less-cols | a | |---| | b | | c | #+header: :colnames no #+begin_src python :var tab=less-cols return [[val + '*' for val in row] for row in tab] #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-python/colnames-no-header-argument () (should (equal '(("col") ("a") ("b")) (org-test-with-temp-text "#+name: eg | col | |-----| | a | | b | #+header: :colnames no #+header: :var x = eg #+begin_src python return x #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-python/session-multiline () ;; Disable the test on older Emacs as built-in python.el sometimes ;; fail to initialize session. (skip-unless (version<= "28" emacs-version)) (should (equal "20" (org-test-with-temp-text "#+begin_src python :session :results output foo = 0 for _ in range(10): foo += 1 foo += 1 print(foo) #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-python/insert-necessary-blank-line-when-sending-code-to-interpreter () ;; Disable the test on older Emacs as built-in python.el sometimes ;; fail to initialize session. (skip-unless (version<= "28" emacs-version)) (should (equal 2 (org-test-with-temp-text "#+begin_src python :session :results value if True: 1 2 #+end_src" ;; Previously, while adding `:session' to a normal code ;; block, also need to add extra blank lines to end ;; indent block or indicate logical sections. Now, the ;; `org-babel-python-evaluate-session' can do it ;; automatically: ;; ;; >>> if True: ;; >>> 1 ;; >>> ;; >>> 2 (org-babel-execute-maybe) (org-babel-execute-src-block))))) (ert-deftest test-ob-python/if-else-block () ;; Disable the test on older Emacs as built-in python.el sometimes ;; fail to initialize session. (skip-unless (version<= "28" emacs-version)) (should (equal "success" (org-test-with-temp-text "#+begin_src python :session :results value value = 'failure' if False: pass else: value = 'success' value #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-python/indent-block-with-blank-lines () ;; Disable the test on older Emacs as built-in python.el sometimes ;; fail to initialize session. (skip-unless (version<= "28" emacs-version)) (should (equal 20 (org-test-with-temp-text "#+begin_src python :session :results value foo = 0 for i in range(10): foo += 1 foo += 1 foo #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-python/assign-underscore () ;; Disable the test on older Emacs as built-in python.el sometimes ;; fail to initialize session. (skip-unless (version<= "28" emacs-version)) (let ((result (org-test-with-temp-text "#+begin_src python :session :results value _ = 'failure' 'success' #+end_src" (org-babel-execute-src-block)))) (should (equal "success" result)))) (ert-deftest test-ob-python/multiline-var () (should (equal "a\nb\nc" (org-test-with-temp-text "#+begin_src python :var text=\"a\\nb\\nc\" return text #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-python/multiline-str () (should (equal "a\nb\nc" (org-test-with-temp-text "#+begin_src python text=\"a\\nb\\nc\" return text #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-python/header-var-assignment () (should (equal "success" (org-test-with-temp-text "#+begin_src python :var text=\"failure\" text text=\"success\" return text #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-python/session-value-sleep () ;; Disable the test on older Emacs as built-in python.el sometimes ;; fail to initialize session. (skip-unless (version<= "28" emacs-version)) (should (equal "success" (org-test-with-temp-text "#+begin_src python :session :results value import time time.sleep(.1) 'success' #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob-python/async-simple-session-output () ;; Disable the test on older Emacs as built-in python.el sometimes ;; fail to initialize session. (skip-unless (version<= "28" emacs-version)) (let ((org-babel-temporary-directory temporary-file-directory) (org-confirm-babel-evaluate nil)) (org-test-with-temp-text "#+begin_src python :session :async yes :results output import time time.sleep(.1) print('Yep!') #+end_src\n" (should (let ((expected "Yep!")) (and (not (string= expected (org-babel-execute-src-block))) (string= expected (progn (sleep-for 0.200) (goto-char (org-babel-where-is-src-block-result)) (org-babel-read-result))))))))) (ert-deftest test-ob-python/async-named-output () ;; Disable the test on older Emacs as built-in python.el sometimes ;; fail to initialize session. (skip-unless (version<= "28" emacs-version)) (let (org-confirm-babel-evaluate (org-babel-temporary-directory temporary-file-directory) (src-block "#+begin_src python :async :session :results output print(\"Yep!\") #+end_src") (results-before " #+NAME: foobar #+RESULTS: : Nope!") (results-after " #+NAME: foobar #+RESULTS: : Yep! ")) (org-test-with-temp-text (concat src-block results-before) (should (progn (org-babel-execute-src-block) (sleep-for 0.200) (string= (concat src-block results-after) (buffer-string))))))) (ert-deftest test-ob-python/async-output-drawer () ;; Disable the test on older Emacs as built-in python.el sometimes ;; fail to initialize session. (skip-unless (version<= "28" emacs-version)) (let (org-confirm-babel-evaluate (org-babel-temporary-directory temporary-file-directory) (src-block "#+begin_src python :async :session :results output drawer print(list(range(3))) #+end_src") (result " #+RESULTS: :results: [0, 1, 2] :end: ")) (org-test-with-temp-text src-block (should (progn (org-babel-execute-src-block) (sleep-for 0.200) (string= (concat src-block result) (buffer-string))))))) (ert-deftest test-ob-python/async-local-python-shell () ;; Disable the test on older Emacs as built-in python.el sometimes ;; fail to initialize session. (skip-unless (version<= "28" emacs-version)) (when-let* ((buf (get-buffer "*Python*"))) (let (kill-buffer-query-functions) (kill-buffer buf))) (org-test-with-temp-text-in-file "# -*- python-shell-buffer-name: \"Python 3\" -*- #+begin_src python :session \"*Python 3*\" :async yes 1 #+end_src" (should (org-babel-execute-src-block)))) (ert-deftest test-ob-python/session-restart () ;; Disable the test on older Emacs as built-in python.el sometimes ;; fail to initialize session. (skip-unless (version<= "28" emacs-version)) (should (equal "success" (progn (org-test-with-temp-text "#+begin_src python :session :results output print('start') #+end_src" (org-babel-execute-src-block)) (let ((proc (python-shell-get-process))) (python-shell-send-string "exit()") (while (accept-process-output proc))) (org-test-with-temp-text "#+begin_src python :session :results output print('success') #+end_src" (org-babel-execute-src-block)))))) (ert-deftest test-ob-python/session-with-existing-inferior-python () ;; Disable the test on older Emacs as built-in python.el sometimes ;; fail to initialize session. (skip-unless (version<= "28" emacs-version)) (let ((session-name "test-ob-python/session-with-existing-inferior-python")) (let ((python-shell-buffer-name session-name)) (run-python)) (unwind-protect (should (equal "success" (org-test-with-temp-text (format "#+begin_src python :session %s :results value 'success' #+end_src" session-name) (org-babel-execute-src-block)))) (let (kill-buffer-hook kill-buffer-query-functions) (kill-buffer (format "*%s*" session-name)))))) (provide 'test-ob-python) ;;; test-ob-python.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-ruby.el000066400000000000000000000042351500430433700215570ustar00rootroot00000000000000;;; test-ob-ruby.el --- tests for ob-ruby.el -*- lexical-binding: t; -*- ;; Copyright (c) 2013-2015, 2019 Oleh Krehel ;; Authors: Oleh Krehel ;; 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 . ;;; Code: (org-test-for-executable "ruby") (unless (featurep 'ob-ruby) (signal 'missing-test-dependency "Support for Ruby code blocks")) (unless (featurep 'inf-ruby) (signal 'missing-test-dependency "inf-ruby")) (ert-deftest test-ob-ruby/session-output-1 () (should (equal (org-test-with-temp-text "#+begin_src ruby :session org-test-ruby :results output s = \"1\" s = \"2\" s = \"3\" puts s s = \"4\" #+end_src" (org-babel-execute-maybe) (substring-no-properties (buffer-string))) "#+begin_src ruby :session org-test-ruby :results output s = \"1\" s = \"2\" s = \"3\" puts s s = \"4\" #+end_src #+RESULTS: : 3 "))) (ert-deftest test-ob-ruby/session-output-2 () (should (equal (org-test-with-temp-text "#+begin_src ruby :session org-test-ruby :results output puts s s = \"5\" #+end_src" (org-babel-execute-maybe) (substring-no-properties (buffer-string))) "#+begin_src ruby :session org-test-ruby :results output puts s s = \"5\" #+end_src #+RESULTS: : 4 "))) (ert-deftest test-ob-ruby/session-output-3 () (should (equal (org-test-with-temp-text "#+begin_src ruby :session org-test-ruby :results output puts s s = \"6\" #+end_src" (org-babel-execute-maybe) (substring-no-properties (buffer-string))) "#+begin_src ruby :session org-test-ruby :results output puts s s = \"6\" #+end_src #+RESULTS: : 5 "))) (provide 'test-ob-ruby) ;;; test-ob-ruby.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-scheme.el000066400000000000000000000062041500430433700220400ustar00rootroot00000000000000;;; test-ob-scheme.el --- Tests for Babel scheme -*- lexical-binding: t; -*- ;; Copyright (C) 2017, 2019 Nicolas Goaziou ;; Author: Nicolas Goaziou ;; 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 . ;;; Commentary: ;; Unit tests for Org Babel Scheme. ;;; Code: (unless (featurep 'ob-scheme) (signal 'missing-test-dependency "Support for Scheme code blocks")) (unless (featurep 'geiser) (signal 'missing-test-dependency "geiser")) (unless (version<= "27.1" emacs-version) (signal 'missing-test-dependency "Geiser required for Scheme code blocks needs Emacs >=27.1")) (ert-deftest test-ob-scheme/tables () "Test table output." (equal "#+begin_src scheme '(1 2 3) #+end_src #+RESULTS: | 1 | 2 | 3 | " (org-test-with-temp-text "#+begin_src scheme\n'(1 2 3)\n#+end_src" (org-babel-execute-maybe) (buffer-string)))) (ert-deftest test-ob-scheme/verbatim () "Test verbatim output." (should (equal ": (1 2 3)\n" (org-test-with-temp-text "#+begin_src scheme :results verbatim\n'(1 2 3)\n#+end_src" (org-babel-execute-src-block) (let ((case-fold-search t)) (search-forward "#+results")) (buffer-substring-no-properties (line-beginning-position 2) (point-max)))))) (ert-deftest test-ob-scheme/list () "Test list output." (should (equal "- 1\n- 2\n- 3\n" (org-test-with-temp-text "#+begin_src scheme :results list\n'(1 2 3)\n#+end_src" (org-babel-execute-maybe) (let ((case-fold-search t)) (search-forward "#+results")) (buffer-substring-no-properties (line-beginning-position 2) (point-max)))))) (ert-deftest test-ob-scheme/prologue () "Test :prologue parameter." (should (equal "#+begin_src scheme :prologue \"(define x 2)\" x #+end_src #+RESULTS: : 2 " (org-test-with-temp-text "#+begin_src scheme :prologue \"(define x 2)\"\nx\n#+end_src" (org-babel-execute-maybe) (buffer-string)))) (should (equal "#+begin_src scheme :prologue \"(define x 2)\" :var y=1 x #+end_src #+RESULTS: : 2 " (org-test-with-temp-text "#+begin_src scheme :prologue \"(define x 2)\" :var y=1\nx\n#+end_src" (org-babel-execute-maybe) (buffer-string))))) (ert-deftest test-ob-scheme/unspecified () "Test <#unspecified> return value." (should (equal "#+begin_src scheme \(define (mysquare x) (* x x)) #+end_src #+RESULTS: : # " (org-test-with-temp-text "#+begin_src scheme (define (mysquare x) (* x x)) #+end_src" (org-babel-execute-maybe) (buffer-string))))) (provide 'test-ob-scheme) ;;; test-ob-scheme.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-sed.el000066400000000000000000000042111500430433700213430ustar00rootroot00000000000000;;; test-ob-sed.el --- tests for ob-sed.el -*- lexical-binding: t; -*- ;; Copyright (c) 2015, 2019 Bjarte Johansen ;; Authors: Bjarte Johansen ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (require 'ob-sed) (org-test-for-executable "sed") (unless (featurep 'ob-sed) (signal 'missing-test-dependency "Support for Sed code blocks")) (ert-deftest ob-sed-test/simple-execution-of-script () "Test simple execution of script." (org-test-at-id "C7E7CA6A-2601-42C9-B534-4102D62E458D" (org-babel-next-src-block) (should (string= "A processed sentence." (org-babel-execute-src-block))))) (ert-deftest ob-sed-test/in-file-header-argument () "Test :in-file header argument." (org-test-at-id "54EC49AA-FE9F-4D58-812E-00FC87FAF562" (let ((default-directory temporary-file-directory)) (with-temp-buffer (insert "A test file.") (write-file "test1.txt")) (org-babel-next-src-block) (should (string= "A tested file." (org-babel-execute-src-block)))))) (ert-deftest ob-sed-test/cmd-line-header-argument () "Test :cmd-line header argument." (org-test-at-id "E3C6A8BA-39FF-4840-BA8E-90D5C4365AB1" (let ((default-directory temporary-file-directory)) (with-temp-buffer (insert "A test file.") (write-file "test2.txt")) (org-babel-next-src-block) (org-babel-execute-src-block) (should (string= "A tested again file.\n" (with-temp-buffer (insert-file-contents "test2.txt") (buffer-string))))))) (provide 'test-ob-sed) ;;; test-ob-sed ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-shell.el000066400000000000000000000424431500430433700217100ustar00rootroot00000000000000;;; test-ob-shell.el -*- lexical-binding: t; -*- ;; Copyright (c) 2010-2014, 2019 Eric Schulte ;; Authors: Eric Schulte ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Comment: ;; See testing/README for how to run tests. ;;; Requirements: (require 'ob-core) (require 'org-macs) (unless (featurep 'ob-shell) (signal 'missing-test-dependency "Support for Shell code blocks")) (org-test-for-executable "sh") ;;; Code: (ert-deftest test-ob-shell/dont-insert-spaces-on-expanded-bodies () "Expanded shell bodies should not start with a blank line unless the body of the tangled block does." (should-not (string-match "^[\n\r][\t ]*[\n\r]" (org-babel-expand-body:generic "echo 2" '()))) (should (string-match "^[\n\r][\t ]*[\n\r]" (org-babel-expand-body:generic "\n\necho 2" '())))) (ert-deftest test-ob-shell/dont-error-on-empty-results () "Empty results should not cause a Lisp error." (should (null (org-babel-execute:sh "" nil)))) (ert-deftest test-ob-shell/dont-error-on-babel-error () "Errors within Babel execution should not cause Lisp errors." (if (should (null (org-babel-execute:sh "ls NoSuchFileOrDirectory.txt" nil))) (kill-buffer "*Org-Babel Error Output*"))) (ert-deftest test-ob-shell/session-single-return-returns-string () "Sessions with a single result should return a string." (let* ((session-name "test-ob-shell/session-evaluation-single-return-returns-string") (kill-buffer-query-functions nil) (result (org-babel-execute:sh (format "echo %s" session-name) `((:session . ,session-name))))) (should result) (if (should (string= session-name result)) (kill-buffer session-name)))) (ert-deftest test-ob-shell/session-multiple-returns-returns-list () "Sessions with multiple results should return a list." (let* ((session-name "test-ob-shell/session-multiple-returns-returns-list") (kill-buffer-query-functions nil) (result (org-babel-execute:sh "echo 1; echo 2" `((:session . ,session-name))))) (should result) (should (listp result)) (if (should (equal '((1) (2)) result)) (kill-buffer session-name)))) (ert-deftest test-ob-shell/session-async-valid-header-arg-values () "Test that session runs asynchronously for certain :async values." (let ((session-name "test-ob-shell/session-async-valid-header-arg-values") (kill-buffer-query-functions nil)) (dolist (arg-val '("t" "")) (org-test-with-temp-text (concat "#+begin_src sh :session " session-name " :async " arg-val " echo 1 #+end_src") (if (should (string-match org-uuid-regexp (org-trim (org-babel-execute-src-block)))) (kill-buffer session-name)))))) (ert-deftest test-ob-shell/session-async-inserts-uuid-before-results-are-returned () "Test that a uuid placeholder is inserted before results are inserted." (let ((session-name "test-ob-shell/session-async-inserts-uuid-before-results-are-returned") (kill-buffer-query-functions nil)) (org-test-with-temp-text (concat "#+begin_src sh :session " session-name " :async t echo 1 #+end_src") (if (should (string-match org-uuid-regexp (org-trim (org-babel-execute-src-block)))) (kill-buffer session-name))))) (ert-deftest test-ob-shell/session-async-evaluation () "Test the async evaluation process." (let* ((session-name "test-ob-shell/session-async-evaluation") (kill-buffer-query-functions nil) (start-time (current-time)) (wait-time (time-add start-time 3)) uuid-placeholder) (org-test-with-temp-text (concat "#+begin_src sh :session " session-name " :async t echo 1 echo 2 #+end_src") (setq uuid-placeholder (org-trim (org-babel-execute-src-block))) (catch 'too-long (while (string-match uuid-placeholder (buffer-string)) (progn (sleep-for 0.01) (when (time-less-p wait-time (current-time)) (throw 'too-long (ert-fail "Took too long to get result from callback")))))) (search-forward "#+results") (beginning-of-line 2) (if (should (string= ": 1\n: 2\n" (buffer-substring-no-properties (point) (point-max)))) (kill-buffer session-name))))) (ert-deftest test-ob-shell/session-async-results () "Test that async evaluation removes prompt from results." (let* ((session-name "test-ob-shell/session-async-results") (kill-buffer-query-functions nil) (start-time (current-time)) (wait-time (time-add start-time 3)) uuid-placeholder) (org-test-with-temp-text (concat "#+begin_src sh :session " session-name " :async t # print message echo \"hello world\" #+end_src") (setq uuid-placeholder (org-trim (org-babel-execute-src-block))) (catch 'too-long (while (string-match uuid-placeholder (buffer-string)) (progn (sleep-for 0.01) (when (time-less-p wait-time (current-time)) (throw 'too-long (ert-fail "Took too long to get result from callback")))))) (search-forward "#+results") (beginning-of-line 2) (if (should (string= ": hello world\n" (buffer-substring-no-properties (point) (point-max)))) (kill-buffer session-name))))) (ert-deftest test-ob-shell/generic-uses-no-arrays () "Test generic serialization of array into a single string." (org-test-with-temp-text " #+NAME: sample_array | one | | two | | three | #+begin_src sh :exports results :results output :var array=sample_array echo ${array} #+end_src" (should (equal "one two three" (org-trim (org-babel-execute-src-block)))))) (ert-deftest test-ob-shell/bash-uses-arrays () "Bash sees named array as a simple indexed array. In this test, we check that the returned value is indeed only the first item of the array, as opposed to the generic serialiation that will return all elements of the array as a single string." (org-test-with-temp-text "#+NAME: sample_array | one | | two | | three | #+begin_src bash :exports results :results output :var array=sample_array echo ${array} #+end_src" (skip-unless (executable-find "bash")) (should (equal "one" (org-trim (org-babel-execute-src-block)))))) (ert-deftest test-ob-shell/generic-uses-no-assoc-arrays-simple-map () "Generic shell: no special handing for key-value mapping table No associative arrays for generic. The shell will see all values as a single string." (org-test-with-temp-text "#+NAME: sample_mapping_table | first | one | | second | two | | third | three | #+begin_src sh :exports results :results output :var table=sample_mapping_table echo ${table} #+end_src" (should (equal "first one second two third three" (org-trim (org-babel-execute-src-block)))))) (ert-deftest test-ob-shell/generic-uses-no-assoc-arrays-3-columns () "Associative array tests (more than 2 columns) No associative arrays for generic. The shell will see all values as a single string." (org-test-with-temp-text "#+NAME: sample_big_table | bread | 2 | kg | | spaghetti | 20 | cm | | milk | 50 | dl | #+begin_src sh :exports results :results output :var table=sample_big_table echo ${table} #+end_src" (should (equal "bread 2 kg spaghetti 20 cm milk 50 dl" (org-trim (org-babel-execute-src-block)))))) (ert-deftest test-ob-shell/bash-uses-assoc-arrays () "Bash shell: support for associative arrays Bash will see a table that contains the first column as the 'index' of the associative array, and the second column as the value. " (skip-unless ;; Old GPLv2 BASH in macOSX does not support associative arrays. (if-let* ((bash (executable-find "bash"))) (eq 0 (process-file bash nil nil nil "-c" "declare -A assoc_array")))) (org-test-with-temp-text "#+NAME: sample_mapping_table | first | one | | second | two | | third | three | #+begin_src bash :exports :results output results :var table=sample_mapping_table echo ${table[second]} #+end_src " (should (equal "two" (org-trim (org-babel-execute-src-block)))))) (ert-deftest test-ob-shell/bash-uses-assoc-arrays-with-lists () "Bash shell: support for associative arrays with lists Bash will see an associative array that contains each row as a single string. Bash cannot handle lists in associative arrays." (skip-unless ;; Old GPLv2 BASH in macOSX does not support associative arrays. (if-let* ((bash (executable-find "bash"))) (eq 0 (process-file bash nil nil nil "-c" "declare -A assoc_array")))) (org-test-with-temp-text "#+NAME: sample_big_table | bread | 2 | kg | | spaghetti | 20 | cm | | milk | 50 | dl | #+begin_src bash :exports results :results output :var table=sample_big_table echo ${table[spaghetti]} #+end_src" (should (equal "20 cm" (org-trim (org-babel-execute-src-block)))))) (ert-deftest test-ob-shell/simple-list () "Test list variables." ;; bash: a list is turned into an array (should (equal "2" (org-test-with-temp-text "#+BEGIN_SRC bash :results output :var l='(1 2) echo ${l[1]} #+END_SRC" (org-trim (org-babel-execute-src-block))))) ;; sh: a list is a string containing all values (should (equal "1 2" (org-test-with-temp-text "#+BEGIN_SRC sh :results output :var l='(1 2) echo ${l} #+END_SRC" (org-trim (org-babel-execute-src-block)))))) (ert-deftest test-ob-shell/remote-with-stdin-or-cmdline () "Test :stdin and :cmdline with a remote directory." ;; We assume `default-directory' is a local directory. (skip-unless (not (memq system-type '(ms-dos windows-nt)))) (org-test-with-tramp-remote-dir remote-dir (dolist (spec `( () (:dir ,remote-dir) (:dir ,remote-dir :cmdline t) (:dir ,remote-dir :stdin t) (:dir ,remote-dir :cmdline t :shebang t) (:dir ,remote-dir :stdin t :shebang t) (:dir ,remote-dir :cmdline t :stdin t :shebang t) (:cmdline t) (:stdin t) (:cmdline t :shebang t) (:stdin t :shebang t) (:cmdline t :stdin t :shebang t))) (let ((default-directory (or (plist-get spec :dir) default-directory)) (org-confirm-babel-evaluate nil) (params-line "") (who-line " export who=tramp") (args-line " echo ARGS: --verbose 23 71")) (when-let* ((dir (plist-get spec :dir))) (setq params-line (concat params-line " " ":dir " dir))) (when (plist-get spec :stdin) (setq who-line " read -r who") (setq params-line (concat params-line " :stdin input"))) (when (plist-get spec :cmdline) (setq args-line " echo \"ARGS: $*\"") (setq params-line (concat params-line " :cmdline \"--verbose 23 71\""))) (when (plist-get spec :shebang) (setq params-line (concat params-line " :shebang \"#!/bin/sh\""))) (let* ((result (org-test-with-temp-text (mapconcat #'identity (list "#+name: input" "tramp" "" (concat "" "#+begin_src sh :results output " params-line) args-line who-line " echo \"hello $who from $(pwd)/\"" "#+end_src") "\n") (org-trim (org-babel-execute-src-block)))) (expected (concat "ARGS: --verbose 23 71" "\nhello tramp from " (file-local-name default-directory)))) (if (should (equal result expected)) ;; FIXME: Fails with non-local exit on Emacs 26. (when (version<= "27" emacs-version) (kill-matching-buffers (format "\\*tramp/mock\\s-%s\\*" system-name) t t)))))))) (ert-deftest test-ob-shell/results-table () "Test :results table." (should (equal '(("I \"want\" it all")) (org-test-with-temp-text "#+BEGIN_SRC sh :results table echo 'I \"want\" it all' #+END_SRC" (org-babel-execute-src-block))))) (ert-deftest test-ob-shell/results-list () "Test :results list." (org-test-with-temp-text "#+BEGIN_SRC sh :results list echo 1 echo 2 echo 3 #+END_SRC" (should (equal '((1) (2) (3)) (org-babel-execute-src-block))) (search-forward "#+results") (beginning-of-line 2) (should (equal "- 1\n- 2\n- 3\n" (buffer-substring-no-properties (point) (point-max)))))) ;;; Standard output (ert-deftest test-ob-shell/standard-output-after-success () "Test standard output after exiting with a zero code." (should (= 1 (org-babel-execute:sh "echo 1" nil)))) (ert-deftest test-ob-shell/standard-output-after-failure () "Test standard output after exiting with a non-zero code." (if (should (= 1 (org-babel-execute:sh "echo 1; exit 2" nil))) (kill-buffer "*Org-Babel Error Output*"))) ;;; Standard error (ert-deftest test-ob-shell/error-output-after-success () "Test that standard error shows in the error buffer, alongside the exit code, after exiting with a zero code." (if (should (string= "1 [ Babel evaluation exited with code 0 ]" (progn (org-babel-eval-wipe-error-buffer) (org-babel-execute:sh "echo 1 >&2" nil) (with-current-buffer org-babel-error-buffer-name (buffer-string))))) (kill-buffer "*Org-Babel Error Output*"))) (ert-deftest test-ob-shell/error-output-after-failure () "Test that standard error shows in the error buffer, alongside the exit code, after exiting with a non-zero code." (if (should (string= "1 [ Babel evaluation exited with code 2 ]" (progn (org-babel-eval-wipe-error-buffer) (org-babel-execute:sh "echo 1 >&2; exit 2" nil) (with-current-buffer org-babel-error-buffer-name (buffer-string))))) (kill-buffer "*Org-Babel Error Output*"))) (ert-deftest test-ob-shell/error-output-after-failure-multiple () "Test that multiple standard error strings show in the error buffer, alongside multiple exit codes." (if (should (string= "1 [ Babel evaluation exited with code 2 ] 3 [ Babel evaluation exited with code 4 ]" (progn (org-babel-eval-wipe-error-buffer) (org-babel-execute:sh "echo 1 >&2; exit 2" nil) (org-babel-execute:sh "echo 3 >&2; exit 4" nil) (with-current-buffer org-babel-error-buffer-name (buffer-string))))) (kill-buffer "*Org-Babel Error Output*"))) ;;; Exit codes (ert-deftest test-ob-shell/exit-code () "Test that the exit code shows in the error buffer after exiting with a non-zero return code." (if (should (string= "[ Babel evaluation exited with code 1 ]" (progn (org-babel-eval-wipe-error-buffer) (org-babel-execute:sh "exit 1" nil) (with-current-buffer org-babel-error-buffer-name (buffer-string))))) (kill-buffer "*Org-Babel Error Output*"))) (ert-deftest test-ob-shell/exit-code-multiple () "Test that multiple exit codes show in the error buffer after exiting with a non-zero return code multiple times." (if (should (string= "[ Babel evaluation exited with code 1 ] [ Babel evaluation exited with code 2 ]" (progn (org-babel-eval-wipe-error-buffer) (org-babel-execute:sh "exit 1" nil) (org-babel-execute:sh "exit 2" nil) (with-current-buffer org-babel-error-buffer-name (buffer-string))))) (kill-buffer "*Org-Babel Error Output*"))) (provide 'test-ob-shell) ;;; test-ob-shell.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-sql.el000066400000000000000000000317341500430433700214010ustar00rootroot00000000000000;;; test-ob-sql.el --- tests for ob-sql.el -*- lexical-binding: t; -*- ;; Copyright (C) 2021 Robin Joy ;; Author: Robin Joy ;; Keywords: lisp ;; 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 . ;;; Code: (unless (featurep 'ob-sql) (signal 'missing-test-dependency "Support for sql code blocks")) (defmacro ob-sql/command (&rest body) "Execute body and return the command that would have been executed." `(cl-letf (((symbol-function 'org-babel-eval) (lambda (command &rest _) (throw 'sql-command command)))) (catch 'sql-command ,@body))) (defmacro ob-sql/command-should-contain (regexp sql-block) "Check that REGEXP is contained in the command executed when evaluating SQL-BLOCK." `(let ((regexps ,(if (listp regexp) regexp `(list ,regexp))) (command (ob-sql/command (org-test-with-temp-text ,sql-block (org-babel-next-src-block) (org-babel-execute-src-block))))) (dolist (regexp regexps) (should (string-match-p regexp command))))) (defmacro ob-sql/command-should-not-contain (regexp sql-block) "Check that REGEXP is not contained in the command executed when evaluating SQL-BLOCK." `(let ((command (ob-sql/command (org-test-with-temp-text ,sql-block (org-babel-next-src-block) (org-babel-execute-src-block))))) (should-not (string-match-p ,regexp command)))) ;;; dbish (ert-deftest ob-sql/engine-dbi-uses-dbish () (ob-sql/command-should-contain "^dbish " " #+begin_src sql :engine dbi select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-dbish-uses-batch-mode () (ob-sql/command-should-contain " --batch " " #+begin_src sql :engine dbi :dbuser dummy select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-dbish-can-pass-additional-cmdline-params () (ob-sql/command-should-contain " cmdlineparams " " #+begin_src sql :engine dbi :dbpassword dummy :cmdline cmdlineparams select * from dummy; #+end_src")) ;;; monetdb (ert-deftest ob-sql/engine-monetdb-uses-mclient () (ob-sql/command-should-contain "^mclient " " #+begin_src sql :engine monetdb select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-monetdb-outputs-values-tab-separated () (ob-sql/command-should-contain " -f tab " " #+begin_src sql :engine monetdb select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-monetdb-can-pass-additional-cmdline-params () (ob-sql/command-should-contain " cmdlineparams " " #+begin_src sql :engine monetdb :dbpassword dummy :cmdline cmdlineparams select * from dummy; #+end_src")) ;;; mssql (ert-deftest ob-sql/engine-mssql-uses-sqlcmd () (ob-sql/command-should-contain "^sqlcmd " " #+begin_src sql :engine mssql select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-mssql-outputs-values-tab-separated () (ob-sql/command-should-contain " -s \"\t\" " " #+begin_src sql :engine mssql select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-mssql-can-pass-additional-cmdline-params () (ob-sql/command-should-contain " cmdlineparams " " #+begin_src sql :engine mssql :dbpassword dummy :cmdline cmdlineparams select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-mssql-passes-user-if-provided () (ob-sql/command-should-contain " -U \"dummy\" " " #+begin_src sql :engine mssql :dbuser dummy select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-mssql-passes-password-if-provided () (ob-sql/command-should-contain " -P \"dummy\" " " #+begin_src sql :engine mssql :dbpassword dummy select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-mssql-passes-dbhost-if-provided () (ob-sql/command-should-contain " -S \"localhost\" " " #+begin_src sql :engine mssql :dbhost localhost select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-mssql-passes-database-if-provided () (ob-sql/command-should-contain " -d \"R01\" " " #+begin_src sql :engine mssql :database R01 select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-mssql-passes-all-parameter-provided () (ob-sql/command-should-contain '(" -d \"R01\" " " -S \"localhost\" " " -P \"pwd\" " " -U \"usr\" ") " #+begin_src sql :engine mssql :database R01 :dbhost localhost :dbport 30101 :dbinstance 1 :dbuser usr :dbpassword pwd select * from dummy; #+end_src")) ;;; MySQL (ert-deftest ob-sql/engine-mysql-uses-mysql () (ob-sql/command-should-contain "^mysql " " #+begin_src sql :engine mysql select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-mysql-passes-user-if-provided () (ob-sql/command-should-contain " -udummy " " #+begin_src sql :engine mysql :dbuser dummy select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-mysql-passes-password-if-provided () (ob-sql/command-should-contain " -pdummy " " #+begin_src sql :engine mysql :dbpassword dummy select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-mysql-passes-dbhost-if-provided () (ob-sql/command-should-contain " -hlocalhost " " #+begin_src sql :engine mysql :dbhost localhost select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-mysql-passes-host-if-provided () (ob-sql/command-should-contain " -P30101 " " #+begin_src sql :engine mysql :dbport 30101 select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-mysql-passes-database-if-provided () (ob-sql/command-should-contain " -dR01 " " #+begin_src sql :engine mysql :database R01 select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-mysql-passes-all-parameter-provided () (ob-sql/command-should-contain '(" -dR01 " " -hlocalhost " " -P30101 " " -ppwd " " -uusr ") " #+begin_src sql :engine mysql :database R01 :dbhost localhost :dbport 30101 :dbinstance 1 :dbuser usr :dbpassword pwd select * from dummy; #+end_src")) ;;; oracle (ert-deftest ob-sql/engine-oracle-uses-sqlplus () (ob-sql/command-should-contain "^sqlplus " " #+begin_src sql :engine oracle :dbuser dummy :dbpassword dummy :database dummy select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-oracle-passes-user-pwd-database-host-port-if-provided () (ob-sql/command-should-contain " dummy/dummypwd@localhost:12345/R01 " " #+begin_src sql :engine oracle :dbuser dummy :dbpassword dummypwd :dbhost localhost :database R01 :dbport 12345 select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-oracle-passes-user-pwd-database-if-no-host-port-provided () (ob-sql/command-should-contain " dummy/dummypwd@R01 " " #+begin_src sql :engine oracle :dbuser dummy :dbpassword dummypwd :database R01 select * from dummy; #+end_src")) ;;; postgresql (ert-deftest ob-sql/engine-postgresql-uses-psql () (ob-sql/command-should-contain "^psql " " #+begin_src sql :engine postgresql select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-postgresql-passes-password-if-provided () (ob-sql/command-should-contain "^PGPASSWORD=dummy " " #+begin_src sql :engine postgresql :dbpassword dummy select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-postgresql-stop-on-error () (ob-sql/command-should-contain " --set=\"ON_ERROR_STOP=1\" " " #+begin_src sql :engine postgresql select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-postgresql-does-not-output-column-names-if-requested () (ob-sql/command-should-contain " -t " " #+begin_src sql :engine postgresql :colnames no select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-postgresql-outputs-column-names-by-default () (ob-sql/command-should-not-contain " -t " " #+begin_src sql :engine postgresql select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-postgresql-can-pass-additional-cmdline-params () (ob-sql/command-should-contain " cmdlineparams$" " #+begin_src sql :engine postgresql :dbpassword dummy :cmdline cmdlineparams select * from dummy; #+end_src")) ;;; SAP HANA (ert-deftest ob-sql/engine-saphana-uses-hdbsql () (ob-sql/command-should-contain "^hdbsql " " #+begin_src sql :engine saphana select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-saphana-passes-user-if-provided () (ob-sql/command-should-contain " -u dummy " " #+begin_src sql :engine saphana :dbuser dummy select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-saphana-passes-password-if-provided () (ob-sql/command-should-contain " -p dummy " " #+begin_src sql :engine saphana :dbpassword dummy select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-saphana-passes-dbinstance-if-provided () (ob-sql/command-should-contain " -i 1 " " #+begin_src sql :engine saphana :dbinstance 1 select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-saphana-passes-dbhost-if-provided () (ob-sql/command-should-contain " -n localhost " " #+begin_src sql :engine saphana :dbhost localhost select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-saphana-passes-dbhost-and-dbport-if-provided () (ob-sql/command-should-contain " -n localhost:30101 " " #+begin_src sql :engine saphana :dbhost localhost :dbport 30101 select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-saphana-does-not-pass-host-port-if-only-port-provided () (ob-sql/command-should-not-contain " -n" " #+begin_src sql :engine saphana :dbport 30101 select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-saphana-passes-database-if-provided () (ob-sql/command-should-contain " -d R01 " " #+begin_src sql :engine saphana :database R01 select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-saphana-passes-all-parameter-provided () (ob-sql/command-should-contain '(" -d R01 " " -n localhost:30101 " " -i 1 " " -p pwd " " -u usr") " #+begin_src sql :engine saphana :database R01 :dbhost localhost :dbport 30101 :dbinstance 1 :dbuser usr :dbpassword pwd select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-saphana-can-pass-additional-cmdline-params () (ob-sql/command-should-contain " cmdlineparams$" " #+begin_src sql :engine saphana :dbpassword dummy :cmdline cmdlineparams select * from dummy; #+end_src")) ;;; sqsh (ert-deftest ob-sql/engine-sqsh-uses-sqsh () (ob-sql/command-should-contain "^sqsh " " #+begin_src sql :engine sqsh select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-sqsh-can-pass-additional-cmdline-params () (ob-sql/command-should-contain " cmdlineparams " " #+begin_src sql :engine sqsh :dbpassword dummy :cmdline cmdlineparams select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-sqsh-passes-user-if-provided () (ob-sql/command-should-contain " -U \"dummy\" " " #+begin_src sql :engine sqsh :dbuser dummy select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-sqsh-passes-password-if-provided () (ob-sql/command-should-contain " -P \"dummy\" " " #+begin_src sql :engine sqsh :dbpassword dummy select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-sqsh-passes-host-if-provided () (ob-sql/command-should-contain " -S \"localhost\" " " #+begin_src sql :engine sqsh :dbhost localhost select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-sqsh-passes-database-if-provided () (ob-sql/command-should-contain " -D \"R01\" " " #+begin_src sql :engine sqsh :database R01 select * from dummy; #+end_src")) ;;; vertica (ert-deftest ob-sql/engine-vertica-uses-vsql () (ob-sql/command-should-contain "^vsql " " #+begin_src sql :engine vertica select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-vertica-can-pass-additional-cmdline-params () (ob-sql/command-should-contain " cmdlineparams$" " #+begin_src sql :engine vertica :dbpassword dummy :cmdline cmdlineparams select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-vertica-passes-user-if-provided () (ob-sql/command-should-contain " -U dummy " " #+begin_src sql :engine vertica :dbuser dummy select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-vertica-passes-password-if-provided () (ob-sql/command-should-contain " -w dummy " " #+begin_src sql :engine vertica :dbpassword dummy select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-vertica-passes-host-if-provided () (ob-sql/command-should-contain " -h localhost " " #+begin_src sql :engine vertica :dbhost localhost select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-vertica-passes-database-if-provided () (ob-sql/command-should-contain " -d R01 " " #+begin_src sql :engine vertica :database R01 select * from dummy; #+end_src")) (ert-deftest ob-sql/engine-vertica-passes-port-if-provided () (ob-sql/command-should-contain " -p 12345 " " #+begin_src sql :engine vertica :dbport 12345 select * from dummy; #+end_src")) (provide 'test-ob-sql) ;;; test-ob-sql.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-sqlite.el000066400000000000000000000050141500430433700220730ustar00rootroot00000000000000;;; test-ob-sqlite.el --- tests for ob-sqlite.el -*- lexical-binding: t; -*- ;; Copyright (C) 2017, 2019 Eduardo Bellani ;; Author: Eduardo Bellani ;; Keywords: lisp ;; 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 . ;;; Code: (org-test-for-executable "sqlite3") (unless (featurep 'ob-sqlite) (signal 'missing-test-dependency "Support for sqlite code blocks")) (ert-deftest ob-sqlite/table-variables-with-commas () "Test of a table variable that contains commas. This guarantees that this code path results in a valid CSV." (should (equal '(("Mr Test A. Sql" "Minister for Science, Eternal Happiness, and Finance")) (org-test-with-temp-text "#+name: test_table1 | \"Mr Test A. Sql\" | Minister for Science, Eternal Happiness, and Finance | #+begin_src sqlite :db /tmp/test.db :var tb=test_table1 drop table if exists TestTable; create table TestTable(person, job); .mode csv TestTable .import $tb TestTable select * from TestTable; #+end_src" (org-babel-next-src-block) (org-babel-execute-src-block))))) (ert-deftest ob-sqlite/in-memory () "Test in-memory temporariness." (should (equal 0 (progn (org-test-with-temp-text "#+BEGIN_SRC sqlite PRAGMA user_version = 1; #+END_SRC" (org-babel-execute-src-block)) (org-test-with-temp-text "#+BEGIN_SRC sqlite PRAGMA user_version; #+END_SRC" (org-babel-execute-src-block)))))) (ert-deftest ob-sqlite/in-file () "Test in-file permanency." (should (equal 1 (let ((file (org-babel-temp-file "test" ".sqlite"))) (org-test-with-temp-text (format "#+BEGIN_SRC sqlite :db %s PRAGMA user_version = 1; #+END_SRC" file) (org-babel-execute-src-block)) (org-test-with-temp-text (format "#+BEGIN_SRC sqlite :db %s PRAGMA user_version; #+END_SRC" file) (org-babel-execute-src-block)))))) (provide 'test-ob-sqlite) ;;; test-ob-sqlite.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-table.el000066400000000000000000000030211500430433700216550ustar00rootroot00000000000000;;; test-ob-table.el -*- lexical-binding: t; -*- ;; Copyright (c) 2011-2014, 2019 Eric Schulte ;; Authors: Eric Schulte ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Comments: ;; Template test file for Org tests ;;; Code: (ert-deftest test-ob-table/sbe () "Test that `sbe' can be used to call code blocks from inside tables." (org-test-with-temp-text "#+name: take-sqrt #+begin_src emacs-lisp :var n=9 (sqrt n) #+end_src" ;; Symbol src block name. (should (equal "2.0" (org-sbe take-sqrt (n "4")))) ;; String src block name. (should (equal "2.0" (org-sbe "take-sqrt" (n "4"))))) (org-test-with-temp-text "#+name: identity #+begin_src emacs-lisp :var x=1 x #+end_src" ;; Escape quotes. (should (equal "123°34'23.34\"otherthing" (org-sbe identity (x $"123°34'23.34\"otherthing")))))) (provide 'test-ob-table) ;;; test-ob-table.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob-tangle.el000066400000000000000000000511621500430433700220510ustar00rootroot00000000000000;;; test-ob-tangle.el --- tests for ob-tangle.el -*- lexical-binding: t; -*- ;; Copyright (c) 2010-2016, 2019 Eric Schulte ;; Authors: Eric Schulte ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Comments: ;; Template test file for Org tests ;;; Code: (require 'subr-x) (require 'ob-tangle) (require 'org) ;; TODO ;; (ert-deftest ob-tangle/noweb-on-tangle () ;; "Noweb header arguments tangle correctly. ;; - yes expand on both export and tangle ;; - no expand on neither export or tangle ;; - tangle expand on only tangle not export" ;; (let ((target-file (make-temp-file "ob-tangle-test-"))) ;; (org-test-at-id "eb1f6498-5bd9-45e0-9c56-50717053e7b7" ;; (org-narrow-to-subtree) ;; (org-babel-tangle target-file)) ;; (let ((tang (with-temp-buffer ;; (insert-file-contents target-file) ;; (buffer-string)))) ;; (flet ((exp-p (arg) ;; (and ;; (string-match ;; (format "noweb-%s-start\\(\\(?:.\\|\n\\)*\\)noweb-%s-end" arg arg) ;; tang) ;; (string-match "expanded" (match-string 1 tang))))) ;; (should (exp-p "yes")) ;; (should-not (exp-p "no")) ;; (should (exp-p "tangle")))))) (ert-deftest ob-tangle/no-excessive-id-insertion-on-tangle () "Don't add IDs to headings without tangling code blocks." (org-test-at-id "ef06fd7f-012b-4fde-87a2-2ae91504ea7e" (org-babel-next-src-block) (org-narrow-to-subtree) (org-babel-tangle) (should (null (org-id-get))))) (ert-deftest ob-tangle/continued-code-blocks-w-noweb-ref () "Test that the :noweb-ref header argument is used correctly." (org-test-at-id "54d68d4b-1544-4745-85ab-4f03b3cbd8a0" (let ((tangled "df|sed '1d'|awk '{print $5 \" \" $6}'|sort -n |tail -1|awk '{print $2}'")) (org-narrow-to-subtree) (org-babel-tangle) (should (unwind-protect (with-temp-buffer (insert-file-contents "babel.sh") (goto-char (point-min)) (re-search-forward (regexp-quote tangled) nil t)) (when (file-exists-p "babel.sh") (delete-file "babel.sh"))))))) (ert-deftest ob-tangle/expand-headers-as-noweb-references () "Test that references to headers are expanded during noweb expansion." (org-test-at-id "2409e8ba-7b5f-4678-8888-e48aa02d8cb4" (org-babel-next-src-block 2) (let ((expanded (org-babel-expand-noweb-references))) (should (string-match (regexp-quote "simple") expanded)) (should (string-match (regexp-quote "length 14") expanded))))) (ert-deftest ob-tangle/comment-links-at-left-margin () "Test commenting of links at left margin." (should (string-match (regexp-quote "# [[https://orgmode.org][Org mode]]") (org-test-with-temp-text-in-file "[[https://orgmode.org][Org mode]] #+header: :comments org :results output :tangle \"test-ob-tangle.sh\" #+begin_src sh echo 1 #+end_src" (unwind-protect (progn (org-babel-tangle) (with-temp-buffer (insert-file-contents "test-ob-tangle.sh") (buffer-string))) (delete-file "test-ob-tangle.sh")))))) (ert-deftest ob-tangle/comment-org () "Test :comments org." (should (string-match (regexp-quote ";; Function heading") (org-test-with-temp-text-in-file "* Function heading #+begin_src elisp :tangle \"test-ob-tange.el\" :comments org (message \"FOO\") #+end_src" (unwind-protect (progn (org-babel-tangle) (with-temp-buffer (insert-file-contents "test-ob-tange.el") (buffer-string))) (delete-file "test-ob-tange.el")))))) (ert-deftest ob-tangle/comment-links-numbering () "Test numbering of source blocks when commenting with links." (should (org-test-with-temp-text-in-file "* H #+header: :tangle \"test-ob-tangle.el\" :comments link #+begin_src emacs-lisp 1 #+end_src #+header: :tangle \"test-ob-tangle.el\" :comments link #+begin_src emacs-lisp 2 #+end_src" (unwind-protect (progn (org-babel-tangle) (with-temp-buffer (insert-file-contents "test-ob-tangle.el") (buffer-string) (goto-char (point-min)) (and (search-forward "[H:1]]" nil t) (search-forward "[H:2]]" nil t)))) (delete-file "test-ob-tangle.el"))))) (ert-deftest ob-tangle/comment-links-relative-file () "Test relative file name handling when commenting with links." (should (org-test-with-temp-text-in-file "* H #+header: :tangle \"test-ob-tangle.el\" :comments link #+begin_src emacs-lisp 1 #+end_src" (unwind-protect (let ((org-babel-tangle-use-relative-file-links t)) (org-babel-tangle) (with-temp-buffer (insert-file-contents "test-ob-tangle.el") (buffer-string) (goto-char (point-min)) (search-forward (concat "[file:" (file-name-nondirectory file)) nil t))) (delete-file "test-ob-tangle.el")))) (should (org-test-with-temp-text-in-file "* H #+header: :tangle \"test-ob-tangle.el\" :comments link #+begin_src emacs-lisp 1 #+end_src" (unwind-protect (let ((org-babel-tangle-use-relative-file-links nil)) (org-babel-tangle) (with-temp-buffer (insert-file-contents "test-ob-tangle.el") (buffer-string) (goto-char (point-min)) (search-forward (concat "[file:" file) nil t))) (delete-file "test-ob-tangle.el"))))) (ert-deftest ob-tangle/comment-noweb-relative () "Test :comments noweb tangling with relative file paths." (should (org-test-with-temp-text-in-file "* Inner #+name: inner #+begin_src emacs-lisp 2 #+end_src * Main #+header: :tangle \"test-ob-tangle.el\" :comments noweb :noweb yes #+begin_src emacs-lisp '(1 <>) #+end_src" (unwind-protect (let ((org-babel-tangle-use-relative-file-links t)) (org-babel-tangle) (with-temp-buffer (insert-file-contents "test-ob-tangle.el") (buffer-string) (goto-char (point-min)) (and (search-forward (concat ";; [[file:" (file-name-nondirectory file) "::inner") nil t) (search-forward ";; inner ends here\n" nil t)))) (delete-file "test-ob-tangle.el"))))) (ert-deftest ob-tangle/comment-noweb-absolute () "Test :comments noweb tangling with absolute file path." (should (org-test-with-temp-text-in-file "* Inner #+name: inner #+begin_src emacs-lisp 2 #+end_src * Main #+header: :tangle \"test-ob-tangle.el\" :comments noweb :noweb yes #+begin_src emacs-lisp 1 <> #+end_src" (unwind-protect (let ((org-babel-tangle-use-relative-file-links nil)) (org-babel-tangle) (with-temp-buffer (insert-file-contents "test-ob-tangle.el") (buffer-string) (goto-char (point-min)) (and (search-forward (concat ";; [[file:" file "::inner") nil t) (search-forward ";; inner ends here" nil t)))) (delete-file "test-ob-tangle.el"))))) (ert-deftest ob-tangle/jump-to-org () "Test `org-babel-tangle-jump-to-org' specifications." ;; Standard test. (let ((org-file-apps '((t . emacs)))) (should (equal "* H\n#+begin_src emacs-lisp\n1\n#+end_src" (org-test-with-temp-text-in-file "* H\n#+begin_src emacs-lisp\n1\n#+end_src" (let ((file (buffer-file-name))) (org-test-with-temp-text (format ";; [[file:%s][H:1]]\n1\n;; H:1 ends here\n" (file-name-nondirectory file)) (org-babel-tangle-jump-to-org) (buffer-string)))))) ;; Multiple blocks in the same section. (should (equal "2" (org-test-with-temp-text-in-file "* H first block #+begin_src emacs-lisp 1 #+end_src another block #+begin_src emacs-lisp 2 #+end_src " (let ((file (buffer-file-name))) (org-test-with-temp-text (format ";; [[file:%s][H:2]]\n2\n;; H:2 ends here\n" (file-name-nondirectory file)) (org-babel-tangle-jump-to-org) (buffer-substring (line-beginning-position) (line-end-position))))))) ;; Preserve position within the source code. (should (equal "1)" (org-test-with-temp-text-in-file "* H\n#+begin_src emacs-lisp\n(+ 1 1)\n#+end_src" (let ((file (buffer-file-name))) (org-test-with-temp-text (format ";; [[file:%s][H:1]]\n(+ 1 1)\n;; H:1 ends here\n" (file-name-nondirectory file)) (org-babel-tangle-jump-to-org) (buffer-substring-no-properties (point) (line-end-position))))))) ;; Blocks before first heading. (should (equal "Buffer start\n#+begin_src emacs-lisp\n1\n#+end_src\n* H" (org-test-with-temp-text-in-file "Buffer start\n#+begin_src emacs-lisp\n1\n#+end_src\n* H" (let ((file (buffer-file-name))) (org-test-with-temp-text (format ";; [[file:%s][H:1]]\n1\n;; H:1 ends here\n" (file-name-nondirectory file)) (org-babel-tangle-jump-to-org) (buffer-string)))))) ;; Special case: buffer starts with a source block. (should (equal "#+begin_src emacs-lisp\n1\n#+end_src\n* H" (org-test-with-temp-text-in-file "#+begin_src emacs-lisp\n1\n#+end_src\n* H" (let ((file (buffer-file-name))) (org-test-with-temp-text (format ";; [[file:%s][H:1]]\n1\n;; H:1 ends here\n" (file-name-nondirectory file)) (org-babel-tangle-jump-to-org) (buffer-string)))))))) (ert-deftest ob-tangle/nested-block () "Test tangling of org file with nested block." (should (string= "#+begin_src org ,#+begin_src emacs-lisp 1 ,#+end_src #+end_src " (org-test-with-temp-text-in-file "#+header: :tangle \"test-ob-tangle.org\" #+begin_src org ,#+begin_src org ,,#+begin_src emacs-lisp 1 ,,#+end_src ,#+end_src #+end_src" (unwind-protect (progn (org-babel-tangle) (with-temp-buffer (insert-file-contents "test-ob-tangle.org") (buffer-string))) (delete-file "test-ob-tangle.org")))))) (ert-deftest ob-tangle/block-order () "Test order of tangled blocks." ;; Order per language. (should (equal '("1" "2") (let ((file (make-temp-file "org-tangle-"))) (unwind-protect (progn (org-test-with-temp-text-in-file (format "#+property: header-args :tangle %S #+begin_src emacs-lisp 1 #+end_src #+begin_src emacs-lisp 2 #+end_src" file) (org-babel-tangle)) (with-temp-buffer (insert-file-contents file) (org-split-string (buffer-string)))) (delete-file file))))) ;; Order per source block. (should (equal '("1" "2") (let ((file (make-temp-file "org-tangle-"))) (unwind-protect (progn (org-test-with-temp-text-in-file (format "#+property: header-args :tangle %S #+begin_src foo 1 #+end_src #+begin_src bar 2 #+end_src" file) (org-babel-tangle)) (with-temp-buffer (insert-file-contents file) (org-split-string (buffer-string)))) (delete-file file))))) ;; Preserve order with mixed languages. (should (equal '("1" "2" "3" "4") (let ((file (make-temp-file "org-tangle-"))) (unwind-protect (progn (org-test-with-temp-text-in-file (format "#+property: header-args :tangle %S #+begin_src foo 1 #+end_src #+begin_src bar 2 #+end_src #+begin_src foo 3 #+end_src #+begin_src bar 4 #+end_src" file) (org-babel-tangle)) (with-temp-buffer (insert-file-contents file) (org-split-string (buffer-string)))) (delete-file file)))))) (ert-deftest ob-tangle/commented-src-blocks () "Test omission of commented src blocks." (should (equal '("A") (let ((file (make-temp-file "org-tangle-"))) (unwind-protect (progn (org-test-with-temp-text-in-file (format "#+property: header-args :tangle %S * A #+begin_src emacs-lisp A #+end_src * COMMENT B #+begin_src emacs-lisp B #+end_src * C # #+begin_src emacs-lisp # C # #+end_src * D #+begin_comment #+begin_src emacs-lisp D #+end_src #+end_comment" file) (org-babel-tangle)) (with-temp-buffer (insert-file-contents file) (org-split-string (buffer-string)))) (delete-file file))))) (should (equal '("A") (let ((file (make-temp-file "org-tangle-"))) (unwind-protect (progn (org-test-with-temp-text-in-file (format "#+property: header-args :tangle %S * A #+begin_src elisp :noweb yes A <> <> <> #+end_src * COMMENT B #+begin_src elisp :noweb-ref B B #+end_src * C # #+begin_src elisp :noweb-ref C # C # #+end_src * D #+begin_comment #+begin_src elisp :noweb-ref D D #+end_src #+end_comment" file) (let (org-babel-noweb-error-all-langs org-babel-noweb-error-langs) (org-babel-tangle))) (with-temp-buffer (insert-file-contents file) (org-split-string (buffer-string)))) (delete-file file)))))) (ert-deftest ob-tangle/multiple-noweb-in-line () "Test handling of multiple noweb references in a single line." (should (equal '("1" "2" "1") (let ((file (make-temp-file "org-tangle-"))) (unwind-protect (progn (org-test-with-temp-text-in-file (format " #+name: block1 #+begin_src elisp 1 #+end_src #+name: block2 #+begin_src elisp 2 #+end_src #+name: block3 #+begin_src elisp :noweb yes :tangle %s <> <> <> #+end_src" file) (let ((org-babel-noweb-error-all-langs nil) (org-babel-noweb-error-langs nil)) (org-babel-tangle))) (with-temp-buffer (insert-file-contents file) (org-split-string (buffer-string)))) (delete-file file)))))) (ert-deftest ob-tangle/strip-tangle () "Test if strip-tangle works correctly when tangling noweb code blocks." (should (equal '("1") (let ((file (make-temp-file "org-tangle-"))) (unwind-protect (progn (org-test-with-temp-text-in-file (format " #+name: block1 #+begin_src elisp 2 #+end_src #+begin_src elisp :noweb strip-tangle :tangle %s 1<> #+end_src " file) (let ((org-babel-noweb-error-all-langs nil) (org-babel-noweb-error-langs nil)) (org-babel-tangle))) (with-temp-buffer (insert-file-contents file) (org-split-string (buffer-string)))) (delete-file file)))))) (ert-deftest ob-tangle/tangle-to-self () "Do not allow tangling into self." (let ((file (make-temp-file "org-tangle-" nil ".org"))) (unwind-protect (with-current-buffer (find-file-noselect file) (insert (format " #+begin_src elisp :tangle %s 2 #+end_src " file)) (should-error (org-babel-tangle))) (delete-file file)))) (ert-deftest ob-tangle/detangle-false-positive () "Test handling of false positive link during detangle." (let (buffer) (unwind-protect (org-test-in-example-file (expand-file-name "babel.el" org-test-example-dir) (org-babel-detangle) (org-test-at-id "73115FB0-6565-442B-BB95-50195A499EF4" (setq buffer (current-buffer)) (org-babel-next-src-block) (should (equal (string-trim (org-element-property :value (org-element-at-point))) ";; detangle changes")))) (with-current-buffer buffer (set-buffer-modified-p nil)) (kill-buffer buffer)))) (ert-deftest ob-tangle/collect-blocks () "Test block collection into groups for tangling." (org-test-with-temp-text-in-file "" ; filled below, it depends on temp file name (let* ((org-file (buffer-file-name)) (test-dir (file-name-directory org-file)) (el-file-abs (concat (file-name-sans-extension org-file) ".el")) (el-file-rel (file-name-nondirectory el-file-abs))) (insert (format-spec "* H1 with :tangle in properties :PROPERTIES: :header-args: :tangle relative.el :END: #+begin_src emacs-lisp \"H1: inherited :tangle relative.el in properties\" #+end_src #+begin_src emacs-lisp :tangle yes \"H1: :tangle yes\" #+end_src #+begin_src emacs-lisp :tangle no \"H1: should be ignored\" #+end_src #+begin_src emacs-lisp :tangle %a \"H1: absolute org-file.lang-ext :tangle %a\" #+end_src #+begin_src emacs-lisp :tangle relative.el \"H1: :tangle relative.el\" #+end_src #+begin_src emacs-lisp :tangle ./relative.el \"H1: :tangle ./relative.el\" #+end_src #+begin_src emacs-lisp :tangle /tmp/absolute.el \"H1: :tangle /tmp/absolute.el\" #+end_src #+begin_src emacs-lisp :tangle ~/../../tmp/absolute.el \"H1: :tangle ~/../../tmp/absolute.el\" #+end_src * H2 without :tangle in properties #+begin_src emacs-lisp \"H2: without :tangle\" #+end_src #+begin_src emacs-lisp :tangle yes \"H2: :tangle yes\" #+end_src #+begin_src emacs-lisp :tangle no \"H2: should be ignored\" #+end_src #+begin_src emacs-lisp :tangle %r \"H2: relative org-file.lang-ext :tangle %r\" #+end_src #+begin_src emacs-lisp :tangle relative.el \"H2: :tangle relative.el\" #+end_src #+begin_src emacs-lisp :tangle ./relative.el \"H2: :tangle ./relative.el\" #+end_src #+begin_src emacs-lisp :tangle /tmp/absolute.el \"H2: :tangle /tmp/absolute.el\" #+end_src #+begin_src emacs-lisp :tangle ~/../../tmp/absolute.el \"H2: :tangle ~/../../tmp/absolute.el\" #+end_src" `((?a . ,el-file-abs) (?r . ,el-file-rel)))) ;; We check the collected blocks to tangle by counting equal ;; file names in the output of ;; `org-babel-tangle-collect-blocks'. (letrec ((sort-fn (lambda (lst) (seq-sort-by #'car #'string-lessp lst))) (normalize-expected-targets-alist (lambda (blocks-per-target-alist) "Convert to absolute file names and sort expected targets." (funcall sort-fn (map-apply (lambda (file nblocks) (cons (expand-file-name file test-dir) nblocks)) blocks-per-target-alist)))) (count-blocks-in-target-files (lambda (collected-blocks) "Get sorted alist of target file names with number of blocks in each." (funcall sort-fn (map-apply (lambda (file blocks) ;; Blocks are grouped by file name. (cons file (length blocks))) ;; From `org-babel-tangle-collect-blocks'. collected-blocks))))) (should (equal (funcall normalize-expected-targets-alist `(("/tmp/absolute.el" . 4) ("relative.el" . 5) ;; file name differs between tests (,el-file-abs . 4))) (funcall count-blocks-in-target-files (org-babel-tangle-collect-blocks)))) ;; Simulate TARGET-FILE to test as `org-babel-tangle' and ;; `org-babel-load-file' would call ;; `org-babel-tangle-collect-blocks'. (let ((org-babel-default-header-args (org-babel-merge-params org-babel-default-header-args (list (cons :tangle el-file-abs))))) (should (equal (funcall normalize-expected-targets-alist `(("/tmp/absolute.el" . 4) ("relative.el" . 5) ;; Default :tangle header now also ;; points to the file name derived from the name of ;; the Org file, so 5 blocks should go there. (,el-file-abs . 5))) (funcall count-blocks-in-target-files (org-babel-tangle-collect-blocks))))))))) (provide 'test-ob-tangle) ;;; test-ob-tangle.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ob.el000066400000000000000000002564341500430433700206120ustar00rootroot00000000000000;;; test-ob.el --- tests for ob.el -*- lexical-binding: t; -*- ;; Copyright (c) 2010-2015, 2019 Eric Schulte ;; Authors: Eric Schulte, Martyn Jago ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (require 'ob-core) (require 'org-src) (require 'ob-ref) (require 'org-table) (eval-and-compile (require 'cl-lib)) (ert-deftest test-ob/indented-cached-org-bracket-link () "When the result of a source block is a cached indented link it should still return the link." (should (let ((default-directory temporary-file-directory)) (org-test-with-temp-text " * Test #+BEGIN_SRC emacs-lisp :results file :file test.txt :cache yes (message \"test\") #+END_SRC" ;; Execute twice as the first time creates the cache. (org-babel-execute-src-block) (string= (expand-file-name "test.txt") (org-babel-execute-src-block)))))) (ert-deftest test-ob/multi-line-header-regexp () (should(equal "^[ \t]*#\\+headers?:[ \t]*\\([^\n]*\\)$" org-babel-multi-line-header-regexp)) ;;TODO can be optimised - and what about blah4 blah5 blah6? (should (string-match org-babel-multi-line-header-regexp " \t #+headers: blah1 blah2 blah3 \t\n\t\n blah4 blah5 blah6 \n")) (should (equal "blah1 blah2 blah3 \t" (match-string 1 " \t #+headers: blah1 blah2 blah3 \t\n\t\n blah4 blah5 blah6 \n"))) ;;TODO Check - should this fail? (should (not (org-test-string-exact-match org-babel-multi-line-header-regexp " \t #+headers : blah1 blah2 blah3 \t\n\t\n blah4 blah5 blah6 \n")))) (ert-deftest test-ob/src-block-regexp () (let ((test-block (concat "#+begin_src language -n-r-a-b -c :argument-1 yes :argument-2 no\n" "echo this is a test\n" "echo Currently in ' $PWD\n" "#+end_src")) (language "language") (flags "-n-r-a-b -c ") (arguments ":argument-1 yes :argument-2 no") (body "echo this is a test\necho Currently in ' $PWD\n")) (should (string-match org-babel-src-block-regexp test-block)) (should (string-match org-babel-src-block-regexp (upcase test-block))) (should (equal language (match-string 2 test-block))) ;;TODO Consider refactoring (should (equal flags (match-string 3 test-block))) (should (equal arguments (match-string 4 test-block))) (should (equal body (match-string 5 test-block))) ;;no switches (should (org-test-string-exact-match org-babel-src-block-regexp (replace-regexp-in-string flags "" test-block))) ;;no header arguments (should (org-test-string-exact-match org-babel-src-block-regexp (replace-regexp-in-string arguments "" test-block))) ;; should be valid with no body (should (org-test-string-exact-match org-babel-src-block-regexp (replace-regexp-in-string body "" test-block))))) (ert-deftest test-ob/default-inline-header-args () (should(equal '((:session . "none") (:results . "replace") (:exports . "results") (:hlines . "yes")) org-babel-default-inline-header-args))) (ert-deftest ob-test/org-babel-combine-header-arg-lists () (let ((results (org-babel-combine-header-arg-lists '((foo . :any) (bar) (baz . ((foo bar) (baz))) (qux . ((foo bar baz qux))) (quux . ((foo bar)))) '((bar) (baz . ((baz))) (quux . :any))))) (dolist (pair '((foo . :any) (bar) (baz . ((baz))) (quux . :any) (qux . ((foo bar baz qux))))) (should (equal (cdr pair) (cdr (assoc (car pair) results))))))) ;;; ob-get-src-block-info (ert-deftest test-ob/get-src-block-info-language () (org-test-at-marker nil org-test-file-ob-anchor (let ((info (org-babel-get-src-block-info))) (should (string= "emacs-lisp" (nth 0 info)))))) (ert-deftest test-ob/get-src-block-info-body () (org-test-at-marker nil org-test-file-ob-anchor (let ((info (org-babel-get-src-block-info))) (should (string-match (regexp-quote org-test-file-ob-anchor) (nth 1 info)))))) (ert-deftest test-ob/get-src-block-info-tangle () (org-test-at-marker nil org-test-file-ob-anchor (let ((info (org-babel-get-src-block-info))) (should (string= "no" (cdr (assq :tangle (nth 2 info)))))))) (ert-deftest test-ob/post-header-arguments () "When the result of a post-processing source block is an empty list, then it should be treated as such; not as the symbol nil." (should (let ((default-directory temporary-file-directory)) (org-test-with-temp-text " #+name: addheader #+header: :var rows=\"\" #+begin_src elisp :hlines yes '() #+end_src #+header: :post addheader(*this*) #+begin_src emacs-lisp :results table #+end_src #+RESULTS: : nil" (org-babel-execute-src-block) (goto-char (1- (point-max))) (equal (buffer-substring-no-properties (line-beginning-position) (line-end-position)) "#+RESULTS:"))))) (ert-deftest test-ob/elisp-in-header-arguments () "Test execution of elisp forms in header arguments." (org-test-with-temp-text-in-file " * elisp forms in header arguments :PROPERTIES: :header-args: :var prop = (* 7 6) :END: #+begin_src emacs-lisp prop #+end_src" (goto-char (point-min)) (org-babel-next-src-block) (should (= 42 (org-babel-execute-src-block))))) (ert-deftest test-ob/simple-named-code-block () "Test that simple named code blocks can be evaluated." (org-test-with-temp-text-in-file " #+name: i-have-a-name #+begin_src emacs-lisp 42 #+end_src" (org-babel-next-src-block 1) (should (= 42 (org-babel-execute-src-block))))) (ert-deftest test-ob/simple-variable-resolution () "Test that simple variable resolution is working." (org-test-with-temp-text-in-file " #+name: four #+begin_src emacs-lisp (list 1 2 3 4) #+end_src #+begin_src emacs-lisp :var four=four (length four) #+end_src" (org-babel-next-src-block 2) (should (= 4 (org-babel-execute-src-block))) (forward-line 5) (should (string= ": 4" (buffer-substring (point-at-bol) (point-at-eol))))) ;; Test reading lists. (org-test-with-temp-text-in-file " #+NAME: example-list - simple - not - nested - list #+BEGIN_SRC emacs-lisp :var x=example-list (print x) #+END_SRC" (should (equal '("simple" "list") (org-babel-execute-src-block))) (forward-line 5) (should (string= "| simple | list |" (buffer-substring (point-at-bol) (point-at-eol)))))) (ert-deftest test-ob/block-content-resolution () "Test block content resolution." (org-test-with-temp-text-in-file " #+name: four #+begin_src emacs-lisp (list 1 2 3 4) #+end_src #+begin_src emacs-lisp :var four=four[] (length (eval (car (read-from-string four)))) #+end_src" (org-babel-next-src-block 2) (should (= 4 (org-babel-execute-src-block))))) (ert-deftest test-ob/cons-cell-as-variable () "Test that cons cell can be assigned as variable." (org-test-with-temp-text " #+name: cons #+begin_src emacs-lisp (cons 1 2) #+end_src #+begin_src emacs-lisp :var x=cons x #+end_src" (org-babel-next-src-block 2) (should (equal (cons 1 2) (org-babel-execute-src-block))))) (ert-deftest test-ob/multi-line-header-arguments () "Test that multi-line header arguments and can be read." (org-test-with-temp-text-in-file " #+headers: :var letters='(a b c d e f g) #+begin_src emacs-lisp :var numbers='(1 2 3 4 5 6 7) (require 'cl-lib) (cl-map 'list #'list numbers letters) #+end_src" (org-babel-next-src-block) (let ((results (org-babel-execute-src-block))) (should (eq 'a (cadr (assoc 1 results)))) (should (eq 'd (cadr (assoc 4 results))))))) (ert-deftest test-ob/parse-header-args () (org-test-with-temp-text-in-file " #+begin_src example-lang :session :results output :var num=9 the body #+end_src" (org-babel-next-src-block) (let* ((info (org-babel-get-src-block-info)) (params (nth 2 info))) (message "%S" params) (should (equal "example-lang" (nth 0 info))) (should (string= "the body" (org-trim (nth 1 info)))) (should-not (member '(:session\ \ \ \ ) params)) (should (equal '(:session) (assq :session params))) (should (equal '(:result-type . output) (assq :result-type params))) (should (equal '(num . 9) (cdr (assq :var params))))))) (ert-deftest test-ob/parse-header-args2 () (org-test-with-temp-text-in-file " * resolving sub-trees as references #+begin_src emacs-lisp :var text=d4faa7b3-072b-4dcf-813c-dd7141c633f3 (length text) #+end_src #+begin_src org :noweb yes <> <> #+end_src ** simple subtree with custom ID :PROPERTIES: :CUSTOM_ID: simple-subtree :END: this is simple" (should (string-match (regexp-quote "this is simple") (org-babel-ref-resolve "simple-subtree"))) (org-babel-next-src-block) (should (= 14 (org-babel-execute-src-block))))) (ert-deftest test-ob/inline-src-blocks () (should (= 1 (org-test-with-temp-text "In the middle src_emacs-lisp{(+ 0 1)} of a line" (org-babel-execute-src-block)))) (should (= 2 (org-test-with-temp-text "One at the end of a line: src_emacs-lisp{(+ 1 1)}" (org-babel-execute-src-block)))) (should (= 3 (org-test-with-temp-text "src_emacs-lisp{(+ 2 1)} at the beginning of a line." (org-babel-execute-src-block)))) (should (= 4 (org-test-with-temp-text "In the middle src_emacs-lisp[:results silent\ :exports code]{(+ 3 1)} of a line" (org-babel-execute-src-block)))) (should (= 5 (org-test-with-temp-text "One at the end of a line: src_emacs-lisp[:results silent\ :exports code]{(+ 4 1)}" (org-babel-execute-src-block)))) (should (= 6 (org-test-with-temp-text "src_emacs-lisp[:results silent :exports code]{(+ 5 1)}\ at the beginning of a line." (org-babel-execute-src-block)))) (should (= 7 (org-test-with-temp-text "One also evaluated: src_emacs-lisp[:exports both\ :results silent]{(+ 6 1)}" (org-babel-execute-src-block))))) (ert-deftest test-ob/inline-src_blk-default-results-replace-line-1 () (let ((test-line "src_sh[:results output]{echo 1}") (org-babel-inline-result-wrap "=%s=")) ;; src_ at bol line 1... (org-test-with-temp-text test-line (goto-char (point-min)) (org-babel-execute-maybe) (should (string= (concat test-line " {{{results(=1=)}}}") (buffer-substring-no-properties (point-at-bol) (point-at-eol)))) (forward-char) (org-babel-execute-maybe) (should (string= (concat test-line " {{{results(=1=)}}}") (buffer-substring-no-properties (point-at-bol) (point-at-eol)))) (re-search-forward "{{{") ;;(should-error (org-ctrl-c-ctrl-c)) (backward-char 4) ;; last char of block body (org-babel-execute-maybe) (should (string= (concat test-line " {{{results(=1=)}}}") (buffer-substring-no-properties (point-at-bol) (point-at-eol))))) ;; src_ follows space line 1... (let ((test-line " src_emacs-lisp{ 1 }")) (org-test-with-temp-text test-line (should-error (org-ctrl-c-ctrl-c)) (forward-char) (org-babel-execute-maybe) (should (string= (concat test-line " {{{results(=1=)}}}") (buffer-substring-no-properties (point-at-bol) (point-at-eol)))) (re-search-forward "{ 1 ") (org-babel-execute-maybe) (should (string= (concat test-line " {{{results(=1=)}}}") (buffer-substring-no-properties (point-at-bol) (point-at-eol)))) (forward-char 6) (should-error (org-ctrl-c-ctrl-c)))) ;; Results on a subsequent line are replaced. (should (equal "src_emacs-lisp{(+ 1 2)}\n {{{results(=3=)}}}" (org-test-with-temp-text "src_emacs-lisp{(+ 1 2)}\n {{{results(=2=)}}}" (let ((org-babel-inline-result-wrap "=%s=")) (org-babel-execute-maybe)) (buffer-string)))) ;; Also handle results at the beginning of a line. (should (equal "src_emacs-lisp{(+ 1 2)}\n{{{results(=3=)}}}" (org-test-with-temp-text "src_emacs-lisp{(+ 1 2)}\n{{{results(=2=)}}}" (let ((org-babel-inline-result-wrap "=%s=")) (org-babel-execute-maybe)) (buffer-string))))) ;; Handle inline src blocks inside parsed affiliated keyword. (should (equal "#+caption: src_elisp{1} {{{results(=1=)}}}\n#+begin_src emacs-lisp\n1\n#+end_src" (org-test-with-temp-text "#+caption: src_elisp{1}\n#+begin_src emacs-lisp\n1\n#+end_src" (let ((org-babel-inline-result-wrap "=%s=")) (org-babel-execute-maybe)) (buffer-string)))) ;; Handle inline src blocks inside heading title. (should (equal "* Heading src_elisp{1} {{{results(=1=)}}}" (org-test-with-temp-text "* Heading src_elisp{1}" (let ((org-babel-inline-result-wrap "=%s=")) (org-babel-execute-maybe)) (buffer-string))))) (ert-deftest test-ob/inline-src_blk-default-results-replace-line-2 () ;; src_ at bol line 2... (let ((test-line " src_emacs-lisp{ \"x\" }") (org-babel-inline-result-wrap "=%s=")) (org-test-with-temp-text (concat "\n" test-line) (should-error (org-ctrl-c-ctrl-c)) (goto-char (point-min)) (should-error (org-ctrl-c-ctrl-c)) (forward-line) (should-error (org-ctrl-c-ctrl-c)) (forward-char) (org-babel-execute-maybe) (should (string= (concat test-line " {{{results(=x=)}}}") (buffer-substring-no-properties (point-at-bol) (point-at-eol)))))) (let ((test-line "Some text prior to block src_emacs-lisp{ \"y\" }") (org-babel-inline-result-wrap "=%s=")) (org-test-with-temp-text test-line (goto-char (point-max)) (insert (concat "\n" test-line " end")) (re-search-backward "src") (org-babel-execute-maybe) (should (string= (concat test-line " {{{results(=y=)}}} end") (buffer-substring-no-properties (point-at-bol) (point-at-eol)))) (re-search-forward "\" ") (org-babel-execute-maybe) (should (string= (concat test-line " {{{results(=y=)}}} end") (buffer-substring-no-properties (point-at-bol) (point-at-eol)))) (forward-char 3) (should-error (org-ctrl-c-ctrl-c))))) (ert-deftest test-ob/inline-src_blk-manual-results-replace () (let ((test-line " src_emacs-lisp[:results replace]{ \"x\" }") (org-babel-inline-result-wrap "=%s=")) (org-test-with-temp-text (concat "\n" test-line) (should-error (org-ctrl-c-ctrl-c)) (goto-char (point-max)) (org-babel-execute-maybe) (beginning-of-line) (should-error (org-ctrl-c-ctrl-c)) (forward-char) (org-babel-execute-maybe) (should (string= (concat test-line " {{{results(=x=)}}}") (buffer-substring-no-properties (point-at-bol) (point-at-eol)))))) (let ((test-line (concat " Some text prior to block " "src_emacs-lisp[:results replace]{ \"y\" }")) (org-babel-inline-result-wrap "=%s=")) (org-test-with-temp-text test-line (goto-char (point-max)) (insert (concat "\n" test-line " end")) (re-search-backward "src") (org-babel-execute-maybe) (should (string= (concat test-line " {{{results(=y=)}}} end") (buffer-substring-no-properties (point-at-bol) (point-at-eol)))) (re-search-forward "\" ") (org-babel-execute-maybe) (should (string= (concat test-line " {{{results(=y=)}}} end") (buffer-substring-no-properties (point-at-bol) (point-at-eol)))) (forward-char 3) (should-error (org-ctrl-c-ctrl-c))))) (ert-deftest test-ob/inline-src_blk-results-silent () (let ((test-line "src_emacs-lisp[ :results silent ]{ \"x\" }")) (org-test-with-temp-text test-line (org-babel-execute-maybe) (should (string= test-line (buffer-substring-no-properties (point-at-bol) (point-at-eol)))))) (let ((test-line (concat " Some text prior to block src_emacs-lisp" "[ :results silent ]{ \"y\" }"))) (org-test-with-temp-text test-line (goto-char (point-max)) (insert (concat "\n" test-line " end")) (re-search-backward "src_") (org-babel-execute-maybe) (should (string= (concat test-line " end") (buffer-substring-no-properties (point-at-bol) (point-at-eol)))) (re-search-forward "\" ") (org-babel-execute-maybe) (should (string= (concat test-line " end") (buffer-substring-no-properties (point-at-bol) (point-at-eol)))) (forward-char 2) (should-error (org-ctrl-c-ctrl-c))))) (ert-deftest test-ob/inline-src_blk-results-raw () (let ((test-line "src_emacs-lisp[ :results raw ]{ \"x\" }")) (org-test-with-temp-text test-line (org-babel-execute-maybe) (should (string= (concat test-line " x") (buffer-string))))) (let ((test-line (concat " Some text prior to block " "src_emacs-lisp[ :results raw ]{ \"the\" }"))) (org-test-with-temp-text (concat test-line " end") (re-search-forward "src_") (org-babel-execute-maybe) (should (string= (concat test-line " the end") (buffer-substring-no-properties (point-at-bol) (point-at-eol)))) (re-search-forward "\" ") (org-babel-execute-maybe) (should (string= (concat test-line " the the end") (buffer-substring-no-properties (point-at-bol) (point-at-eol)))) (forward-char 2) (should-error (org-ctrl-c-ctrl-c))))) (ert-deftest test-ob/inline-src_blk-results-file () (let ((test-line "src_emacs-lisp[ :results file ]{ \"~/test-file\" }")) (org-test-with-temp-text test-line (org-babel-execute-maybe) (should (string= (concat test-line " {{{results([[file:~/test-file]])}}}") (buffer-substring-no-properties (point-min) (point-max))))))) (ert-deftest test-ob/inline-src_blk-results-scalar () (let ((test-line "src_emacs-lisp[ :results scalar ]{ \"x\" }") (org-babel-inline-result-wrap "=%s=")) (org-test-with-temp-text test-line (org-babel-execute-maybe) (should (string= (concat test-line " {{{results(=\"x\"=)}}}") (buffer-substring-no-properties (point-min) (point-max))))))) (ert-deftest test-ob/inline-src_blk-results-verbatim () (let ((test-line "src_emacs-lisp[ :results verbatim ]{ \"x\" }") (org-babel-inline-result-wrap "=%s=")) (org-test-with-temp-text test-line (org-babel-execute-maybe) (should (string= (concat test-line " {{{results(=\"x\"=)}}}") (buffer-substring-no-properties (point-min) (point-max))))))) (ert-deftest test-ob/inline-src_blk-wrap () (let ((org-babel-inline-result-wrap "=%s=")) ;; Export: use export snippet. (should (string-match-p "@@foo:1@@" (org-test-with-temp-text "src_emacs-lisp[:wrap export foo]{1}" (org-babel-execute-maybe) (buffer-string)))) (should (string-match-p "src_foo{1}" (org-test-with-temp-text "src_emacs-lisp[:wrap src foo]{1}" (org-babel-execute-maybe) (buffer-string)))) (should (string-match-p "src_foo\\[parameter\\]{1}" (org-test-with-temp-text "src_emacs-lisp[:wrap src foo parameter]{1}" (org-babel-execute-maybe) (buffer-string)))) (should (string-match-p "=1=" (org-test-with-temp-text "src_emacs-lisp[:wrap example]{1}" (org-babel-execute-maybe) (buffer-string)))) ;; Anything else is ignored. (should (string-match-p "{{{results(1)}}}" (org-test-with-temp-text "src_emacs-lisp[:wrap foo]{1}" (org-babel-execute-maybe) (buffer-string)))) (should (string-match-p "{{{results(a\\\\,b)}}}" (org-test-with-temp-text "src_emacs-lisp[:wrap foo]{\"a,b\"}" (org-babel-execute-maybe) (buffer-string)))))) (ert-deftest test-ob/combining-scalar-and-raw-result-types () (org-test-with-temp-text-in-file " #+begin_src sh :results scalar echo \"[[file:./cv.cls]]\" #+end_src #+name: : [[file:./cv.cls]] #+begin_src sh :results raw scalar echo \"[[file:./cv.cls]]\" #+end_src " (cl-flet ((next-result () (org-babel-next-src-block) (org-babel-execute-src-block) (goto-char (org-babel-where-is-src-block-result)) (forward-line 1))) (goto-char (point-min)) (next-result) (should (eq (org-element-type (org-element-at-point)) 'fixed-width)) (next-result) (should-not (eq (org-element-type (org-element-at-point)) 'fixed-width))))) (ert-deftest test-ob/no-defaut-value-for-var () "Test that the absence of a default value for a variable DOES THROW a proper error." (org-test-at-id "f2df5ba6-75fa-4e6b-8441-65ed84963627" (org-babel-next-src-block) (let ((err (should-error (org-babel-execute-src-block) :type 'error))) (should (equal '(error "Variable \"x\" must be assigned a default value") err))))) (ert-deftest test-ob/just-one-results-block () "Test that evaluating two times the same code block does not result in a duplicate results block." (org-test-with-temp-text "#+begin_src sh :results output\necho Hello\n#+end_src\n" (org-babel-execute-src-block) (org-babel-execute-src-block) ; second code block execution (should (search-forward "Hello")) ; the string inside the source code block (should (search-forward "Hello")) ; the same string in the results block (should-error (search-forward "Hello")))) (ert-deftest test-ob/nested-code-block () "Test nested code blocks inside code blocks don't cause problems." (should (string= "#+begin_src emacs-lisp\n 'foo\n#+end_src" (org-test-with-temp-text "#+begin_src org :results silent ,#+begin_src emacs-lisp 'foo ,#+end_src #+end_src" (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation nil)) (org-babel-execute-src-block)))))) (ert-deftest test-ob/partial-nested-code-block () "Test nested code blocks inside code blocks don't cause problems." (org-test-with-temp-text "#+begin_src org :results silent ,#+begin_src emacs-lisp #+end_src" (should (string= "#+begin_src emacs-lisp" (org-babel-execute-src-block))))) (ert-deftest test-ob/does-not-replace-a-block-with-the-results () (org-test-with-temp-text "#+NAME: foo #+BEGIN_SRC emacs-lisp 'foo #+END_SRC\n" (org-babel-next-src-block 1) (should (eq 'foo (org-babel-execute-src-block))) (goto-char (point-min)) (org-babel-next-src-block 1) (should (looking-at org-babel-src-block-regexp)))) (ert-deftest test-ob/replace-special-block-result () (should-error (org-test-with-temp-text " #+begin_src emacs-lisp :wrap special 'foo #+end_src" (org-babel-execute-src-block) (org-babel-execute-src-block) (buffer-string) (search-forward "#+begin_special" nil nil 2)))) (ert-deftest test-ob/catches-all-references () (org-test-with-temp-text " #+NAME: literal-example #+BEGIN_EXAMPLE A literal example on two lines #+END_EXAMPLE #+NAME: read-literal-example #+BEGIN_SRC emacs-lisp :var x=literal-example (cl-concatenate 'string x \" for me.\") #+END_SRC" (org-babel-next-src-block 1) (should (string= (org-babel-execute-src-block) "A literal example\non two lines\n for me.")))) (ert-deftest test-ob/ignore-reference-in-commented-headings () (should (= 2 (org-test-with-temp-text " * COMMENT H1 #+NAME: n : 1 * H2 #+NAME: n : 2 * Code #+BEGIN_SRC emacs-lisp :var x=n x #+END_SRC" (org-babel-execute-src-block))))) (ert-deftest test-ob/do-not-resolve-to-partial-names-data () (org-test-with-temp-text " #+name: base_plus | 1 | | 2 | #+name: base | 3 | | 4 | #+begin_src emacs-lisp :var x=base x #+end_src" (org-babel-next-src-block 1) (should (equal (org-babel-execute-src-block) '((3) (4)))))) (ert-deftest test-ob/do-not-resolve-to-partial-names-code () (org-test-with-temp-text " #+name: base_plus #+begin_src emacs-lisp 'bar #+end_src #+name: base #+begin_src emacs-lisp 'foo #+end_src #+begin_src emacs-lisp :var x=base x #+end_src" (org-babel-next-src-block 3) (should (equal (org-babel-execute-src-block) "foo")))) (ert-deftest test-ob/allow-spaces-around-=-in-var-specs () (org-test-with-temp-text "#+begin_src emacs-lisp :var a = 1 b = 2 c= 3 d =4 (+ a b c d) #+end_src " (should (= 10 (org-babel-execute-src-block))))) (ert-deftest test-ob/org-babel-update-intermediate () (org-test-with-temp-text "#+name: foo #+begin_src emacs-lisp 2 #+end_src #+results: foo : 4 #+begin_src emacs-lisp :var it=foo (+ it 1) #+end_src" (let ((org-babel-update-intermediate nil)) (goto-char (point-min)) (org-babel-next-src-block 2) (should (= 3 (org-babel-execute-src-block))) (goto-char (point-min)) (forward-line 6) (should (looking-at ": 4"))) (let ((org-babel-update-intermediate t)) (goto-char (point-min)) (org-babel-next-src-block 2) (should (= 3 (org-babel-execute-src-block))) (goto-char (point-min)) (forward-line 6) (should (looking-at ": 2"))))) (ert-deftest test-ob/eval-header-argument () (defvar test-ob--foo) (cl-flet ((check-eval (eval runp) (org-test-with-temp-text (format "#+begin_src emacs-lisp :eval %s (setq test-ob--foo :evald) #+end_src" eval) (let ((test-ob--foo :not-run)) (if runp (progn (should (org-babel-execute-src-block)) (should (eq test-ob--foo :evald))) (progn (should-not (org-babel-execute-src-block)) (should-not (eq test-ob--foo :evald)))))))) (check-eval "never" nil) (check-eval "no" nil) (check-eval "never-export" t) (check-eval "no-export" t) (let ((org-babel-exp-reference-buffer (current-buffer))) (check-eval "never" nil) (check-eval "no" nil) (check-eval "never-export" nil) (check-eval "no-export" nil)))) (ert-deftest test-ob/noweb-expansion () ;; Standard test. (should (string= "bar" (org-test-with-temp-text "#+begin_src sh :results output :tangle yes <> #+end_src #+name: foo #+begin_src sh bar #+end_src" (org-babel-expand-noweb-references)))) ;; Handle :noweb-sep. (should (string= "barbaz" (org-test-with-temp-text "#+begin_src sh :results output :tangle yes <> #+end_src #+begin_src sh :noweb-ref foo :noweb-sep \"\" bar #+end_src #+begin_src sh :noweb-ref foo :noweb-sep \"\" baz #+end_src" (org-babel-expand-noweb-references)))) ;; :noweb-ref is extracted from definition, not point of call. (should (string= "(+ 1 1)" (org-test-with-temp-text " * Call :PROPERTIES: :header-args: :noweb-ref bar :END: #+begin_src emacs-lisp :results output :tangle yes <> #+end_src * Evaluation :PROPERTIES: :header-args: :noweb-ref foo :END: #+begin_src sh :noweb-sep \"\" (+ 1 1) #+end_src" (org-babel-expand-noweb-references)))) ;; Handle recursive expansion. (should (equal "baz" (org-test-with-temp-text " #+begin_src emacs-lisp :noweb yes <> #+end_src #+name: foo #+begin_src emacs-lisp :noweb yes <> #+end_src #+name: bar #+begin_src emacs-lisp baz #+end_src" (org-babel-expand-noweb-references)))) ;; During recursive expansion, obey to `:noweb' property. (should (equal "<>" (org-test-with-temp-text " #+begin_src emacs-lisp :noweb yes <> #+end_src #+name: foo #+begin_src emacs-lisp :noweb no <> #+end_src #+name: bar #+begin_src emacs-lisp baz #+end_src" (org-babel-expand-noweb-references)))) ;; Respect COMMENT headlines (should (equal "C" (org-test-with-temp-text " #+begin_src emacs-lisp :noweb yes <> #+end_src * COMMENT A #+name: foo #+begin_src emacs-lisp A #+end_src * COMMENT B #+begin_src emacs-lisp :noweb-ref foo B #+end_src * C #+begin_src emacs-lisp :noweb-ref foo C #+end_src" (org-babel-expand-noweb-references)))) ;; Preserve case when replacing Noweb reference. (should (equal "(ignore)" (org-test-with-temp-text " #+begin_src emacs-lisp :noweb-ref AA \(ignore) #+end_src #+begin_src emacs-lisp :noweb yes <> #+end_src" (org-babel-expand-noweb-references)))) ;; Test :noweb-ref expansion. (should (equal "(message \"!! %s\" \"Running confpkg-test-setup\") (message \"- Ran `%s'\" 'confpkg-test-strip-package-statements) (message \"!! %s\" \"Still running confpkg-test-setup\") (message \"- Ran elisp blocks in `%s'\" 'confpkg-test-dependency-analysis) (message \"!! %s\" \"End of confpkg-test-setup\")" (org-test-with-temp-text " * Setup #+name: confpkg-test-setup #+begin_src emacs-lisp :results silent :noweb no-export (message \"!! %s\" \"Running confpkg-test-setup\") <> (message \"!! %s\" \"Still running confpkg-test-setup\") <> (message \"!! %s\" \"End of confpkg-test-setup\") #+end_src #+call: confpkg-test-setup[:results none]() * Identify cross-package dependencies #+begin_src emacs-lisp :noweb-ref confpkg-test-dependency-analysis (message \"- Ran elisp blocks in `%s'\" 'confpkg-test-dependency-analysis) #+end_src * Commenting out ~package!~ statements #+name: confpkg-test-strip-package-statements #+begin_src emacs-lisp (message \"- Ran `%s'\" 'confpkg-test-strip-package-statements) #+end_src " (goto-char (point-min)) (search-forward "begin_src") (org-babel-expand-noweb-references))))) (ert-deftest test-ob/splitting-variable-lists-in-references () (org-test-with-temp-text "" (should (= 1 (length (org-babel-ref-split-args "a=\"this, no work\"")))) (should (= 2 (length (org-babel-ref-split-args "a=\"this, no work\", b=1")))))) (ert-deftest test-ob/balanced-split () "Test `org-babel-balanced-split' specifications." (should (equal '(":a 1" "b [2 3]" "c (4 :d (5 6))") (org-babel-balanced-split ":a 1 :b [2 3] :c (4 :d (5 6))" '((32 9) . 58)))) ;; Handle un-balanced parens. (should (equal '(":foo ((6)" "bar 1") (org-babel-balanced-split ":foo ((6) :bar 1" '((32 9) . 58)))) (should (equal '(":foo \"(foo\"" "bar 2") (org-babel-balanced-split ":foo \"(foo\" :bar 2" '((32 9) . 58)))) ;; Handle un-balanced quotes. (should (equal '(":foo \"1" "bar 3") (org-babel-balanced-split ":foo \"1 :bar 3" '((32 9) . 58)))) ;; Handle empty string. (should (equal '(":foo \"\"") (org-babel-balanced-split ":foo \"\"" '((32 9) . 58)))) ;; Handle control characters within double quotes. (should (equal '(":foo \"\\n\"") (org-babel-balanced-split ":foo \"\\n\"" '((32 9) . 58))))) (ert-deftest test-ob/commented-last-block-line-no-var () (org-test-with-temp-text-in-file " #+begin_src emacs-lisp ;; #+end_src" (org-babel-next-src-block) (org-babel-execute-maybe) (should (re-search-forward "\\#\\+results:" nil t)) (forward-line) (should (string= "" (buffer-substring-no-properties (point-at-bol) (point-at-eol))))) (org-test-with-temp-text-in-file " #+begin_src emacs-lisp \"some text\";; #+end_src" (org-babel-next-src-block) (org-babel-execute-maybe) (should (re-search-forward "\\#\\+results:" nil t)) (forward-line) (should (string= ": some text" (buffer-substring-no-properties (point-at-bol) (point-at-eol)))))) (ert-deftest test-ob/commented-last-block-line-with-var () (org-test-with-temp-text-in-file " #+begin_src emacs-lisp :var a=1 ;; #+end_src" (org-babel-next-src-block) (org-babel-execute-maybe) (re-search-forward "\\#\\+results:" nil t) (forward-line) (should (string= "" (buffer-substring-no-properties (point-at-bol) (point-at-eol))))) (org-test-with-temp-text-in-file " #+begin_src emacs-lisp :var a=2 2;; #+end_src" (org-babel-next-src-block) (org-babel-execute-maybe) (re-search-forward "\\#\\+results:" nil t) (forward-line) (should (string= ": 2" (buffer-substring-no-properties (point-at-bol) (point-at-eol)))))) (ert-deftest test-ob/org-babel-insert-result () "Test `org-babel-insert-result' specifications." ;; Do not error when output is an improper list. (should (org-test-with-temp-text " #+BEGIN_SRC emacs-lisp '((1 . nil) (2 . 3)) #+END_SRC " (org-babel-execute-maybe) t)) ;; Escape headlines when producing an example block. (should (string-match-p ",\\* Not an headline" (org-test-with-temp-text " #+BEGIN_SRC emacs-lisp \"* Not an headline\" #+END_SRC " (let ((org-babel-min-lines-for-block-output 1)) (org-babel-execute-maybe)) (buffer-string)))) ;; Escape special syntax in example blocks. (should (string-match-p ",#\\+END_SRC" (org-test-with-temp-text " #+BEGIN_SRC emacs-lisp \"#+END_SRC\" #+END_SRC " (let ((org-babel-min-lines-for-block-output 1)) (org-babel-execute-maybe)) (buffer-string)))) ;; No escaping is done with other blocks or raw type. (should-not (string-match-p ",\\* Not an headline" (org-test-with-temp-text " #+BEGIN_SRC emacs-lisp \"* Not an headline\" #+END_SRC " (let ((org-babel-min-lines-for-block-output 10)) (org-babel-execute-maybe)) (buffer-string)))) (should-not (string-match-p ",\\* Not an headline" (org-test-with-temp-text " #+BEGIN_SRC emacs-lisp :results raw \"* Not an headline\" #+END_SRC " (org-babel-execute-maybe) (buffer-string)))) (should-not (string-match-p ",\\* Not an headline" (org-test-with-temp-text " #+BEGIN_SRC emacs-lisp :results drawer \"* Not an headline\" #+END_SRC " (org-babel-execute-maybe) (buffer-string))))) (ert-deftest test-ob/remove-inline-result () "Test `org-babel-remove-inline-result' honors whitespace." (let* ((inline-sb "src_emacs-lisp{(+ 1 2)}") (inline-res " {{{results(=3=)}}}") (inline-sb-dot (concat inline-sb ".")) (inline-sb-res-dot (concat inline-sb inline-res "."))) (org-test-with-temp-text ;; Insert inline_src_block followed by dot. inline-sb-dot ;; Insert result before dot. (org-babel-execute-maybe) (should (string= inline-sb-res-dot (buffer-substring-no-properties (point-at-bol) (point-at-eol)))) ;; Delete whitespace and result. (org-babel-remove-inline-result) (should (string= inline-sb-dot (buffer-substring-no-properties (point-at-bol) (point-at-eol)))) ;; Add whitespace and result before dot. (search-forward inline-sb) (insert " " inline-res) (goto-char (point-at-bol)) ;; Remove whitespace and result. (org-babel-remove-inline-result) (should (string= inline-sb-dot (buffer-substring-no-properties (point-at-bol) (point-at-eol)))) ;; Add whitespace before dot. (search-forward inline-sb) (insert " ") (goto-char (point-at-bol)) ;; Add result before whitespace. (org-babel-execute-maybe) ;; Remove result - leave trailing whitespace and dot. (org-babel-remove-inline-result) (should (string= (concat inline-sb " .") (buffer-substring-no-properties (point-at-bol) (point-at-eol))))))) (ert-deftest test-ob/org-babel-remove-result--results-default () "Test `org-babel-remove-result' with default :results." (mapcar (lambda (language) (test-ob-verify-result-and-removed-result "\n" (concat "* org-babel-remove-result #+begin_src " language " #+end_src * next heading"))) '("emacs-lisp"))) (ert-deftest test-ob/org-babel-results-indented-wrap () "Ensure that wrapped results are inserted correction when indented. If not inserted correctly then the second evaluation will fail trying to find the :END: marker." (org-test-with-temp-text "- indented #+begin_src sh :results file wrap echo test.txt #+end_src" (org-babel-next-src-block 1) (org-babel-execute-src-block) (org-babel-execute-src-block))) (ert-deftest test-ob/org-babel-results-indented-list () "Test that :results value list indents multi-line items correctly." (should (string= "- Foo1 Bar1 - Foo2 Bar2 " (org-test-with-temp-text "#+begin_src emacs-lisp :results value list '(\"Foo1 Bar1\" \"Foo2 Bar2\") #+end_src" (org-babel-execute-src-block) (org-forward-element) (org-narrow-to-element) (delete-trailing-whitespace) (buffer-string))))) (ert-deftest test-ob/file-desc-header-argument () "Test that the :file-desc header argument is used." (org-test-with-temp-text "#+begin_src emacs-lisp :results file :file-desc bar \"foo\" #+end_src #+begin_src emacs-lisp :results file :file-desc \"foo\" #+end_src" (org-babel-execute-src-block) (org-babel-next-src-block 1) (org-babel-execute-src-block) (goto-char (point-min)) (should (search-forward "[[file:foo][bar]]" nil t)) (should (search-forward "[[file:foo][foo]]" nil t))) (should (string-match-p (regexp-quote "[[file:foo]]") (org-test-with-temp-text " #+begin_src emacs-lisp :results file :file-desc [] \"foo\" #+end_src" (org-babel-next-src-block) (org-babel-execute-src-block) (buffer-substring-no-properties (point-min) (point-max))))) (should (string-match-p (regexp-quote "[[file:foo][foo]]") (org-test-with-temp-text " #+begin_src emacs-lisp :results file :file-desc \"foo\" #+end_src" (org-babel-next-src-block) (org-babel-execute-src-block) (buffer-substring-no-properties (point-min) (point-max)))))) (ert-deftest test-ob/result-file-link-type-header-argument () "Ensure that the result is a link to a file. The file is just a link to `:file' value. Inhibit non-empty result write to `:file' value." (org-test-with-temp-text " #+begin_src shell :results value file link :file \"/tmp/test.txt\" echo \"hello\" > /tmp/test.txt echo \"test\" #+end_src" (org-babel-execute-src-block) (should (search-forward "[[file:/tmp/test.txt]]" nil t)) (should (with-temp-buffer (insert-file-contents "/tmp/test.txt") (string= "hello\n" (buffer-string))))) ;; Without "link" output type, the result is not a file. (should-not (org-test-with-temp-text " #+begin_src shell :results value link :file \"/tmp/test.txt\" echo \"hello\" > /tmp/test.txt echo \"test\" #+end_src" (org-babel-execute-src-block) (search-forward "[[file:/tmp/test.txt]]" nil t)))) (ert-deftest test-ob/result-graphics-link-type-header-argument () "Ensure that the result is a link to a file. The file is just a link to `:file' value. Inhibit non-empty result write to `:file' value." (org-test-with-temp-text " #+begin_src shell :results value file graphics :file \"/tmp/test.txt\" echo \"hello\" > /tmp/test.txt echo \"test\" #+end_src" (org-babel-execute-src-block) (should (search-forward "[[file:/tmp/test.txt]]" nil nil)) (should (with-temp-buffer (insert-file-contents "/tmp/test.txt") (string= "hello\n" (buffer-string))))) ;; Without "link" output type, the result is not a file. (should-not (org-test-with-temp-text " #+begin_src shell :results value graphics :file \"/tmp/test.txt\" echo \"hello\" > /tmp/test.txt echo \"test\" #+end_src" (org-babel-execute-src-block) (search-forward "[[file:/tmp/test.txt]]" nil t)))) (ert-deftest test-ob/inline-src_blk-preceded-punct-preceded-by-point () (let ((test-line ".src_emacs-lisp[ :results verbatim ]{ \"x\" }") (org-babel-inline-result-wrap "=%s=")) (org-test-with-temp-text test-line (forward-char 1) (org-babel-execute-maybe) (should (re-search-forward "=\"x\"=" nil t)) (forward-line)))) (defun test-ob-verify-result-and-removed-result (result buffer-text) "Test helper function to test `org-babel-remove-result'. A temp buffer is populated with BUFFER-TEXT, the first block is executed, and the result of execution is verified against RESULT. The block is actually executed /twice/ to ensure result replacement happens correctly." (org-test-with-temp-text buffer-text (org-babel-next-src-block) (org-babel-execute-maybe) (org-babel-execute-maybe) (should (re-search-forward "\\#\\+results:" nil t)) (forward-line) (should (string= result (buffer-substring-no-properties (point-at-bol) (- (point-max) 16)))) (org-babel-previous-src-block) (org-babel-remove-result) (should (string= buffer-text (buffer-substring-no-properties (point-min) (point-max)))))) (ert-deftest test-ob/org-babel-remove-result--results-list () "Test `org-babel-remove-result' with :results list." (test-ob-verify-result-and-removed-result "- 1 - 2 - 3" "* org-babel-remove-result #+begin_src emacs-lisp :results list '(1 2 3) #+end_src * next heading")) (ert-deftest test-ob/org-babel-remove-result--results-wrap () "Test `org-babel-remove-result' with :results wrap." (test-ob-verify-result-and-removed-result ":results: hello there :end:" "* org-babel-remove-result #+begin_src emacs-lisp :results wrap \"hello there\" #+end_src * next heading")) (ert-deftest test-ob/org-babel-remove-result--results-org () "Test `org-babel-remove-result' with :results org." (test-ob-verify-result-and-removed-result "#+begin_src org ,* heading ,** subheading content #+end_src" "* org-babel-remove-result #+begin_src emacs-lisp :results org \"* heading ,** subheading content\" #+end_src * next heading")) (ert-deftest test-ob/org-babel-remove-result--results-html () "Test `org-babel-remove-result' with :results html." (test-ob-verify-result-and-removed-result "#+begin_export html #+end_export" "* org-babel-remove-result #+begin_src emacs-lisp :results html \"\" #+end_src * next heading")) (ert-deftest test-ob/org-babel-remove-result--results-latex () "Test `org-babel-remove-result' with :results latex." (test-ob-verify-result-and-removed-result "#+begin_export latex Line 1 Line 2 Line 3 #+end_export" "* org-babel-remove-result #+begin_src emacs-lisp :results latex \"Line 1 Line 2 Line 3\" #+end_src * next heading")) (ert-deftest test-ob/org-babel-remove-result--results-code () "Test `org-babel-remove-result' with :results code." (test-ob-verify-result-and-removed-result "#+begin_src emacs-lisp \"I am working!\" #+end_src" "* org-babel-remove-result #+begin_src emacs-lisp :results code (message \"I am working!\") #+end_src * next heading")) (ert-deftest test-ob/org-babel-remove-result--results-pp () "Test `org-babel-remove-result' with :results pp." (test-ob-verify-result-and-removed-result ": \"I /am/ working!\"" "* org-babel-remove-result #+begin_src emacs-lisp :results pp \"I /am/ working!\") #+end_src * next heading")) (ert-deftest test-ob/org-babel-remove-result--no-blank-line () "Test `org-babel-remove-result' without blank line between code and results." (should (equal " #+begin_src emacs-lisp (+ 1 1) #+end_src #+results: : 2 * next heading" (org-test-with-temp-text " #+begin_src emacs-lisp (+ 1 1) #+end_src #+results: : 2 * next heading" (org-babel-execute-maybe) (buffer-string))))) (ert-deftest test-ob/results-do-not-replace-code-blocks () (org-test-with-temp-text "Block two has a space after the name. #+name: foo #+begin_src emacs-lisp 1 #+end_src #+name: foo #+begin_src emacs-lisp 2 #+end_src #+name: foo #+begin_src emacs-lisp 3 #+end_src #+RESULTS: foo : foo " (dolist (num '(1 2 3)) ;; execute the block (goto-char (point-min)) (org-babel-next-src-block num) (org-babel-execute-src-block) ;; check the results (goto-char (point-max)) (move-beginning-of-line 0) (should (looking-at (format ": %d" num)))))) (ert-deftest test-ob/blocks-with-spaces () "Test expansion of blocks followed by blank lines." ;; Preserve number of blank lines after block. (should (equal "#+BEGIN_SRC emacs-lisp \(+ 1 2) #+END_SRC #+RESULTS: : 3\n\n\n" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp \(+ 1 2) #+END_SRC\n\n\n" (let ((org-babel-results-keyword "RESULTS")) (org-babel-execute-src-block)) (buffer-string)))) ;; Do not add spurious blank lines after results. (should (equal " - item 1 #+begin_src emacs-lisp 0 #+end_src #+RESULTS: : 0 - item 2" (org-test-with-temp-text " - item 1 #+begin_src emacs-lisp 0 #+end_src - item 2" (org-babel-execute-src-block) (buffer-string)))) (should (equal " - item 1 #+begin_src emacs-lisp 1 #+end_src #+RESULTS: : 1 - item 2" (org-test-with-temp-text " - item 1 #+begin_src emacs-lisp 1 #+end_src #+RESULTS: : 1 - item 2" (org-babel-execute-src-block) (buffer-string))))) (ert-deftest test-ob/results-in-narrowed-buffer () "Test block execution in a narrowed buffer." ;; If results don't exist, they should be inserted in visible part ;; of the buffer. (should (equal "#+BEGIN_SRC emacs-lisp\n(+ 1 2)\n#+END_SRC\n\n#+RESULTS:\n: 3" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n(+ 1 2)\n#+END_SRC\n\nParagraph" (narrow-to-region (point) (save-excursion (forward-line 3) (point))) (let ((org-babel-results-keyword "RESULTS")) (org-babel-execute-src-block)) (org-trim (buffer-string))))) (should (equal "#+NAME: test\n#+BEGIN_SRC emacs-lisp\n(+ 1 2)\n#+END_SRC\n\n#+RESULTS: test\n: 3" (org-test-with-temp-text "#+NAME: test\n#+BEGIN_SRC emacs-lisp\n(+ 1 2)\n#+END_SRC\n\nParagraph" (narrow-to-region (point) (save-excursion (forward-line 4) (point))) (let ((org-babel-results-keyword "RESULTS")) (org-babel-execute-src-block)) (org-trim (buffer-string))))) ;; Results in visible part of buffer, should be updated here. (should (equal "#+NAME: test #+BEGIN_SRC emacs-lisp \(+ 1 2) #+END_SRC #+RESULTS: test : 3" (org-test-with-temp-text "#+NAME: test #+BEGIN_SRC emacs-lisp \(+ 1 2) #+END_SRC #+RESULTS: test : 4 Paragraph" (narrow-to-region (point-min) (point)) (goto-char (point-min)) (let ((org-babel-results-keyword "RESULTS")) (org-babel-execute-src-block)) (org-trim (buffer-string))))) ;; Results in invisible part of buffer, should be updated there. (org-test-with-temp-text "#+NAME: test #+BEGIN_SRC emacs-lisp \(+ 1 2) #+END_SRC #+RESULTS: test : 4 Paragraph" (narrow-to-region (point) (save-excursion (forward-line 4) (point))) (let ((org-babel-results-keyword "RESULTS")) (org-babel-execute-src-block)) (should-not (re-search-forward "^#\\+RESULTS:" nil t)) (widen) (should (re-search-forward "^: 3" nil t)))) (ert-deftest test-ob/specific-colnames () "Test passing specific column names." (should (equal "#+name: input-table | id | var1 | |----+------| | 1 | bar | | 2 | baz | #+begin_src sh :var data=input-table :results table :exports results :colnames '(Rev Author) echo \"$data\" #+end_src #+RESULTS: | Rev | Author | |-----+--------| | 1 | bar | | 2 | baz | " (org-test-with-temp-text "#+name: input-table | id | var1 | |----+------| | 1 | bar | | 2 | baz | #+begin_src sh :var data=input-table :results table :exports results :colnames '(Rev Author) echo \"$data\" #+end_src " ;; we should find a code block (should (re-search-forward org-babel-src-block-regexp nil t)) (goto-char (match-beginning 0)) ;; now that we've located the code block, it may be evaluated (let ((org-babel-results-keyword "RESULTS")) (org-babel-execute-src-block)) (buffer-string))))) (ert-deftest test-ob/location-of-header-arg-eval () "Test location of header argument evaluation." (org-test-with-temp-text " #+name: top-block #+begin_src emacs-lisp :var pt=(point) pt #+end_src #+name: bottom-block #+begin_src emacs-lisp :var pt=top-block() pt #+end_src " ;; the value of the second block should be greater than the first (should (< (progn (re-search-forward org-babel-src-block-regexp nil t) (goto-char (match-beginning 0)) (prog1 (save-match-data (org-babel-execute-src-block)) (goto-char (match-end 0)))) (progn (re-search-forward org-babel-src-block-regexp nil t) (goto-char (match-beginning 0)) (org-babel-execute-src-block)))))) (ert-deftest test-ob/preserve-results-indentation () "Preserve indentation when executing a source block." (should (equal '(2 2) (org-test-with-temp-text " #+begin_src emacs-lisp\n(+ 1 1)\n #+end_src" (org-babel-execute-src-block) (let ((case-fold-search t)) (search-forward "RESULTS")) (list (current-indentation) (progn (forward-line) (current-indentation)))))) (should (equal '(2 2) (org-test-with-temp-text " #+name: block\n #+begin_src emacs-lisp\n(+ 1 1)\n #+end_src" (org-babel-execute-src-block) (let ((case-fold-search t)) (search-forward "RESULTS")) (list (current-indentation) (progn (forward-line) (current-indentation)))))) ;; Don't get fooled by TAB-based indentation. (should (equal '(6 6) (org-test-with-temp-text "\t #+begin_src emacs-lisp\n\t (+ 1 1)\n\t #+end_src" (setq tab-width 4) (org-babel-execute-src-block) (let ((case-fold-search t)) (search-forward "RESULTS")) (list (current-indentation) (progn (forward-line) (current-indentation)))))) ;; Properly indent examplified blocks. (should (equal " #+begin_example 0 1 2 3 4 5 6 7 8 9 #+end_example " (org-test-with-temp-text " #+begin_src emacs-lisp :results output (dotimes (i 10) (princ i) (princ \"\\n\")) #+end_src" (org-babel-execute-src-block) (search-forward "begin_example") (downcase (buffer-substring-no-properties (line-beginning-position) (point-max)))))) ;; Properly indent "org" blocks. (should (equal " #+begin_src org 0 1 2 3 4 5 6 7 8 9 #+end_src " (org-test-with-temp-text " #+begin_src emacs-lisp :results output org (dotimes (i 10) (princ i) (princ \"\\n\")) #+end_src" (org-babel-execute-src-block) (search-forward "begin_src org") (downcase (buffer-substring-no-properties (line-beginning-position) (point-max))))))) (ert-deftest test-ob/preserve-comma-escape () "Preserve comma escapes when inserting results." (should (equal "#+begin_example line 1 ,* headline 2 ,* headline 3 ,* headline 4 ,* headline 5 #+end_example " (org-test-with-temp-text "#+begin_src emacs-lisp :wrap example \"line 1 ,* headline 2 ,* headline 3 ,* headline 4 ,* headline 5 \" #+end_src " (org-babel-execute-src-block) (let ((case-fold-search t)) (search-forward "result" nil t)) (downcase (buffer-substring-no-properties (line-beginning-position 2) (point-max)))))) (should (string-match-p ",#" (org-test-with-temp-text "#+begin_src emacs-lisp :wrap export foo \"#+keyword: value\" #+end_src" (org-babel-execute-src-block) (buffer-string)))) (should (string-match-p ",#" (org-test-with-temp-text "#+begin_src emacs-lisp :wrap src foo \"#+keyword: value\" #+end_src" (org-babel-execute-src-block) (buffer-string)))) ;; Do not comma-escape when the block is not verbatim. (should-not (string-match-p ",#" (org-test-with-temp-text "#+begin_src emacs-lisp :wrap special \"#+keyword: value\" #+end_src" (org-babel-execute-src-block) (buffer-string))))) (ert-deftest test-ob/safe-header-args () "Detect safe and unsafe header args." (let ((safe-args '((:cache . "foo") (:results . "output") (:eval . "never") (:eval . "query"))) (unsafe-args '((:eval . "yes") (:results . "output file") (:foo . "bar"))) (malformed-args '((bar . "foo") ("foo" . "bar") :foo)) (safe-p (org-babel-header-args-safe-fn org-babel-safe-header-args))) (dolist (arg safe-args) (should (org-babel-one-header-arg-safe-p arg org-babel-safe-header-args))) (dolist (arg unsafe-args) (should (not (org-babel-one-header-arg-safe-p arg org-babel-safe-header-args)))) (dolist (arg malformed-args) (should (not (org-babel-one-header-arg-safe-p arg org-babel-safe-header-args)))) (should (not (funcall safe-p (append safe-args unsafe-args)))))) (ert-deftest test-ob/noweb-expansions-in-cache () "Ensure that noweb expansions are expanded before caching." (defvar noweb-expansions-in-cache-var) (let ((noweb-expansions-in-cache-var 0)) (org-test-with-temp-text " #+name: foo #+begin_src emacs-lisp \"I said\" #+end_src #+name: bar #+begin_src emacs-lisp :noweb yes :cache yes (setq noweb-expansions-in-cache-var (+ 1 noweb-expansions-in-cache-var)) (concat <> \" check noweb expansions\") #+end_src " ;; run the second block to create the cache (goto-char (point-min)) (re-search-forward (regexp-quote "#+name: bar")) (should (string= "I said check noweb expansions" (org-babel-execute-src-block))) (should (= noweb-expansions-in-cache-var 1)) ;; change the value of the first block (goto-char (point-min)) (re-search-forward (regexp-quote "said")) (goto-char (match-beginning 0)) (insert "haven't ") (re-search-forward (regexp-quote "#+name: bar")) (should (string= "I haven't said check noweb expansions" (org-babel-execute-src-block))) (should (= noweb-expansions-in-cache-var 2))))) (ert-deftest test-ob/file-ext-and-output-dir () (org-test-at-id "93573e1d-6486-442e-b6d0-3fedbdc37c9b" (org-babel-next-src-block) (should (equal "file-ext-basic.txt" (cdr (assq :file (nth 2 (org-babel-get-src-block-info t)))))) (org-babel-next-src-block) (should (equal "foo/file-ext-dir-relative.txt" (cdr (assq :file (nth 2 (org-babel-get-src-block-info t)))))) (org-babel-next-src-block) (should (equal "foo/file-ext-dir-relative-slash.txt" (cdr (assq :file (nth 2 (org-babel-get-src-block-info t)))))) (org-babel-next-src-block) (should (equal "/tmp/file-ext-dir-absolute.txt" (cdr (assq :file (nth 2 (org-babel-get-src-block-info t)))))) (org-babel-next-src-block) (should (equal "foo.bar" (cdr (assq :file (nth 2 (org-babel-get-src-block-info t)))))) (org-babel-next-src-block) (should (equal "xxx/foo.bar" (cdr (assq :file (nth 2 (org-babel-get-src-block-info t)))))) )) (ert-deftest test-ob/file-mode () "Ensure that :file-mode results in expected permissions." (should (equal #o755 (org-test-with-temp-text-in-file " #+begin_src emacs-lisp :results file :file t.sh :file-mode (identity #o755) nil #+end_src" (org-babel-next-src-block) (org-babel-execute-src-block) (unwind-protect (file-modes "t.sh") (delete-file "t.sh")))))) (ert-deftest test-ob-core/dir-attach () "Test :dir header using special 'attach value" (should (org-test-with-temp-text-in-file "* 'attach Symbol #+begin_src elisp :dir 'attach :results file (with-temp-file \"test.txt\" (insert \"attachment testing\n\")) \"test.txt\" #+end_src" (org-id-get-create) (org-babel-execute-src-block) (goto-char (org-babel-where-is-src-block-result)) (forward-line) (and (file-exists-p (format "%s/test.txt" (org-attach-dir nil t))) (string= (buffer-substring-no-properties (point) (line-end-position)) "[[attachment:test.txt]]")))) (should (org-test-with-temp-text-in-file "* 'attach String #+begin_src elisp :dir \"'attach\" :results file (with-temp-file \"test.txt\" (insert \"attachment testing\n\")) \"test.txt\" #+end_src" (org-id-get-create) (org-babel-execute-src-block) (goto-char (org-babel-where-is-src-block-result)) (forward-line) (and (file-exists-p (format "%s/test.txt" (org-attach-dir nil t))) (string= (buffer-substring-no-properties (point) (line-end-position)) "[[attachment:test.txt]]")))) (should (org-test-with-temp-text-in-file "* 'attach with Existing DIR property :PROPERTIES: :DIR: custom-attach-dir :END: #+begin_src elisp :dir 'attach :results file (with-temp-file \"test.txt\" (insert \"attachment testing\n\")) \"test.txt\" #+end_src" (message "DIR: %s" (org-attach-dir t)) (org-babel-execute-src-block) (goto-char (org-babel-where-is-src-block-result)) (forward-line) (and (file-exists-p (format "%s/test.txt" (org-attach-dir nil t))) (string= (buffer-substring-no-properties (point) (line-end-position)) "[[attachment:test.txt]]")))) ;; Strip attach dir from the file path. (should (org-test-with-temp-text-in-file "* heading :PROPERTIES: :DIR: custom-attach-dir :END: #+begin_src elisp :results value file \"custom-attach-dir/test.txt\" #+end_src" (message "DIR: %s" (org-attach-dir t)) (org-babel-execute-src-block) (goto-char (org-babel-where-is-src-block-result)) (forward-line) (string= (buffer-substring-no-properties (point) (line-end-position)) "[[attachment:test.txt]]"))) (should-error (org-test-with-temp-text-in-file "* 'attach with no ID or DIR #+begin_src elisp :dir 'attach :results file (with-temp-file \"test.txt\" (insert \"attachment testing\n\")) \"test.txt\" #+end_src" (org-babel-execute-src-block) (goto-char (org-babel-where-is-src-block-result)) (forward-line) (and (file-exists-p (format "%s/test.txt" (org-attach-dir nil t))) (string= (buffer-substring-no-properties (point) (line-end-position)) "[[attachment:test.txt]]"))))) (ert-deftest test-ob-core/dir-mkdirp () "Test :mkdirp with :dir header combination." (should-not (org-test-with-temp-text-in-file "#+begin_src emacs-lisp :dir \"data/code\" t #+end_src" (org-babel-execute-src-block) (message default-directory) (file-directory-p "data/code"))) (should-not (org-test-with-temp-text-in-file "#+begin_src emacs-lisp :mkdirp no :dir \"data/code\" t #+end_src" (org-babel-execute-src-block) (message default-directory) (file-directory-p "data/code"))) (should (org-test-with-temp-text-in-file "#+begin_src emacs-lisp :mkdirp yes :dir \"data/code\" t #+end_src" (org-babel-execute-src-block) (message default-directory) (prog1 (file-directory-p "data/code") (delete-directory "data" t)))) (should (equal "/tmp/test-dir-no-mkdirp/" (org-test-with-temp-text-in-file "#+begin_src emacs-lisp :dir /tmp/test-dir-no-mkdirp default-directory #+end_src" (org-babel-execute-src-block))))) (ert-deftest test-ob/script-escape () ;; Empty list. (should (equal nil (org-babel-script-escape "[]"))) (should (equal nil (org-babel-script-escape "()"))) (should (equal nil (org-babel-script-escape "'()"))) (should (equal nil (org-babel-script-escape "{}"))) ;; Delimited lists of numbers (should (equal '(1 2 3) (org-babel-script-escape "[1 2 3]"))) (should (equal '(1 2 3) (org-babel-script-escape "{1 2 3}"))) (should (equal '(1 2 3) (org-babel-script-escape "(1 2 3)"))) ;; Delimited lists of double-quoted strings (should (equal '("foo" "bar") (org-babel-script-escape "(\"foo\" \"bar\")"))) (should (equal '("foo" "bar") (org-babel-script-escape "[\"foo\" \"bar\"]"))) (should (equal '("foo" "bar") (org-babel-script-escape "{\"foo\" \"bar\"}"))) ;; ... with commas (should (equal '("foo" "bar") (org-babel-script-escape "(\"foo\", \"bar\")"))) (should (equal '("foo" "bar") (org-babel-script-escape "[\"foo\", \"bar\"]"))) (should (equal '("foo" "bar") (org-babel-script-escape "{\"foo\", \"bar\"}"))) ;; Delimited lists of single-quoted strings (should (equal '("foo" "bar") (org-babel-script-escape "('foo' 'bar')"))) (should (equal '("foo" "bar") (org-babel-script-escape "['foo' 'bar']"))) (should (equal '("foo" "bar") (org-babel-script-escape "{'foo' 'bar'}"))) ;; ... with commas (should (equal '("foo" "bar") (org-babel-script-escape "('foo', 'bar')"))) (should (equal '("foo" "bar") (org-babel-script-escape "['foo', 'bar']"))) (should (equal '("foo" "bar") (org-babel-script-escape "{'foo', 'bar'}"))) ;; Single quoted strings (should (equal "foo" (org-babel-script-escape "'foo'"))) ;; ... with internal double quote (should (equal "foo\"bar" (org-babel-script-escape "'foo\"bar'"))) ;; ... with internal backslash (should (equal "foo\\bar" (org-babel-script-escape "'foo\\bar'"))) ;; ... with internal escaped backslash (should (equal "foo\\bar" (org-babel-script-escape "'foo\\\\bar'"))) ;; ... with internal backslash-double quote (should (equal "foo\\\"bar" (org-babel-script-escape "'foo\\\"bar'"))) ;; ... with internal escaped backslash-double quote (should (equal "foo\\\"bar" (org-babel-script-escape "'foo\\\\\"bar'"))) ;; ... with internal escaped single quote (should (equal "foo'bar" (org-babel-script-escape "'foo\\'bar'"))) ;; ... with internal escaped backslash-escaped single quote (should (equal "foo\\'bar" (org-babel-script-escape "'foo\\\\\\'bar'"))) ;; Double quoted strings (should (equal "foo" (org-babel-script-escape "\"foo\""))) ;; ... with internal single quote (should (equal "foo'bar" (org-babel-script-escape "\"foo'bar\""))) ;; ... with internal backslash (should (equal "foo\\bar" (org-babel-script-escape "\"foo\\bar\""))) ;; ... with internal escaped backslash (should (equal "foo\\bar" (org-babel-script-escape "\"foo\\\\bar\""))) ;; ... with internal backslash-single quote (should (equal "foo\\'bar" (org-babel-script-escape "\"foo\\'bar\""))) ;; ... with internal escaped backslash-single quote (should (equal "foo\\'bar" (org-babel-script-escape "\"foo\\\\'bar\""))) ;; ... with internal escaped double quote (should (equal "foo\"bar" (org-babel-script-escape "\"foo\\\"bar\""))) ;; ... with internal escaped backslash-escaped double quote (should (equal "foo\\\"bar" (org-babel-script-escape "\"foo\\\\\\\"bar\"")))) (ert-deftest test-ob/process-params-no-duplicates () (should (equal (org-babel-process-params '((:colname-names) (:rowname-names) (:result-params) (:result-type) (:var . "\"foo\""))) '((:var) (:colname-names) (:rowname-names) (:result-params) (:result-type . value))))) (defun org-test-babel-confirm-evaluate (eval-value) (org-test-with-temp-text (format "#+begin_src emacs-lisp :eval %s nil #+end_src" eval-value) (goto-char (point-min)) (let ((info (org-babel-get-src-block-info))) (org-babel-check-confirm-evaluate info)))) (ert-deftest test-ob/check-eval () (let ((org-confirm-babel-evaluate t)) ;; Non-export tests (dolist (pair '(("no" . nil) ("never" . nil) ("query" . query) ("yes" . query))) (should (eq (org-test-babel-confirm-evaluate (car pair)) (cdr pair)))) ;; Export tests (let ((org-babel-exp-reference-buffer t)) (dolist (pair '(("no" . nil) ("never" . nil) ("query" . query) ("yes" . query) ("never-export" . nil) ("no-export" . nil) ("query-export" . query))) (message (car pair)) (should (eq (org-test-babel-confirm-evaluate (car pair)) (cdr pair)))))) (let ((org-confirm-babel-evaluate nil)) ;; Non-export tests (dolist (pair '(("no" . nil) ("never" . nil) ("query" . query) ("yes" . t))) (should (eq (org-test-babel-confirm-evaluate (car pair)) (cdr pair)))) ;; Export tests (let ((org-babel-exp-reference-buffer t)) (dolist (pair '(("no" . nil) ("never" . nil) ("query" . query) ("yes" . t) ("never-export" . nil) ("no-export" . nil) ("query-export" . query))) (message (car pair)) (should (eq (org-test-babel-confirm-evaluate (car pair)) (cdr pair))))))) (ert-deftest test-ob/check-eval-noweb-expanded () "`org-confirm-babel-evaluate' function receives expanded noweb refs." (should (equal t (org-test-with-temp-text " #+name: foo #+begin_src emacs-lisp :bar #+end_src #+begin_src emacs-lisp :noweb yes <> #+end_src" (let ((org-confirm-babel-evaluate (lambda (_ body) (not (string-match-p ":bar" body))))) (org-babel-check-confirm-evaluate (org-babel-get-src-block-info)))))) ;; The code block passed to `org-confirm-babel-evaluate' does not ;; include coderefs. (should (equal t (org-test-with-temp-text " #+name: foo #+begin_src emacs-lisp :bar #+end_src #+begin_src emacs-lisp :noweb yes #(ref:foo) <> #+end_src" (let ((org-coderef-label-format "#(ref:%s)") (org-confirm-babel-evaluate (lambda (_ body) (string-match-p "ref:foo" body)))) (org-babel-check-confirm-evaluate (org-babel-get-src-block-info))))))) (defun org-test-ob/update-block-body () "Test `org-babel-update-block-body' specifications." (should (equal "#+begin_src elisp\n 2\n#+end_src" (let ((org-edit-src-content-indentation 2)) (org-test-with-temp-text "#+begin_src elisp\n(+ 1 1)\n#+end_src" (org-babel-update-block-body "2") (buffer-string))))) ;; Preserve block indentation. (should (equal " #+begin_src elisp\n 2\n #+end_src" (let ((org-edit-src-content-indentation 1)) (org-test-with-temp-text " #+begin_src elisp\n (+ 1 1)\n #+end_src" (org-babel-update-block-body "2") (buffer-string))))) ;; Ignore NEW-BODY global indentation. (should (equal "#+begin_src elisp\n 2\n#+end_src" (let ((org-edit-src-content-indentation 2)) (org-test-with-temp-text "#+begin_src elisp\n(+ 1 1)\n#+end_src" (org-babel-update-block-body " 2") (buffer-string))))) ;; When indentation should be preserved ignore the two rules above. (should (equal " #+begin_src elisp\n2\n #+end_src" (let ((org-edit-src-content-indentation 1) (org-src-preserve-indentation t)) (org-test-with-temp-text " #+begin_src elisp\n (+ 1 1)\n #+end_src" (org-babel-update-block-body "2") (buffer-string))))) (should (equal " #+begin_src elisp -i\n2\n #+end_src" (let ((org-edit-src-content-indentation 1)) (org-test-with-temp-text " #+begin_src elisp -i\n (+ 1 1)\n #+end_src" (org-babel-update-block-body "2") (buffer-string))))) (should (equal "#+begin_src elisp\n 2\n#+end_src" (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation t)) (org-test-with-temp-text "#+begin_src elisp\n(+ 1 1)\n#+end_src" (org-babel-update-block-body " 2") (buffer-string))))) (should (equal "#+begin_src elisp -i\n 2\n#+end_src" (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation t)) (org-test-with-temp-text "#+begin_src elisp -i\n(+ 1 1)\n#+end_src" (org-babel-update-block-body " 2") (buffer-string)))))) (ert-deftest test-ob/find-named-result () "Test `org-babel-find-named-result' specifications." (should (= 1 (org-test-with-temp-text "#+results: foo\n: result" (org-babel-find-named-result "foo")))) (should-not (org-test-with-temp-text "#+results: foo\n: result" (org-babel-find-named-result "bar"))) (should-not (org-test-with-temp-text "#+results: foobar\n: result" (org-babel-find-named-result "foo"))) ;; Search is case insensitive. (should (org-test-with-temp-text "#+RESULTS: FOO\n: result" (org-babel-find-named-result "foo"))) ;; Handle hash in results keyword. (should (org-test-with-temp-text "#+results[hash]: FOO\n: result" (org-babel-find-named-result "foo"))) ;; Accept orphaned affiliated keywords. (should (org-test-with-temp-text "#+results: foo" (org-babel-find-named-result "foo")))) (ert-deftest test-ob/where-is-src-block-result () "Test `org-babel-where-is-src-block-result' specifications." ;; Find anonymous results. (should (equal "#+RESULTS:" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n(+ 1 1)\n#+END_SRC\n\n#+RESULTS:\n: 2" (goto-char (org-babel-where-is-src-block-result)) (buffer-substring-no-properties (point) (line-end-position))))) ;; Find named results. Those have priority over anonymous ones. (should (equal "#+RESULTS: example" (org-test-with-temp-text " #+NAME: example #+BEGIN_SRC emacs-lisp \(+ 1 1) #+END_SRC #+RESULTS: example : 2" (goto-char (org-babel-where-is-src-block-result)) (buffer-substring-no-properties (point) (line-end-position))))) (should (equal "#+RESULTS: example" (org-test-with-temp-text " #+NAME: example #+BEGIN_SRC emacs-lisp \(+ 1 1) #+END_SRC #+RESULTS: : fake #+RESULTS: example : 2" (goto-char (org-babel-where-is-src-block-result)) (buffer-substring-no-properties (point) (line-end-position))))) ;; Return nil when no result is found. (should-not (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n(+ 1 1)\n#+END_SRC" (org-babel-where-is-src-block-result))) (should-not (org-test-with-temp-text "- item\n #+BEGIN_SRC emacs-lisp\n(+ 1 1)\n#+END_SRC\n\n" (org-babel-where-is-src-block-result))) ;; When optional argument INSERT is non-nil, add RESULTS keyword ;; whenever no RESULTS can be found. (should (equal "#+RESULTS:" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n(+ 1 1)\n#+END_SRC" (let ((org-babel-results-keyword "RESULTS")) (goto-char (org-babel-where-is-src-block-result t))) (buffer-substring-no-properties (point) (line-end-position))))) ;; Insert a named RESULTS keyword if possible. (should (equal "#+RESULTS: e" (org-test-with-temp-text "#+NAME: e\n#+BEGIN_SRC emacs-lisp\n(+ 1 1)\n#+END_SRC" (let ((org-babel-results-keyword "RESULTS")) (goto-char (org-babel-where-is-src-block-result t))) (buffer-substring-no-properties (point) (line-end-position))))) ;; When optional argument HASH is provided, clear RESULTS keyword ;; and related contents if they do not match it. (should (equal "#+RESULTS[bbbb]:" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n(+ 1 1)\n#+END_SRC\n\n#+RESULTS[aaaa]:\n: 3" (let ((org-babel-results-keyword "RESULTS")) (goto-char (org-babel-where-is-src-block-result nil nil "bbbb"))) (org-trim (buffer-substring-no-properties (point) (point-max)))))) (should (equal "#+RESULTS[bbbb]:" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n(+ 1 1)\n#+END_SRC\n\n#+RESULTS[aaaa]:" (let ((org-babel-results-keyword "RESULTS")) (goto-char (org-babel-where-is-src-block-result nil nil "bbbb"))) (org-trim (buffer-substring-no-properties (point) (point-max)))))) ;; Handle hashes with times. (should (equal "#+RESULTS[(2014-03-04 00:41:10) bbbb]:" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp \(+ 1 1) #+END_SRC #+RESULTS[(2012-03-29 16:40:12) aaaa]:" (let ((org-babel-results-keyword "RESULTS") (org-babel-hash-show-time t)) (cl-letf (((symbol-function 'format-time-string) (lambda (&rest _) "(2014-03-04 00:41:10)"))) (goto-char (org-babel-where-is-src-block-result nil nil "bbbb")) (org-trim (buffer-substring-no-properties (point) (point-max)))))))) (should (equal "#+RESULTS[(2012-03-29 16:40:12) aaaa]:" (org-test-with-temp-text " #+BEGIN_SRC emacs-lisp \(+ 1 1) #+END_SRC #+RESULTS[(2012-03-29 16:40:12) aaaa]:" (let ((org-babel-results-keyword "RESULTS") (org-babel-hash-show-time t)) (cl-letf (((symbol-function 'format-time-string) (lambda (&rest _) "(2014-03-04 00:41:10)"))) (goto-char (org-babel-where-is-src-block-result nil nil "aaaa")) (org-trim (buffer-substring-no-properties (point) (point-max)))))))) ;; RESULTS keyword may not be the last affiliated keyword. (should (equal "#+RESULTS[bbbb]:" (org-test-with-temp-text " #+BEGIN_SRC emacs-lisp \(+ 1 1) #+END_SRC #+RESULTS[aaaa]: #+NAME: e : 3" (let ((org-babel-results-keyword "RESULTS")) (goto-char (org-babel-where-is-src-block-result nil nil "bbbb"))) (org-trim (buffer-substring-no-properties (point) (point-max)))))) ;; HASH does nothing if no RESULTS can be found. However, if INSERT ;; is also non-nil, RESULTS keyword is inserted along with the ;; expected hash. (should (equal "#+BEGIN_SRC emacs-lisp\n(+ 1 1)\n#+END_SRC" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n(+ 1 1)\n#+END_SRC" (org-babel-where-is-src-block-result nil nil "bbbb") (buffer-string)))) (should (equal "#+RESULTS[bbbb]:" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n(+ 1 1)\n#+END_SRC" (let ((org-babel-results-keyword "RESULTS")) (goto-char (org-babel-where-is-src-block-result t nil "bbbb"))) (org-trim (buffer-substring-no-properties (point) (point-max))))))) (ert-deftest test-ob/goto-named-src-block () "Test interactive use of `org-babel-goto-named-src-block'." (org-test-with-temp-text-in-file " #+NAME: abc #+BEGIN_SRC emacs-lisp :results value (1+ 1) #+END_SRC #+CALL: abc( lorem() ) :results raw :wrap EXAMPLE #+BEGIN_SRC emacs-lisp <> #+END_SRC abc #+RESULTS: abc : 2 " ;; non-existent name (should-not (execute-kbd-macro "\M-xorg-babel-goto-named-src-block\nno-name\n")) ;; correct name (execute-kbd-macro "\M-xorg-babel-goto-named-src-block\nabc\n") (should (= 14 (point))) ;; call line - autocompletion (forward-line 3) (execute-kbd-macro "\M-xorg-babel-goto-named-src-block\n\n") (should (= 14 (point))) ;; noweb reference - autocompletion (forward-line 5) (execute-kbd-macro "\M-xorg-babel-goto-named-src-block\n\n") (should (= 14 (point))) ;; at symbol - autocompletion (forward-line 7) (execute-kbd-macro "\M-xorg-babel-goto-named-src-block\n\n") (should (= 14 (point))) ;; in results - autocompletion (forward-line 8) (execute-kbd-macro "\M-xorg-babel-goto-named-src-block\n\n") (should (= 14 (point))) (forward-line 9) (execute-kbd-macro "\M-xorg-babel-goto-named-src-block\n\n") (should (= 14 (point))))) (ert-deftest test-ob/evaluate-body-with-coderefs () (should (= 2 (org-test-with-temp-text "#+begin_src emacs-lisp -l \"#(ref:%s)\"\n2 #(ref:foo)\n#+end_src" (org-babel-execute-src-block)))) (should (= 3 (org-test-with-temp-text "#+begin_src emacs-lisp\n3 #(ref:foo)\n#+end_src" (let ((org-coderef-label-format "#(ref:%s)")) (org-babel-execute-src-block)))))) (ert-deftest test-ob/string-to-number () (should (= 0 (org-babel--string-to-number "0"))) (should (= 1 (org-babel--string-to-number "1"))) (should (eq nil (org-babel--string-to-number "1 2"))) (should (= 1000.0 (org-babel--string-to-number "1e3"))) (should (eq 0 (org-babel--string-to-number "000"))) (should (eq 1 (org-babel--string-to-number "001"))) (should (eq 10 (org-babel--string-to-number "010"))) (should (= 100 (org-babel--string-to-number "100"))) (should (= 0.1 (org-babel--string-to-number "0.1"))) (should (= 1.0 (org-babel--string-to-number "1.0")))) (ert-deftest test-ob/import-elisp-from-file () "Test `org-babel-import-elisp-from-file'." (should (equal (org-test-with-temp-text-in-file "line 1\nline 2\n" (cl-letf (((symbol-function 'display-warning) (lambda (&rest _) (error "No warnings should occur")) (org-table-convert-region-max-lines 2))) (org-babel-import-elisp-from-file (buffer-file-name)))) '(("line" 1) ("line" 2)))) ;; If an error occurs during table conversion, it is shown with ;; `display-warning' rather than as a message to make sure the ;; caller sees it. (should-error (org-test-with-temp-text-in-file "line 1\nline 2\n" (cl-letf (((symbol-function 'display-warning) (lambda (&rest _) (error "Warning should be displayed"))) (org-table-convert-region-max-lines 1)) (org-babel-import-elisp-from-file (buffer-file-name))))) ;; But an empty file (as is the case when there are no execution ;; results) does not trigger a warning. (should-not (org-test-with-temp-text-in-file "" (cl-letf (((symbol-function 'display-warning) (lambda (&rest _) (error "No warnings should occur")))) (org-babel-import-elisp-from-file (buffer-file-name)))))) (ert-deftest test-ob/org-babel-read () "Test `org-babel-read' specifications." (dolist (inhibit '(t nil)) ;; A number (should (equal 1 (org-babel-read "1" inhibit))) (should (equal -1 (org-babel-read "-1" inhibit))) (should (equal 1.2 (org-babel-read "1.2" inhibit))) ;; Allow whitespace (should (equal 1 (org-babel-read " 1 " inhibit))) (should (equal 1 (org-babel-read " 1\n" inhibit))) ;; Not a number (should-not (equal 1 (org-babel-read "1foo" inhibit))) ;; Empty string (should (equal "" (org-babel-read "" inhibit))) (should (equal " " (org-babel-read " " inhibit))) ;; Elisp function call (should (equal (if inhibit ;; Verbatim string, with spaces "(+ 1 2) " ;; Result of evaluation 3) (org-babel-read "(+ 1 2) " inhibit))) ;; Elisp function call must start from ( (should-not (equal 3 (org-babel-read " (+ 1 2)" nil))) (should (equal (if inhibit "'(1 2)" ;; Result of evaluation '(1 2)) (org-babel-read "'(1 2)" inhibit))) ;; `(...) (should (equal (if inhibit "`(1 ,(+ 1 2))" ;; Result of evaluation '(1 3)) (org-babel-read "`(1 ,(+ 1 2))" inhibit))) ;; [...] (should (equal (if inhibit "[1 2 (foo)]" ;; Result of evaluation [1 2 (foo)]) (org-babel-read "[1 2 (foo)]" inhibit))) ;; Special case: *this* literal is evaluated (defvar *this* nil) (let ((*this* 100)) (should (equal (if inhibit "*this*" 100) (org-babel-read "*this*" inhibit)))) ;; Special case: data inside quotes (should (equal "foo" (org-babel-read " \"foo\" " inhibit))) (should (equal "foo" (org-babel-read " \"foo\"\n" inhibit))) (should (equal "foo with\" inside" (org-babel-read " \"foo with\\\" inside\" " inhibit))) (should (equal "abc\nsdf" (org-babel-read "\"abc\nsdf\"" inhibit))) (should (equal "foo" (org-babel-read "\"foo\"" inhibit))) (should (equal "\"foo\"(\"bar\"" (org-babel-read "\"foo\"(\"bar\"" inhibit))) ;; Unpaired quotes (should (equal "\"foo\"\"bar\"" (org-babel-read "\"foo\"\"bar\"" inhibit))) ;; Recover from `read' parsing errors. (org-babel-read "\"Quoted closing quote:\\\"" inhibit))) (ert-deftest test-ob/demarcate-block-split-duplication () "Test duplication of language, body, switches, and headers in splitting." (let ((caption "#+caption: caption.") (regexp (rx "#+caption: caption.")) (org-adapt-indentation nil)) (org-test-with-temp-text (format " %s #+header: :var edge=\"also duplicated\" #+header: :wrap \"src any-spanish -n\" #+name: Nobody #+begin_src any-english -i -n :var here=\"duplicated\" :wrap \"src any-english -n\" above split below split #+end_src do not org-indent-block text here " caption) (let ((wrap-val "src any-spanish -n") above below avars bvars) (org-babel-demarcate-block) (goto-char (point-min)) (org-babel-next-src-block) ;; upper source block (setq above (org-babel-get-src-block-info)) (setq avars (org-babel--get-vars (nth 2 above))) (org-babel-next-src-block) ;; lower source block (setq below (org-babel-get-src-block-info)) (setq bvars (org-babel--get-vars (nth 2 below))) ;; duplicated multi-line header arguments: (should (string= "also duplicated" (cdr (assq 'edge avars)))) (should (string= "also duplicated" (cdr (assq 'edge bvars)))) (should (string= wrap-val (cdr (assq :wrap (nth 2 above))))) (should (string= wrap-val (cdr (assq :wrap (nth 2 below))))) ;; duplicated language, other header arguments, and switches: (should (string= "any-english" (nth 0 above))) (should (string= "any-english" (nth 0 below))) (should (string= "above split" (org-trim (nth 1 above)))) (should (string= "below split" (org-trim (nth 1 below)))) (should (string= "duplicated" (cdr (assq 'here avars)))) (should (string= "duplicated" (cdr (assq 'here bvars)))) (should (string= "-i -n" (nth 3 above))) (should (string= "-i -n" (nth 3 below))) ;; non-duplication of name and caption, which is not in above/below. (should (string= "Nobody" (nth 4 above))) (should-not (string= "" (nth 4 below))) (goto-char (point-min)) (should (re-search-forward regexp)) (should-not (re-search-forward regexp nil 'noerror)))))) (ert-deftest test-ob/demarcate-block-split-prefix-point () "Test prefix argument point splitting." (let ((org-adapt-indentation t) (org-edit-src-content-indentation 2) (org-src-preserve-indentation nil) (ok-col 11) (stars "^\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*")) (org-test-with-temp-text " ********** 10 stars with point between two lines #+begin_src emacs-lisp ;; to upper block ;; to lower block #+end_src " (org-babel-demarcate-block 'a-prefix-arg) (goto-char (point-min)) (dolist (regexp `(,stars "#\\+beg" ";; to upper block" "#\\+end" ,stars "#\\+beg" ";; to lower block" "#\\+end")) (should (re-search-forward regexp)) (goto-char (match-beginning 0)) (cond ((string= regexp stars) (should (= 0 (current-column)))) ((string-prefix-p ";;" regexp) (should (= (+ ok-col org-edit-src-content-indentation) (current-column)))) (t (should (= ok-col (current-column))))))))) (ert-deftest test-ob/demarcate-block-split-prefix-region () "Test prefix argument region splitting." (let ((org-adapt-indentation t) (org-edit-src-content-indentation 2) (org-src-preserve-indentation nil) (ok-col 11) (stars "^\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*") (parts '("to upper block" "mark those words as region" "to lower block"))) (org-test-with-temp-text (format " ********** 10 stars with region between two lines #+header: :var b=\"also seen\" #+begin_src any-language -i -n :var a=\"seen\" %s %s %s #+end_src " (nth 0 parts) (nth 1 parts) (nth 2 parts)) (let ((n 0) info vars) (transient-mark-mode 1) (push-mark (point) t t) (search-forward (nth 1 parts)) (org-babel-demarcate-block 'a-prefix-argument) (goto-char (point-min)) (while (< n (length parts)) (org-babel-next-src-block) (setq info (org-babel-get-src-block-info)) (setq vars (org-babel--get-vars (nth 2 info))) (should (string= "any-language" (nth 0 info))) (should (string= (nth n parts) (org-trim (nth 1 info)))) (should (string= "seen" (cdr (assq 'a vars)))) (should (string= "also seen" (cdr (assq 'b vars)))) (should (string= "-i -n" (nth 3 info))) (cl-incf n))) (goto-char (point-min)) (dolist (regexp `(,stars "#\\+beg" ,(nth 0 parts) "#\\+end" ,stars "#\\+beg" ,(nth 1 parts) "#\\+end" ,stars "#\\+beg" ,(nth 2 parts) "#\\+end")) (should (re-search-forward regexp)) (goto-char (match-beginning 0)) (cond ((string= regexp stars) (should (= 0 (current-column)))) ((memq regexp parts) (should (= (+ ok-col org-edit-src-content-indentation) (current-column)))) (t (should (= ok-col (current-column))))))))) (ert-deftest test-ob/demarcate-block-split-user-errors () "Test for `user-error's in splitting" (let ((org-adapt-indentation t) (org-edit-src-content-indentation 2) (org-src-preserve-indentation)) (let* ((caption "#+caption: caption.") (within-body ";; within-body") (below-block "# below block") (template " %s%s #+begin_src emacs-lisp %s #+end_src %s%s ")) ;; Test point at caption. (org-test-with-temp-text (format template "" caption within-body below-block "") (should-error (org-babel-demarcate-block) :type 'user-error)) ;; Test region from below the block (mark) to within the body (point). (org-test-with-temp-text (format template "" caption within-body below-block "") ;; Set mark. (transient-mark-mode 1) (push-mark (point) t t) ;; Set point. (should (search-backward within-body nil 'noerror)) (goto-char (match-beginning 0)) (should-error (org-babel-demarcate-block) :type 'user-error))))) (ert-deftest test-ob/demarcate-block-wrap-point () "Test wrapping point in blank lines below a source block." (org-test-with-temp-text " #+begin_src any-language -i -n :var here=\"not duplicated\" to upper block #+end_src " (let (info vars) (org-babel-demarcate-block) (goto-char (point-min)) (org-babel-next-src-block) (setq info (org-babel-get-src-block-info)) ;; upper source block info (setq vars (org-babel--get-vars (nth 2 info))) (should (string= "any-language" (nth 0 info))) (should (string= "to upper block" (org-trim (nth 1 info)))) (should (string= "not duplicated" (cdr (assq 'here vars)))) (should (string= "-i -n" (nth 3 info))) (org-babel-next-src-block) (setq info (org-babel-get-src-block-info)) ;; lower source block info (setq vars (org-babel--get-vars (nth 2 info))) (should (string= "any-language" (nth 0 info))) (should (string= "" (org-trim (nth 1 info)))) (should-not vars) (should (string= "" (nth 3 info)))))) (ert-deftest test-ob/demarcate-block-wrap-region () "Test wrapping region in blank lines below a source block." (let ((region-text "mark this line as region leaving point in blank lines")) (org-test-with-temp-text (format " #+begin_src any-language -i -n :var here=\"not duplicated\" to upper block #+end_src %s " region-text) (let (info vars) (transient-mark-mode 1) (push-mark (point) t t) (search-forward region-text) (exchange-point-and-mark) (org-babel-demarcate-block) (goto-char (point-min)) (org-babel-next-src-block) (setq info (org-babel-get-src-block-info)) ;; upper source block info (setq vars (org-babel--get-vars (nth 2 info))) (should (string= "any-language" (nth 0 info))) (should (string= "to upper block" (org-trim (nth 1 info)))) (should (string= "not duplicated" (cdr (assq 'here vars)))) (should (string= "-i -n" (nth 3 info))) (org-babel-next-src-block) (setq info (org-babel-get-src-block-info)) ;; lower source block info (setq vars (org-babel--get-vars (nth 2 info))) (should (string= "any-language" (nth 0 info))) (should (string= region-text (org-trim (nth 1 info)))) (should-not vars) (should (string= "" (nth 3 info))))))) (provide 'test-ob) ;;; test-ob ends here org-mode-9.7.29+dfsg/testing/lisp/test-oc-basic.el000066400000000000000000000164521500430433700216640ustar00rootroot00000000000000;;; test-oc-basic.el --- Tests for Org Cite basic processor -*- lexical-binding: t; -*- ;; Copyright (C) 2024 Ihor Radchenko ;; Author: Ihor Radchenko ;; 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 . ;;; Commentary: ;; Unit tests for Org cite basic processor. ;;; Code: (require 'oc-basic) (ert-deftest test-org-cite-basic/parse-bibliography () "Test `org-cite-basic--parse-bibliography'." ;; Bibtex bibliography. (org-test-with-temp-text (format "#+bibliography: %s" (expand-file-name "examples/Basic.bib" org-test-dir)) (let ((data (org-cite-basic--parse-bibliography))) (should (= 1 (length data))) (should (equal (expand-file-name "examples/Basic.bib" org-test-dir) (caar data))) (dolist (k (hash-table-keys (cdar data))) (when (equal k "friends") (should (equal (gethash k (cdar data)) '((type . "book") (id . "friends") (title . "{{LaTeX}} and Friends") (author . "van Dongen, M.R.C.") (date . "2012") (location . "Berlin") (publisher . "Springer") (doi . "10.1007/978-3-642-23816-1") (isbn . "9783642238161"))))))))) (ert-deftest test-org-cite-basic/export-citation () "Test `org-cite-basic-export-citation'." ;; Default "nil" citation style. (org-test-with-temp-text (format "#+bibliography: %s #+cite_export: basic Default: [cite:Citing ; @friends; and @friends also; is duplication.]" (expand-file-name "examples/Basic.bib" org-test-dir)) (let ((export-buffer "*Test ASCII Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'ascii export-buffer) (with-current-buffer export-buffer (let ((case-fold-search t)) (search-forward "Default: (Citing van Dongen, M.R.C., 2012, and van Dongen, M.R.C., 2012 also is duplication.)" nil t))))) ;; "author" citation style. (org-test-with-temp-text (format "#+bibliography: %s #+cite_export: basic Author: [cite/a:Citing ; @friends; and @friends also; is duplication.]" (expand-file-name "examples/Basic.bib" org-test-dir)) (let ((export-buffer "*Test ASCII Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'ascii export-buffer) (with-current-buffer export-buffer (let ((case-fold-search t)) (should (search-forward "Author: Citing van Dongen, M.R.C., and van Dongen, M.R.C. also is duplication." nil t)))))) ;; "note" citation style. (org-test-with-temp-text (format "#+bibliography: %s #+cite_export: basic Note: [cite/ft:Citing ; @friends; and @friends also; is duplication.]" (expand-file-name "examples/Basic.bib" org-test-dir)) (let ((export-buffer "*Test ASCII Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'ascii export-buffer) (with-current-buffer export-buffer (let ((case-fold-search t)) (should (search-forward "[1] Citing van Dongen, M.R.C. (2012), and van Dongen, M.R.C. (2012) also is duplication." nil t)))))) ;; "nocite" citation style. (org-test-with-temp-text (format "#+bibliography: %s #+cite_export: basic Nocite (should be blank): [cite/n:Citing ; @friends; and @friends also; is duplication.]" (expand-file-name "examples/Basic.bib" org-test-dir)) (let ((export-buffer "*Test ASCII Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'ascii export-buffer) (with-current-buffer export-buffer (let ((case-fold-search t)) (should (search-forward "Nocite (should be blank):\n" nil t)) (goto-char (point-min)) (should-not (search-forward "2012" nil t)) (goto-char (point-min)) (should-not (search-forward "Dongen" nil t)))))) ;; "noauthor" citation style. (org-test-with-temp-text (format "#+bibliography: %s #+cite_export: basic Noauthor: [cite/na:Citing ; @friends; and @friends also; is duplication.]" (expand-file-name "examples/Basic.bib" org-test-dir)) (let ((export-buffer "*Test ASCII Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'ascii export-buffer) (with-current-buffer export-buffer (let ((case-fold-search t)) (should (search-forward "Noauthor: (Citing 2012, and 2012 also is duplication.)" nil t)))))) ;; "numeric" citation style. (org-test-with-temp-text (format "#+bibliography: %s #+cite_export: basic Numeric (should \"use global affixes and ignore local ones\"): [cite/nb:Citing ; @friends; and @friends also; is duplication.]" (expand-file-name "examples/Basic.bib" org-test-dir)) (let ((export-buffer "*Test ASCII Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'ascii export-buffer) (with-current-buffer export-buffer (let ((case-fold-search t)) (should (search-forward "Numeric (should \"use global affixes and ignore local ones\"): (Citing 1, 1 is duplication.)" nil t)))))) ;; "text" citation style. (org-test-with-temp-text (format "#+bibliography: %s #+cite_export: basic Text: [cite/t: Citing ; @friends; and @friends also; is duplication.]" (expand-file-name "examples/Basic.bib" org-test-dir)) (let ((export-buffer "*Test ASCII Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'ascii export-buffer) (with-current-buffer export-buffer (let ((case-fold-search t)) (should (search-forward "Text: Citing van Dongen, M.R.C. (2012), and van Dongen, M.R.C. (2012) also is duplication." nil t))))))) (ert-deftest test-org-cite-basic/export-bibliography () "Test `org-cite-basic-export-bibliography'." ;; Drop {...} Bibtex brackets and render entities. (org-test-with-temp-text (format "#+bibliography: %s #+cite_export: basic Foo [cite/plain:@Geyer2011] #+print_bibliography:" (expand-file-name "examples/Basic.bib" org-test-dir)) (let ((export-buffer "*Test ASCII Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'ascii export-buffer) (with-current-buffer export-buffer (let ((case-fold-search t)) (should ;; Rendered from {Introduction to Markov\plus Chain Monte Carlo} (search-forward "Introduction to Markov+ Chain Monte Carlo" nil t))))))) (provide 'test-oc-basic) ;;; test-oc-basic.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-oc.el000066400000000000000000002365551500430433700206150ustar00rootroot00000000000000;;; test-oc.el --- Tests for Org Cite library -*- lexical-binding: t; -*- ;; Copyright (C) 2021 Nicolas Goaziou ;; Author: Nicolas Goaziou ;; 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 . ;;; Code: (require 'oc) (require 'ox) ;; We need `org-test-with-parsed-data' macro. (require 'test-ox "../testing/lisp/test-ox.el") (ert-deftest test-org-cite/register-processor () "Test `org-cite-register-processor'." ;; Default test. (should (let ((org-cite--processors nil)) (org-cite-register-processor 'name))) ;; Handle duplicate processor. (should (let ((org-cite--processors nil)) (org-cite-register-processor 'name) (org-cite-register-processor 'name))) ;; Invalid name type. (should-error (org-cite-register-processor "name")) ;; Unknown property. (should-error (let ((org-cite--processors nil)) (org-cite-register-processor :foo 'bar)))) (ert-deftest test-org-cite/unregister-processor () "Test `org-cite-unregister-processor'." ;; Default test. (should-not (let ((org-cite--processors nil)) (org-cite-register-processor 'name) (org-cite-unregister-processor 'name) org-cite--processors)) ;; Error out with an unknown processor. (should-error (let ((org-cite--processors nil)) (org-cite-unregister-processor 'name)))) (ert-deftest test-org-cite/inside-footnote-p () "Test `org-cite-inside-footnote-p'." ;; Regular tests. (should (org-test-with-parsed-data "[fn:1] [cite:@key]" (org-cite-inside-footnote-p (org-element-map tree 'citation #'identity info t)))) (should (org-test-with-parsed-data "[fn::[cite:@key]]" (org-cite-inside-footnote-p (org-element-map tree 'citation #'identity info t)))) (should-not (org-test-with-parsed-data "[cite:@key]" (org-cite-inside-footnote-p (org-element-map tree 'citation #'identity info t)))) (should (org-test-with-parsed-data "[fn:1] Text.[cite:@key]" (org-cite-inside-footnote-p (org-element-map tree 'citation #'identity info t)))) (should (org-test-with-parsed-data "[fn:1] [cite:@key]\n: fixed width" (org-cite-inside-footnote-p (org-element-map tree 'citation #'identity info t)))) (should (org-test-with-parsed-data "[fn:1] [cite:@key] " (org-cite-inside-footnote-p (org-element-map tree 'citation #'identity info t)))) ;; Test optional argument. (should (org-test-with-parsed-data "[fn:1] [cite:@key]" (org-cite-inside-footnote-p (org-element-map tree 'citation #'identity info t) t))) (should-not (org-test-with-parsed-data "[fn:1] See [cite:@key]." (org-cite-inside-footnote-p (org-element-map tree 'citation #'identity info t) t))) (should (org-test-with-parsed-data "[fn::[cite:@key]]" (org-cite-inside-footnote-p (org-element-map tree 'citation #'identity info t) t))) (should-not (org-test-with-parsed-data "[fn::See [cite:@key].]" (org-cite-inside-footnote-p (org-element-map tree 'citation #'identity info t) t)))) (ert-deftest test-org-cite/processor-has-capability-p () "Test `org-cite-processor-has-capability-p'." ;; Unknown capability error. (should-error (let ((org-cite--processors nil)) (org-cite-register-processor 'name :activate #'ignore) (org-cite-processor-has-capability-p 'name 'unknown))) ;; Test `activate' capability. (should (let ((org-cite--processors nil)) (org-cite-register-processor 'name :activate #'ignore) (org-cite-processor-has-capability-p 'name 'activate))) (should-not (let ((org-cite--processors nil)) (org-cite-register-processor 'name :follow #'ignore) (org-cite-processor-has-capability-p 'name 'activate))) ;; Test `export' capability. (should (let ((org-cite--processors nil)) (org-cite-register-processor 'name :export-bibliography #'ignore :export-citation #'ignore) (org-cite-processor-has-capability-p 'name 'export))) (should (let ((org-cite--processors nil)) (org-cite-register-processor 'name :export-citation #'ignore) (org-cite-processor-has-capability-p 'name 'export))) (should-not (let ((org-cite--processors nil)) (org-cite-register-processor 'name :export-bibliography #'ignore) (org-cite-processor-has-capability-p 'name 'export))) ;; Test `follow' capability. (should (let ((org-cite--processors nil)) (org-cite-register-processor 'name :follow #'ignore) (org-cite-processor-has-capability-p 'name 'follow))) (should-not (let ((org-cite--processors nil)) (org-cite-register-processor 'name :activate #'ignore) (org-cite-processor-has-capability-p 'name 'follow))) ;; Unknown processors have no capabilities. (should-not (org-cite-processor-has-capability-p 'foo 'activate)) (should-not (org-cite-processor-has-capability-p 'foo 'export)) (should-not (org-cite-processor-has-capability-p 'foo 'follow))) (ert-deftest test-org-cite/get-references () "Test `org-cite-get-references'." ;; Return a list of citation reference objects. (should (equal '(citation-reference) (org-test-with-temp-text "[cite:@a]" (mapcar #'org-element-type (org-cite-get-references (org-element-context)))))) (should (equal '("a") (org-test-with-temp-text "[cite:@a]" (mapcar (lambda (r) (org-element-property :key r)) (org-cite-get-references (org-element-context)))))) ;; Preserve order of references. (should (equal '("a" "b") (org-test-with-temp-text "[cite:@a;@b]" (mapcar (lambda (r) (org-element-property :key r)) (org-cite-get-references (org-element-context)))))) ;; Parse prefix and suffix. (should (equal '("a" "b") (org-test-with-temp-text "[cite:prefix @a suffix;@b]" (mapcar (lambda (r) (org-element-property :key r)) (org-cite-get-references (org-element-context)))))) (should (equal '(("prefix ") nil) (org-test-with-temp-text "[cite:prefix @a suffix;@b]" (mapcar (lambda (r) (org-element-property :prefix r)) (org-cite-get-references (org-element-context)))))) (should (equal '((" suffix") nil) (org-test-with-temp-text "[cite:prefix @a suffix;@b]" (mapcar (lambda (r) (org-element-property :suffix r)) (org-cite-get-references (org-element-context)))))) ;; Ignore common prefix and suffix. (should (equal '("a") (org-test-with-temp-text "[cite:common prefix; @a ; common suffix]" (mapcar (lambda (r) (org-element-property :key r)) (org-cite-get-references (org-element-context)))))) ;; Preserve buffer positions. (should (org-test-with-temp-text "[cite:@a] [cite:@b]" (= (1+ (point)) (org-element-property :begin (car (org-cite-get-references (org-element-context))))))) ;; Handle citation from a full parse tree. (should (equal '(1 2) (org-test-with-temp-text "[cite:@a] [cite:@a;@b]" (org-element-map (org-element-parse-buffer) 'citation (lambda (c) (length (org-cite-get-references c))))))) ;; Test optional argument. (should (equal '("a" "b") (org-test-with-temp-text "[cite:@a;@b]" (org-cite-get-references (org-element-context) t)))) (should (equal '("a" "b") (org-test-with-temp-text "[cite:@a;@b]" (org-element-map (org-element-parse-buffer) 'citation (lambda (c) (org-cite-get-references c t)) nil t))))) (ert-deftest test-org-cite/key-boundaries () "Test `org-cite-key-boundaries'." (should (equal "@key" (org-test-with-temp-text "[cite:@key]" (let ((boundaries (org-cite-key-boundaries (org-element-context)))) (buffer-substring-no-properties (car boundaries) (cdr boundaries)))))) (should (equal "@key" (org-test-with-temp-text "[cite:prefix @key]" (let ((boundaries (org-cite-key-boundaries (org-element-context)))) (buffer-substring-no-properties (car boundaries) (cdr boundaries)))))) (should (equal "@key" (org-test-with-temp-text "[cite:@key suffix]" (let ((boundaries (org-cite-key-boundaries (org-element-context)))) (buffer-substring-no-properties (car boundaries) (cdr boundaries)))))) (should (equal "@key" (org-test-with-temp-text "[cite:global ;@key]" (let ((boundaries (org-cite-key-boundaries (org-element-context)))) (buffer-substring-no-properties (car boundaries) (cdr boundaries)))))) (should (equal "@key" (org-test-with-temp-text "[cite:@key; global]" (let ((boundaries (org-cite-key-boundaries (org-element-context)))) (buffer-substring-no-properties (car boundaries) (cdr boundaries))))))) (ert-deftest test-org-cite/main-affixes () "Test `org-cite-main-affixes'." (should (equal '(nil . nil) (org-test-with-temp-text "[cite:@key]" (org-cite-main-affixes (org-element-context))))) (should (equal '(nil . nil) (org-test-with-temp-text "[cite:@key1;@key2]" (org-cite-main-affixes (org-element-context))))) (should (equal '(("pre ") . nil) (org-test-with-temp-text "[cite:pre @key]" (org-cite-main-affixes (org-element-context))))) (should (equal '(("pre ") . (" post")) (org-test-with-temp-text "[cite:pre @key post]" (org-cite-main-affixes (org-element-context))))) (should (equal '(("pre ") . nil) (org-test-with-temp-text "[cite:global pre;pre @key]" (org-cite-main-affixes (org-element-context))))) (should (equal '(nil . (" post")) (org-test-with-temp-text "[cite:@key post;global post]" (org-cite-main-affixes (org-element-context))))) (should (equal '(("global pre") . ("global post")) (org-test-with-temp-text "[cite:global pre;@key1;@key2;global post]" (org-cite-main-affixes (org-element-context))))) (should (equal '(("global pre") . nil) (org-test-with-temp-text "[cite:global pre;pre1 @key1;pre2 @key2]" (org-cite-main-affixes (org-element-context))))) (should (equal '(nil . (" global post")) (org-test-with-temp-text "[cite:@key1 post1;@key2 post2; global post]" (org-cite-main-affixes (org-element-context)))))) (ert-deftest test-org-cite/supported-styles () "Test `org-cite-supported-styles'." ;; Default behavior is to use export processors. (should (equal '(((""))) (let ((org-cite--processors nil) (org-cite-export-processors '((t test)))) (org-cite-register-processor 'test :cite-styles '((("")))) (org-cite-supported-styles)))) (should (equal '((("foo" "f")) ((""))) (let ((org-cite--processors nil) (org-cite-export-processors '((t test)))) (org-cite-register-processor 'test :cite-styles '((("foo" "f")) (("")))) (org-cite-supported-styles)))) ;; Also support functions generating the list. (should (equal '((("foo" "f")) ((""))) (let ((org-cite--processors nil) (org-cite-export-processors '((t test)))) (org-cite-register-processor 'test :cite-styles (lambda () '((("foo" "f")) ((""))))) (org-cite-supported-styles)))) ;; Explicitly provide a processor. (should (equal '(((""))) (let ((org-cite--processors nil)) (org-cite-register-processor 'test :cite-styles '((("")))) (org-cite-supported-styles '(test))))) ;; Merge style shortcuts. (should (equal '((("foo" "f" "g"))) (let ((org-cite--processors nil)) (org-cite-register-processor 'test :cite-styles '((("foo" "f")))) (org-cite-register-processor 'test2 :cite-styles '((("foo" "g")))) (org-cite-supported-styles '(test test2))))) ;; Merge style variants. (should (equal '((("foo") ("bar") ("baz"))) (let ((org-cite--processors nil)) (org-cite-register-processor 'test :cite-styles '((("foo") ("bar")))) (org-cite-register-processor 'test2 :cite-styles '((("foo") ("baz")))) (org-cite-supported-styles '(test test2))))) ;; Merge variant shortcuts. (should (equal '((("foo") ("bar" "b" "c"))) (let ((org-cite--processors nil)) (org-cite-register-processor 'test :cite-styles '((("foo") ("bar" "b")))) (org-cite-register-processor 'test2 :cite-styles '((("foo") ("bar" "c")))) (org-cite-supported-styles '(test test2))))) ;; Ignore duplicates. (should (equal '((("foo") ("bar"))) (let ((org-cite--processors nil)) (org-cite-register-processor 'test :cite-styles '((("foo") ("bar")))) (org-cite-register-processor 'test2 :cite-styles '((("foo") ("bar")))) (org-cite-supported-styles '(test test2))))) (should (equal '((("foo") ("bar" "b"))) (let ((org-cite--processors nil)) (org-cite-register-processor 'test :cite-styles '((("foo") ("bar" "b")))) (org-cite-register-processor 'test2 :cite-styles '((("foo") ("bar" "b")))) (org-cite-supported-styles '(test test2)))))) (ert-deftest test-org-cite/delete-citation () "Test `org-cite-delete-citation'." ;; Error when not on a citation or citation reference. (should-error (org-test-with-temp-text "Text" (org-cite-delete-citation (org-element-context)))) ;; When argument is a citation, delete it completely. Manage ;; properly blanks around it. (should (equal "" (org-test-with-temp-text "[cite:@key]" (org-cite-delete-citation (org-element-context)) (buffer-string)))) (should (equal "Before After" (org-test-with-temp-text "Before [cite:@key] After" (org-cite-delete-citation (org-element-context)) (buffer-string)))) (should (equal "Before After" (org-test-with-temp-text "Before [cite:@key]After" (org-cite-delete-citation (org-element-context)) (buffer-string)))) (should (equal "Before After" (org-test-with-temp-text "Before[cite:@key] After" (org-cite-delete-citation (org-element-context)) (buffer-string)))) ;; Ensure there is at least a blank to separate consecutive objects. (should (equal "Before After" (org-test-with-temp-text "Before[cite:@key]After" (org-cite-delete-citation (org-element-context)) (buffer-string)))) ;; Remove trailing blanks when removing the citation. (should (equal "Before" (org-test-with-temp-text "Before[cite:@key] " (org-cite-delete-citation (org-element-context)) (buffer-string)))) ;; Preserve indentation if citation is at the beginning of the line. (should (equal " After" (org-test-with-temp-text " [cite:@key] After" (org-cite-delete-citation (org-element-context)) (buffer-string)))) ;; When the citation is alone on a line, remove the whole line. (should (equal "Line 1\nLine 3" (org-test-with-temp-text "Line 1\n[cite:@key]\nLine 3" (org-cite-delete-citation (org-element-context)) (buffer-string)))) ;; When there is only one citation reference object, remove the full ;; citation. (should (equal "" (org-test-with-temp-text "[cite:@key]" (org-cite-delete-citation (org-element-context)) (buffer-string)))) (should (equal "" (org-test-with-temp-text "[cite:pre @key post]" (org-cite-delete-citation (org-element-context)) (buffer-string)))) (should (equal "" (org-test-with-temp-text "[cite:pre; @key ;post]" (org-cite-delete-citation (org-element-context)) (buffer-string)))) ;; Otherwise, remove the reference, including any affix. (should (equal "[cite:@before;@after]" (org-test-with-temp-text "[cite:@before;@key;@after]" (org-cite-delete-citation (org-element-context)) (buffer-string)))) (should (equal "[cite:@before;@after]" (org-test-with-temp-text "[cite:@before;pre @key post;@after]" (org-cite-delete-citation (org-element-context)) (buffer-string)))) (should (equal "[cite:@before]" (org-test-with-temp-text "[cite:@before;@key]" (org-cite-delete-citation (org-element-context)) (buffer-string)))) (should (equal "[cite:@before;post]" (org-test-with-temp-text "[cite:@before;@key;post]" (org-cite-delete-citation (org-element-context)) (buffer-string)))) (should (equal "[cite:@after]" (org-test-with-temp-text "[cite:@key;@after]" (org-cite-delete-citation (org-element-context)) (buffer-string)))) (should (equal "[cite:pre;@after]" (org-test-with-temp-text "[cite:pre;@key;@after]" (org-cite-delete-citation (org-element-context)) (buffer-string))))) (ert-deftest test-org-cite/list-bibliography-files () "Test `org-cite-list-bibliography-files'." (should (equal '("/bibliography") (org-test-with-temp-text "#+bibliography: /bibliography" (let ((org-cite-global-bibliography nil)) (org-cite-list-bibliography-files))))) (should (equal '("/bibliography") (org-test-with-temp-text "#+bibliography: \"/bibliography\"" (let ((org-cite-global-bibliography nil)) (org-cite-list-bibliography-files))))) (should (equal '("/bibliography" "/other-bibliography") (org-test-with-temp-text "#+bibliography: /bibliography" (let ((org-cite-global-bibliography '("/other-bibliography"))) (org-cite-list-bibliography-files))))) (should (equal '("./bibliography") (org-test-with-temp-text "#+bibliography: ./bibliography" (let ((org-cite-global-bibliography nil)) (org-cite-list-bibliography-files))))) (should (equal '("/foo.bib") (org-test-with-temp-text (format "#+SETUPFILE: \"%s/examples/sub-bib/include-global-bib.org\"" org-test-dir) (let ((org-cite-global-bibliography nil)) (org-cite-list-bibliography-files))))) (should (equal '(nil) (org-test-with-temp-text (format "#+SETUPFILE: \"%s/examples/sub-bib/include-relative-bib.org\"" org-test-dir) (let ((org-cite-global-bibliography nil)) (mapcar #'file-name-absolute-p (org-cite-list-bibliography-files)))))) (should (equal '("/bibliographyA" "/bibliographyB") (org-test-with-temp-text "#+bibliography: /bibliographyA\n#+bibliography: /bibliographyB" (let ((org-cite-global-bibliography nil)) (org-cite-list-bibliography-files))))) (should (equal '("/bibliographyA") (org-test-with-temp-text "#+bibliography: /bibliographyA\n#+bibliography: /bibliographyA" (let ((org-cite-global-bibliography nil)) (org-cite-list-bibliography-files)))))) (ert-deftest test-org-cite/bibliography-style () "Test `org-cite-bibliography-style'." ;; Extract style from global processor definition. (should (equal "a" (catch :exit (org-test-with-temp-text "#+print_bibliography:" (let ((org-cite-export-processors '((t . (foo "a" "b")))) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-bibliography (lambda (_ _ s _ _ _) (throw :exit s)) :export-citation #'ignore) (org-export-as (org-export-create-backend))))))) ;; Extract style from local processor definition. (should (equal "a" (catch :exit (org-test-with-temp-text "#+cite_export: foo a b\n#+print_bibliography:" (let ((org-cite-export-processors nil) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-bibliography (lambda (_ _ s _ _ _) (throw :exit s)) :export-citation #'ignore) (org-export-as (org-export-create-backend))))))) (should (equal "a b" (catch :exit (org-test-with-temp-text "#+cite_export: foo \"a b\" c\n#+print_bibliography:" (let ((org-cite-export-processors nil) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-bibliography (lambda (_ _ s _ _ _) (throw :exit s)) :export-citation #'ignore) (org-export-as (org-export-create-backend))))))) ;; Test priority: first keyword, then local. (should (equal "local" (catch :exit (org-test-with-temp-text "#+print_bibliography:\n#+cite_export: foo local a\n[cite:@a]" (let ((org-cite-export-processors '((t . (foo "global" "b")))) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-bibliography (lambda (_ _ s _ _ _) (throw :exit s)) :export-citation #'ignore) (org-export-as (org-export-create-backend))))))) ;; Explicit "nil" styles forces default style. (should-not (catch :exit (org-test-with-temp-text "#+print_bibliography:\n#+cite_export: foo nil a\n[cite:@a]" (let ((org-cite-export-processors '((t . (foo "global" "b")))) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-bibliography (lambda (_ _ s _ _ _) (throw :exit s)) :export-citation #'ignore) (org-export-as (org-export-create-backend))))))) (ert-deftest test-org-cite/bibliography-properties () "Test `org-cite-bibliography-properties'." ;; Return nil without properties. (should-not (org-test-with-parsed-data "#+print_bibliography:" (org-element-map tree 'keyword #'org-cite-bibliography-properties info t))) ;; Regular tests. (should (equal '(:key "value") (org-test-with-parsed-data "#+print_bibliography: :key value" (org-element-map tree 'keyword #'org-cite-bibliography-properties info t)))) (should (equal '(:key "value" :key2 "value2") (org-test-with-parsed-data "#+print_bibliography: :key value :key2 value2" (org-element-map tree 'keyword #'org-cite-bibliography-properties info t)))) ;; Allow empty values. (should (equal '(:key) (org-test-with-parsed-data "#+print_bibliography: :key" (org-element-map tree 'keyword #'org-cite-bibliography-properties info t)))) (should (equal '(:key "") (org-test-with-parsed-data "#+print_bibliography: :key \"\"" (org-element-map tree 'keyword #'org-cite-bibliography-properties info t)))) ;; Allow space with double quotes. (should (equal '(:key "space space") (org-test-with-parsed-data "#+print_bibliography: :key \"space space\"" (org-element-map tree 'keyword #'org-cite-bibliography-properties info t)))) ;; Ignore spurious values. (should (equal '(:key "space") (org-test-with-parsed-data "#+print_bibliography: :key space space" (org-element-map tree 'keyword #'org-cite-bibliography-properties info t)))) ;; Gracefully handle incomplete quotations. (should (equal '(:key "\"space" :key2 "value2") (org-test-with-parsed-data "#+print_bibliography: :key \"space :key2 value2" (org-element-map tree 'keyword #'org-cite-bibliography-properties info t))))) (ert-deftest test-org-cite/citation-style () "Test `org-cite-citation-style'." ;; Extract style from global processor definition. (should (equal '("b") (catch :exit (org-test-with-temp-text "[cite:@a]" (let ((org-cite-export-processors '((t . (foo "a" "b")))) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-citation (lambda (_ s _ _) (throw :exit s))) (org-export-as (org-export-create-backend))))))) (should (equal '("b" . "variant") (catch :exit (org-test-with-temp-text "[cite:@a]" (let ((org-cite-export-processors '((t . (foo "a" "b/variant")))) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-citation (lambda (_ s _ _) (throw :exit s))) (org-export-as (org-export-create-backend))))))) ;; Extract style from local processor definition. (should (equal '("b") (catch :exit (org-test-with-temp-text "#+cite_export: foo a b\n[cite:@a]" (let ((org-cite-export-processors nil) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-citation (lambda (_ s _ _) (throw :exit s))) (org-export-as (org-export-create-backend))))))) (should (equal '("b c") (catch :exit (org-test-with-temp-text "#+cite_export: foo a \"b c\"\n[cite:@a]" (let ((org-cite-export-processors nil) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-citation (lambda (_ s _ _) (throw :exit s))) (org-export-as (org-export-create-backend))))))) (should (equal '("b" . "variant") (catch :exit (org-test-with-temp-text "#+cite_export: foo a b/variant\n[cite:@a]" (let ((org-cite-export-processors nil) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-citation (lambda (_ s _ _) (throw :exit s))) (org-export-as (org-export-create-backend))))))) (should (equal '("b c" . "variant") (catch :exit (org-test-with-temp-text "#+cite_export: foo a \"b c/variant\"\n[cite:@a]" (let ((org-cite-export-processors nil) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-citation (lambda (_ s _ _) (throw :exit s))) (org-export-as (org-export-create-backend))))))) ;; Extract style from citation itself. (should (equal '("b") (catch :exit (org-test-with-temp-text "[cite/b:@a]" (let ((org-cite-export-processors '((t . (foo nil nil)))) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-citation (lambda (_ s _ _) (throw :exit s))) (org-export-as (org-export-create-backend))))))) (should (equal '("b" . "variant") (catch :exit (org-test-with-temp-text "[cite/b/variant:@a]" (let ((org-cite-export-processors '((t . (foo nil nil)))) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-citation (lambda (_ s _ _) (throw :exit s))) (org-export-as (org-export-create-backend))))))) ;; Test priority: first object, then local. (should (equal '("object") (catch :exit (org-test-with-temp-text "#+cite_export: foo nil local\n[cite/object:@a]" (let ((org-cite-export-processors '((t . (foo nil "global")))) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-citation (lambda (_ s _ _) (throw :exit s))) (org-export-as (org-export-create-backend))))))) (should (equal '("local") (catch :exit (org-test-with-temp-text "#+cite_export: foo nil local\n[cite:@a]" (let ((org-cite-export-processors '((t . (foo nil "global")))) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-citation (lambda (_ s _ _) (throw :exit s))) (org-export-as (org-export-create-backend))))))) ;; Force default style with "nil". (should (equal '(nil) (catch :exit (org-test-with-temp-text "#+cite_export: foo nil nil\n[cite:@a]" (let ((org-cite-export-processors '((t . (foo nil "global")))) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-citation (lambda (_ s _ _) (throw :exit s))) (org-export-as (org-export-create-backend))))))) (should (equal '(nil) (catch :exit (org-test-with-temp-text "[cite/nil:@a]" (let ((org-cite-export-processors '((t . (foo nil "global")))) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-citation (lambda (_ s _ _) (throw :exit s))) (org-export-as (org-export-create-backend))))))) ;; Test variant inheritance. (should (equal '("local" . "v2") (catch :exit (org-test-with-temp-text "[cite/local/v2:@a]" (let ((org-cite-export-processors '((t . (foo nil "global/v1")))) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-citation (lambda (_ s _ _) (throw :exit s))) (org-export-as (org-export-create-backend))))))) (should (equal '("global" . "v2") (catch :exit (org-test-with-temp-text "[cite//v2:@a]" (let ((org-cite-export-processors '((t . (foo nil "global/v1")))) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-citation (lambda (_ s _ _) (throw :exit s))) (org-export-as (org-export-create-backend))))))) (should (equal '(nil . "v2") (catch :exit (org-test-with-temp-text "[cite/nil/v2:@a]" (let ((org-cite-export-processors '((t . (foo nil "global/v1")))) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-citation (lambda (_ s _ _) (throw :exit s))) (org-export-as (org-export-create-backend))))))) (should (equal '("local" . nil) (catch :exit (org-test-with-temp-text "[cite/local:@a]" (let ((org-cite-export-processors '((t . (foo nil "global/v1")))) (org-cite--processors nil)) (org-cite-register-processor 'foo :export-citation (lambda (_ s _ _) (throw :exit s))) (org-export-as (org-export-create-backend)))))))) (ert-deftest test-org-cite/read-processor-declaration () "Test `org-cite-read-processor-declaration'." ;; Argument should contain 1-3 tokens. (should-error (org-cite-read-processor-declaration "")) (should (equal '(foo nil nil) (org-cite-read-processor-declaration "foo"))) (should (equal '(foo "bar" nil) (org-cite-read-processor-declaration "foo bar"))) (should (equal '(foo "bar" "baz") (org-cite-read-processor-declaration "foo bar baz"))) (should-error (org-cite-read-processor-declaration "foo bar baz qux")) ;; nil in second and third arguments is read as `nil'. (should (equal '(foo nil "baz") (org-cite-read-processor-declaration "foo nil baz"))) (should (equal '(foo "bar" nil) (org-cite-read-processor-declaration "foo bar nil"))) ;; Second and third arguments may contain spaces if they are quoted. (should (equal '(foo "bar baz" nil) (org-cite-read-processor-declaration "foo \"bar baz\""))) (should (equal '(foo "bar" "baz qux") (org-cite-read-processor-declaration "foo bar \"baz qux\""))) ;; Spurious spaces are ignored. (should (equal '(foo "bar" "baz") (org-cite-read-processor-declaration " foo bar baz ")))) (ert-deftest test-org-cite/list-citations () "Test `org-cite-list-citations'." (should (equal '("a") (org-test-with-parsed-data "Test [cite:@a]" (cl-mapcan (lambda (c) (mapcar (lambda (ref) (org-element-property :key ref)) (org-element-contents c))) (org-cite-list-citations info))))) (should (equal '("a" "b") (org-test-with-parsed-data "Test [cite:@a] [cite:@b]" (cl-mapcan (lambda (c) (mapcar (lambda (ref) (org-element-property :key ref)) (org-element-contents c))) (org-cite-list-citations info))))) (should (equal '("a") (org-test-with-parsed-data "Test[fn:1]\n[fn:1] [cite:@a]" (cl-mapcan (lambda (c) (mapcar (lambda (ref) (org-element-property :key ref)) (org-element-contents c))) (org-cite-list-citations info))))) (should (equal '("a" "b") (org-test-with-parsed-data "First[cite:@a] Second[fn:1]\n[fn:1] [cite:@b]" (cl-mapcan (lambda (c) (mapcar (lambda (ref) (org-element-property :key ref)) (org-element-contents c))) (org-cite-list-citations info))))) (should (equal '("b" "a") (org-test-with-parsed-data "First[fn:1] Second[cite:@a]\n[fn:1] [cite:@b]" (cl-mapcan (lambda (c) (mapcar (lambda (ref) (org-element-property :key ref)) (org-element-contents c))) (org-cite-list-citations info))))) (should (equal '("a" "b") (org-test-with-parsed-data "Text[fn:1][fn:2]\n[fn:1] [cite:@a]\n\n[fn:2] [cite:@b]" (cl-mapcan (lambda (c) (mapcar (lambda (ref) (org-element-property :key ref)) (org-element-contents c))) (org-cite-list-citations info))))) (should (equal '("b" "a") (org-test-with-parsed-data "Text[fn:1]\n[fn:1] [fn:2][cite:@a]\n\n[fn:2] [cite:@b]" (cl-mapcan (lambda (c) (mapcar (lambda (ref) (org-element-property :key ref)) (org-element-contents c))) (org-cite-list-citations info))))) (should (equal '("a" "b") (org-test-with-parsed-data "Text[fn:1]\n[fn:1] [cite:@a][fn:2]\n\n[fn:2] [cite:@b]" (cl-mapcan (lambda (c) (mapcar (lambda (ref) (org-element-property :key ref)) (org-element-contents c))) (org-cite-list-citations info))))) (should (equal '("a") (org-test-with-parsed-data "Text[fn::[cite:@a]]" (cl-mapcan (lambda (c) (mapcar (lambda (ref) (org-element-property :key ref)) (org-element-contents c))) (org-cite-list-citations info)))))) (ert-deftest test-org-cite/list-keys () "Test `org-cite-list-keys'." (should (equal '("a") (org-test-with-parsed-data "Test [cite:@a]" (org-cite-list-keys info)))) (should (equal '("a" "b") (org-test-with-parsed-data "Test [cite:@a] [cite:@b]" (org-cite-list-keys info)))) ;; Remove duplicates. (should (equal '("a") (org-test-with-parsed-data "Test [cite:@a] [cite:@a]" (org-cite-list-keys info)))) ;; Keys are ordered by first appearance in the document. (should (equal '("a" "b") (org-test-with-parsed-data "Test [cite:@a] [cite:@b] [cite:@a]" (org-cite-list-keys info)))) (should (equal '("a" "b" "c") (org-test-with-parsed-data "Test [cite:@a][fn:1] [cite:@c] [cite:@a]\n[fn:1] [cite:@b]" (org-cite-list-keys info))))) (ert-deftest test-org-cite/key-number () "Test `org-cite-key-number'." (should (= 1 (org-test-with-parsed-data "[cite:@key]" (org-cite-key-number "key" info)))) (should (equal '(1 2) (org-test-with-parsed-data "[cite:@key] [cite:@key2] [cite:@key]" (list (org-cite-key-number "key" info) (org-cite-key-number "key2" info))))) ;; When "predicate" is nil, keys are sorted by appearance order in ;; the buffer. (should (equal '((1 . "a") (2 . "c") (3 . "b")) (org-test-with-parsed-data "[cite:@a][fn:1] [cite:@b]\n[fn:1] [cite:@c]" (sort (mapcar (lambda (key) (cons (org-cite-key-number key info) key)) '("a" "b" "c")) #'car-less-than-car)))) (should (equal '((1 . "a") (2 . "b") (3 . "c")) (org-test-with-parsed-data "[cite:@a][fn:1] [cite:@b]\n[fn:1] [cite:@c]" (sort (mapcar (lambda (key) (cons (org-cite-key-number key info #'string<) key)) '("a" "b" "c")) #'car-less-than-car))))) (ert-deftest test-org-cite/wrap-citation () "Test `org-cite-wrap-citation'." ;; Reference test. (should (org-test-with-parsed-data "[cite:@key]" (org-element-map tree 'citation (lambda (c) (org-cite-wrap-citation c info) (org-cite-inside-footnote-p c)) info))) ;; Created footnote is anonymous. (should-not (org-test-with-parsed-data "[cite:@key] " (org-element-map tree 'citation (lambda (c) (org-cite-wrap-citation c info) (org-element-property :label (org-cite-inside-footnote-p c))) info))) ;; Created footnote is inline. (should (equal '(inline) (org-test-with-parsed-data "[cite:@key]" (org-element-map tree 'citation (lambda (c) (org-cite-wrap-citation c info) (org-element-property :type (org-cite-inside-footnote-p c))) info)))) ;; Preserve `:post-blank' property. (should (equal '(2) (org-test-with-parsed-data "[cite:@key] " (org-element-map tree 'citation (lambda (c) (org-cite-wrap-citation c info) (org-element-property :post-blank (org-cite-inside-footnote-p c))) info)))) ;; Set `:post-blank' to 0 in the element before new footnote. (should-not (org-test-with-parsed-data "Text [cite:@key]" (org-element-map tree 'citation (lambda (c) (org-cite-wrap-citation c info) (let ((previous (org-export-get-previous-element (org-cite-inside-footnote-p c) info))) (string-match (rx blank string-end) previous))) info))) (should (equal '(0) (org-test-with-parsed-data "*Text* [cite:@key]" (org-element-map tree 'citation (lambda (c) (org-cite-wrap-citation c info) (let ((previous (org-export-get-previous-element (org-cite-inside-footnote-p c) info))) (org-element-property :post-blank previous))) info)))) (should (equal '("Text") (org-test-with-parsed-data "Text [cite:@key]" (org-element-map tree 'citation (lambda (c) (org-cite-wrap-citation c info) (org-export-get-previous-element (org-cite-inside-footnote-p c) info)) info))))) (defun test-org-cite--export-with-rule (text &optional rule punct) "Export TEXT string using RULE for punctuation positioning. Call `org-cite-adjust-note' on each citation object with RULE and, PUNCT arguments. Replace citation with \"@\" character in the output." (org-test-with-temp-text text (let ((org-cite--processors nil)) (org-cite-register-processor 'test :export-citation (lambda (citation _s _b info) (org-cite-adjust-note citation info rule punct) "@")) (let ((org-cite-export-processors '((t . (test nil nil))))) (org-trim (org-export-as (org-export-create-backend :transcoders '((section . (lambda (_s c _i) (replace-regexp-in-string " @" "@" c))) (paragraph . (lambda (_s c _i) c)))))))))) (ert-deftest test-org-cite/adjust-note () "Test `org-cite-adjust-note' function." ;; Basic tests for all rules. In the output, @ replaces citation. (let ((cases '("\"[cite:@k]!" ".\"[cite:@k]!" "\"[cite:@k]" ".\"[cite:@k]" ".[cite:@k]" "[cite:@k]!"))) (should ;test (inside inside after) (equal '(iia "!@\"" ".@\"!" "@\"" ".@\"" ".@" "!@") (cons 'iia (mapcar (lambda (c) (test-org-cite--export-with-rule c '(inside inside after))) cases)))) (should ;test (inside inside before) (equal '(iib "@!\"" "@.\"!" "@\"" "@.\"" "@." "@!") (cons 'iib (mapcar (lambda (c) (test-org-cite--export-with-rule c '(inside inside before))) cases)))) (should ;test (inside outside after) (equal '(ioa "!\"@" ".\"!@" "\"@" ".\"@" ".@" "!@") (cons 'ioa (mapcar (lambda (c) (test-org-cite--export-with-rule c '(inside outside after))) cases)))) (should ;test (inside outside before) (equal '(iob "!\"@" ".\"@!" "\"@" ".\"@" "@." "@!") (cons 'iob (mapcar (lambda (c) (test-org-cite--export-with-rule c '(inside outside before))) cases)))) (should ;test (inside same after) (equal '(isa "!@\"" ".\"!@" "\"@" ".@\"" ".@" "!@") (cons 'isa (mapcar (lambda (c) (test-org-cite--export-with-rule c '(inside same after))) cases)))) (should ;test (inside same before) (equal '(isb "@!\"" ".\"@!" "\"@" "@.\"" "@." "@!") (cons 'isb (mapcar (lambda (c) (test-org-cite--export-with-rule c '(inside same before))) cases)))) (should ;test (outside inside after) (equal '(oia "@\"!" ".@\"!" "@\"" "@\"." ".@" "!@") (cons 'oia (mapcar (lambda (c) (test-org-cite--export-with-rule c '(outside inside after))) cases)))) (should ;test (outside inside before) (equal '(oib "@\"!" "@.\"!" "@\"" "@\"." "@." "@!") (cons 'oib (mapcar (lambda (c) (test-org-cite--export-with-rule c '(outside inside before))) cases)))) (should ;test (outside outside after) (equal '(ooa "\"!@" ".\"!@" "\"@" "\".@" ".@" "!@") (cons 'ooa (mapcar (lambda (c) (test-org-cite--export-with-rule c '(outside outside after))) cases)))) (should ;test (outside outside before) (equal '(oob "\"@!" ".\"@!" "\"@" "\"@." "@." "@!") (cons 'oob (mapcar (lambda (c) (test-org-cite--export-with-rule c '(outside outside before))) cases)))) (should ;test (outside same after) (equal '(osa "\"!@" ".\"!@" "\"@" "\".@" ".@" "!@") (cons 'osa (mapcar (lambda (c) (test-org-cite--export-with-rule c '(outside same after))) cases)))) (should ;test (outside same before) (equal '(osb "\"@!" ".\"@!" "\"@" "\"@." "@." "@!") (cons 'osb (mapcar (lambda (c) (test-org-cite--export-with-rule c '(outside same before))) cases))))) ;; Test `adaptive' behaviour. (should (equal "@\"." (test-org-cite--export-with-rule ".\" [cite:@k]" '(adaptive inside after)))) (should (equal "@\"!" (test-org-cite--export-with-rule "\" [cite:@k]!" '(adaptive inside after)))) (should (equal ".@\"" (test-org-cite--export-with-rule ".\"[cite:@k]" '(adaptive inside after)))) (should (equal "!@\"" (test-org-cite--export-with-rule "\"[cite:@k]!" '(adaptive inside after)))) ;; Handle white space when inserting citation before quotation mark ;; or punctuation. (should (equal ",@\" next" (test-org-cite--export-with-rule ",\" [cite:@k] next" '(inside inside after)))) (should (equal "@,\" next" (test-org-cite--export-with-rule ",\" [cite:@k] next" '(inside inside before)))) (should (equal "@\"." (test-org-cite--export-with-rule "\" [cite:@k]." '(outside inside before)))) (should (equal "@\" !" (test-org-cite--export-with-rule "\" [cite:@k] !" '(outside inside before)))) (should (equal "text@ !" (test-org-cite--export-with-rule "text ![cite:@k]" '(inside outside before)))) ;; Preserve white space between citation and final punctuation when ;; moving citation past final punctuation. (should (equal "text !@" (test-org-cite--export-with-rule "text [cite:@k] !" '(inside inside after)))) (should (equal "text\n !@" (test-org-cite--export-with-rule "text [cite:@k]\n !" '(inside inside after)))) ;; Choose punctuation with optional argument. (should-not (equal "!@" (test-org-cite--export-with-rule "[cite:@k]!" '(inside outside after) '(".")))) (should (equal ".@" (test-org-cite--export-with-rule "[cite:@k]." '(inside outside after) '("."))))) (ert-deftest test-org-cite/parse-elements () "Test `org-cite-parse-elements' function." (should-error (org-cite-parse-elements "* H")) (should-error (org-cite-parse-elements "Paragraph\n* H")) (should (equal '(paragraph) (mapcar #'org-element-type (org-cite-parse-elements "s")))) (should (equal '(paragraph paragraph) (mapcar #'org-element-type (org-cite-parse-elements "Text\n\nText"))))) (ert-deftest test-org-cite/parse-objects () "Test `org-cite-parse-objects' function." (should (equal '(plain-text) (mapcar #'org-element-type (org-cite-parse-objects "s")))) (should (equal '(plain-text bold) (mapcar #'org-element-type (org-cite-parse-objects "s *b*")))) (should (equal '(link) (mapcar #'org-element-type (org-cite-parse-objects "[[link]]")))) ;; When optional argument is non-nil, only recognize types allowed ;; in as a citation reference affix. (should-not (equal '(link) (mapcar #'org-element-type (org-cite-parse-objects "[[link]]" t)))) (should (equal '(bold) (mapcar #'org-element-type (org-cite-parse-objects "*b*" t))))) (ert-deftest test-org-cite/make-paragraph () "Test `org-cite-make-paragraph' function." ;; Check string as argument. (should (eq 'paragraph (org-element-type (org-cite-make-paragraph "a")))) (should (equal '("a") (org-element-contents (org-cite-make-paragraph "a")))) ;; Check object as argument. (should (eq 'paragraph (org-element-type (org-cite-make-paragraph (org-element-create 'bold nil "b"))))) (should (equal '(bold) (mapcar #'org-element-type (org-element-contents (org-cite-make-paragraph (org-element-create 'bold nil "b")))))) ;; Check secondary string as argument. (should (eq 'paragraph (org-element-type (org-cite-make-paragraph '("a"))))) (should (equal '("a") (org-element-contents (org-cite-make-paragraph '("a"))))) ;; Mix all types of arguments. (should (equal '(plain-text bold plain-text) (mapcar #'org-element-type (org-element-contents (org-cite-make-paragraph "a" (org-element-create 'bold nil "b") '("c")))))) ;; Check `:parent' property. (should (eq 'paragraph (org-element-type (org-element-property :parent (car (org-element-contents (org-cite-make-paragraph "a")))))))) (ert-deftest test-org-cite/emphasize () "Test `org-cite-emphasize' function." ;; Raise an error if first argument has wrong type. (should-error (org-cite-emphasize 'code "a")) ;; Check string argument. (should (eq 'bold (org-element-type (org-cite-emphasize 'bold "a")))) (should (equal '("a") (org-element-contents (org-cite-emphasize 'bold "a")))) ;; Check object argument. (should (eq 'bold (org-element-type (org-cite-emphasize 'bold (org-element-create 'bold nil "a"))))) (should (equal '(italic) (mapcar #'org-element-type (org-element-contents (org-cite-emphasize 'bold (org-element-create 'italic nil "a")))))) ;; Check secondary string argument. (should (eq 'bold (org-element-type (org-cite-emphasize 'bold '("a"))))) (should (equal '("a") (org-element-contents (org-cite-emphasize 'bold '("a"))))) ;; Mix all types of arguments. (should (equal '(plain-text italic plain-text) (mapcar #'org-element-type (org-element-contents (org-cite-emphasize 'bold "a" (org-element-create 'italic nil "b") '("c")))))) ;; Check `:parent' property. (should (eq 'bold (org-element-type (org-element-property :parent (car (org-element-contents (org-cite-emphasize 'bold "a")))))))) (ert-deftest test-org-cite/concat () "Test `org-cite-concat' function." ;; Return nil when there is no data. (should (equal "" (org-element-interpret-data (org-cite-concat)))) ;; Concatenate strings, objects and secondary strings. (should (equal "ab" (org-element-interpret-data (org-cite-concat "a" "b")))) (should (equal "*a* b" (org-element-interpret-data (org-cite-concat (org-element-create 'bold nil "a") " b")))) (should (equal "*a* b" (org-element-interpret-data (org-cite-concat (list (org-element-create 'bold nil "a")) " b")))) ;; Return an error for any other object type. (should-error (org-cite-concat 2))) (ert-deftest test-org-cite/mapconcat () "Test `org-cite-mapconcat' function." (should (equal "" (org-element-interpret-data (org-cite-mapconcat #'identity nil "")))) (should (equal "ab" (org-element-interpret-data (org-cite-mapconcat #'identity '("a" "b") "")))) (should (equal "*a* b *c*" (org-element-interpret-data (org-cite-mapconcat #'identity (list (org-element-create 'bold nil "a") (list " b " (org-element-create 'bold nil "c"))) "")))) (should (equal "*a* *b*" (org-element-interpret-data (org-cite-mapconcat (lambda (s) (org-element-create 'bold nil s)) '("a" "b") " ")))) (should (equal "*a* b*c*" (org-element-interpret-data (org-cite-mapconcat #'identity (list (org-element-create 'bold nil "a") (list "b" (org-element-create 'bold nil "c"))) " "))))) ;;; Test capabilities. (ert-deftest test-org-cite/activate-capability () "Test \"activate\" capability." ;; Standard test. (should (eq 'success (catch :exit (org-test-with-temp-text "[cite:@key]" (let ((org-cite--processors nil) (org-cite-activate-processor 'foo)) (org-cite-register-processor 'foo :activate (lambda (_) (throw :exit 'success))) (font-lock-ensure)))))) ;; If there is no "follow" processor, or if processor does not ;; handle this capability, fall back to fontifying whole citation ;; with `org-cite' face and each key with `org-cite-key' face. (should (eq 'org-cite (org-test-with-temp-text "[cite:@key]" (let ((org-cite-activate-processor nil)) (font-lock-ensure) (face-at-point))))) (should (eq 'org-cite-key (org-test-with-temp-text "[cite:@key]" (let ((org-cite-activate-processor nil)) (font-lock-ensure) (face-at-point))))) (should (eq 'org-cite (org-test-with-temp-text "[cite:@key]" (let ((org-cite--processors nil) (org-cite-activate-processor 'foo)) (org-cite-register-processor 'foo) (font-lock-ensure) (face-at-point)))))) (ert-deftest test-org-cite/export-capability () "Test \"export\" capability." ;; Regular citations export. (should (eq 'success (catch :exit (org-test-with-temp-text "[cite:@key]" (let ((org-cite--processors nil) (org-cite-export-processors '((t . (foo nil nil))))) (org-cite-register-processor 'foo :export-citation (lambda (&rest _) (throw :exit 'success))) (org-export-as (org-export-create-backend))))))) ;; Export citation as string. (should (equal "citation\n" (org-test-with-temp-text "[cite:@key]" (let ((org-cite--processors nil) (org-cite-export-processors '((t . (foo nil nil))))) (org-cite-register-processor 'foo :export-citation (lambda (&rest _) "citation")) (org-export-as (org-export-create-backend :transcoders '((section . (lambda (_ c _) c)) (paragraph . (lambda (_ c _) c))))))))) ;; Export citation as parsed object. (should (equal "success\n" (org-test-with-temp-text "[cite:@key]" (let ((org-cite--processors nil) (org-cite-export-processors '((t . (foo nil nil))))) (org-cite-register-processor 'foo :export-citation (lambda (&rest _) (org-element-create 'bold nil "cite"))) (org-export-as (org-export-create-backend :transcoders '((section . (lambda (_ c _) c)) (paragraph . (lambda (_ c _) c)) (bold . (lambda (&rest _) "success"))))))))) ;; Export citation as a secondary string. (should (equal "boldtwo\n" (org-test-with-temp-text "[cite:@key]" (let ((org-cite--processors nil) (org-cite-export-processors '((t . (foo nil nil))))) (org-cite-register-processor 'foo :export-citation (lambda (&rest _) (list (org-element-create 'bold nil "one") "two"))) (org-export-as (org-export-create-backend :transcoders '((section . (lambda (_ c _) c)) (paragraph . (lambda (_ c _) c)) (bold . (lambda (&rest _) "bold"))))))))) ;; When exporting citation as a secondary string, last object ;; inherits post-blank from initial citation. (should (equal "twobold one-space\n" (org-test-with-temp-text "[cite:@key] one-space" (let ((org-cite--processors nil) (org-cite-export-processors '((t . (foo nil nil))))) (org-cite-register-processor 'foo :export-citation (lambda (&rest _) (list "two" (org-element-create 'bold nil "one")))) (org-export-as (org-export-create-backend :transcoders '((section . (lambda (_ c _) c)) (paragraph . (lambda (_ c _) c)) (bold . (lambda (&rest _) "bold"))))))))) (should (equal "boldtwo one-space\n" (org-test-with-temp-text "[cite:@key] one-space" (let ((org-cite--processors nil) (org-cite-export-processors '((t . (foo nil nil))))) (org-cite-register-processor 'foo :export-citation (lambda (&rest _) (list (org-element-create 'bold nil "one") "two"))) (org-export-as (org-export-create-backend :transcoders '((section . (lambda (_ c _) c)) (paragraph . (lambda (_ c _) c)) (bold . (lambda (&rest _) "bold"))))))))) ;; Make sure to have a space between a quote and a citation. (should (equal "\"quotation\" citation\n" (org-test-with-temp-text "\"quotation\"[cite:@key]" (let ((org-cite--processors nil) (org-cite-export-processors '((t . (foo nil nil))))) (org-cite-register-processor 'foo :export-citation (lambda (&rest _) "citation")) (org-export-as (org-export-create-backend :transcoders '((section . (lambda (_ c _) c)) (paragraph . (lambda (_ c _) c))))))))) (should (equal "\"quotation\" citation\n" (org-test-with-temp-text "\"quotation\" [cite:@key]" (let ((org-cite--processors nil) (org-cite-export-processors '((t . (foo nil nil))))) (org-cite-register-processor 'foo :export-citation (lambda (&rest _) "citation")) (org-export-as (org-export-create-backend :transcoders '((section . (lambda (_ c _) c)) (paragraph . (lambda (_ c _) c))))))))) ;; Regular bibliography export. (should (eq 'success (catch :exit (org-test-with-temp-text "#+print_bibliography:" (let ((org-cite--processors nil) (org-cite-export-processors '((t . (foo nil nil))))) (org-cite-register-processor 'foo :export-bibliography (lambda (&rest _) (throw :exit 'success)) :export-citation #'ignore) (org-export-as (org-export-create-backend))))))) (should (equal "" (org-test-with-temp-text "#+print_bibliography:" (let ((org-cite--processors nil) (org-cite-export-processors '((t . (foo nil nil))))) (org-cite-register-processor 'foo :export-citation #'ignore) (org-export-as (org-export-create-backend :transcoders '((section . (lambda (_ c _) c)) (paragraph . (lambda (_ c _) c))))))))) ;; Export bibliography as string. (should (equal "bibliography\n" (org-test-with-temp-text "#+print_bibliography:" (let ((org-cite--processors nil) (org-cite-export-processors '((t . (foo nil nil))))) (org-cite-register-processor 'foo :export-bibliography (lambda (&rest _) "bibliography") :export-citation #'ignore) (org-export-as (org-export-create-backend :transcoders '((section . (lambda (_ c _) c)) (paragraph . (lambda (_ c _) c))))))))) ;; Export bibliography as a parsed element. (should (equal "success\n" (org-test-with-temp-text "#+print_bibliography:" (let ((org-cite--processors nil) (org-cite-export-processors '((t . (foo nil nil))))) (org-cite-register-processor 'foo :export-bibliography (lambda (&rest _) (org-element-create 'example-block '(:value "foo"))) :export-citation #'ignore) (org-export-as (org-export-create-backend :transcoders '((section . (lambda (_ c _) c)) (example-block . (lambda (&rest _) "success"))))))))) ;; Export bibliography as a list of parsed elements. (should (equal "success\nsuccess\n" (org-test-with-temp-text "#+print_bibliography:" (let ((org-cite--processors nil) (org-cite-export-processors '((t . (foo nil nil))))) (org-cite-register-processor 'foo :export-bibliography (lambda (&rest _) (list (org-element-create 'example-block '(:value "foo")) (org-element-create 'example-block '(:value "bar")))) :export-citation #'ignore) (org-export-as (org-export-create-backend :transcoders '((section . (lambda (_ c _) c)) (example-block . (lambda (&rest _) "success"))))))))) ;; When exporting bibliography as a list of parsed elements, the ;; last element inherits post-blank from initial keyword. (should (equal "success\nsuccess\n\nText\n" (org-test-with-temp-text "#+print_bibliography:\n\nText" (let ((org-cite--processors nil) (org-cite-export-processors '((t . (foo nil nil))))) (org-cite-register-processor 'foo :export-bibliography (lambda (&rest _) (list (org-element-create 'example-block '(:value "foo")) (org-element-create 'example-block '(:value "bar")))) :export-citation #'ignore) (org-export-as (org-export-create-backend :transcoders '((section . (lambda (_ c _) c)) (example-block . (lambda (&rest _) "success")) (paragraph . (lambda (_ c _) c))))))))) ;; Use more appropriate citation processor. (should (equal '(p1 p1 p1 p3) (org-test-with-temp-text "[cite:@a]" (let ((org-export-registered-backends nil) (org-cite--processors nil) (org-cite-export-processors '((b1 . (p1)) (t . (p3))))) (org-cite-register-processor 'p1 :export-citation (lambda (&rest _) (throw :exit 'p1))) (org-cite-register-processor 'p2 :export-citation (lambda (&rest _) (throw :exit 'p2))) (org-cite-register-processor 'p3 :export-citation (lambda (&rest _) (throw :exit 'p3))) (org-export-define-backend 'b1 nil) (org-export-define-derived-backend 'b2 'b1) (org-export-define-derived-backend 'b3 'b2) (list (catch :exit (org-export-as 'b1)) (catch :exit (org-export-as 'b2)) (catch :exit (org-export-as 'b3)) (catch :exit (org-export-as (org-export-create-backend)))))))) (should (eq 'p2 (org-test-with-temp-text "#+cite_export: p2\n[cite:@a]" (let ((org-export-registered-backends nil) (org-cite--processors nil) (org-cite-export-processors '((t . (p1))))) (org-cite-register-processor 'p1 :export-citation (lambda (&rest _) (throw :exit 'p1))) (org-cite-register-processor 'p2 :export-citation (lambda (&rest _) (throw :exit 'p2))) (catch :exit (org-export-as (org-export-create-backend))))))) ;; Test finalizer. (should (eq 'success (catch :exit (org-test-with-temp-text "[cite:@key]" (let ((org-cite--processors nil) (org-cite-export-processors '((t . (foo nil nil))))) (org-cite-register-processor 'foo :export-citation (lambda (&rest _) "") :export-finalizer (lambda (&rest _) (throw :exit 'success))) (org-export-as (org-export-create-backend))))))) (should (equal "finalized!" (org-test-with-temp-text "[cite:@key]" (let ((org-cite--processors nil) (org-cite-export-processors '((t . (foo nil nil))))) (org-cite-register-processor 'foo :export-citation #'ignore :export-finalizer (lambda (&rest _) "finalized!")) (org-export-as (org-export-create-backend)))))) ;; Ignore citations when there is no selected "export" processor. ;; In that case, white space is removed before the citation, not ;; after. (should (equal "" (org-test-with-temp-text "[cite:@key]" (let ((org-cite-export-processors nil)) (org-export-as (org-export-create-backend :transcoders '((section . (lambda (_ c _) c)) (paragraph . (lambda (_ c _) c))))))))) (should (equal "Text.\n" (org-test-with-temp-text "Text [cite:@key]." (let ((org-cite-export-processors nil)) (org-export-as (org-export-create-backend :transcoders '((section . (lambda (_ c _) c)) (paragraph . (lambda (_ c _) c))))))))) ;; Throw an error if selected processor does not handle "export" ;; capability. (should-error (org-test-with-temp-text "[cite:@key]" (let ((org-cite--processors nil) (org-cite-export-processors '((t . (foo nil nil))))) (org-cite-register-processor 'foo) (org-export-as (org-export-create-backend)))))) (ert-deftest test-org-cite/follow-capability () "Test \"follow\" capability." ;; Standard test. (should (eq 'success (catch :exit (org-test-with-temp-text "[cite:@key]" (let ((org-cite--processors nil) (org-cite-follow-processor 'foo)) (org-cite-register-processor 'foo :follow (lambda (_ _) (throw :exit 'success))) (org-open-at-point)))))) ;; Throw an error if there is no "follow" processor, or if it is ;; unable to follow a citation. (should-error (org-test-with-temp-text "[cite:@key]" (let ((org-cite-follow-processor nil)) (org-open-at-point)))) (should-error (org-test-with-temp-text "[cite:@key]" (let ((org-cite--processors nil) (org-cite-follow-processor 'foo)) (org-cite-register-processor 'foo) (org-open-at-point))))) (ert-deftest test-org-cite/make-insert-processor () "Test `org-cite-make-insert-processor'." (should-error (org-cite-make-insert-processor 1 2)) (should-error (org-test-with-temp-text "[cite:@a]" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (org-cite-make-insert-processor #'ignore (lambda (&rest _) "s"))) (org-cite-insert nil)))) (should (equal "[cite:@k]" (org-test-with-temp-text "[cite:@a]" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (org-cite-make-insert-processor (lambda (&rest _) "k") (lambda (&rest _) "s"))) (org-cite-insert nil) (buffer-string))))) (should (equal "[cite:@k;@a]" (org-test-with-temp-text "[cite:@a]" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (org-cite-make-insert-processor (lambda (&rest _) "k") (lambda (&rest _) "s"))) (org-cite-insert nil) (buffer-string))))) (should (equal "[cite:@k;pre @a]" (org-test-with-temp-text "[cite:pre @a]" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (org-cite-make-insert-processor (lambda (&rest _) "k") (lambda (&rest _) "s"))) (org-cite-insert nil) (buffer-string))))) (should (equal "[cite:pre;@k;@a]" (org-test-with-temp-text "[cite:pre;@a]" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (org-cite-make-insert-processor (lambda (&rest _) "k") (lambda (&rest _) "s"))) (org-cite-insert nil) (buffer-string))))) (should (equal "[cite:@a;@k]" (org-test-with-temp-text "[cite:@a]" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (org-cite-make-insert-processor (lambda (&rest _) "k") (lambda (&rest _) "s"))) (org-cite-insert nil) (buffer-string))))) (should (equal "[cite:@a post;@k]" (org-test-with-temp-text "[cite:@a post]" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (org-cite-make-insert-processor (lambda (&rest _) "k") (lambda (&rest _) "s"))) (org-cite-insert nil) (buffer-string))))) (should (equal "[cite:@a;@k;post]" (org-test-with-temp-text "[cite:@a;post]" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (org-cite-make-insert-processor (lambda (&rest _) "k") (lambda (&rest _) "s"))) (org-cite-insert nil) (buffer-string))))) (should (equal "" (org-test-with-temp-text "[cite:@a]" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (org-cite-make-insert-processor (lambda (&rest _) "k") (lambda (&rest _) "s"))) (org-cite-insert t) (buffer-string))))) (should (equal "[cite/s:@a]" (org-test-with-temp-text "[cite:@a]" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (org-cite-make-insert-processor (lambda (&rest _) "k") (lambda (&rest _) "s"))) (org-cite-insert nil) (buffer-string))))) (should (equal "[cite:@a]" (org-test-with-temp-text "[cite/style:@a]" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (org-cite-make-insert-processor (lambda (&rest _) "k") (lambda (&rest _) ""))) (org-cite-insert nil) (buffer-string))))) (should-error (org-test-with-temp-text "[cite/style:@a]" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (org-cite-make-insert-processor (lambda (&rest _) "k") #'ignore)) (org-cite-insert nil)))) (should (equal "[cite:@a][cite:@k]" (org-test-with-temp-text "[cite:@a]" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (org-cite-make-insert-processor (lambda (&rest _) '("k")) (lambda (&rest _) "s"))) (org-cite-insert nil) (buffer-string))))) (should (equal "[cite:@k][cite:@a]" (org-test-with-temp-text "[cite:@a]" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (org-cite-make-insert-processor (lambda (&rest _) '("k")) (lambda (&rest _) "s"))) (org-cite-insert nil) (buffer-string))))) (should (equal "[cite/s:@k][cite:@a]" (org-test-with-temp-text "[cite:@a]" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (org-cite-make-insert-processor (lambda (&rest _) '("k")) (lambda (&rest _) "s"))) (org-cite-insert t) (buffer-string)))))) (ert-deftest test-org-cite/insert-capability () "Test \"insert\" capability." ;; Standard test. (should (eq 'success (catch :exit (org-test-with-temp-text "" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (lambda (_ _) (throw :exit 'success))) (call-interactively #'org-cite-insert)))))) ;; Throw an error if there is no "insert" processor, or if it is ;; unable to insert a citation. (should-error (org-test-with-temp-text "" (let ((org-cite-insert-processor nil)) (call-interactively #'org-cite-insert)))) (should-error (org-test-with-temp-text "" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo) (call-interactively #'org-cite-insert)))) ;; Throw an error if the location is inappropriate for a citation. (should-error (org-test-with-temp-text "=verbatim text=" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (lambda (_ _) (throw :exit 'success))) (call-interactively #'org-cite-insert)))) ;; Allow inserting citations at the beginning of a footnote ;; definition, right after the label. (should (eq 'success (catch :exit (org-test-with-temp-text "[fn:1]" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (lambda (_ _) (throw :exit 'success))) (call-interactively #'org-cite-insert)))))) (should (eq 'success (catch :exit (org-test-with-temp-text "[fn:1] " (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (lambda (_ _) (throw :exit 'success))) (call-interactively #'org-cite-insert)))))) (should (eq 'success (catch :exit (org-test-with-temp-text "[fn:1]\nParagraph" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (lambda (_ _) (throw :exit 'success))) (call-interactively #'org-cite-insert)))))) (should-error (org-test-with-temp-text "[fn:1]" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (lambda (_ _) (throw :exit 'success))) (call-interactively #'org-cite-insert)))) (should-error (org-test-with-temp-text "[fn:1]" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (lambda (_ _) (throw :exit 'success))) (call-interactively #'org-cite-insert)))) ;; Allow inserting citations in captions. (should (eq 'success (catch :exit (org-test-with-temp-text "#+caption: \n| table |" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (lambda (_ _) (throw :exit 'success))) (call-interactively #'org-cite-insert)))))) ;; Allow inserting citations in table cells. (should (eq 'success (catch :exit (org-test-with-temp-text "| table |" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (lambda (_ _) (throw :exit 'success))) (call-interactively #'org-cite-insert)))))) (should (eq 'success (catch :exit (org-test-with-temp-text "| table |" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (lambda (_ _) (throw :exit 'success))) (call-interactively #'org-cite-insert)))))) (should (eq 'success (catch :exit (org-test-with-temp-text "| table |" (let ((org-cite--processors nil) (org-cite-insert-processor 'foo)) (org-cite-register-processor 'foo :insert (lambda (_ _) (throw :exit 'success))) (call-interactively #'org-cite-insert))))))) (provide 'test-oc) ;;; test-oc.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ol-bbdb.el000066400000000000000000000023161500430433700214770ustar00rootroot00000000000000;;; test-ol-bbdb.el --- tests for ol-bbdb.el -*- lexical-binding: t; -*- ;; Copyright (C) 2018, 2019 Marco Wahl ;; Author: ;; Keywords: calendar ;; 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 . ;;; Commentary: ;; Test some of ol-bbdb.el. ;;; Code: (require 'ol-bbdb) (ert-deftest test-org-bbdb-anniv-extract-date () (should (equal nil (org-bbdb-anniv-extract-date "foo"))) (should (equal '(9 22 2018) (org-bbdb-anniv-extract-date "2018-09-22"))) (should (equal '(9 22 nil) (org-bbdb-anniv-extract-date "09-22")))) (provide 'test-ol-bbdb) ;;; test-org-bbdb.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ol.el000066400000000000000000000736751500430433700206300ustar00rootroot00000000000000;;; test-ol.el --- Tests for Org Links library -*- lexical-binding: t; -*- ;; Copyright (C) 2019 Nicolas Goaziou ;; Author: Nicolas Goaziou ;; 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 . ;;; Code: (require 'cl-lib) (require 'ol) (require 'org-id) ;;; Decode and Encode Links (ert-deftest test-org-link/encode () "Test `org-link-encode' specifications." ;; Regural test. (should (string= "Foo%3A%42ar" (org-link-encode "Foo:Bar" '(?\: ?\B)))) ;; Encode an ASCII character. (should (string= "%5B" (org-link-encode "[" '(?\[)))) ;; Encode an ASCII control character. (should (string= "%09" (org-link-encode "\t" '(9)))) ;; Encode a Unicode multibyte character. (should (string= "%E2%82%AC" (org-link-encode "€" '(?\€))))) (ert-deftest test-org-link/decode () "Test `org-link-decode' specifications." ;; Decode an ASCII character. (should (string= "[" (org-link-decode "%5B"))) ;; Decode an ASCII control character. (should (string= "\n" (org-link-decode "%0A"))) ;; Decode a Unicode multibyte character. (should (string= "€" (org-link-decode "%E2%82%AC")))) (ert-deftest test-org-link/encode-url-with-escaped-char () "Encode and decode a URL that includes an encoded char." (should (string= "http://some.host.com/form?&id=blah%2Bblah25" (org-link-decode (org-link-encode "http://some.host.com/form?&id=blah%2Bblah25" '(?\s ?\[ ?\] ?%)))))) (ert-deftest test-org-link/toggle-link-display () "Make sure that `org-toggle-link-display' is working. See https://github.com/yantar92/org/issues/4." (dolist (org-link-descriptive '(nil t)) (org-test-with-temp-text "* Org link test [[https://example.com][A link to a site]]" (dotimes (_ 2) (font-lock-ensure) (goto-char 1) (re-search-forward "\\[") (should-not (org-xor org-link-descriptive (org-invisible-p))) (re-search-forward "example") (should-not (org-xor org-link-descriptive (org-invisible-p))) (re-search-forward "com") (should-not (org-xor org-link-descriptive (org-invisible-p))) (re-search-forward "]") (should-not (org-xor org-link-descriptive (org-invisible-p))) (re-search-forward "\\[") (should-not (org-invisible-p)) (re-search-forward "link") (should-not (org-invisible-p)) (re-search-forward "]") (should-not (org-xor org-link-descriptive (org-invisible-p))) (org-toggle-link-display))))) ;;; Escape and Unescape Links (ert-deftest test-org-link/escape () "Test `org-link-escape' specifications." ;; No-op when there is no backslash or square bracket. (should (string= "foo" (org-link-escape "foo"))) ;; Escape square brackets at boundaries of the link. (should (string= "\\[foo\\]" (org-link-escape "[foo]"))) ;; Escape square brackets followed by another square bracket. (should (string= "foo\\]\\[bar" (org-link-escape "foo][bar"))) (should (string= "foo\\]\\]bar" (org-link-escape "foo]]bar"))) (should (string= "foo\\[\\[bar" (org-link-escape "foo[[bar"))) (should (string= "foo\\[\\]bar" (org-link-escape "foo[]bar"))) ;; Escape backslashes at the end of the link. (should (string= "foo\\\\" (org-link-escape "foo\\"))) ;; Escape backslashes that could be confused with escaping ;; characters. (should (string= "foo\\\\\\]" (org-link-escape "foo\\]"))) (should (string= "foo\\\\\\]\\[" (org-link-escape "foo\\]["))) (should (string= "foo\\\\\\]\\]bar" (org-link-escape "foo\\]]bar"))) ;; Do not escape backslash characters when unnecessary. (should (string= "foo\\bar" (org-link-escape "foo\\bar"))) ;; Pathological cases: consecutive closing square brackets. (should (string= "\\[\\[\\[foo\\]\\]\\]" (org-link-escape "[[[foo]]]"))) (should (string= "\\[\\[foo\\]\\] bar" (org-link-escape "[[foo]] bar")))) (ert-deftest test-org-link/unescape () "Test `org-link-unescape' specifications." ;; No-op if there is no backslash. (should (string= "foo" (org-link-unescape "foo"))) ;; No-op if backslashes are not escaping backslashes. (should (string= "foo\\bar" (org-link-unescape "foo\\bar"))) ;; Unescape backslashes before square brackets. (should (string= "foo]bar" (org-link-unescape "foo\\]bar"))) (should (string= "foo\\]" (org-link-unescape "foo\\\\\\]"))) (should (string= "foo\\][" (org-link-unescape "foo\\\\\\]["))) (should (string= "foo\\]]bar" (org-link-unescape "foo\\\\\\]\\]bar"))) (should (string= "foo\\[[bar" (org-link-unescape "foo\\\\\\[\\[bar"))) (should (string= "foo\\[]bar" (org-link-unescape "foo\\\\\\[\\]bar"))) ;; Unescape backslashes at the end of the link. (should (string= "foo\\" (org-link-unescape "foo\\\\"))) ;; Unescape closing square bracket at boundaries of the link. (should (string= "[foo]" (org-link-unescape "\\[foo\\]"))) ;; Pathological cases: consecutive closing square brackets. (should (string= "[[[foo]]]" (org-link-unescape "\\[\\[\\[foo\\]\\]\\]"))) (should (string= "[[foo]] bar" (org-link-unescape "\\[\\[foo\\]\\] bar")))) (ert-deftest test-org-link/make-string () "Test `org-link-make-string' specifications." ;; Throw an error on empty URI. (should-error (org-link-make-string "")) ;; Empty description returns a [[URI]] construct. (should (string= "[[uri]]"(org-link-make-string "uri"))) ;; Non-empty description returns a [[URI][DESCRIPTION]] construct. (should (string= "[[uri][description]]" (org-link-make-string "uri" "description"))) ;; Escape "]]" strings in the description with zero-width spaces. (should (let ((zws (string ?\x200B))) (string= (format "[[uri][foo]%s]bar]]" zws) (org-link-make-string "uri" "foo]]bar")))) ;; Prevent description from ending with a closing square bracket ;; with a zero-width space. (should (let ((zws (string ?\x200B))) (string= (format "[[uri][foo]%s]]" zws) (org-link-make-string "uri" "foo]"))))) ;;; Store links (ert-deftest test-org-link/store-link () "Test `org-store-link' specifications." ;; On a headline, link to that headline. Use heading as the ;; description of the link. (should (let (org-store-link-props org-stored-links) (org-test-with-temp-text-in-file "* H1" (let ((file (buffer-file-name))) (equal (format "[[file:%s::*H1][H1]]" file) (org-store-link nil)))))) ;; On a headline, remove TODO and COMMENT keywords, priority cookie, ;; and tags. (should (let (org-store-link-props org-stored-links) (org-test-with-temp-text-in-file "* TODO H1" (let ((file (buffer-file-name))) (equal (format "[[file:%s::*H1][H1]]" file) (org-store-link nil)))))) (should (let (org-store-link-props org-stored-links) (org-test-with-temp-text-in-file "* COMMENT H1" (let ((file (buffer-file-name))) (equal (format "[[file:%s::*H1][H1]]" file) (org-store-link nil)))))) (should (let (org-store-link-props org-stored-links) (org-test-with-temp-text-in-file "* [#A] H1" (let ((file (buffer-file-name))) (equal (format "[[file:%s::*H1][H1]]" file) (org-store-link nil)))))) (should (let (org-store-link-props org-stored-links) (org-test-with-temp-text-in-file "* H1 :tag:" (let ((file (buffer-file-name))) (equal (format "[[file:%s::*H1][H1]]" file) (org-store-link nil)))))) ;; On a headline, remove any link from description. (should (let (org-store-link-props org-stored-links) (org-test-with-temp-text-in-file "* [[#l][d]]" (let ((file (buffer-file-name))) (equal (format "[[file:%s::*%s][d]]" file (org-link-escape "[[#l][d]]")) (org-store-link nil)))))) (should (let (org-store-link-props org-stored-links) (org-test-with-temp-text-in-file "* [[l]]" (let ((file (buffer-file-name))) (equal (format "[[file:%s::*%s][l]]" file (org-link-escape "[[l]]")) (org-store-link nil)))))) (should (let (org-store-link-props org-stored-links) (org-test-with-temp-text-in-file "* [[l1][d1]] [[l2][d2]]" (let ((file (buffer-file-name))) (equal (format "[[file:%s::*%s][d1 d2]]" file (org-link-escape "[[l1][d1]] [[l2][d2]]")) (org-store-link nil)))))) ;; On a named element, link to that element. (should (let (org-store-link-props org-stored-links) (org-test-with-temp-text-in-file "#+NAME: foo\nParagraph" (let ((file (buffer-file-name))) (equal (format "[[file:%s::foo][foo]]" file) (org-store-link nil)))))) ;; Store link to Org buffer, with context. (should (let ((org-stored-links nil) (org-id-link-to-org-use-id nil) (org-context-in-file-links t)) (org-test-with-temp-text-in-file "* h1" (let ((file (buffer-file-name))) (equal (format "[[file:%s::*h1][h1]]" file) (org-store-link nil)))))) ;; Store link to Org buffer, without context. (should (let ((org-stored-links nil) (org-id-link-to-org-use-id nil) (org-context-in-file-links nil)) (org-test-with-temp-text-in-file "* h1" (let ((file (buffer-file-name))) (equal (format "[[file:%s]]" file file) (org-store-link nil)))))) ;; C-u prefix reverses `org-context-in-file-links' in Org buffer. (should (let ((org-stored-links nil) (org-id-link-to-org-use-id nil) (org-context-in-file-links nil)) (org-test-with-temp-text-in-file "* h1" (let ((file (buffer-file-name))) (equal (format "[[file:%s::*h1][h1]]" file) (org-store-link '(4))))))) ;; A C-u C-u does *not* reverse `org-context-in-file-links' in Org ;; buffer. (should (let ((org-stored-links nil) (org-id-link-to-org-use-id nil) (org-context-in-file-links nil)) (org-test-with-temp-text-in-file "* h1" (let ((file (buffer-file-name))) (equal (format "[[file:%s]]" file file) (org-store-link '(16))))))) ;; Store file link to non-Org buffer, with context. (should (let ((org-stored-links nil) (org-link-context-for-files t)) (org-test-with-temp-text-in-file "one\ntwo" (fundamental-mode) (let ((file (buffer-file-name))) (equal (format "[[file:%s::two]]" file) (org-store-link nil)))))) ;; Store file link to non-Org buffer, without context. (should (let ((org-stored-links nil) (org-context-in-file-links nil)) (org-test-with-temp-text-in-file "one\ntwo" (fundamental-mode) (let ((file (buffer-file-name))) (equal (format "[[file:%s]]" file file) (org-store-link nil)))))) ;; C-u prefix reverses `org-context-in-file-links' in non-Org ;; buffer. (should (let ((org-stored-links nil) (org-link-context-for-files nil)) (org-test-with-temp-text-in-file "one\ntwo" (fundamental-mode) (let ((file (buffer-file-name))) (equal (format "[[file:%s::two]]" file) (org-store-link '(4))))))) ;; A C-u C-u does *not* reverse `org-context-in-file-links' in ;; non-Org buffer. (should (let ((org-stored-links nil) (org-context-in-file-links nil)) (org-test-with-temp-text-in-file "one\ntwo" (fundamental-mode) (let ((file (buffer-file-name))) (equal (format "[[file:%s]]" file file) (org-store-link '(16))))))) ;; Context does not include special search syntax. (should (let ((org-stored-links nil) (org-context-in-file-links t)) (org-test-with-temp-text-in-file "(two)" (fundamental-mode) (let ((file (buffer-file-name))) (equal (format "[[file:%s::two]]" file file) (org-store-link nil)))))) (should (let ((org-stored-links nil) (org-context-in-file-links t)) (org-test-with-temp-text-in-file "#two" (fundamental-mode) (let ((file (buffer-file-name))) (equal (format "[[file:%s::two]]" file file) (org-store-link nil)))))) (should (let ((org-stored-links nil) (org-context-in-file-links t)) (org-test-with-temp-text-in-file "*two" (fundamental-mode) (let ((file (buffer-file-name))) (equal (format "[[file:%s::two]]" file file) (org-store-link nil)))))) (should (let ((org-stored-links nil) (org-context-in-file-links t)) (org-test-with-temp-text-in-file "( two )" (fundamental-mode) (let ((file (buffer-file-name))) (equal (format "[[file:%s::two]]" file file) (org-store-link nil)))))) (should (let ((org-stored-links nil) (org-context-in-file-links t)) (org-test-with-temp-text-in-file "# two" (fundamental-mode) (let ((file (buffer-file-name))) (equal (format "[[file:%s::two]]" file file) (org-store-link nil)))))) (should (let ((org-stored-links nil) (org-context-in-file-links t)) (org-test-with-temp-text-in-file "#( two )" (fundamental-mode) (let ((file (buffer-file-name))) (equal (format "[[file:%s::two]]" file file) (org-store-link nil)))))) (should (let ((org-stored-links nil) (org-context-in-file-links t)) (org-test-with-temp-text-in-file "#** ((## two) )" (fundamental-mode) (let ((file (buffer-file-name))) (equal (format "[[file:%s::two]]" file file) (org-store-link nil)))))) (should-not (let ((org-stored-links nil) (org-context-in-file-links t)) (org-test-with-temp-text-in-file "(two" (fundamental-mode) (let ((file (buffer-file-name))) (equal (format "[[file:%s::two]]" file file) (org-store-link nil)))))) ;; Context also ignore statistics cookies and special headlines ;; data. (should (let ((org-stored-links nil) (org-context-in-file-links t)) (org-test-with-temp-text-in-file "* TODO [#A] COMMENT foo :bar:" (let ((file (buffer-file-name))) (equal (format "[[file:%s::*foo][foo]]" file file) (org-store-link nil)))))) (should (let ((org-stored-links nil) (org-context-in-file-links t)) (org-test-with-temp-text-in-file "* foo[33%]bar" (let ((file (buffer-file-name))) (equal (format "[[file:%s::*foo bar][foo bar]]" file file) (org-store-link nil)))))) (should (let ((org-stored-links nil) (org-context-in-file-links t)) (org-test-with-temp-text-in-file "* [%][/] foo [35%] bar[3/5]" (let ((file (buffer-file-name))) (equal (format "[[file:%s::*foo bar][foo bar]]" file file) (org-store-link nil))))))) (ert-deftest test-org-link/precise-link-target () "Test `org-link-precise-link-target` specifications." (org-test-with-temp-text "* H1\n* H2\n" (should (equal '("*H1" "H1" 1) (org-link-precise-link-target)))) (org-test-with-temp-text "* H1\n#+name: foo\n#+begin_example\nhi\n#+end_example\n" (should (equal '("foo" "foo" 6) (org-link-precise-link-target)))) (org-test-with-temp-text "\nText\n* H1\n" (should (equal '("Text" nil 2) (org-link-precise-link-target)))) (org-test-with-temp-text "\n\n* H1\n" (should (equal nil (org-link-precise-link-target))))) (defmacro test-ol-stored-link-with-text (text &rest body) "Return :link and :description from link stored in body." (declare (indent 1)) `(let (org-store-link-plist) (org-test-with-temp-text-in-file ,text ,@body (list (plist-get org-store-link-plist :link) (plist-get org-store-link-plist :description))))) (ert-deftest test-org-link/id-store-link () "Test `org-id-store-link' specifications." (let ((org-id-link-to-org-use-id nil)) (should (equal '(nil nil) (test-ol-stored-link-with-text "* H1\n:PROPERTIES:\n:ID: abc\n:END:\n" (org-id-store-link-maybe t))))) ;; On a headline, link to that headline's ID. Use heading as the ;; description of the link. (let ((org-id-link-to-org-use-id t)) (should (equal '("id:abc" "H1") (test-ol-stored-link-with-text "* H1\n:PROPERTIES:\n:ID: abc\n:END:\n" (org-id-store-link-maybe t))))) ;; Remove TODO keywords etc from description of the link. (let ((org-id-link-to-org-use-id t)) (should (equal '("id:abc" "H1") (test-ol-stored-link-with-text "* TODO [#A] H1 :tag:\n:PROPERTIES:\n:ID: abc\n:END:\n" (org-id-store-link-maybe t))))) ;; create-if-interactive (let ((org-id-link-to-org-use-id 'create-if-interactive)) (should (equal '("id:abc" "H1") (cl-letf (((symbol-function 'org-id-new) (lambda (&rest _rest) "abc"))) (test-ol-stored-link-with-text "* H1\n" (org-id-store-link-maybe t))))) (should (equal '(nil nil) (test-ol-stored-link-with-text "* H1\n" (org-id-store-link-maybe nil))))) ;; create-if-interactive-and-no-custom-id (let ((org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id)) (should (equal '("id:abc" "H1") (cl-letf (((symbol-function 'org-id-new) (lambda (&rest _rest) "abc"))) (test-ol-stored-link-with-text "* H1\n" (org-id-store-link-maybe t))))) (should (equal '(nil nil) (test-ol-stored-link-with-text "* H1\n:PROPERTIES:\n:CUSTOM_ID: xyz\n:END:\n" (org-id-store-link-maybe t)))) (should (equal '(nil nil) (test-ol-stored-link-with-text "* H1\n" (org-id-store-link-maybe nil))))) ;; use-context should have no effect when on the headline with an id (let ((org-id-link-to-org-use-id t) (org-id-link-use-context t)) (should (equal '("id:abc" "H2") (test-ol-stored-link-with-text "* H1\n** H2\n:PROPERTIES:\n:ID: abc\n:END:\n" ;; simulate previously getting an inherited value (move-marker org-entry-property-inherited-from 1) (org-id-store-link-maybe t)))))) (ert-deftest test-org-link/id-store-link-using-parent () "Test `org-id-store-link' specifications with `org-id-link-consider-parent-id` set." ;; when using context to still find specific heading (let ((org-id-link-to-org-use-id t) (org-id-link-consider-parent-id t) (org-id-link-use-context t)) (should (equal '("id:abc::*H2" "H2") (test-ol-stored-link-with-text "* H1\n:PROPERTIES:\n:ID: abc\n:END:\n** H2\n" (org-id-store-link)))) (should (equal '("id:abc::name" "name") (test-ol-stored-link-with-text "* H1\n:PROPERTIES:\n:ID: abc\n:END:\n\n#+name: name\n#+begin_example\nhi\n#+end_example\n" (org-id-store-link)))) (should (equal '("id:abc" "H1") (test-ol-stored-link-with-text "* H1\n:PROPERTIES:\n:ID: abc\n:END:\n** H2\n" (org-id-store-link)))) ;; should not use newly added ids as search string, e.g. in an empty file (should (let (name result) (setq result (cl-letf (((symbol-function 'org-id-new) (lambda (&rest _rest) "abc"))) (test-ol-stored-link-with-text "" (setq name (buffer-name)) (org-id-store-link)))) (equal `("id:abc" ,name) result)))) ;; should not find targets in the next section (let ((org-id-link-to-org-use-id 'use-existing) (org-id-link-consider-parent-id t) (org-id-link-use-context t)) (should (equal '(nil nil) (test-ol-stored-link-with-text "* H1\n:PROPERTIES:\n:ID: abc\n:END:\n* H2\n** Target\n" (org-id-store-link-maybe t)))))) ;;; Radio Targets (ert-deftest test-org-link/update-radio-target-regexp () "Test `org-update-radio-target-regexp' specifications." ;; Properly update cache with no previous radio target regexp. (should (eq 'link (org-test-with-temp-text "radio\n\nParagraph\n\nradio" (save-excursion (goto-char (point-max)) (org-element-context)) (insert "<<<") (search-forward "o") (insert ">>>") (org-update-radio-target-regexp) (goto-char (point-max)) (org-element-type (org-element-context))))) ;; Properly update cache with previous radio target regexp. (should (eq 'link (org-test-with-temp-text "radio\n\nParagraph\n\nradio" (save-excursion (goto-char (point-max)) (org-element-context)) (insert "<<<") (search-forward "o") (insert ">>>") (org-update-radio-target-regexp) (search-backward "r") (delete-char 5) (insert "new") (org-update-radio-target-regexp) (goto-char (point-max)) (delete-region (line-beginning-position) (point)) (insert "new") (org-element-type (org-element-context)))))) ;;; Navigation (ert-deftest test-org-link/next-link () "Test `org-next-link' specifications." ;; Move to any type of link. (should (equal "[[link]]" (org-test-with-temp-text "foo [[link]]" (org-next-link) (buffer-substring (point) (line-end-position))))) (should (equal "http://link" (org-test-with-temp-text "foo http://link" (org-next-link) (buffer-substring (point) (line-end-position))))) (should (equal "" (org-test-with-temp-text "foo " (org-next-link) (buffer-substring (point) (line-end-position))))) ;; Ignore link at point. (should (equal "[[link2]]" (org-test-with-temp-text "[[link1]] [[link2]]" (org-next-link) (buffer-substring (point) (line-end-position))))) ;; Ignore fake links. (should (equal "[[truelink]]" (org-test-with-temp-text "foo\n: [[link]]\n[[truelink]]" (org-next-link) (buffer-substring (point) (line-end-position))))) ;; Do not move point when there is no link. (should (org-test-with-temp-text "foo bar" (org-next-link) (bobp))) ;; Wrap around after a failed search. (should (equal "[[link]]" (org-test-with-temp-text "[[link]]\nfoo" (org-next-link) (let* ((this-command 'org-next-link) (last-command this-command)) (org-next-link)) (buffer-substring (point) (line-end-position))))) ;; Find links with item tags. (should (equal "[[link1]]" (org-test-with-temp-text "- tag [[link1]] :: description" (org-next-link) (buffer-substring (point) (search-forward "]]" nil t)))))) (ert-deftest test-org-link/previous-link () "Test `org-previous-link' specifications." ;; Move to any type of link. (should (equal "[[link]]" (org-test-with-temp-text "[[link]]\nfoo" (org-previous-link) (buffer-substring (point) (line-end-position))))) (should (equal "http://link" (org-test-with-temp-text "http://link\nfoo" (org-previous-link) (buffer-substring (point) (line-end-position))))) (should (equal "" (org-test-with-temp-text "\nfoo" (org-previous-link) (buffer-substring (point) (line-end-position))))) ;; Ignore link at point. (should (equal "[[link1]]" (org-test-with-temp-text "[[link1]]\n[[link2]]" (org-previous-link) (buffer-substring (point) (line-end-position))))) (should (equal "[[link1]]" (org-test-with-temp-text "line\n[[link1]]\n[[link2]]" (org-previous-link) (buffer-substring (point) (line-end-position))))) ;; Ignore fake links. (should (equal "[[truelink]]" (org-test-with-temp-text "[[truelink]]\n: [[link]]\n" (org-previous-link) (buffer-substring (point) (line-end-position))))) ;; Do not move point when there is no link. (should (org-test-with-temp-text "foo bar" (org-previous-link) (eobp))) ;; Wrap around after a failed search. (should (equal "[[link]]" (org-test-with-temp-text "foo\n[[link]]" (org-previous-link) (let* ((this-command 'org-previous-link) (last-command this-command)) (org-previous-link)) (buffer-substring (point) (line-end-position)))))) ;;; Link regexps (defmacro test-ol-parse-link-in-text (text) "Return list of :type and :path of link parsed in TEXT. \"\" string must be at the beginning of the link to be parsed." (declare (indent 1)) `(org-test-with-temp-text ,text (list (org-element-property :type (org-element-link-parser)) (org-element-property :path (org-element-link-parser))))) (ert-deftest test-org-link/plain-link-re () "Test `org-link-plain-re'." (should (equal '("https" "//example.com") (test-ol-parse-link-in-text "(https://example.com)"))) (should (equal '("https" "//example.com/qwe()") (test-ol-parse-link-in-text "(Some text https://example.com/qwe())"))) (should (equal '("https" "//doi.org/10.1016/0160-791x(79)90023-x") (test-ol-parse-link-in-text "https://doi.org/10.1016/0160-791x(79)90023-x"))) (should (equal '("file" "aa") (test-ol-parse-link-in-text "The file:aa link"))) (should (equal '("file" "a(b)c") (test-ol-parse-link-in-text "The file:a(b)c link"))) (should (equal '("file" "a()") (test-ol-parse-link-in-text "The file:a() link"))) (should (equal '("file" "aa((a))") (test-ol-parse-link-in-text "The file:aa((a)) link"))) (should (equal '("file" "aa(())") (test-ol-parse-link-in-text "The file:aa(()) link"))) (should (equal '("file" "/a") (test-ol-parse-link-in-text "The file:/a link"))) (should (equal '("file" "/a/") (test-ol-parse-link-in-text "The file:/a/ link"))) (should (equal '("http" "//") (test-ol-parse-link-in-text "The http:// link"))) (should (equal '("file" "ab") (test-ol-parse-link-in-text "The (some file:ab) link"))) (should (equal '("file" "aa") (test-ol-parse-link-in-text "The file:aa) link"))) (should (equal '("file" "aa") (test-ol-parse-link-in-text "The file:aa( link"))) (should (equal '("http" "//foo.com/more_(than)_one_(parens)") (test-ol-parse-link-in-text "The http://foo.com/more_(than)_one_(parens) link"))) (should (equal '("http" "//foo.com/blah_(wikipedia)#cite-1") (test-ol-parse-link-in-text "The http://foo.com/blah_(wikipedia)#cite-1 link"))) (should (equal '("http" "//foo.com/blah_(wikipedia)_blah#cite-1") (test-ol-parse-link-in-text "The http://foo.com/blah_(wikipedia)_blah#cite-1 link"))) (should (equal '("http" "//foo.com/unicode_(✪)_in_parens") (test-ol-parse-link-in-text "The http://foo.com/unicode_(✪)_in_parens link"))) (should (equal '("http" "//foo.com/(something)?after=parens") (test-ol-parse-link-in-text "The http://foo.com/(something)?after=parens link")))) ;;; Insert Links (defmacro test-ol-with-link-parameters-as (type parameters &rest body) "Pass TYPE/PARAMETERS to `org-link-parameters' and execute BODY. Save the original value of `org-link-parameters', execute `org-link-set-parameters' with the relevant args, execute BODY and restore `org-link-parameters'. TYPE is as in `org-link-set-parameters'. PARAMETERS is a plist to be passed to `org-link-set-parameters'." (declare (indent 2)) (let (orig-parameters) ;; Copy all keys in `parameters' and their original values to ;; `orig-parameters'. (cl-loop for param in parameters by 'cddr do (setq orig-parameters (plist-put orig-parameters param (org-link-get-parameter type param)))) `(unwind-protect ;; Set `parameters' values and execute body. (progn (org-link-set-parameters ,type ,@parameters) ,@body) ;; Restore original values. (apply 'org-link-set-parameters ,type ',orig-parameters)))) (defun test-ol-insert-link-get-desc (&optional link-location description) "Insert link in temp buffer, return description. LINK-LOCATION and DESCRIPTION are passed to `org-insert-link' (COMPLETE-FILE is always nil)." (org-test-with-temp-text "" (org-insert-link nil link-location description) (save-match-data (when (and (org-in-regexp org-link-bracket-re 1) (match-end 2)) (match-string-no-properties 2))))) (defun test-ol/return-foobar (_link-test _desc) "Return string \"foobar\". Take (and ignore) arguments conforming to `:insert-description' API in `org-link-parameters'. Used in test `test-ol/insert-link-insert-description', for the case where `:insert-description' is a function symbol." "foobar-from-function") (ert-deftest test-org-link/insert-link-insert-description () "Test `:insert-description' parameter handling." ;; String case. (should (string= "foobar-string" (test-ol-with-link-parameters-as "id" (:insert-description "foobar-string") (test-ol-insert-link-get-desc "id:foo-bar")))) ;; Lambda case. (should (string= "foobar-lambda" (test-ol-with-link-parameters-as "id" (:insert-description (lambda (_link-test _desc) "foobar-lambda")) (test-ol-insert-link-get-desc "id:foo-bar")))) ;; Function symbol case. (should (string= "foobar-from-function" (test-ol-with-link-parameters-as "id" (:insert-description #'test-ol/return-foobar) (test-ol-insert-link-get-desc "id:foo-bar")))) ;; `:insert-description' parameter is defined, but doesn't return a ;; string. (should (null (test-ol-with-link-parameters-as "id" (:insert-description #'ignore) (test-ol-insert-link-get-desc "id:foo-bar")))) ;; Description argument should override `:insert-description'. (should (string= "foobar-desc-arg" (test-ol-with-link-parameters-as "id" (:insert-description "foobar") (test-ol-insert-link-get-desc "id:foo-bar" "foobar-desc-arg")))) ;; When neither `:insert-description' nor ;; `org-link-make-description-function' is defined, there should be ;; no description (should (null (let ((org-link-make-description-function nil)) (test-ol-insert-link-get-desc "fake-link-type:foo-bar"))))) (provide 'test-ol) ;;; test-ol.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-agenda.el000066400000000000000000001037141500430433700222060ustar00rootroot00000000000000;;; test-org-agenda.el --- Tests for org-agenda.el -*- lexical-binding: t ; -*- ;; Copyright (C) 2017, 2019 Marco Wahl ;; Author: Marco Wahl ;; 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 . ;;; Commentary: ;; Unit tests for Org Agenda. ;;; Code: (require 'org-test "../testing/org-test") (require 'org-agenda) (eval-and-compile (require 'cl-lib)) ;; General auxiliaries (defun org-test-agenda--agenda-buffers () "Return agenda buffers in a list." (cl-remove-if-not (lambda (x) (with-current-buffer x (eq major-mode 'org-agenda-mode))) (buffer-list))) (defun org-test-agenda--kill-all-agendas () "Kill all agenda buffers." (mapc #'kill-buffer (org-test-agenda--agenda-buffers))) (defmacro org-test-agenda-with-agenda (text &rest body) (declare (indent 1)) `(org-test-with-temp-text-in-file ,text (let ((org-agenda-files `(,buffer-file-name))) ,@body (org-test-agenda--kill-all-agendas)))) ;; Test the Agenda (ert-deftest test-org-agenda/empty () "Empty agenda." (cl-assert (not org-agenda-sticky) nil "precondition violation") (cl-assert (not (org-test-agenda--agenda-buffers)) nil "precondition violation") (let ((org-agenda-span 'day) org-agenda-files) (org-agenda-list) (set-buffer org-agenda-buffer-name) (should (= 2 (count-lines (point-min) (point-max))))) (org-test-agenda--kill-all-agendas)) (ert-deftest test-org-agenda/one-line () "One informative line in the agenda." (cl-assert (not org-agenda-sticky) nil "precondition violation") (cl-assert (not (org-test-agenda--agenda-buffers)) nil "precondition violation") (let ((org-agenda-span 'day) (org-agenda-files `(,(expand-file-name "examples/agenda-file.org" org-test-dir)))) ;; NOTE: Be aware that `org-agenda-list' may or may not display ;; past scheduled items depending whether the date is today ;; `org-today' or not. (org-agenda-list nil "<2017-03-10 Fri>") (set-buffer org-agenda-buffer-name) (should (= 3 (count-lines (point-min) (point-max))))) (org-test-agenda--kill-all-agendas)) (ert-deftest test-org-agenda/time-grid () "Test time grid settings." (cl-assert (not org-agenda-sticky) nil "precondition violation") (cl-assert (not (org-test-agenda--agenda-buffers)) nil "precondition violation") ;; Default time grid. (org-test-at-time "2024-01-17 8:00" (let ((org-agenda-span 'day) (org-agenda-files `(,(expand-file-name "examples/agenda-file2.org" org-test-dir)))) ;; NOTE: Be aware that `org-agenda-list' may or may not display ;; past scheduled items depending whether the date is today ;; `org-today' or not. (org-agenda-list nil "<2024-01-17 Fri>") (set-buffer org-agenda-buffer-name) (save-excursion (goto-char (point-min)) (should (search-forward "8:00...... now - - - - - - - - - - - - - - - - - - - - - - - - -"))) (save-excursion (goto-char (point-min)) (should (search-forward "agenda-file2: 9:30-10:00 Scheduled: TODO one"))) (save-excursion (goto-char (point-min)) (should (search-forward "agenda-file2:10:00-12:30 Scheduled: TODO two"))) (save-excursion (goto-char (point-min)) (should (search-forward "10:00...... ----------------"))) (save-excursion (goto-char (point-min)) (should (search-forward "agenda-file2:13:00-15:00 Scheduled: TODO three"))) (save-excursion (goto-char (point-min)) (should (search-forward "agenda-file2:19:00...... Scheduled: TODO four")))) (org-test-agenda--kill-all-agendas)) ;; Custom time grid strings (org-test-at-time "2024-01-17 8:00" (let ((org-agenda-span 'day) (org-agenda-files `(,(expand-file-name "examples/agenda-file2.org" org-test-dir))) (org-agenda-time-grid '((daily today require-timed) (800 1000 1200 1400 1600 1800 2000) "..." "^^^^^^^^^^^^^^" ))) ;; NOTE: Be aware that `org-agenda-list' may or may not display ;; past scheduled items depending whether the date is today ;; `org-today' or not. (org-agenda-list nil "<2024-01-17 Fri>") (set-buffer org-agenda-buffer-name) (save-excursion (goto-char (point-min)) (should (search-forward "10:00... ^^^^^^^^^^^^^^")))) (org-test-agenda--kill-all-agendas)) ;; Time grid remove-match (org-test-at-time "2024-01-17 8:00" (let ((org-agenda-span 'day) (org-agenda-files `(,(expand-file-name "examples/agenda-file2.org" org-test-dir))) (org-agenda-time-grid '((today remove-match) (800 1000 1200 1400 1600 1800 2000) "......" "----------------" ))) ;; NOTE: Be aware that `org-agenda-list' may or may not display ;; past scheduled items depending whether the date is today ;; `org-today' or not. (org-agenda-list nil "<2024-01-17 Fri>") (set-buffer org-agenda-buffer-name) (save-excursion (goto-char (point-min)) (should (search-forward "agenda-file2: 9:30-10:00 Scheduled: TODO one"))) (save-excursion (goto-char (point-min)) (should-not (search-forward "10:00...... ----------------" nil t)))) (org-test-agenda--kill-all-agendas)) ;; Time grid with `org-agenda-default-appointment-duration' (org-test-at-time "2024-01-17 8:00" (let ((org-agenda-span 'day) (org-agenda-files `(,(expand-file-name "examples/agenda-file2.org" org-test-dir))) (org-agenda-time-grid '((today remove-match) (800 1000 1200 1400 1600 1800 2000) "......" "----------------" )) (org-agenda-default-appointment-duration 60)) ;; NOTE: Be aware that `org-agenda-list' may or may not display ;; past scheduled items depending whether the date is today ;; `org-today' or not. (org-agenda-list nil "<2024-01-17 Fri>") (set-buffer org-agenda-buffer-name) (save-excursion (goto-char (point-min)) (should (search-forward "agenda-file2:19:00-20:00 Scheduled: TODO four"))) ;; Bug https://list.orgmode.org/orgmode/20211119135325.7f3f85a9@hsu-hh.de/ (save-excursion (goto-char (point-min)) (should (search-forward "14:00...... ----------------")))) (org-test-agenda--kill-all-agendas))) (ert-deftest test-org-agenda/todo-selector () "Test selecting keywords in `org-todo-list'." (cl-assert (not org-agenda-sticky) nil "precondition violation") (cl-assert (not (org-test-agenda--agenda-buffers)) nil "precondition violation") (let ((org-todo-keywords '((sequence "[ ]" "[X]") (sequence "TODO" "NEXT" "|" "DONE")))) (org-test-agenda-with-agenda " * [ ] Unchecked and will appear in agenda * NEXT NEXT will appear in agenda * [X] Checked and will not appear in agenda * TODO Todo and will appear in agenda * DONE Done and will not appear in agenda " ;; All todo keywords. (org-todo-list) (set-buffer org-agenda-buffer-name) (should (progn "all todo" (goto-char (point-min)) (search-forward "[ ] Unchecked and will appear in agenda" nil t))) (should (progn "all todo" (goto-char (point-min)) (search-forward "NEXT NEXT will appear in agenda" nil t))) (should (progn "all todo" (goto-char (point-min)) (search-forward "TODO Todo and will appear in agenda" nil t))) ;; All todo keywords, including not done. (org-todo-list "*") (should (progn "all keywords" (goto-char (point-min)) (search-forward "[ ] Unchecked and will appear in agenda" nil t))) (should (progn "all keywords" (goto-char (point-min)) (search-forward "[X] Checked and will not appear in agenda" nil t))) (should (progn "all keywords" (goto-char (point-min)) (search-forward "DONE Done and will not appear in agenda" nil t))) (should (progn "all keywords" (goto-char (point-min)) (search-forward "NEXT NEXT will appear in agenda" nil t))) (should (progn "all keywords" (goto-char (point-min)) (search-forward "TODO Todo and will appear in agenda" nil t))) ;; Just [ ] regexp-like entry. (org-todo-list "[ ]") (should (progn "[ ] keyword" (goto-char (point-min)) (search-forward "[ ] Unchecked and will appear in agenda" nil t))) (should-not (progn "[ ] keyword" (goto-char (point-min)) (search-forward "NEXT NEXT will appear in agenda" nil t))) (should-not (progn "[ ] keyword" (goto-char (point-min)) (search-forward "TODO Todo and will appear in agenda" nil t))) ;; Two keywords. (org-todo-list "NEXT|TODO") (should-not (progn "NEXT|TODO" (goto-char (point-min)) (search-forward "[ ] Unchecked and will appear in agenda" nil t))) (should (progn "NEXT|TODO" (goto-char (point-min)) (search-forward "NEXT NEXT will appear in agenda" nil t))) (should (progn "NEXT|TODO" (goto-char (point-min)) (search-forward "TODO Todo and will appear in agenda" nil t)))))) (ert-deftest test-org-agenda/scheduled-non-todo () "One informative line in the agenda from scheduled non-todo-keyword-item." (cl-assert (not org-agenda-sticky) nil "precondition violation") (cl-assert (not (org-test-agenda--agenda-buffers)) nil "precondition violation") (dolist (org-element-use-cache '(t nil)) (let ((org-agenda-span 'day) (org-agenda-files `(,(expand-file-name "examples/agenda-file.org" org-test-dir)))) ;; NOTE: Be aware that `org-agenda-list' may or may not display ;; past scheduled items depending whether the date is today ;; `org-today' or not. (org-agenda-list nil "<2017-07-19 Wed>") (set-buffer org-agenda-buffer-name) (should (progn (goto-line 3) (looking-at " *agenda-file:Scheduled: *test agenda")))) (org-test-agenda--kill-all-agendas))) (ert-deftest test-org-agenda/non-scheduled-re-matches () "Make sure that scheduled-looking elements do not appear in agenda. See https://list.orgmode.org/20220101200103.GB29829@itccanarias.org/T/#t." (cl-assert (not org-agenda-sticky) nil "precondition violation") (cl-assert (not (org-test-agenda--agenda-buffers)) nil "precondition violation") (let ((org-agenda-span 'day) (org-agenda-files `(,(expand-file-name "examples/agenda-file.org" org-test-dir)))) ;; NOTE: Be aware that `org-agenda-list' may or may not display ;; past scheduled items depending whether the date is today ;; `org-today' or not. (org-agenda-list nil "<2022-01-03 Mon>") (set-buffer org-agenda-buffer-name) (should (= 2 (count-lines (point-min) (point-max))))) (org-test-agenda--kill-all-agendas)) (ert-deftest test-org-agenda/org-search-view () "Test `org-search-view' specifications." (cl-assert (not org-agenda-sticky) nil "precondition violation") (cl-assert (not (org-test-agenda--agenda-buffers)) nil "precondition violation") ;; Search a string. (let ((org-agenda-files `(,(expand-file-name "examples/agenda-search.org" org-test-dir)))) (org-search-view nil "foo") (set-buffer org-agenda-buffer-name) (should (= 4 (count-lines (point-min) (point-max))))) ;; Search past inlinetask. (let ((org-agenda-files `(,(expand-file-name "examples/agenda-search.org" org-test-dir)))) (org-search-view nil "bar") (set-buffer org-agenda-buffer-name) (should (= 3 (count-lines (point-min) (point-max))))) ;; Search inside inlinetask. (let ((org-agenda-files `(,(expand-file-name "examples/agenda-search.org" org-test-dir)))) (org-search-view nil "text inside inlinetask") (set-buffer org-agenda-buffer-name) (should (= 3 (count-lines (point-min) (point-max))))) (org-test-agenda--kill-all-agendas)) (ert-deftest test-org-agenda/property-timestamp () "Match timestamps inside property drawer. See https://list.orgmode.org/06d301d83d9e$f8b44340$ea1cc9c0$@tomdavey.com" (cl-assert (not org-agenda-sticky) nil "precondition violation") (cl-assert (not (org-test-agenda--agenda-buffers)) nil "precondition violation") (let ((org-agenda-span 'day) (org-agenda-files `(,(expand-file-name "examples/agenda-file.org" org-test-dir)))) ;; NOTE: Be aware that `org-agenda-list' may or may not display ;; past scheduled items depending whether the date is today ;; `org-today' or not. (org-agenda-list nil "<2022-03-22 Tue>") (set-buffer org-agenda-buffer-name) (should (= 3 (count-lines (point-min) (point-max)))) ;; NOTE: Be aware that `org-agenda-list' may or may not display ;; past scheduled items depending whether the date is today ;; `org-today' or not. (org-agenda-list nil "<2022-03-25 Fri>") (set-buffer org-agenda-buffer-name) (should (= 3 (count-lines (point-min) (point-max))))) (org-test-agenda--kill-all-agendas)) (ert-deftest test-org-agenda/set-priority () "One informative line in the agenda. Check that org-agenda-priority updates the agenda." (cl-assert (not org-agenda-sticky) nil "precondition violation") (cl-assert (not (org-test-agenda--agenda-buffers)) nil "precondition violation") (let ((org-agenda-span 'day) (org-agenda-files `(,(expand-file-name "examples/agenda-file.org" org-test-dir)))) ;; NOTE: Be aware that `org-agenda-list' may or may not display ;; past scheduled items depending whether the date is today ;; `org-today' or not. (org-agenda-list nil "<2017-07-19 Wed>") (set-buffer org-agenda-buffer-name) (should (progn (goto-line 3) (org-agenda-priority ?B) (looking-at-p " *agenda-file:Scheduled: *\\[#B\\] test agenda")))) (org-test-agenda--kill-all-agendas)) (ert-deftest test-org-agenda/sticky-agenda-name () "Agenda buffer name after having created one sticky agenda buffer." (cl-assert (not org-agenda-sticky) nil "precondition violation") (cl-assert (not (org-test-agenda--agenda-buffers)) nil "precondition violation") (let ((org-agenda-span 'day) (buf (get-buffer org-agenda-buffer-name)) org-agenda-files) (when buf (kill-buffer buf)) (dolist (fn '(org-agenda-list org-todo-list)) (org-test-with-temp-text "<2017-03-17 Fri>" (org-follow-timestamp-link)) ;creates a sticky agenda (org-test-agenda--kill-all-agendas) (funcall fn) (should (= 1 (length (org-test-agenda--agenda-buffers)))) (should (string= "*Org Agenda*" (buffer-name (car (org-test-agenda--agenda-buffers))))))) (org-test-agenda--kill-all-agendas)) (ert-deftest test-org-agenda/sticky-agenda-name-after-reload () "Agenda buffer name of sticky agenda after reload." (cl-assert (not org-agenda-sticky) nil "precondition violation") (cl-assert (not (org-test-agenda--agenda-buffers)) nil "precondition violation") (org-toggle-sticky-agenda) (let (org-agenda-files) (org-agenda-list) (let* ((agenda-buffer-name (progn (cl-assert (= 1 (length (org-test-agenda--agenda-buffers)))) (buffer-name (car (org-test-agenda--agenda-buffers)))))) (set-buffer agenda-buffer-name) (org-agenda-redo) (should (= 1 (length (org-test-agenda--agenda-buffers)))) (should (string= agenda-buffer-name (buffer-name (car (org-test-agenda--agenda-buffers))))))) (org-toggle-sticky-agenda) (org-test-agenda--kill-all-agendas)) (ert-deftest test-org-agenda/sticky-agenda-filter-preset () "Update sticky agenda buffers properly with preset of filters." (unless org-agenda-sticky (org-toggle-sticky-agenda)) (org-test-agenda-with-agenda "* TODO Foo" (org-set-property "CATEGORY" "foo") (let ((org-agenda-custom-commands '(("f" "foo: multi-command" ((tags-todo "+CATEGORY=\"foo\"") (alltodo "")) ((org-agenda-category-filter-preset '("+foo")))) ("b" "bar: multi-command" ((tags-todo "+CATEGORY=\"bar\"") (alltodo "")) ((org-agenda-category-filter-preset '("+bar")))) ("f1" "foo: single-command" tags-todo "+CATEGORY=\"foo\"" ((org-agenda-category-filter-preset '("+foo")))) ("b1" "bar: single-command" tags-todo "+CATEGORY=\"bar\"" ((org-agenda-category-filter-preset '("+bar")))) ("f2" "foo: single-command" alltodo "" ((org-agenda-category-filter-preset '("+foo")))) ("b2" "bar: single-command" alltodo "" ((org-agenda-category-filter-preset '("+bar"))))))) (org-agenda nil "f") (org-agenda nil "b") (set-buffer "*Org Agenda(f)*") (org-agenda-redo) (goto-char (point-min)) (should (not (invisible-p (1- (search-forward "TODO Foo"))))) (org-test-agenda--kill-all-agendas) (org-agenda nil "f1") (org-agenda nil "b1") (set-buffer "*Org Agenda(f1:+CATEGORY=\"foo\")*") (org-agenda-redo) (goto-char (point-min)) (should (not (invisible-p (1- (search-forward "TODO Foo"))))) (org-test-agenda--kill-all-agendas) (org-agenda nil "f2") (org-agenda nil "b2") (set-buffer "*Org Agenda(f2)*") (org-agenda-redo) (goto-char (point-min)) (should (not (invisible-p (1- (search-forward "TODO Foo"))))))) (org-toggle-sticky-agenda)) (ert-deftest test-org-agenda/skip-if () "Test `org-agenda-skip-if'." (dolist (options '((scheduled) (notscheduled) (deadline) (notdeadline) (timestamp) (nottimestamp) (regexp "hello") (notregexp "hello") ;; TODO: Test for specific TODO keywords (todo ("*")) (nottodo ("*")))) (should (equal (if (memq (car options) '(notscheduled notdeadline nottimestamp regexp nottodo)) 8 nil) (org-test-with-temp-text "* hello" (org-agenda-skip-if nil options)))) (should (equal (if (memq (car options) '(scheduled notdeadline timestamp regexp nottodo)) 36 nil) (org-test-with-temp-text "* hello SCHEDULED: <2023-07-15 Sat>" (org-agenda-skip-if nil options)))) (should (equal (if (memq (car options) '(notscheduled deadline timestamp regexp nottodo)) 35 nil) (org-test-with-temp-text "* hello DEADLINE: <2023-07-15 Sat>" (org-agenda-skip-if nil options)))) (should (equal (if (memq (car options) '(notscheduled notdeadline timestamp regexp nottodo)) 25 nil) (org-test-with-temp-text "* hello <2023-07-15 Sat>" (org-agenda-skip-if nil options)))) (should (equal (if (memq (car options) '(notscheduled notdeadline nottimestamp notregexp nottodo)) 10 nil) (org-test-with-temp-text "* goodbye" (org-agenda-skip-if nil options)))) (should (equal (if (memq (car options) '(notscheduled notdeadline nottimestamp notregexp todo)) 26 nil) (org-test-with-temp-text "* TODO write better tests" (org-agenda-skip-if nil options)))))) (ert-deftest test-org-agenda/timestamp-ignore-todo-item () "Test if `org-agenda' ignores a todo item with a timestamp. Based on the following variables: `org-agenda-todo-ignore-deadlines', `org-agenda-todo-ignore-scheduled', and `org-agenda-todo-ignore-timestamp'." ;; TODO: test `org-agenda-todo-ignore-with-date'. ;; Maybe test having multiple variables set. (let ((org-agenda-custom-commands '(("f" "no fluff" todo "" ((org-agenda-todo-keyword-format "") (org-agenda-overriding-header "") (org-agenda-prefix-format ""))))) (org-deadline-warning-days 1) (expected-return (lambda (timestamp value) (cl-case timestamp (past (memq value '(all near past -1 -2 -3))) (yesteryesterday (memq value '(all near past -1 -2))) (yesterday (memq value '(all near past -1))) (today (memq value '(all near past 0))) (tomorrow (memq value '(all near future 0 1))) (tomorroworrow (memq value '(all far future 0 1 2))) (future (memq value '(all far future 0 1 2 3)))))) ;; Lexically bind the variables we're changing org-agenda-todo-ignore-deadlines org-agenda-todo-ignore-scheduled org-agenda-todo-ignore-timestamp) (org-test-at-time "2023-01-15" (dolist (variable '(org-agenda-todo-ignore-deadlines org-agenda-todo-ignore-scheduled org-agenda-todo-ignore-timestamp)) (dolist (type '(timestamp scheduled deadline)) ;; nil is last so it resets the variable for the next one (dolist (value `(past future all 3 2 1 0 -1 -2 -3 ,@(when (eq type 'deadline) '(near far nil)))) (dolist (timestamp '((past . "<2023-01-01>") (yesteryesterday . "<2023-01-13>") (yesterday . "<2023-01-14>") (today . "<2023-01-15>") (tomorrow . "<2023-01-16>") (tomorroworrow . "<2023-01-17>") (future . "<2023-01-31>"))) ;; Uncomment to debug failure ;; (message "Type: %S, Variable: %S, Value: %S, Time: %S" type variable value (car timestamp)) (set variable value) (org-test-agenda-with-agenda (cl-case type (timestamp (concat "* TODO hello\n" (cdr timestamp))) (scheduled (concat "* TODO hello SCHEDULED: " (cdr timestamp))) (deadline (concat "* TODO hello DEADLINE: " (cdr timestamp)))) (should (string-equal (or (and (funcall expected-return (car timestamp) value) (cl-case variable (org-agenda-todo-ignore-deadlines (eq type 'deadline)) (org-agenda-todo-ignore-scheduled (eq type 'scheduled)) (org-agenda-todo-ignore-timestamp (eq type 'timestamp))) "") "hello\n") (progn (org-agenda nil "f") (buffer-string)))))))))))) (ert-deftest test-org-agenda/skip-scheduled-repeats-after-deadline () "Test `org-agenda-skip-scheduled-repeats-after-deadline'." (cl-assert (not org-agenda-sticky) nil "precondition violation") (cl-assert (not (org-test-agenda--agenda-buffers)) nil "precondition violation") (dolist (org-agenda-skip-scheduled-repeats-after-deadline '(nil t)) (org-test-at-time "2024-01-01 8:00" (org-test-with-temp-text-in-file " * TODO Do me every day until Jan, 5th (inclusive) SCHEDULED: <2024-01-03 Wed +1d> DEADLINE: <2024-01-05 Fri> " (let ((org-agenda-span 'week) (org-agenda-files `(,(buffer-file-name)))) ;; NOTE: Be aware that `org-agenda-list' may or may not display ;; past scheduled items depending whether the date is today ;; `org-today' or not. (org-agenda-list nil "<2024-01-01 Mon>") (set-buffer org-agenda-buffer-name) (if org-agenda-skip-scheduled-repeats-after-deadline (should ;; Not displayed after deadline. (string-match-p "Week-agenda (W01): Monday 1 January 2024 W01 [^:]+:In 4 d.: TODO Do me every day until Jan, 5th (inclusive) Tuesday 2 January 2024 Wednesday 3 January 2024 [^:]+:Scheduled: TODO Do me every day until Jan, 5th (inclusive) Thursday 4 January 2024 [^:]+:Scheduled: TODO Do me every day until Jan, 5th (inclusive) Friday 5 January 2024 [^:]+:Scheduled: TODO Do me every day until Jan, 5th (inclusive) [^:]+:Deadline: TODO Do me every day until Jan, 5th (inclusive) Saturday 6 January 2024 Sunday 7 January 2024" (buffer-string))) (should ;; Displayed after deadline. (string-match-p "Week-agenda (W01): Monday 1 January 2024 W01 [^:]+:In 4 d.: TODO Do me every day until Jan, 5th (inclusive) Tuesday 2 January 2024 Wednesday 3 January 2024 [^:]+:Scheduled: TODO Do me every day until Jan, 5th (inclusive) Thursday 4 January 2024 [^:]+:Scheduled: TODO Do me every day until Jan, 5th (inclusive) Friday 5 January 2024 [^:]+:Scheduled: TODO Do me every day until Jan, 5th (inclusive) [^:]+:Deadline: TODO Do me every day until Jan, 5th (inclusive) Saturday 6 January 2024 [^:]+:Scheduled: TODO Do me every day until Jan, 5th (inclusive) Sunday 7 January 2024 [^:]+:Scheduled: TODO Do me every day until Jan, 5th (inclusive)" (buffer-string)))))) (org-test-agenda--kill-all-agendas)))) (ert-deftest test-org-agenda/goto-date () "Test `org-agenda-goto-date'." (unwind-protect (should (equal (time-to-days (org-time-string-to-time "2019-12-30")) (let ((org-agenda-files nil)) (org-agenda-list nil nil 'day) (org-agenda-goto-date "2019-12-30") (get-text-property (point) 'day)))) (org-test-agenda--kill-all-agendas))) (ert-deftest test-org-agenda/file-restriction () "Test file restriction for org agenda." (org-test-with-temp-text-in-file "* TODO Foo" (org-agenda-set-restriction-lock t) (org-agenda nil "t") (should (search-forward "Foo")) (should (org-agenda-files)) (should-not (org-agenda-files t)) (org-agenda-remove-restriction-lock) (goto-char (point-min)) (should-not (search-forward "Foo" nil t)) (should-not (org-agenda-files))) (org-test-with-temp-text-in-file "* TODO Bar" (org-agenda nil "t" 'buffer) (should (search-forward "Bar")) (should (org-agenda-files)) (should-not (org-agenda-files t)) (org-agenda-remove-restriction-lock) (goto-char (point-min)) (should-not (search-forward "Bar" nil t)) (should-not (org-agenda-files))) (org-test-with-temp-text-in-file "* TODO Bar" (org-agenda nil "t" 'buffer) (org-agenda nil "t") (should-not (search-forward "Bar" nil t)) (should-not (org-agenda-files))) (org-test-agenda--kill-all-agendas)) (ert-deftest test-org-agenda/skip-deadline-prewarning-if-scheduled () "Test `org-agenda-skip-deadline-prewarning-if-scheduled'." (org-test-at-time "2024-01-15" (let ((org-agenda-skip-deadline-prewarning-if-scheduled t)) (org-test-agenda-with-agenda "* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>" (org-agenda-list nil nil 1) (should-not (search-forward "In " nil t)))) (let ((org-agenda-skip-deadline-prewarning-if-scheduled 10)) (org-test-agenda-with-agenda "* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>" (org-agenda-list nil nil 1) (should (search-forward "In " nil t)))) ;; Custom prewarning cookie "-3d", so there should be no warning anyway. (let ((org-agenda-skip-deadline-prewarning-if-scheduled 10)) (org-test-agenda-with-agenda "* TODO foo\nDEADLINE: <2024-01-20 Sat -3d> SCHEDULED: <2024-01-19 Fri>" (org-agenda-list nil nil 1) (should-not (search-forward "In " nil t)))) (let ((org-agenda-skip-deadline-prewarning-if-scheduled 3)) (org-test-agenda-with-agenda "* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>" (org-agenda-list nil nil 1) (should-not (search-forward "In " nil t)))) (let ((org-agenda-skip-deadline-prewarning-if-scheduled nil)) (org-test-agenda-with-agenda "* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>" (org-agenda-list nil nil 1) (should (search-forward "In " nil t)))) (let ((org-agenda-skip-deadline-prewarning-if-scheduled 'pre-scheduled)) (org-test-agenda-with-agenda "* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-16 Tue>" (org-agenda-list nil nil 1) (should-not (search-forward "In " nil t)))) (let ((org-agenda-skip-deadline-prewarning-if-scheduled 'pre-scheduled)) (org-test-agenda-with-agenda "* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-15 Mon>" (org-agenda-list nil nil 1) (should (search-forward "In " nil t)))))) (ert-deftest test-org-agenda/diary-timestamp () "Test diary timestamp handling." (org-test-at-time "2024-01-15" (org-test-agenda-with-agenda "* TODO foo\n<%%(diary-date 01 15 2024)>" (org-agenda-list nil nil 1) (should (search-forward "foo" nil t))) (org-test-agenda-with-agenda "* TODO foo\n<%%(diary-date 02 15 2024)>" (org-agenda-list nil nil 1) (should-not (search-forward "foo" nil t))) ;; Test time and time ranges in diary timestamps. (org-test-agenda-with-agenda "* TODO foo\n<%%(diary-date 01 15 2024) 12:00>" (org-agenda-list nil nil 1) (should (search-forward "12:00" nil t))) (org-test-agenda-with-agenda "* TODO foo\n<%%(diary-date 01 15 2024) 12:00-14:00>" (org-agenda-list nil nil 1) (should (search-forward "12:00-14:00" nil t))))) ;; agenda redo (require 'face-remap) (ert-deftest test-org-agenda/rescale () "Text scale survives `org-agenda-redo'." (org-test-agenda--kill-all-agendas) (unwind-protect (let ((org-agenda-span 'day) org-agenda-files) (org-agenda-list) (set-buffer org-agenda-buffer-name) (text-scale-mode) (text-scale-set 11) (cl-assert (and (boundp text-scale-mode) text-scale-mode)) (org-agenda-redo) (should text-scale-mode) (should (= 11 text-scale-mode-amount))) (org-test-agenda--kill-all-agendas))) (ert-deftest test-org-agenda/redo-setting () "Command settings survives `org-agenda-redo'." (org-test-agenda--kill-all-agendas) (let ((org-agenda-custom-commands '(("t" "TODOs" alltodo "" ((org-agenda-overriding-header "Test")))))) (org-agenda nil "t") (org-agenda-redo) (org-agenda-redo) (goto-char (point-min)) (should (looking-at-p "Test"))) (org-test-agenda--kill-all-agendas)) (ert-deftest test-org-agenda/diary-inclusion () "Diary inclusion happens." (org-test-agenda--kill-all-agendas) (let ((diary-file (expand-file-name "examples/diary-file" org-test-dir)) (org-agenda-files `(,(expand-file-name "examples/agenda-file.org" org-test-dir))) (diary-date-forms '((month "[-/]" day "[^-/0-9]") (year "[-/]" month "[-/]" day "[^0-9]") (monthname " *" day "[^-0-9]") (year " *" monthname " *" day "[^0-9]") (dayname "\\W"))) (org-agenda-span 'day) (org-agenda-include-diary t)) ;; NOTE: Be aware that `org-agenda-list' may or may not display ;; past scheduled items depending whether the date is today ;; `org-today' or not. (org-agenda-list nil "<2019-01-08>") (should (search-forward "f0bcf0cd8bad93c1451bb6e1b2aaedef5cce7cbb" nil t)) (org-test-agenda--kill-all-agendas))) ;; agenda bulk actions (ert-deftest test-org-agenda/bulk () "Bulk actions are applied to marked items." (org-test-agenda-with-agenda "* TODO a\n* TODO b" (org-todo-list) (org-agenda-bulk-mark-all) (cl-letf (((symbol-function 'read-char-exclusive) (lambda () ?t)) ((symbol-function 'completing-read) (lambda (&rest _rest) "DONE"))) (org-agenda-bulk-action)) (org-agenda-previous-item 99) (should (looking-at ".*DONE a")) (org-agenda-next-item 1) (should (looking-at ".*DONE b")))) (ert-deftest test-org-agenda/bulk-custom () "Custom bulk actions are applied to all marked items." (org-test-agenda-with-agenda "* TODO a\n* TODO b" (org-todo-list) (org-agenda-bulk-mark-all) ;; Mock read functions (let* ((f-call-cnt 0) (org-agenda-bulk-custom-functions `((?P ,(lambda () (setq f-call-cnt (1+ f-call-cnt))))))) (cl-letf* (((symbol-function 'read-char-exclusive) (lambda () ?P))) (org-agenda-bulk-action) (should (= f-call-cnt 2)))))) (ert-deftest test-org-agenda/bulk-custom-arg-func () "Argument collection functions can be specified for custom bulk functions." (org-test-agenda-with-agenda "* TODO a\n* TODO b" (org-todo-list) (org-agenda-bulk-mark-all) (let* ((f-called-cnt 0) (arg-f-call-cnt 0) (f-called-args nil) (org-agenda-bulk-custom-functions `((?P ;; Custom bulk function ,(lambda (&rest args) (message "test") (setq f-called-cnt (1+ f-called-cnt) f-called-args args)) ;; Argument collection function ,(lambda () (setq arg-f-call-cnt (1+ arg-f-call-cnt)) '(1 2 3)))))) (cl-letf (((symbol-function 'read-char-exclusive) (lambda () ?P))) (org-agenda-bulk-action)) (should (= f-called-cnt 2)) (should (= arg-f-call-cnt 1)) (should (equal f-called-args '(1 2 3)))))) (provide 'test-org-agenda) ;;; test-org-agenda.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-archive.el000066400000000000000000000153731500430433700224130ustar00rootroot00000000000000;;; test-org-archive.el --- Test for Org Archive -*- lexical-binding: t; -*- ;; Copyright (C) 2017, 2019 Jay Kamat ;; Author: Jay Kamat ;; 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 . ;;; Code: (require 'org-archive) (ert-deftest test-org-archive/update-status-cookie () "Test archiving properly updating status cookies." ;; Test org-archive-subtree with two children. (should (equal "Top [0%]" (org-test-with-temp-text-in-file "* Top [%]\n** DONE One\n** TODO Two" (forward-line) (org-archive-subtree) (forward-line -1) (org-element-property :raw-value (org-element-at-point))))) ;; Test org-archive-subtree with one child. (should (equal "Top [100%]" (org-test-with-temp-text-in-file "* Top [%]\n** TODO Two" (forward-line) (org-archive-subtree) (forward-line -1) (org-element-property :raw-value (org-element-at-point))))) ;; Test org-archive-to-archive-sibling with two children. (should (equal "Top [100%]" (org-test-with-temp-text "* Top [%]\n** TODO One\n** DONE Two" (org-archive-to-archive-sibling) (forward-line -1) (org-element-property :raw-value (org-element-at-point))))) ;; Test org-archive-to-archive-sibling with two children. (should (equal "Top [0%]" (org-test-with-temp-text "* Top [%]\n** DONE Two" (org-archive-to-archive-sibling) (forward-line -1) (org-element-property :raw-value (org-element-at-point)))))) (ert-deftest test-org-archive/datetree () "Test `org-archive-subtree' with a datetree target." (org-test-at-time "<2020-07-05 Sun>" ;; Test in buffer target with no additional subheadings... (should (string-match-p (regexp-quote (format-time-string "*** 2020-07-05 %A\n**** a")) (org-test-with-temp-text-in-file "* a\n" (let ((org-archive-location "::datetree/")) (org-archive-subtree) (buffer-string))))) ;; ... and with `org-odd-levels-only' non-nil. (should (string-match-p (regexp-quote (format-time-string "***** 2020-07-05 %A\n******* a")) (org-test-with-temp-text-in-file "* a\n" (let ((org-archive-location "::datetree/") (org-odd-levels-only t)) (org-archive-subtree) (buffer-string))))) ;; Test in buffer target with an additional subheading... (should (string-match-p (regexp-quote (format-time-string "*** 2020-07-05 %A\n**** a\n***** b")) (org-test-with-temp-text-in-file "* b\n" (let ((org-archive-location "::datetree/* a")) (org-archive-subtree) (buffer-string))))) ;; ... and with `org-odd-levels-only' non-nil. (should (string-match-p (regexp-quote (format-time-string "***** 2020-07-05 %A\n******* a\n********* b")) (org-test-with-temp-text-in-file "* b\n" (let ((org-archive-location "::datetree/* a") (org-odd-levels-only t)) (org-archive-subtree) (buffer-string))))))) (ert-deftest test-org-archive/to-archive-sibling () "Test `org-archive-to-archive-sibling' specifications." ;; Archive sibling before or after archive heading. (should (equal "* Archive :ARCHIVE:\n** H\n" (org-test-with-temp-text "* H\n* Archive :ARCHIVE:\n" (let ((org-archive-sibling-heading "Archive") (org-archive-tag "ARCHIVE")) (org-archive-to-archive-sibling) (goto-char (point-min)) (buffer-substring-no-properties (point) (line-beginning-position 3)))))) (should (equal "* Archive :ARCHIVE:\n** H\n" (org-test-with-temp-text "* Archive :ARCHIVE:\n* H\n" (let ((org-archive-sibling-heading "Archive") (org-archive-tag "ARCHIVE")) (org-archive-to-archive-sibling) (goto-char (point-min)) (buffer-substring-no-properties (point) (line-beginning-position 3)))))) ;; When there is no sibling archive heading, create it. (should (equal "* Archive :ARCHIVE:\n** H\n" (org-test-with-temp-text "* H\n" (let ((org-archive-sibling-heading "Archive") (org-archive-tag "ARCHIVE") (org-tags-column 1)) (org-archive-to-archive-sibling) (goto-char (point-min)) (buffer-substring-no-properties (point) (line-beginning-position 3)))))) ;; Ignore non-sibling archive headings. (should (equal "* Archive :ARCHIVE:\n* Top\n** Archive :ARCHIVE:\n*** H\n" (org-test-with-temp-text "* Archive :ARCHIVE:\n* Top\n** H\n" (let ((org-archive-sibling-heading "Archive") (org-archive-tag "ARCHIVE") (org-tags-column 0)) (org-archive-to-archive-sibling) (goto-char (point-min)) (buffer-substring-no-properties (point) (line-beginning-position 5)))))) ;; When archiving a heading, leave point on next heading. (should (equal "* H2" (org-test-with-temp-text "* H1\n* H2\n* Archive :ARCHIVE:\n" (let ((org-archive-sibling-heading "Archive") (org-archive-tag "ARCHIVE")) (org-archive-to-archive-sibling) (buffer-substring-no-properties (point) (line-end-position)))))) (should (equal "* H2" (org-test-with-temp-text "* Archive :ARCHIVE:\n* H1\n* H2\n" (let ((org-archive-sibling-heading "Archive") (org-archive-tag "ARCHIVE")) (org-archive-to-archive-sibling) (buffer-substring-no-properties (point) (line-end-position)))))) ;; If `org-archive-reversed-order' is nil, archive as the last ;; child. Otherwise, archive as the first one. (should (equal "* Archive :ARCHIVE:\n** A\n" (org-test-with-temp-text "* H\n* Archive :ARCHIVE:\n** A\n" (let ((org-archive-sibling-heading "Archive") (org-archive-tag "ARCHIVE") (org-archive-reversed-order nil)) (org-archive-to-archive-sibling) (goto-char (point-min)) (buffer-substring-no-properties (point) (line-beginning-position 3)))))) (should (equal "* Archive :ARCHIVE:\n** H\n" (org-test-with-temp-text "* H\n* Archive :ARCHIVE:\n** A\n" (let ((org-archive-sibling-heading "Archive") (org-archive-tag "ARCHIVE") (org-archive-reversed-order t)) (org-archive-to-archive-sibling) (goto-char (point-min)) (buffer-substring-no-properties (point) (line-beginning-position 3))))))) (provide 'test-org-archive) ;;; test-org-archive.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-attach-git.el000066400000000000000000000104311500430433700230050ustar00rootroot00000000000000;;; test-org-attach-git.el --- Tests for Org Attach with git-annex -*- lexical-binding: t; -*- ;; ;; Copyright (c) 2016, 2019 Erik Hetzner ;; Authors: Erik Hetzner ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (org-test-for-executable "git-annex") (require 'org-attach-git) (require 'cl-lib) (defmacro test-org-attach-git/with-annex (&rest body) `(let ((tmpdir (make-temp-file "org-annex-test" t "/"))) (unwind-protect (let ((default-directory tmpdir) (org-attach-id-dir tmpdir)) ;; Ignore global git config. (with-environment-variables (("GIT_CONFIG_GLOBAL" (concat tmpdir ".global-config")) ("GIT_CONFIG_SYSTEM" "")) ;; Otherwise, some git operations may err. (shell-command "git config --global user.email \"john.doe@example.com\"") (shell-command "git config --global user.name \"John Doe\"") (shell-command "git init") (shell-command "git annex init") ,@body))))) (ert-deftest test-org-attach-git/use-annex () (test-org-attach-git/with-annex (let ((org-attach-git-annex-cutoff 1)) (should (org-attach-git-use-annex))) (let ((org-attach-git-annex-cutoff nil)) (should-not (org-attach-git-use-annex)))) ;; test with non annex directory (let ((tmpdir (make-temp-file "org-annex-test" t "/"))) (unwind-protect (let ((default-directory tmpdir) (org-attach-id-dir tmpdir)) (shell-command "git init") (should-not (org-attach-git-use-annex))) (delete-directory tmpdir 'recursive)))) (ert-deftest test-org-attach-git/get-maybe () (test-org-attach-git/with-annex (let ((path (expand-file-name "test-file")) (annex-dup (make-temp-file "org-annex-test" t "/"))) (with-temp-buffer (insert "hello world\n") (write-file path)) (shell-command "git annex add test-file") (shell-command "git annex sync") ;; Set up remote & copy files there (let ((annex-original default-directory) (default-directory annex-dup)) (shell-command (format "git clone %s ." (shell-quote-argument annex-original))) (shell-command "git annex init dup") (shell-command (format "git remote add original %s" (shell-quote-argument annex-original))) (shell-command "git annex get test-file") (shell-command "git annex sync")) (shell-command (format "git remote add dup %s" (shell-quote-argument annex-dup))) (shell-command "git annex sync") (shell-command "git annex drop --force test-file") ;; test getting the file from the dup when we should ALWAYS get (should (not (file-exists-p (file-symlink-p (expand-file-name "test-file"))))) (let ((org-attach-git-annex-auto-get t)) (org-attach-git-annex-get-maybe (expand-file-name "test-file")) ;; check that the file has the right contents (with-temp-buffer (insert-file-contents path) (should (string-equal "hello world\n" (buffer-string))))) ;; test getting the file from the dup when we should NEVER get (shell-command "git annex drop --force test-file") (let ((org-attach-git-annex-auto-get nil)) (should-error (org-attach-git-annex-get-maybe (expand-file-name "test-file")))) (let ((org-attach-git-annex-auto-get 'ask) (called nil)) (cl-letf (((symbol-function 'y-or-n-p) (lambda (_) (setq called 'was-called) t))) (org-attach-git-annex-get-maybe (expand-file-name "test-file")) ;; check that the file has the right contents (with-temp-buffer (insert-file-contents path) (should (string-equal "hello world\n" (buffer-string)))) (should (eq called 'was-called))))))) (provide 'test-org-attach-git) ;;; test-org-attach-git.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-attach.el000066400000000000000000000144551500430433700222360ustar00rootroot00000000000000;;; test-org-attach.el --- tests for org-attach.el -*- lexical-binding: t; -*- ;; Copyright (C) 2017, 2019 ;; Author: Marco Wahl ;; Keywords: internal ;; 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 . ;;; Commentary: ;; ;;; Code: (require 'org-test "../testing/org-test") (require 'org-attach) (eval-and-compile (require 'cl-lib)) (ert-deftest test-org-attach/dir () "Test `org-attach-get' specifications." (let ((org-file-apps '((t . emacs)))) (should (equal "Text in fileA\n" (org-test-in-example-file org-test-attachments-file (goto-char 157) ;; First attachment link (org-open-at-point) (buffer-string)))) (should-not (equal "Text in fileB\n" (org-test-in-example-file org-test-attachments-file (goto-char 219) ;; Second attachment link (let ((org-attach-use-inheritance nil)) (org-open-at-point) (buffer-string))))) (should (equal "Text in fileB\n" (org-test-in-example-file org-test-attachments-file (goto-char 219) ;; Second attachment link (let ((org-attach-use-inheritance t)) (org-open-at-point) (buffer-string))))) (should-not (equal "att1" (org-test-in-example-file org-test-attachments-file (goto-char 179) ;; H1.1 (let ((org-attach-use-inheritance nil)) (org-attach-dir))))) (should (equal "att1" (org-test-in-example-file org-test-attachments-file (goto-char 179) ;; H1.1 (let ((org-attach-use-inheritance t)) (org-attach-dir))))) (should (equal '("fileC" "fileD") (org-test-in-example-file org-test-attachments-file (goto-char 239) ;; H1.2 (org-attach-file-list (org-attach-dir))))) (should (equal '("fileC" "fileD") (org-test-in-example-file org-test-attachments-file (goto-char 239) ;; H1.2 (org-attach-file-list (org-attach-dir))))) (should (equal '("fileE") (org-test-in-example-file org-test-attachments-file (goto-char 289) ;; H2 (let ((org-attach-id-dir "data/")) (org-attach-file-list (org-attach-dir)))))) (should (equal "peek-a-boo\n" (org-test-in-example-file org-test-attachments-file (goto-char 289) ;; H2 (let ((org-attach-id-dir "data/")) (org-attach-open-in-emacs) (buffer-string))))) (should (equal '("fileA" "fileB") (org-test-in-example-file org-test-attachments-file (goto-char 336) ;; H3 (org-attach-file-list (org-attach-dir))))) ;; Test for folder not initialized in the filesystem (should-not (org-test-in-example-file org-test-attachments-file (goto-char 401) ;; H3.1 (let ((org-attach-use-inheritance nil) (org-attach-id-dir "data/")) (org-attach-dir)))) ;; Not yet initialized folder should be found if no-fs-check is ;; non-nil (should (equal "data/ab/cd12345" (org-test-in-example-file org-test-attachments-file (goto-char 401) ;; H3.1 (let ((org-attach-use-inheritance nil) (org-attach-id-dir "data/")) (file-relative-name (org-attach-dir nil t)))))) (should (equal '("fileA" "fileB") (org-test-in-example-file org-test-attachments-file (goto-char 401) ;; H3.1 (let ((org-attach-use-inheritance t)) ;; This is where it gets a bit sketchy...! DIR always has ;; priority over ID, even if ID is declared "higher up" in the ;; tree. This can potentially be revised. But it is also ;; pretty clean. DIR is always higher in priority than ID right ;; now, no matter the depth in the tree. (org-attach-file-list (org-attach-dir)))))))) (ert-deftest test-org-attach/dired-attach-to-next-best-subtree/1 () "Attach file at point in dired to subtree." (should (let ((a-filename (make-temp-file "a")) ; file is an attach candidate. (org-attach-id-dir "data/")) (unwind-protect (org-test-with-temp-text-in-file "* foo :foo:" (split-window) (let ((org-buffer (current-buffer)) (_dired-buffer (dired temporary-file-directory))) (cl-assert (eq 'dired-mode major-mode)) (revert-buffer) (dired-goto-file a-filename) ; action (call-interactively #'org-attach-dired-to-subtree) ; check (delete-window) (switch-to-buffer org-buffer) (cl-assert (eq 'org-mode major-mode))) (beginning-of-buffer) (search-forward "* foo") ; expectation. tag ATTACH has been appended. (cl-reduce (lambda (x y) (or x y)) (mapcar (lambda (x) (string-equal "ATTACH" x)) (org-element-property :tags (org-element-at-point))))) (delete-file a-filename))))) (ert-deftest test-org-attach/dired-attach-to-next-best-subtree/2 () "Attach 2 marked files." (should (let ((a-filename (make-temp-file "a")) (b-filename (make-temp-file "b")) ; attach candidates. (org-attach-id-dir "data/")) (unwind-protect (org-test-with-temp-text-in-file "* foo" (split-window) (let ((org-buffer (current-buffer)) (_dired-buffer (dired temporary-file-directory))) (cl-assert (eq 'dired-mode major-mode)) (revert-buffer) (dired-goto-file a-filename) (dired-mark 1) (dired-goto-file b-filename) (dired-mark 1) ; action (call-interactively #'org-attach-dired-to-subtree) ; check (delete-window) (switch-to-buffer org-buffer)) (cl-assert (eq 'org-mode major-mode)) (beginning-of-buffer) (search-forward "* foo") (and (file-exists-p (concat (org-attach-dir) "/" (file-name-nondirectory a-filename))) (file-exists-p (concat (org-attach-dir) "/" (file-name-nondirectory b-filename))))) (delete-file a-filename) (delete-file b-filename))))) (provide 'test-org-attach) ;;; test-org-attach.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-capture.el000066400000000000000000000667301500430433700224400ustar00rootroot00000000000000;;; test-org-capture.el --- Tests for org-capture.el -*- lexical-binding: t; -*- ;; Copyright (C) 2015, 2017, 2019 Nicolas Goaziou ;; Author: Nicolas Goaziou ;; 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 . ;;; Commentary: ;; Unit tests for Org Capture library. ;;; Code: (require 'org-capture) (ert-deftest test-org-capture/fill-template () "Test `org-capture-fill-template' specifications." ;; When working on these tests consider to also change ;; `test-org-feed/fill-template'. ;; %(sexp) placeholder. (should (equal "success!\n" (org-capture-fill-template "%(concat \"success\" \"!\")"))) ;; It is possible to include other place holders in %(sexp). In ;; that case properly escape \ and " characters. (should (equal "Nested string \"\\\"\\\"\"\n" (let ((org-store-link-plist nil)) (org-capture-fill-template "%(concat \"%i\")" "Nested string \"\\\"\\\"\"")))) ;; %<...> placeholder. (should (equal (concat (format-time-string "%Y") "\n") (org-capture-fill-template "%<%Y>"))) ;; %t and %T placeholders. (should (equal (concat (format-time-string (org-time-stamp-format nil nil)) "\n") (org-capture-fill-template "%t"))) (should (equal (concat (format-time-string (org-time-stamp-format t nil)) "\n") (org-capture-fill-template "%T"))) ;; %u and %U placeholders. (should (equal (concat (format-time-string (org-time-stamp-format nil t)) "\n") (org-capture-fill-template "%u"))) (should (equal (concat (format-time-string (org-time-stamp-format t t)) "\n") (org-capture-fill-template "%U"))) ;; %i placeholder. Make sure sexp placeholders are not expanded ;; when they are inserted through this one. (should (equal "success!\n" (let ((org-store-link-plist nil)) (org-capture-fill-template "%i" "success!")))) (should (equal "%(concat \"no \" \"evaluation\")\n" (let ((org-store-link-plist nil)) (org-capture-fill-template "%i" "%(concat \"no \" \"evaluation\")")))) ;; When %i contents span over multiple line, repeat initial leading ;; characters over each line. Also try possibly problematic ;; prefixes such as "\\". (should (equal "> line 1\n> line 2\n" (let ((org-store-link-plist nil)) (org-capture-fill-template "> %i" "line 1\nline 2")))) (should (equal "\\ line 1\n\\ line 2\n" (let ((org-store-link-plist nil)) (org-capture-fill-template "\\ %i" "line 1\nline 2")))) ;; Test %-escaping with \ character. (should (equal "%i\n" (let ((org-store-link-plist nil)) (org-capture-fill-template "\\%i" "success!")))) (should (equal "\\success!\n" (let ((org-store-link-plist nil)) (org-capture-fill-template "\\\\%i" "success!")))) (should (equal "\\%i\n" (let ((org-store-link-plist nil)) (org-capture-fill-template "\\\\\\%i" "success!")))) ;; More than one placeholder in the same template. (should (equal "success! success! success! success!\n" (let ((org-store-link-plist nil)) (org-capture-fill-template "%i %i %i %i" "success!")))) ;; %(sexp) placeholder with an input containing the traps %, " and ) ;; all at once which is complicated to parse. (should (equal "5 % Less (See Item \"3)\" Somewhere)\n" (let ((org-store-link-plist nil)) (org-capture-fill-template "%(capitalize \"%i\")" "5 % less (see item \"3)\" somewhere)"))))) (ert-deftest test-org-capture/refile () "Test `org-capture-refile' specifications." ;; When refiling, make sure the headline being refiled is the one ;; being captured. In particular, empty lines after the entry may ;; be removed, and we don't want to shift onto the next heading. (should (string-prefix-p "** H1" (org-test-with-temp-text-in-file "* A\n* B\n" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Todo" entry (file+headline ,file "A") "** H1 %?")))) (org-capture nil "t") (insert "\n") (cl-letf (((symbol-function 'org-refile) (lambda () (interactive) (throw :return (buffer-substring-no-properties (line-beginning-position) (line-end-position)))))) (catch :return (org-capture-refile))))))) ;; When the entry is refiled, `:jump-to-captured' moves point to the ;; refile location, not the initial capture target. (should (org-test-with-temp-text-in-file "* Refile target" (let ((file1 (buffer-file-name))) (org-test-with-temp-text-in-file "* A" (let* ((file2 (buffer-file-name)) (org-capture-templates `(("t" "Todo" entry (file+headline ,file2 "A") "** H1 %?" :jump-to-captured t)))) (org-capture nil "t") (cl-letf (((symbol-function 'org-refile-get-location) (lambda (&rest _args) (list (file-name-nondirectory file1) file1 nil nil)))) (org-capture-refile) (list file1 file2 (buffer-file-name))))))))) (ert-deftest test-org-capture/abort () "Test aborting a capture process." ;; Newly create capture buffer should not be saved. (let ((capture-file (make-temp-name (org-file-name-concat temporary-file-directory "org-test")))) (unwind-protect (let ((org-capture-templates `(("t" "Todo" entry (file ,capture-file) nil :no-save t)))) (org-capture nil "t") (org-capture-kill) (should-not (file-exists-p capture-file))) (when (file-exists-p capture-file) (delete-file capture-file)))) ;; Test that capture can be aborted after inserting at end of ;; capture buffer. (should (equal "* A\n* B\n" (org-test-with-temp-text-in-file "* A\n* B\n" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Todo" entry (file+headline ,file "A") "** H1 %?")))) (org-capture nil "t") (goto-char (point-max)) (insert "Capture text") (org-capture-kill)) (buffer-string)))) (should (equal "- A\n - B\n" (org-test-with-temp-text-in-file "- A\n - B" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Item" item (file ,file) "- X")))) (org-capture nil "t") (org-capture-kill)) (buffer-string)))) (should (equal "| a |\n| b |\n" (org-test-with-temp-text-in-file "| a |\n| b |" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Table" table-line (file ,file) "| x |")))) (org-capture nil "t") (org-capture-kill)) (buffer-string)))) ;; Test aborting a capture that split the line. (should (equal "* AB\n" (org-test-with-temp-text-in-file "* AB\n" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Todo" entry (file+function ,file (lambda () (goto-char 4))) "** H1 %?")))) (org-capture nil "t") (org-capture-kill)) (buffer-string))))) (ert-deftest test-org-capture/entry () "Test `entry' type in capture template." ;; Do not break next headline. (should (equal "* A\n** H1 Capture text\n* B\n" (org-test-with-temp-text-in-file "* A\n* B\n" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Todo" entry (file+headline ,file "A") "** H1 %?")))) (org-capture nil "t") (insert "Capture text") (org-capture-finalize)) (buffer-string)))) ;; Correctly save position of inserted entry. (should (equal "** H" (org-test-with-temp-text-in-file "* A" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Test" entry (file+headline ,file "A") "** H\nFoo" :immediate-finish t)))) (org-capture nil "t") (org-capture '(16)) (buffer-substring (point) (line-end-position)))))) ;; Do not raise an error on empty entries. (should (org-test-with-temp-text-in-file "" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Test" entry (file+headline ,file "A") "** " :immediate-finish t)))) (org-capture nil "t") (buffer-string)))) ;; With a 0 prefix argument, ignore surrounding lists. (should (equal "Foo\n* X\nBar\n" (org-test-with-temp-text-in-file "Foo\nBar" (forward-line) (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Test" entry (file ,file) "* X" :immediate-finish t)))) (org-capture 0 "t") (buffer-string))))) ;; With a 0 prefix argument, also obey to :empty-lines. (should (equal "Foo\n\n* X\n\nBar\n" (org-test-with-temp-text-in-file "Foo\nBar" (forward-line) (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Test" entry (file ,file) "* X" :immediate-finish t :empty-lines 1)))) (org-capture 0 "t") (buffer-string)))))) (ert-deftest test-org-capture/item () "Test `item' type in capture template." ;; Insert item in the first plain list found at the target location. (should (equal "* A\n- list 1\n- X\n\n\n1. list 2\n" (org-test-with-temp-text-in-file "* A\n- list 1\n\n\n1. list 2" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Item" item (file+headline ,file "A") "- X")))) (org-capture nil "t") (org-capture-finalize)) (buffer-string)))) (should (equal "Text\n- list 1\n- X\n\n\n1. list 2\n" (org-test-with-temp-text-in-file "Text\n- list 1\n\n\n1. list 2" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Item" item (file ,file) "- X")))) (org-capture nil "t") (org-capture-finalize)) (buffer-string)))) ;; When targeting a specific location, start looking for plain lists ;; from there. (should (equal "* A\n- skip\n\n\n1. here\n2. X\n" (org-test-with-temp-text-in-file "* A\n- skip\n\n\n1. here" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Item" item (file+regexp ,file "here") "1. X")))) (org-capture nil "t") (org-capture-finalize)) (buffer-string)))) ;; If there is no such list, create it. (should (equal "* A\n- X\n" (org-test-with-temp-text-in-file "* A" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Item" item (file+headline ,file "A") "- X")))) (org-capture nil "t") (org-capture-finalize)) (buffer-string)))) ;; When `:prepend' is non-nil, insert new item as the first item. (should (equal "* A\n- X\n- 1\n- 2\n" (org-test-with-temp-text-in-file "* A\n- 1\n- 2" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Item" item (file+headline ,file "A") "- X" :prepend t)))) (org-capture nil "t") (org-capture-finalize)) (buffer-string)))) ;; If there is no list and `:prepend' is non-nil, insert list at the ;; beginning of the entry, or the beginning of the buffer. However, ;; preserve properties drawer and planning info, if any. (should (equal "* A\n- X\nSome text\n" (org-test-with-temp-text-in-file "* A\nSome text" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Item" item (file+headline ,file "A") "- X" :prepend t)))) (org-capture nil "t") (org-capture-finalize)) (buffer-string)))) (should (equal "- X\nText\n" (org-test-with-temp-text-in-file "Text" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Item" item (file ,file) "- X" :prepend t)))) (org-capture nil "t") (org-capture-finalize)) (buffer-string)))) (should (equal "* A\nSCHEDULED: <2012-03-29 Thu>\n- X\nText\n" (org-test-with-temp-text-in-file "* A\nSCHEDULED: <2012-03-29 Thu>\nText" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Item" item (file+headline ,file "A") "- X" :prepend t)))) (org-capture nil "t") (org-capture-finalize)) (buffer-string)))) ;; When `:prepend' is nil, insert new item as the last top-level ;; item. (should (equal "* A\n- 1\n - 2\n- X\n" (org-test-with-temp-text-in-file "* A\n- 1\n - 2" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Item" item (file+headline ,file "A") "- X")))) (org-capture nil "t") (org-capture-finalize)) (buffer-string)))) ;; When targeting a specific location, one can insert in a sub-list. (should (equal "* A\n- skip\n - here\n - X\n- skip\n" (org-test-with-temp-text-in-file "* A\n- skip\n - here\n- skip" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Item" item (file+regexp ,file "here") "- X")))) (org-capture nil "t") (org-capture-finalize)) (buffer-string)))) ;; Obey `:empty-lines' when creating a new list. (should (equal "\n- X\n\n\n* H\n" (org-test-with-temp-text-in-file "\n* H" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Item" item (file ,file) "- X" :empty-lines-before 1 :empty-lines-after 2 :prepend t)))) (org-capture nil "t") (org-capture-finalize)) (buffer-string)))) ;; Obey `:empty-lines' in an existing list only between items, and ;; only if the value doesn't break the list. (should (equal "- A\n\n- X\nText\n" (org-test-with-temp-text-in-file "- A\nText" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Item" item (file ,file) "- X" :empty-lines 1)))) (org-capture nil "t") (org-capture-finalize)) (buffer-string)))) (should (equal "Text\n- X\n\n- A\n" (org-test-with-temp-text-in-file "Text\n- A" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Item" item (file ,file) "- X" :prepend t :empty-lines 1)))) (org-capture nil "t") (org-capture-finalize)) (buffer-string)))) (should-not (equal "- A\n\n\n- X\n" (org-test-with-temp-text-in-file "- A" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Item" item (file ,file) "- X" :empty-lines 2)))) (org-capture nil "t") (org-capture-finalize)) (buffer-string)))) ;; Preserve list type when pre-pending. (should (equal "1. X\n2. A\n" (org-test-with-temp-text-in-file "1. A" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Item" item (file ,file) "- X" :prepend t)))) (org-capture nil "t") (org-capture-finalize)) (buffer-string)))) ;; Handle indentation. Handle multi-lines templates. (should (equal " - A\n - X\n" (org-test-with-temp-text-in-file " - A" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Item" item (file ,file) "- X")))) (org-capture nil "t") (org-capture-finalize)) (buffer-string)))) (should (equal " - A\n - X\n Line 2\n" (org-test-with-temp-text-in-file " - A" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Item" item (file ,file) "- X\n Line 2")))) (org-capture nil "t") (org-capture-finalize)) (buffer-string)))) ;; Handle incomplete templates. (should (equal "- A\n- X\n" (org-test-with-temp-text-in-file "- A" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Item" item (file ,file) "X")))) (org-capture nil "t") (org-capture-finalize)) (buffer-string)))) ;; Do not break next headline. (should-not (equal "- A\n- X\nFoo* H" (org-test-with-temp-text-in-file "- A\n* H" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Item" item (file ,file) "- X")))) (org-capture nil "t") (goto-char (point-max)) (insert "Foo") (org-capture-finalize)) (buffer-string)))) ;; With a 0 prefix argument, ignore surrounding lists. (should (equal "- X\nFoo\n\n- A\n" (org-test-with-temp-text-in-file "Foo\n\n- A" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Test" item (file ,file) "- X" :immediate-finish t)))) (org-capture 0 "t") (buffer-string))))) ;; With a 0 prefix argument, also obey to `:empty-lines'. (should (equal "\n- X\n\nFoo\n\n- A\n" (org-test-with-temp-text-in-file "Foo\n\n- A" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Test" item (file ,file) "- X" :immediate-finish t :empty-lines 1)))) (org-capture 0 "t") (buffer-string)))))) (ert-deftest test-org-capture/table-line () "Test `table-line' type in capture template." ;; When a only file is specified, use the first table available. (should (equal "Text | a | | x | | b | " (org-test-with-temp-text-in-file "Text\n\n| a |\n\n| b |" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Table" table-line (file ,file) "| x |" :immediate-finish t)))) (org-capture nil "t")) (buffer-string)))) ;; When an entry is specified, find the first table in the ;; corresponding section. (should (equal "* Foo | a | * Inbox | b | | x | " (org-test-with-temp-text-in-file "* Foo\n| a |\n* Inbox\n| b |\n" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Table" table-line (file+headline ,file "Inbox") "| x |" :immediate-finish t)))) (org-capture nil "t")) (buffer-string)))) (should (equal "* Inbox | a | | x | | b | " (org-test-with-temp-text-in-file "* Inbox\n| a |\n\n| b |\n" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Table" table-line (file+headline ,file "Inbox") "| x |" :immediate-finish t)))) (org-capture nil "t")) (buffer-string)))) ;; When a precise location is specified, find the first table after ;; point, down to the end of the section. (should (equal "| a | | b | | x | " (org-test-with-temp-text-in-file "| a |\n\n\n| b |\n" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Table" table-line (file+function ,file forward-line) "| x |" :immediate-finish t)))) (org-capture nil "t")) (buffer-string)))) ;; Create a new table with an empty header when none can be found. (should (equal "| | |\n|---+---|\n| a | b |\n" (org-test-with-temp-text-in-file "" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Table" table-line (file ,file) "| a | b |" :immediate-finish t)))) (org-capture nil "t")) (buffer-string)))) ;; Properly insert row with formulas. (should (equal "| 1 |\n| 2 |\n#+TBLFM: \n" (org-test-with-temp-text-in-file "| 1 |\n#+TBLFM: " (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Table" table-line (file ,file) "| 2 |" :immediate-finish t)))) (org-capture nil "t")) (buffer-string)))) ;; When `:prepend' is nil, add the row at the end of the table. (should (equal "| a |\n| x |\n" (org-test-with-temp-text-in-file "| a |" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Table" table-line (file ,file) "| x |" :immediate-finish t)))) (org-capture nil "t")) (buffer-string)))) ;; When `:prepend' is non-nil, add it as the first row after the ;; header, if there is one, or the first row otherwise. (should (equal "| a |\n|---|\n| x |\n| b |\n" (org-test-with-temp-text-in-file "| a |\n|---|\n| b |" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Table" table-line (file ,file) "| x |" :immediate-finish t :prepend t)))) (org-capture nil "t")) (buffer-string)))) (should (equal "| x |\n| a |\n" (org-test-with-temp-text-in-file "| a |" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Table" table-line (file ,file) "| x |" :immediate-finish t :prepend t)))) (org-capture nil "t")) (buffer-string)))) ;; When `:table-line-pos' is set and is meaningful, obey it. (should (equal "| a |\n|---|\n| b |\n| x |\n|---|\n| c |\n" (org-test-with-temp-text-in-file "| a |\n|---|\n| b |\n|---|\n| c |" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Table" table-line (file ,file) "| x |" :immediate-finish t :table-line-pos "II-1")))) (org-capture nil "t")) (buffer-string)))) (should (equal "| a |\n|---|\n| x |\n| b |\n|---|\n| c |\n" (org-test-with-temp-text-in-file "| a |\n|---|\n| b |\n|---|\n| c |" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Table" table-line (file ,file) "| x |" :immediate-finish t :table-line-pos "I+1")))) (org-capture nil "t")) (buffer-string)))) ;; Throw an error on invalid `:table-line-pos' specifications. (should-error (org-test-with-temp-text-in-file "| a |" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Table" table-line (file ,file) "| x |" :immediate-finish t :table-line-pos "II+99")))) (org-capture nil "t") t))) ;; Update formula when capturing one or more rows. (should (equal '(("@3$1" . "9")) (org-test-with-temp-text-in-file "| 1 |\n|---|\n| 9 |\n#+tblfm: @2$1=9" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Table" table-line (file ,file) "| 2 |" :immediate-finish t :table-line-pos "I-1")))) (org-capture nil "t") (org-table-get-stored-formulas))))) (should (equal '(("@4$1" . "9")) (org-test-with-temp-text-in-file "| 1 |\n|---|\n| 9 |\n#+tblfm: @2$1=9" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Table" table-line (file ,file) "| 2 |\n| 3 |" :immediate-finish t :table-line-pos "I-1")))) (org-capture nil "t") (org-table-get-stored-formulas))))) ;; Do not update formula when cell in inserted below affected row. (should-not (equal '(("@3$1" . "9")) (org-test-with-temp-text-in-file "| 1 |\n|---|\n| 9 |\n#+tblfm: @2$1=9" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Table" table-line (file ,file) "| 2 |" :immediate-finish t)))) (org-capture nil "t") (org-table-get-stored-formulas))))) ;; With a 0 prefix argument, ignore surrounding tables. (should (equal "| |\n|---|\n| B |\nFoo\n\n| A |\n" (org-test-with-temp-text-in-file "Foo\n\n| A |" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Test" table-line (file ,file) "| B |" :immediate-finish t)))) (org-capture 0 "t") (buffer-string)))))) (ert-deftest test-org-capture/plain () "Test `plain' type in capture template." ;; Insert at end of the file, unless `:prepend' is non-nil. (should (equal "Some text.\nFoo\n" (org-test-with-temp-text-in-file "Some text." (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Text" plain (file ,file) "Foo" :immediate-finish t)))) (org-capture nil "t") (buffer-string))))) (should (equal "Foo\nSome text.\n" (org-test-with-temp-text-in-file "Some text." (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Text" plain (file ,file) "Foo" :immediate-finish t :prepend t)))) (org-capture nil "t") (buffer-string))))) ;; When a headline is specified, add it at the beginning of the ;; entry, past any meta-data, or at its end, depending on ;; `:prepend'. (should (equal "* A\nSCHEDULED: <2012-03-29 Thu>\nSome text.\nFoo\n* B\n" (org-test-with-temp-text-in-file "* A\nSCHEDULED: <2012-03-29 Thu>\nSome text.\n* B" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Text" plain (file+headline ,file "A") "Foo" :immediate-finish t)))) (org-capture nil "t") (buffer-string))))) (should (equal "* A\nSCHEDULED: <2012-03-29 Thu>\nFoo\nSome text.\n* B\n" (org-test-with-temp-text-in-file "* A\nSCHEDULED: <2012-03-29 Thu>\nSome text.\n* B" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Text" plain (file+headline ,file "A") "Foo" :immediate-finish t :prepend t)))) (org-capture nil "t") (buffer-string))))) ;; At an exact position, in the middle of a line, make sure to ;; insert text on a line on its own. (should (equal "A\nX\nB\n" (org-test-with-temp-text-in-file "AB" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Text" plain (file+function ,file forward-char) "X" :immediate-finish t)))) (org-capture nil "t") (buffer-string))))) ;; Pathological case: insert an empty template in an empty file. (should (equal "" (org-test-with-temp-text-in-file "" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Text" plain (file ,file) "" :immediate-finish t)))) (org-capture nil "t") (buffer-string))))) ;; Test :unnarrowed property without a "%?" marker. (should (equal "SUCCESS\n" (org-test-with-temp-text-in-file "" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Text" plain (file ,file) "SUCCESS" :unnarrowed t :immediate-finish t)))) (org-capture nil "t") (buffer-string)))))) (ert-deftest test-org-capture/template-specific-hooks () "Test template-specific hook execution." ;; Runs each template hook prior to corresponding global hook (should (equal "hook\nglobal-hook\nprepare\nglobal-prepare before\nglobal-before\nafter\nglobal-after" (org-test-with-temp-text-in-file "" (let* ((file (buffer-file-name)) (org-capture-mode-hook '((lambda () (insert "global-hook\n")))) (org-capture-prepare-finalize-hook '((lambda () (insert "global-prepare\n")))) (org-capture-before-finalize-hook '((lambda () (insert "global-before\n")))) (org-capture-after-finalize-hook '((lambda () (with-current-buffer (org-capture-get :buffer) (goto-char (point-max)) (insert "global-after"))))) (org-capture-templates `(("t" "Test" plain (file ,file) "" :hook (lambda () (insert "hook\n")) :prepare-finalize (lambda () (insert "prepare\n")) :before-finalize (lambda () (insert "before\n")) :after-finalize (lambda () (with-current-buffer (org-capture-get :buffer) (goto-char (point-max)) (insert "after\n"))) :immediate-finish t)))) (org-capture nil "t") (buffer-string))))) ;; Accepts a list of nullary functions (should (equal "one\ntwo" (org-test-with-temp-text-in-file "" (let* ((file (buffer-file-name)) (org-capture-templates `(("t" "Test" plain (file ,file) "" :hook ((lambda () (insert "one\n")) (lambda () (insert "two"))))))) (org-capture nil "t") (buffer-string)))))) (provide 'test-org-capture) ;;; test-org-capture.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-clock.el000066400000000000000000001270061500430433700220620ustar00rootroot00000000000000;;; test-org-clock.el --- Tests for org-clock.el -*- lexical-binding: t; -*- ;; Copyright (C) 2012, 2014, 2015, 2019 Nicolas Goaziou ;; Author: Nicolas Goaziou ;; Released under the GNU General Public License version 3 ;; see: https://www.gnu.org/licenses/gpl-3.0.html ;;;; Comments ;;; Code: (require 'org-duration) (require 'org-clock) (defun org-test-clock-create-timestamp (input &optional inactive with-time) "Create a timestamp out of a date/time prompt string. INPUT is a string as expected in a date/time prompt, i.e \"+2d\" or \"2/5\". When optional argument INACTIVE is non-nil, return an inactive timestamp. When optional argument WITH-TIME is non-nil, also insert hours and minutes. Return the timestamp as a string." (org-element-interpret-data (let ((time (decode-time (org-encode-time (org-fix-decoded-time (org-read-date-analyze input nil (decode-time (current-time)))))))) (list 'timestamp (list :type (if inactive 'inactive 'active) :minute-start (and with-time (nth 1 time)) :hour-start (and with-time (nth 2 time)) :day-start (nth 3 time) :month-start (nth 4 time) :year-start (nth 5 time)))))) (defun org-test-clock-create-clock (input1 &optional input2) "Create a clock line out of two date/time prompts. INPUT1 and INPUT2 are strings as expected in a date/time prompt, i.e \"+2d\" or \"2/5\". They respectively refer to start and end range. INPUT2 can be omitted if clock hasn't finished yet. Return the clock line as a string." (let* ((beg (org-test-clock-create-timestamp input1 t t)) (end (and input2 (org-test-clock-create-timestamp input2 t t))) (sec-diff (and input2 (floor (- (org-time-string-to-seconds end) (org-time-string-to-seconds beg)))))) (concat org-clock-string " " beg (when end (concat "--" end " => " (format "%2d:%02d" (/ sec-diff 3600) (/ (mod sec-diff 3600) 60)))) "\n"))) (defun test-org-clock-clocktable-contents (options &optional initial) "Return contents of a Clock table for current buffer OPTIONS is a string of Clock table options. Optional argument INITIAL is a string specifying initial contents within the Clock table. Caption is ignored in contents. The clocktable doesn't appear in the buffer." (declare (indent 2)) (goto-char (point-min)) (save-excursion (insert "#+BEGIN: clocktable " options "\n") (when initial (insert initial)) (unless (string-suffix-p "\n" initial) (insert "\n")) (insert "#+END:\n")) (unwind-protect (save-excursion (let ((org-duration-format 'h:mm)) (org-update-dblock)) (forward-line) ;; Skip caption. (when (looking-at "#\\+CAPTION:") (forward-line)) (buffer-substring-no-properties (point) (progn (search-forward "#+END:") (line-end-position 0)))) ;; Remove clocktable. (delete-region (point) (search-forward "#+END:\n")))) (ert-deftest test-org-clock/org-clock-timestamps-change () "Test `org-clock-timestamps-change' specifications." (let ((sun (org-test-get-day-name "Sun")) (mon (org-test-get-day-name "Mon"))) (should (equal (format "CLOCK: [2023-02-19 %s 21:30]--[2023-02-19 %s 23:35] => 2:05" sun sun) (org-test-with-temp-text "CLOCK: [2023-02-19 Sun 22:30]--[2023-02-20 Mon 00:35] => 2:05" (org-clock-timestamps-change 'down 1) (buffer-string)))) (should (equal (format "CLOCK: [2023-02-20 %s 00:00]--[2023-02-20 %s 00:40] => 0:40" mon mon) (org-test-with-temp-text "CLOCK: [2023-02-19 Sun 23:55]--[2023-02-20 Mon 00:35] => 0:40" (org-clock-timestamps-change 'up 1) (buffer-string)))) (should (equal (format "CLOCK: [2023-02-20 %s 00:30]--[2023-02-20 %s 01:35] => 1:05" mon mon) (org-test-with-temp-text "CLOCK: [2023-02-19 Sun 23:30]--[2023-02-20 Mon 00:35] => 1:05" (org-clock-timestamps-change 'up 1) (buffer-string)))))) (ert-deftest test-org-clock/org-clock-update-time-maybe () "Test `org-clock-update-time-maybe' specifications." (should (equal (format "CLOCK: [2023-04-29 %s 00:00]--[2023-05-04 %s 01:00] => 121:00" (org-test-get-day-name "Sat") (org-test-get-day-name "Thu")) (org-test-with-temp-text "CLOCK: [2023-04-29 Sat 00:00]--[2023-05-04 Thu 01:00]" (should (org-clock-update-time-maybe)) (buffer-string)))) (should-not (org-test-with-temp-text "[2023-04-29 Sat 00:00]--[2023-05-04 Thu 01:00]" (org-clock-update-time-maybe)))) ;;; Clock drawer (ert-deftest test-org-clock/into-drawer () "Test `org-clock-into-drawer' specifications." ;; When `org-clock-into-drawer' is nil, do not use a clock drawer. (should-not (org-test-with-temp-text "* H" (let ((org-clock-into-drawer nil) (org-log-into-drawer nil)) (org-clock-into-drawer)))) (should-not (org-test-with-temp-text "* H" (let ((org-clock-into-drawer nil) (org-log-into-drawer t)) (org-clock-into-drawer)))) (should-not (org-test-with-temp-text "* H" (let ((org-clock-into-drawer nil) (org-log-into-drawer "BAR")) (org-clock-into-drawer)))) ;; When `org-clock-into-drawer' is a string, use it ;; unconditionally. (should (equal "FOO" (org-test-with-temp-text "* H" (let ((org-clock-into-drawer "FOO") (org-log-into-drawer nil)) (org-clock-into-drawer))))) (should (equal "FOO" (org-test-with-temp-text "* H" (let ((org-clock-into-drawer "FOO") (org-log-into-drawer t)) (org-clock-into-drawer))))) (should (equal "FOO" (org-test-with-temp-text "* H" (let ((org-clock-into-drawer "FOO") (org-log-into-drawer "BAR")) (org-clock-into-drawer))))) ;; When `org-clock-into-drawer' is an integer, return it. (should (= 1 (org-test-with-temp-text "* H" (let ((org-clock-into-drawer 1) (org-log-into-drawer nil)) (org-clock-into-drawer))))) (should (= 1 (org-test-with-temp-text "* H" (let ((org-clock-into-drawer 1) (org-log-into-drawer t)) (org-clock-into-drawer))))) (should (= 1 (org-test-with-temp-text "* H" (let ((org-clock-into-drawer 1) (org-log-into-drawer "BAR")) (org-clock-into-drawer))))) ;; Otherwise, any non-nil value defaults to `org-log-into-drawer' or ;; "LOGBOOK" if it is nil. (should (equal "LOGBOOK" (org-test-with-temp-text "* H" (let ((org-clock-into-drawer t) (org-log-into-drawer nil)) (org-clock-into-drawer))))) (should (equal "LOGBOOK" (org-test-with-temp-text "* H" (let ((org-clock-into-drawer t) (org-log-into-drawer t)) (org-clock-into-drawer))))) (should (equal "FOO" (org-test-with-temp-text "* H" (let ((org-clock-into-drawer t) (org-log-into-drawer "FOO")) (org-clock-into-drawer))))) ;; A non-nil "CLOCK_INTO_DRAWER" property overrides ;; `org-clock-into-drawer' value. (should (equal "LOGBOOK" (org-test-with-temp-text "* H\n:PROPERTIES:\n:CLOCK_INTO_DRAWER: t\n:END:" (let ((org-clock-into-drawer nil) (org-log-into-drawer nil)) (org-clock-into-drawer))))) (should (equal "FOO" (org-test-with-temp-text "* H\n:PROPERTIES:\n:CLOCK_INTO_DRAWER: FOO\n:END:" (let ((org-clock-into-drawer nil) (org-log-into-drawer nil)) (org-clock-into-drawer))))) (should-not (org-test-with-temp-text "* H\n:PROPERTIES:\n:CLOCK_INTO_DRAWER: nil\n:END:" (let ((org-clock-into-drawer t) (org-log-into-drawer nil)) (org-clock-into-drawer)))) ;; "CLOCK_INTO_DRAWER" can be inherited. (should (equal "LOGBOOK" (org-test-with-temp-text "* H\n:PROPERTIES:\n:CLOCK_INTO_DRAWER: t\n:END:\n** H2" (let ((org-clock-into-drawer nil) (org-log-into-drawer nil)) (org-clock-into-drawer))))) (should (equal "FOO" (org-test-with-temp-text "* H\n:PROPERTIES:\n:CLOCK_INTO_DRAWER: FOO\n:END:\n** H2" (let ((org-clock-into-drawer nil) (org-log-into-drawer nil)) (org-clock-into-drawer))))) (should-not (org-test-with-temp-text "* H\n:PROPERTIES:\n:CLOCK_INTO_DRAWER: nil\n:END:\n** H2" (let ((org-clock-into-drawer t) (org-log-into-drawer nil)) (org-clock-into-drawer))))) (ert-deftest test-org-clock/drawer-name () "Test `org-clock-drawer-name' specifications." ;; A nil value for `org-clock-into-drawer' means no drawer is ;; expected whatsoever. (should-not (org-test-with-temp-text "* H" (let ((org-clock-into-drawer nil) (org-log-into-drawer nil)) (org-clock-drawer-name)))) (should-not (org-test-with-temp-text "* H" (let ((org-clock-into-drawer nil) (org-log-into-drawer t)) (org-clock-drawer-name)))) (should-not (org-test-with-temp-text "* H" (let ((org-clock-into-drawer nil) (org-log-into-drawer "FOO")) (org-clock-drawer-name)))) ;; A string value for `org-clock-into-drawer' means to use it ;; unconditionally. (should (equal "FOO" (org-test-with-temp-text "* H" (let ((org-clock-into-drawer "FOO") (org-log-into-drawer nil)) (org-clock-drawer-name))))) (should (equal "FOO" (org-test-with-temp-text "* H" (let ((org-clock-into-drawer "FOO") (org-log-into-drawer t)) (org-clock-drawer-name))))) (should (equal "FOO" (org-test-with-temp-text "* H" (let ((org-clock-into-drawer "FOO") (org-log-into-drawer "BAR")) (org-clock-drawer-name))))) ;; When the value in `org-clock-into-drawer' is a number, re-use ;; `org-log-into-drawer' or use default "LOGBOOK" value. (should (equal "FOO" (org-test-with-temp-text "* H" (let ((org-clock-into-drawer 1) (org-log-into-drawer "FOO")) (org-clock-drawer-name))))) (should (equal "LOGBOOK" (org-test-with-temp-text "* H" (let ((org-clock-into-drawer 1) (org-log-into-drawer t)) (org-clock-drawer-name))))) (should (equal "LOGBOOK" (org-test-with-temp-text "* H" (let ((org-clock-into-drawer 1) (org-log-into-drawer nil)) (org-clock-drawer-name)))))) (ert-deftest test-org-clock/clock-drawer-dwim () "Test DWIM update of days for clocks in logbook drawers." (let ((thu (org-test-get-day-name "Thu"))) (should (equal (format "* Foo :LOGBOOK: CLOCK: [2022-11-03 %s 06:00]--[2022-11-03 %s 06:01] => 0:01 :END: " thu thu) (org-test-with-temp-text "* Foo :LOGBOOK: CLOCK: [2022-11-03 ??? 06:00]--[2022-11-03 ??? 06:01] => 0:01 :END: " (org-ctrl-c-ctrl-c) (buffer-string)))))) ;;; Clocktable (ert-deftest test-org-clock/clocktable/insert () "Test insert clocktable dynamic block with `org-dynamic-block-insert-dblock'." (should (equal "| Headline | Time | |--------------+--------| | *Total time* | *1:00* | |--------------+--------| | H1 | 1:00 |" (org-test-with-temp-text "* H1\n" (insert (org-test-clock-create-clock ". 1:00" ". 2:00")) (goto-line 2) (require 'org-clock) (org-dynamic-block-insert-dblock "clocktable") (goto-line 1) (unwind-protect (save-excursion (when (search-forward "#+CAPTION:") (forward-line)) (buffer-substring-no-properties (point) (progn (search-forward "#+END:") (line-end-position 0)))) (delete-region (point) (search-forward "#+END:\n"))))))) (ert-deftest test-org-clock/clocktable/ranges () "Test ranges in Clock table." ;; Relative time: Previous two days. (should (equal "| Headline | Time | | |------------------------------+--------+------| | *Total time* | *8:00* | | |------------------------------+--------+------| | Relative times in clocktable | 8:00 | | | Foo | | 8:00 |" (org-test-with-temp-text "* Relative times in clocktable\n** Foo\n" (insert (org-test-clock-create-clock "-3d 8:00" "-3d 12:00")) (insert (org-test-clock-create-clock "-2d 15:00" "-2d 18:00")) (insert (org-test-clock-create-clock "-1d 8:00" "-1d 13:00")) (test-org-clock-clocktable-contents ":tstart \"<-2d>\" :tend \"\" :indent nil")))) ;; Relative time: Yesterday until now. (should (equal "| Headline | Time | | |------------------------------+--------+------| | *Total time* | *6:00* | | |------------------------------+--------+------| | Relative times in clocktable | 6:00 | | | Foo | | 6:00 |" (org-test-with-temp-text "* Relative times in clocktable\n** Foo\n" (insert (org-test-clock-create-clock "-2d 15:00" "-2d 18:00")) (insert (org-test-clock-create-clock "-1d 8:00" "-1d 13:00")) (insert (org-test-clock-create-clock ". 1:00" ". 2:00")) (test-org-clock-clocktable-contents ":tstart \"\" :tend \"\" :indent nil")))) ;; Test `untilnow' block. (should (equal "| Headline | Time | | |------------------------------+--------+------| | *Total time* | *6:00* | | |------------------------------+--------+------| | Relative times in clocktable | 6:00 | | | Foo | | 6:00 |" (org-test-with-temp-text "* Relative times in clocktable\n** Foo\n" (insert (org-test-clock-create-clock "-10y 15:00" "-10y 18:00")) (insert (org-test-clock-create-clock "-2d 15:00" "-2d 18:00")) (test-org-clock-clocktable-contents ":block untilnow :indent nil"))))) (ert-deftest test-org-clock/clocktable/match () "Test \":match\" parameter in Clock table." ;; Test match filtering. (should (equal "| Headline | Time | | |--------------+--------+------| | *Total time* | *2:00* | | |--------------+--------+------| | H1 | | 2:00 |" (org-test-with-temp-text "** H1\n\n*** H2 :tag:\n\n*** H3\n" (insert (org-test-clock-create-clock ". 8:00" ". 9:00")) (goto-line 4) (insert (org-test-clock-create-clock ". 9:00" ". 11:00")) (test-org-clock-clocktable-contents ":match \"tag\" :indent nil"))))) (ert-deftest test-org-clock/clocktable/tags () "Test \":tags\" parameter in Clock table." ;; Test tags column. (should (equal "| Tags | Headline | Time | | |------+--------------+--------+------| | | *Total time* | *1:00* | | |------+--------------+--------+------| | tag | H1 | | 1:00 |" (org-test-with-temp-text "** H1 :tag:\n\n*** H2 \n" (insert (org-test-clock-create-clock ". 1:00" ". 2:00")) (goto-line 4) (test-org-clock-clocktable-contents ":tags t :indent nil"))))) (ert-deftest test-org-clock/clocktable/scope () "Test \":scope\" parameter in Clock table." ;; Test `file-with-archives' scope. In particular, preserve "TBLFM" ;; line, and ignore "file" column. (should (equal "| Headline | Time | | |--------------+--------+-----| | *Total time* | *8:40* | foo | |--------------+--------+-----| | Test | 8:40 | foo | #+TBLFM: $3=string(\"foo\")" (org-test-with-temp-text-in-file "* Test CLOCK: [2012-03-29 Thu 8:00]--[2012-03-29 Thu 16:40] => 8:40" (test-org-clock-clocktable-contents ":scope file-with-archives" "#+TBLFM: $3=string(\"foo\")")))) ;; Test "function" scope. (should (string-match-p (regexp-quote "| ALL *Total time* | *1:00* |") (org-test-with-temp-text-in-file "* Test CLOCK: [2012-03-29 Thu 16:00]--[2012-03-29 Thu 17:00] => 1:00" (let ((the-file (buffer-file-name))) (org-test-with-temp-text-in-file "" (test-org-clock-clocktable-contents (format ":scope (lambda () (list %S))" the-file)))))))) (ert-deftest test-org-clock/clocktable/maxlevel () "Test \":maxlevel\" parameter in Clock table." (should (equal "| Headline | Time | | |--------------+--------+------| | *Total time* | *6:00* | | |--------------+--------+------| | Foo | 6:00 | | | \\_ Bar | | 2:00 |" (org-test-with-temp-text "* Foo CLOCK: [2016-12-28 Wed 11:09]--[2016-12-28 Wed 15:09] => 4:00 ** Bar CLOCK: [2016-12-28 Wed 13:09]--[2016-12-28 Wed 15:09] => 2:00" (test-org-clock-clocktable-contents ":maxlevel 3")))) (should (equal "| Headline | Time | | |--------------+--------+------| | *Total time* | *6:00* | | |--------------+--------+------| | Foo | 6:00 | | | \\_ Bar | | 2:00 |" (org-test-with-temp-text "* Foo CLOCK: [2016-12-28 Wed 11:09]--[2016-12-28 Wed 15:09] => 4:00 ** Bar CLOCK: [2016-12-28 Wed 13:09]--[2016-12-28 Wed 15:09] => 2:00" (test-org-clock-clocktable-contents ":maxlevel 2")))) (should (equal "| Headline | Time | |--------------+--------| | *Total time* | *6:00* | |--------------+--------| | Foo | 6:00 |" (org-test-with-temp-text "* Foo CLOCK: [2016-12-28 Wed 11:09]--[2016-12-28 Wed 15:09] => 4:00 ** Bar CLOCK: [2016-12-28 Wed 13:09]--[2016-12-28 Wed 15:09] => 2:00" (test-org-clock-clocktable-contents ":maxlevel 1")))) ;; Special ":maxlevel 0" case: only report total file time. (should (equal "| Headline | Time | |--------------+--------| | *Total time* | *6:00* | |--------------+--------|" (org-test-with-temp-text "* Foo CLOCK: [2016-12-28 Wed 11:09]--[2016-12-28 Wed 15:09] => 4:00 ** Bar CLOCK: [2016-12-28 Wed 13:09]--[2016-12-28 Wed 15:09] => 2:00" (test-org-clock-clocktable-contents ":maxlevel 0"))))) (ert-deftest test-org-clock/clocktable/formula () "Test \":formula\" parameter in Clock table." ;; Test ":formula %". Handle various duration formats. (should (equal "| Headline | Time | % | |--------------+--------+-------| | *Total time* | *6:00* | 100.0 | |--------------+--------+-------| | Foo | 4:00 | 66.7 | | Bar | 2:00 | 33.3 |" (org-test-with-temp-text "* Foo CLOCK: [2016-12-28 Wed 11:09]--[2016-12-28 Wed 15:09] => 4:00 * Bar CLOCK: [2016-12-28 Wed 13:09]--[2016-12-28 Wed 15:09] => 2:00" (test-org-clock-clocktable-contents ":maxlevel 1 :formula %")))) (should (equal "| Headline | Time | % | |--------------+---------+-------| | *Total time* | *28:00* | 100.0 | |--------------+---------+-------| | Foo | 26:00 | 92.9 | | Bar | 2:00 | 7.1 |" (org-test-with-temp-text "* Foo CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00 * Bar CLOCK: [2016-12-28 Wed 13:09]--[2016-12-28 Wed 15:09] => 2:00" (test-org-clock-clocktable-contents ":maxlevel 1 :formula %")))) ;; Properly align column with different depths. (should (equal "| Headline | Time | | | % | |---------------+--------+------+------+-------| | *Total time* | *1:00* | | | 100.0 | |---------------+--------+------+------+-------| | foo | 1:00 | | | 100.0 | | \\_ sub | | 0:15 | | 25.0 | | \\_ sub2 | | 0:15 | | 25.0 | | \\_ sub3 | | 0:30 | | 50.0 | | \\_ subsub1 | | | 0:15 | 25.0 | | \\_ subsub1 | | | 0:15 | 25.0 |" (org-test-with-temp-text "* foo ** sub :LOGBOOK: CLOCK: [2017-03-18 Sat 15:00]--[2017-03-18 Sat 15:15] => 0:15 :END: ** sub2 :LOGBOOK: CLOCK: [2017-03-18 Sat 15:15]--[2017-03-18 Sat 15:30] => 0:15 :END: ** sub3 *** subsub1 :LOGBOOK: CLOCK: [2017-03-18 Sat 13:00]--[2017-03-18 Sat 13:15] => 0:15 :END: *** subsub1 :LOGBOOK: CLOCK: [2017-03-18 Sat 14:00]--[2017-03-18 Sat 14:15] => 0:15 :END:" (test-org-clock-clocktable-contents ":maxlevel 3 :formula %"))))) (ert-deftest test-org-clock/clocktable/lang () "Test \":lang\" parameter in Clock table." ;; Test foreign translation (should (equal "| Headline | Time | |--------------+---------| | *Total time* | *26:00* | |--------------+---------| | Foo | 26:00 |" (org-test-with-temp-text "* Foo CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":maxlevel 1 :lang en")))) (should (equal "| En-tête | Durée | |----------------+---------| | *Durée totale* | *26:00* | |----------------+---------| | Foo | 26:00 |" (org-test-with-temp-text "* Foo CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":maxlevel 1 :lang fr")))) ;; No :lang parameter is equivalent to "en". (should (equal (org-test-with-temp-text "* Foo CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":maxlevel 1 :lang en")) (org-test-with-temp-text "* Foo CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":maxlevel 1")))) ;; Unknown translation fall backs to "en". (should (equal "| Headline | Time | |--------------+---------| | *Total time* | *26:00* | |--------------+---------| | Foo | 26:00 |" (org-test-with-temp-text "* Foo CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":maxlevel 1 :lang foo"))))) (ert-deftest test-org-clock/clocktable/link () "Test \":link\" parameter in Clock table." ;; If there is no file attached to the document, link directly to ;; the headline. (should (string-match-p "| +\\[\\[\\*Foo]\\[Foo]] +| 26:00 +|" (org-test-with-temp-text "* Foo CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":link t")))) ;; Otherwise, link to the headline in the current file. (should (string-match-p "| \\[\\[file:filename::\\*Foo]\\[Foo]] +| 26:00 +|" (org-test-with-temp-text (org-test-with-temp-text-in-file "* Foo CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (let ((file (buffer-file-name))) (replace-regexp-in-string (regexp-quote file) "filename" (test-org-clock-clocktable-contents ":link t :lang en")))) (org-table-align) (buffer-substring-no-properties (point-min) (point-max))))) ;; Ignore TODO keyword, priority cookie, COMMENT and tags in ;; headline. (should (string-match-p "| \\[\\[\\*Foo]\\[Foo]] +| 26:00 +|" (org-test-with-temp-text "* TODO Foo CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":link t :lang en")))) (should (string-match-p "| \\[\\[\\*Foo]\\[Foo]] +| 26:00 +|" (org-test-with-temp-text "* [#A] Foo CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":link t :lang en")))) (should (string-match-p "| \\[\\[\\*Foo]\\[Foo]] +| 26:00 +|" (org-test-with-temp-text "* COMMENT Foo CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":link t")))) (should (string-match-p "| \\[\\[\\*Foo]\\[Foo]] +| 26:00 +|" (org-test-with-temp-text "* Foo :tag: CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":link t :lang en")))) ;; Remove statistics cookie from headline description. (should (string-match-p "| \\[\\[\\*Foo]\\[Foo]] +| 26:00 +|" (org-test-with-temp-text "* Foo [50%] CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":link t :lang en")))) (should (string-match-p "| \\[\\[\\*Foo]\\[Foo]] +| 26:00 +|" (org-test-with-temp-text "* Foo [1/2] CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":link t :lang en")))) ;; Replace links with their description, or turn them into plain ;; links if there is no description. (should (string-match-p "| \\[\\[\\*Foo \\\\\\[\\\\\\[https://orgmode\\.org\\\\]\\\\\\[Org mode\\\\]\\\\]]\\[Foo Org mode]] +| 26:00 +|" (org-test-with-temp-text "* Foo [[https://orgmode.org][Org mode]] CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":link t :lang en")))) (should (string-match-p "| \\[\\[\\*Foo \\\\\\[\\\\\\[https://orgmode\\.org\\\\]\\\\]]\\[Foo https://orgmode\\.org]] +| 26:00 +|" (org-test-with-temp-text "* Foo [[https://orgmode.org]] CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":link t :lang en"))))) (ert-deftest test-org-clock/clocktable/compact () "Test \":compact\" parameter in Clock table." ;; With :compact, all headlines are in the same column. (should (equal "| Headline | Time | |--------------+---------| | *Total time* | *26:00* | |--------------+---------| | Foo | 26:00 |" (org-test-with-temp-text "* Foo CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":compact t")))) (should (equal "| Headline | Time | |--------------+---------| | *Total time* | *52:00* | |--------------+---------| | Foo | 52:00 | | \\_ Bar | 26:00 |" (org-test-with-temp-text "* Foo CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00 ** Bar CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":compact t")))) ;; :maxlevel does not affect :compact parameter. (should (equal "| Headline | Time | |--------------+---------| | *Total time* | *52:00* | |--------------+---------| | Foo | 52:00 | | \\_ Bar | 26:00 |" (org-test-with-temp-text "* Foo CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00 ** Bar CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":compact t :maxlevel 2")))) ;; :compact implies a non-nil :indent parameter. (should (equal "| Headline | Time | |--------------+---------| | *Total time* | *52:00* | |--------------+---------| | Foo | 52:00 | | \\_ Bar | 26:00 |" (org-test-with-temp-text "* Foo CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00 ** Bar CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":compact t :indent nil")))) ;; :compact implies a nil :level parameter. (should (equal "| Headline | Time | |--------------+---------| | *Total time* | *52:00* | |--------------+---------| | Foo | 52:00 | | \\_ Bar | 26:00 |" (org-test-with-temp-text "* Foo CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00 ** Bar CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":compact t :level t"))))) (ert-deftest test-org-clock/clocktable/properties () "Test \":properties\" parameter in Clock table." ;; Include a new column with list properties. (should (equal "| A | Headline | Time | |---+--------------+---------| | | *Total time* | *26:00* | |---+--------------+---------| | 1 | Foo | 26:00 |" (org-test-with-temp-text "* Foo :PROPERTIES: :A: 1 :END: CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":properties (\"A\")")))) (should (equal "| A | Headline | Time | | |---+--------------+---------+-------| | | *Total time* | *52:00* | | |---+--------------+---------+-------| | | Foo | 52:00 | | | 1 | \\_ Bar | | 26:00 |" (org-test-with-temp-text "* Foo CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00 ** Bar :PROPERTIES: :A: 1 :END: CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":properties (\"A\")"))))) (ert-deftest test-org-clock/clocktable/tcolumns () "Test \":tcolumns\" parameter in Clock table." ;; When :tcolumns is smaller than the deepest headline level, lump ;; lower levels in the last column. (should (equal "| Headline | Time | |--------------+---------| | *Total time* | *52:00* | |--------------+---------| | Foo | 52:00 | | \\_ Bar | 26:00 |" (org-test-with-temp-text "* Foo CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00 ** Bar CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":tcolumns 1")))) ;; :tcolumns cannot create more columns than the deepest headline ;; level. (should (equal "| Headline | Time | | |--------------+---------+-------| | *Total time* | *52:00* | | |--------------+---------+-------| | Foo | 52:00 | | | \\_ Bar | | 26:00 |" (org-test-with-temp-text "* Foo CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00 ** Bar CLOCK: [2016-12-27 Wed 13:09]--[2016-12-28 Wed 15:09] => 26:00" (test-org-clock-clocktable-contents ":tcolumns 3")))) ;; Pathological case: when no headline contributes to the total ;; time, there is only one time column. (should (equal "| Headline | Time | |--------------+--------| | *Total time* | *0:00* |" (org-test-with-temp-text "* Foo CLOCK: [2016-12-28 Wed 11:09]--[2016-12-28 Wed 11:09] => 0:00 ** Bar CLOCK: [2016-12-28 Wed 13:09]--[2016-12-28 Wed 13:09] => 0:00" (test-org-clock-clocktable-contents ":tcolumns 2"))))) (ert-deftest test-org-clock/clocktable/step () "Test \":step\" parameter in Clock table." ;; Regression test: week crossing month boundary before :wstart ;; day-of-week. (should (string-match-p " .*?\\[2017-09-25 .* .* .* |.*?| \\*1:00\\* | .* | Foo +| 1:00 +|" (org-test-with-temp-text "* Foo CLOCK: [2017-09-30 Sat 12:00]--[2017-09-30 Sat 13:00] => 1:00 CLOCK: [2017-10-01 Sun 11:00]--[2017-10-01 Sun 13:00] => 2:00 CLOCK: [2017-10-02 Mon 11:00]--[2017-10-02 Mon 14:00] => 3:00" (let ((system-time-locale "en_US")) (test-org-clock-clocktable-contents ":step week :block 2017-09 :stepskip0 t"))))) (should (string-match-p " .*?\\[2017-10-01 .* .* .* |.*?| \\*2:00\\* | .* | Foo +| 2:00 | .*?\\[2017-10-02 .* .* .* |.*?| \\*7:00\\* | .* | Foo +| 7:00 +| .*?\\[2017-10-09 .* .* .* |.*?| \\*5:00\\* | .* | Foo +| 5:00 +| " (org-test-with-temp-text "* Foo CLOCK: [2017-09-30 Sat 12:00]--[2017-09-30 Sat 13:00] => 1:00 CLOCK: [2017-10-01 Sun 11:00]--[2017-10-01 Sun 13:00] => 2:00 CLOCK: [2017-10-02 Mon 11:00]--[2017-10-02 Mon 14:00] => 3:00 CLOCK: [2017-10-08 Sun 09:00]--[2017-10-08 Sun 13:00] => 4:00 CLOCK: [2017-10-09 Mon 09:00]--[2017-10-09 Mon 14:00] => 5:00" (let ((system-time-locale "en_US")) (test-org-clock-clocktable-contents ":step week :block 2017-10 :stepskip0 t"))))) ;; :step day (should (string-match-p " .*?\\[2017-10-02 .* .* .* |.*?| \\*3:00\\* | .* | Foo +| 3:00 +| .*?\\[2017-10-03 .* .* .* |.*?| \\*0:00\\* | .*?\\[2017-10-04 .* .* .* |.*?| \\*0:00\\* | .*?\\[2017-10-05 .* .* .* |.*?| \\*0:00\\* | .*?\\[2017-10-06 .* .* .* |.*?| \\*0:00\\* | .*?\\[2017-10-07 .* .* .* |.*?| \\*0:00\\* | .*?\\[2017-10-08 .* .* .* |.*?| \\*4:00\\* | .* | Foo +| 4:00 +|" (org-test-with-temp-text "* Foo CLOCK: [2017-09-30 Sat 12:00]--[2017-09-30 Sat 13:00] => 1:00 CLOCK: [2017-10-01 Sun 11:00]--[2017-10-01 Sun 13:00] => 2:00 CLOCK: [2017-10-02 Mon 11:00]--[2017-10-02 Mon 14:00] => 3:00 CLOCK: [2017-10-08 Sun 09:00]--[2017-10-08 Sun 13:00] => 4:00 CLOCK: [2017-10-09 Mon 09:00]--[2017-10-09 Mon 14:00] => 5:00" (let ((system-time-locale "en_US")) (test-org-clock-clocktable-contents ":step day :block 2017-W40"))))) ;; Regression test: take :tstart and :tend hours into consideration. (should (string-match-p " .*?\\[2017-12-25 .* .* .* |.*?| \\*8:00\\* | .* | Foo +| 8:00 +|" (org-test-with-temp-text "* Foo CLOCK: [2017-12-27 Wed 08:00]--[2017-12-27 Wed 16:00] => 8:00" (let ((system-time-locale "en_US")) (test-org-clock-clocktable-contents (concat ":step week :tstart \"<2017-12-25 Mon>\" " ":tend \"<2017-12-27 Wed 23:59>\"")))))) (should (string-match-p " .*?\\[2017-12-27 .* .* .* |.*?| \\*8:00\\* | .* | Foo +| 8:00 +|" (org-test-with-temp-text "* Foo CLOCK: [2017-12-27 Wed 08:00]--[2017-12-27 Wed 16:00] => 8:00" (let ((system-time-locale "en_US")) (test-org-clock-clocktable-contents (concat ":step day :tstart \"<2017-12-25 Mon>\" " ":tend \"<2017-12-27 Wed 23:59>\" :stepskip0 t")))))) ;; Test :step week", without or with ":wstart" parameter. (should (string-match-p " .*?\\[2012-03-26 .* .* .* |.*?| \\*8:00\\* | .* | Foo +| 8:00 +| .*?\\[2012-04-02 .* .* .* |.*?| \\*8:00\\* | .* | Foo +| 8:00 +| " (org-test-with-temp-text "* Foo CLOCK: [2012-03-29 Thu 08:00]--[2012-03-29 Thu 16:00] => 8:00 CLOCK: [2012-04-03 Thu 08:00]--[2012-04-03 Thu 16:00] => 8:00" (let ((system-time-locale "en_US")) (test-org-clock-clocktable-contents ":step week :block 2012 :stepskip0 t"))))) (should (string-match-p " .*?\\[2012-03-29 .* .* .* |.*?| \\*16:00\\* | .* | Foo +| 16:00 +| " (org-test-with-temp-text "* Foo CLOCK: [2012-03-29 Thu 08:00]--[2012-03-29 Thu 16:00] => 8:00 CLOCK: [2012-04-03 Thu 08:00]--[2012-04-03 Thu 16:00] => 8:00" (let ((system-time-locale "en_US")) (test-org-clock-clocktable-contents ":step week :wstart 4 :block 2012 :stepskip0 t"))))) ;; Test ":step month" without and with ":mstart". (should (string-match-p " .*?\\[2014-03-01 .* .* .* |.*?| \\*8:00\\* | .* | Foo +| 8:00 +| .*?\\[2014-04-01 .* .* .* |.*?| \\*8:00\\* | .* | Foo +| 8:00 +| " (org-test-with-temp-text "* Foo CLOCK: [2014-03-04 Tue 08:00]--[2014-03-04 Tue 16:00] => 8:00 CLOCK: [2014-04-03 Thu 08:00]--[2014-04-03 Thu 16:00] => 8:00" (let ((system-time-locale "en_US")) (test-org-clock-clocktable-contents ":step month :block 2014 :stepskip0 t"))))) (should (string-match-p " .*?\\[2014-03-04 .* .* .* |.*?| \\*16:00\\* | .* | Foo +| 16:00 +| " (org-test-with-temp-text "* Foo CLOCK: [2014-03-04 Tue 08:00]--[2014-03-04 Tue 16:00] => 8:00 CLOCK: [2014-04-03 Thu 08:00]--[2014-04-03 Thu 16:00] => 8:00" (let ((system-time-locale "en_US")) (test-org-clock-clocktable-contents ":step month :mstart 4 :block 2014 :stepskip0 t"))))) ;; Test ":step quarter". (should (string-match-p " Quarterly report starting on:.*?\\[2014-01-01 .* .* .* |.*?| \\*8:00\\* | .* | Foo +| 8:00 +| .*?\\[2014-04-01 .* .* .* |.*?| \\*16:00\\* | .* | Foo +| 16:00 +| .*?\\[2014-07-01 .* .* .* |.*?| \\*8:00\\* | .* | Foo +| 8:00 +| " (org-test-with-temp-text "* Foo CLOCK: [2014-03-04 Tue 08:00]--[2014-03-04 Tue 16:00] => 8:00 CLOCK: [2014-04-03 Thu 08:00]--[2014-04-03 Thu 16:00] => 8:00 CLOCK: [2014-06-04 Wed 08:00]--[2014-06-04 Wed 16:00] => 8:00 CLOCK: [2014-07-03 Thu 08:00]--[2014-07-03 Thu 16:00] => 8:00" (let ((system-time-locale "en_US")) (test-org-clock-clocktable-contents ":step quarter :block 2014 :stepskip0 t"))))) ;; Test ":step semimonth". (should (string-match-p " .*?\\[2014-03-01 .* .* .* |.*?| \\*8:00\\* | .* | Foo +| 8:00 +| .*?\\[2014-03-16 .* .* .* |.*?| \\*2:00\\* | .* | Foo +| 2:00 +| .*?\\[2014-04-01 .* .* .* |.*?| \\*7:00\\* | .* | Foo +| 7:00 +| " (org-test-with-temp-text "* Foo CLOCK: [2014-03-04 Tue 08:00]--[2014-03-04 Tue 16:00] => 8:00 CLOCK: [2014-03-24 Mon 08:00]--[2014-03-24 Mon 10:00] => 2:00 CLOCK: [2014-04-03 Thu 08:00]--[2014-04-03 Thu 15:00] => 7:00" (let ((system-time-locale "en_US")) (test-org-clock-clocktable-contents ":step semimonth :block 2014 :stepskip0 t"))))) ;; Test ":step year". (should (string-match-p " .*?\\[2012-01-01 .* .* .* |.*?| \\*8:00\\* | .* | Foo +| 8:00 +| .*?\\[2014-01-01 .* .* .* |.*?| \\*8:00\\* | .* | Foo +| 8:00 +| " (org-test-with-temp-text "* Foo CLOCK: [2012-03-29 Thu 08:00]--[2012-03-29 Thu 16:00] => 8:00 CLOCK: [2014-03-04 Tue 08:00]--[2014-03-04 Tue 16:00] => 8:00" (let ((system-time-locale "en_US")) (test-org-clock-clocktable-contents ":step year :block untilnow :stepskip0 t"))))) ;; Regression test: Respect DST (should (string-match-p " .*?\\[2018-10-29 .* .* .* |.*?| \\*8:00\\* | .* | Foo +| 8:00 +| " (org-test-with-temp-text "* Foo CLOCK: [2018-10-29 Mon 08:00]--[2018-10-29 Mon 16:00] => 8:00" (let ((system-time-locale "en_US")) (test-org-clock-clocktable-contents (concat ":step day " ":stepskip0 t " ":tstart \"2018-10-01\" " ":tend \"2018-11-01\""))))))) (ert-deftest test-org-clock/clocktable/extend-today-until () "Test assignment of clock time to days in presence of \"org-extend-today-until\"." ;; Basic test of :block with org-extend-today-until - the report for ;; 2017-09-30 should include the time clocked on 2017-10-01 before ;; 04:00. (should (equal "| Headline | Time | |--------------+--------| | *Total time* | *2:00* | |--------------+--------| | Foo | 2:00 |" (org-test-with-temp-text "* Foo CLOCK: [2017-09-30 Sat 12:00]--[2017-09-30 Sat 13:00] => 1:00 CLOCK: [2017-10-01 Sun 02:00]--[2017-10-01 Sun 03:00] => 1:00 CLOCK: [2017-10-01 Sun 11:00]--[2017-10-01 Sun 13:00] => 2:00" (setq-local org-extend-today-until 4) (let ((system-time-locale "en_US")) (test-org-clock-clocktable-contents ":block 2017-09-30"))))) ;; Week-length block - time on Monday before 04:00 should be ;; assigned to previous week. (should (string-match-p " .*? \\[2017-10-01 .* .* .* |.*?| \\*2:00\\* | .* | Foo +| 2:00 | .*? \\[2017-10-02 .* .* .* |.*?| \\*2:00\\* | .* | Foo +| 2:00 | " (org-test-with-temp-text "* Foo CLOCK: [2017-10-01 Sun 12:00]--[2017-10-01 Sun 13:00] => 1:00 CLOCK: [2017-10-02 Mon 02:00]--[2017-10-02 Mon 03:00] => 1:00 CLOCK: [2017-10-02 Mon 11:00]--[2017-10-02 Mon 13:00] => 2:00" (setq-local org-extend-today-until 4) (let ((system-time-locale "en_US")) (test-org-clock-clocktable-contents ":step week :block 2017-10 :stepskip0 t")))))) (ert-deftest test-org-clock/clocktable/hidefiles () "Test \":hidefiles\" parameter in Clock table." ;; Test that hidefiles removes the file column. (should (equal "| Headline | Time | |--------------+--------| | *Total time* | *1:00* | |--------------+--------| | Test | 1:00 |" (org-test-with-temp-text-in-file "* Test CLOCK: [2012-03-29 Thu 16:00]--[2012-03-29 Thu 17:00] => 1:00" (let ((the-file (buffer-file-name))) (org-test-with-temp-text-in-file "" (test-org-clock-clocktable-contents (format ":hidefiles t :scope (lambda () (list %S))" the-file)))))))) ;;; Mode line (ert-deftest test-org-clock/mode-line () "Test mode line string ends in a space. \"Elements that are added to [the mode line] should normally end in a space (to ensure that consecutive 'global-mode-string' elements display properly)\" per the Info node `(elisp)Mode Line Variables'." ;; Test the variant without effort. (should (equal " [0:00] (Heading) " (org-test-with-temp-text "* Heading" (org-clock-in) (prog1 (concat " " (org-clock-get-clock-string) " ") (org-clock-out))))) ;; Test the variant with effort. (should (equal " [0:00/1:00] (Heading) " (org-test-with-temp-text "* Heading :PROPERTIES: :EFFORT: 1h :END:" (org-clock-in) (prog1 (concat " " (org-clock-get-clock-string) " ") (org-clock-out)))))) ;;; Helpers (ert-deftest test-org-clock/special-range () "Test `org-clock-special-range'." (let* ((cases '((("2023-04-23 Sun" "2023-04-24 Mon" "2023-04-25 Tue" "2023-04-26 Wed" "2023-04-27 Thu" "2023-04-28 Fri" "2023-04-29 Sat") thisweek 0 "2023-04-23 Sun" "2023-04-30 Sun") (("2023-04-24 Mon" "2023-04-25 Tue" "2023-04-26 Wed" "2023-04-27 Thu" "2023-04-28 Fri" "2023-04-29 Sat" "2023-04-30 Sun") thisweek 1 "2023-04-24 Mon" "2023-05-01 Mon") (("2023-04-24 Mon" "2023-04-25 Tue" "2023-04-26 Wed" "2023-04-27 Thu" "2023-04-28 Fri" "2023-04-29 Sat" "2023-04-30 Sun") thisweek nil ; Copy of 1. "2023-04-24 Mon" "2023-05-01 Mon") (("2023-04-22 Sat" "2023-04-23 Sun" "2023-04-24 Mon" "2023-04-25 Tue" "2023-04-26 Wed" "2023-04-27 Thu" "2023-04-28 Fri") thisweek 6 "2023-04-22 Sat" "2023-04-29 Sat") (("2023-04-23 Sun" "2023-04-24 Mon" "2023-04-25 Tue" "2023-04-26 Wed" "2023-04-27 Thu" "2023-04-28 Fri" "2023-04-29 Sat") thisweek 7 ; Copy of 0. "2023-04-23 Sun" "2023-04-30 Sun"))) (failed (delq nil (mapcar (lambda (params) (pcase-let ((`(,days ,key ,wstart ,begin ,end) params)) (delq nil (mapcar (lambda (today) (let* ((ts-today (org-time-string-to-time today)) (range (org-clock-special-range key ts-today nil wstart nil)) (ts-begin (nth 0 range)) (ts-end (nth 1 range)) (expected-begin (org-time-string-to-time begin)) (expected-end (org-time-string-to-time end))) (unless (and (equal ts-begin expected-begin) (equal ts-end expected-end)) (format "%s..%s != %s..%s %s %s :wstart %s" begin end (format-time-string "%F" ts-begin) (format-time-string "%F" ts-end) today key wstart)))) days)))) cases)))) (should-not failed))) (provide 'test-org-clock) ;;; test-org-clock.el end here org-mode-9.7.29+dfsg/testing/lisp/test-org-colview.el000066400000000000000000001344531500430433700224430ustar00rootroot00000000000000;;; test-org-colview.el --- Tests for org-colview.el -*- lexical-binding: t; -*- ;; Copyright (C) 2016, 2019 Nicolas Goaziou ;; Author: Nicolas Goaziou ;; 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 . ;;; Code: ;;; Column view (require 'cl-lib) (require 'org-colview) (require 'org-duration) (require 'org-inlinetask) (ert-deftest test-org-colview/uncompile-format () "Test `org-columns-uncompile-format' specifications." ;; With minimum data, one element (should (equal "%ITEM" (org-columns-uncompile-format '(("ITEM" "ITEM" nil nil nil))))) ;; With minimum data, two element (should (equal "%ITEM %TODO" (org-columns-uncompile-format `(("ITEM" "ITEM" nil nil nil) ("TODO" "TODO" nil nil nil))))) ;; Read width (should (equal "%10ITEM" (org-columns-uncompile-format `(("ITEM" "ITEM" 10 nil nil))))) ;; Read title (should (equal "%ITEM(some title)" (org-columns-uncompile-format `(("ITEM" "some title" nil nil nil))))) ;; Read operator (should (equal "%ITEM{+}" (org-columns-uncompile-format `(("ITEM" "ITEM" nil "+" nil))))) ;; Read operator printf (should (equal "%ITEM{+;%.1f}" (org-columns-uncompile-format `(("ITEM" "ITEM" nil "+" "%.1f")))))) (ert-deftest test-org-colview/compile-format () "Test `org-columns-compile-format' specifications." ;; With minimum data, one element (should (equal `(("ITEM" "ITEM" nil nil nil)) (org-columns-compile-format "%ITEM"))) ;; With minimum data, two element (should (equal `(("ITEM" "ITEM" nil nil nil) ("TODO" "TODO" nil nil nil)) (org-columns-compile-format "%ITEM %TODO"))) ;; Read width (should (equal `(("ITEM" "ITEM" 10 nil nil)) (org-columns-compile-format "%10ITEM"))) ;; Upcase property name (should (equal `(("ITEM" "item" nil nil nil)) (org-columns-compile-format "%item"))) ;; Read title (should (equal `(("ITEM" "some title" nil nil nil)) (org-columns-compile-format "%ITEM(some title)"))) ;; Read operator (should (equal `(("ITEM" "ITEM" nil "+" nil)) (org-columns-compile-format "%ITEM{+}"))) ;; Read operator printf (should (equal `(("ITEM" "ITEM" nil "+" "%.1f")) (org-columns-compile-format "%ITEM{+;%.1f}")))) (ert-deftest test-org-colview/substring-below-width () "Test `org-columns--truncate-below-width'." (cl-flet ((check (string width expect) (string= expect (org-columns--truncate-below-width string width)))) (if (= (char-width ?…) 2) (progn (should (check "12…" 3 "12")) (should (check "1…2" 1 "1")) (should (check "1…2" 2 "1")) (should (check "1…2" 3 "1…")) (should (check "……………………" 7 "………"))) (progn (should (check "12…" 4 "12…")) (should (check "1…2" 1 "1")) (should (check "1…2" 2 "1…")) (should (check "1…2" 3 "1…2")) (should (check "……………………" 7 "…………………")))))) (ert-deftest test-org-colview/get-format () "Test `org-columns-get-format' specifications." ;; Without any clue, use `org-columns-default-format'. (should (equal "%A" (org-test-with-temp-text "* H" (let ((org-columns-default-format "%A")) (org-columns-get-format))))) ;; If COLUMNS keyword is set, use it. (should (equal "%B" (org-test-with-temp-text "#+COLUMNS: %B\n* H" (let ((org-columns-default-format "%A")) (org-columns-get-format))))) (should (equal "%B" (org-test-with-temp-text "#+columns: %B\n* H" (let ((org-columns-default-format "%A")) (org-columns-get-format))))) (should (equal "%B" (org-test-with-temp-text "* H\n#+COLUMNS: %B" (let ((org-columns-default-format "%A")) (org-columns-get-format))))) ;; When :COLUMNS: property is set somewhere in the tree, use it over ;; the previous ways. (should (equal "%C" (org-test-with-temp-text "#+COLUMNS: %B\n* H\n:PROPERTIES:\n:COLUMNS: %C\n:END:\n** S\n" (let ((org-columns-default-format "%A")) (org-columns-get-format))))) ;; When optional argument is provided, prefer it. (should (equal "%D" (org-test-with-temp-text "#+COLUMNS: %B\n* H\n:PROPERTIES:\n:COLUMNS: %C\n:END:\n** S\n" (let ((org-columns-default-format "%A")) (org-columns-get-format "%D")))))) (ert-deftest test-org-colview/columns-scope () "Test `org-columns' scope." ;; Before the first headline, view all document. (should (equal '("H1" "H2" "H3") (org-test-with-temp-text "Top\n* H1\n** H2\n* H3" (let ((org-columns-default-format "%ITEM")) (org-columns)) (org-map-entries (lambda () (get-char-property (point) 'org-columns-value)))))) ;; When :COLUMNS: is set up in the hierarchy, view tree starting ;; there. (should (equal '(nil "H2" "H3" nil) (org-test-with-temp-text "* H1\n** H2\n:PROPERTIES:\n:COLUMNS: %ITEM\n:END:\n*** H3\n* H4" (let ((org-columns-default-format "%ITEM")) (org-columns)) (org-map-entries (lambda () (get-char-property (point) 'org-columns-value)))))) ;; Otherwise, view tree starting at the current headline. (should (equal '(nil "H2" "H3" nil) (org-test-with-temp-text "Top\n* H1\n** H2\n*** H3\n* H4" (let ((org-columns-default-format "%ITEM")) (org-columns)) (org-map-entries (lambda () (get-char-property (point) 'org-columns-value)))))) ;; With a non-nil prefix argument, always view all document. (should (equal '("H1" "H2" "H3" "H4") (org-test-with-temp-text "* H1\n** H2\n:PROPERTIES:\n:COLUMNS: %ITEM\n:END:\n*** H3\n* H4" (let ((org-columns-default-format "%ITEM")) (org-columns t)) (org-map-entries (lambda () (get-char-property (point) 'org-columns-value)))))) (should (equal '("1" "1") (org-test-with-temp-text "Top\n* H1\n** H2\n:PROPERTIES:\n:A: 1\n:END:" (let ((org-columns-default-format "%A{+}")) (org-columns t)) (org-map-entries (lambda () (get-char-property (point) 'org-columns-value))))))) (ert-deftest test-org-colview/columns-width () "Test `org-columns' column widths." ;; When a width is specified in the format, use it. (should (= 9 (org-test-with-temp-text "* H" (let ((org-columns-default-format "%9ITEM")) (org-columns)) (aref org-columns-current-maxwidths 0)))) ;; Otherwise, use the width of the largest value in the column. (should (= 2 (org-test-with-temp-text "* H\n:PROPERTIES:\n:P: X\n:END:\n** H2\n:PROPERTIES:\n:P: XX\n:END:" (let ((org-columns-default-format "%P")) (org-columns)) (aref org-columns-current-maxwidths 0)))) ;; If the title is wider than the widest value, use title width ;; instead. (should (= 4 (org-test-with-temp-text "* H" (let ((org-columns-default-format "%ITEM")) (org-columns)) (aref org-columns-current-maxwidths 0)))) ;; Special case: stars do count for ITEM. (should (= 6 (org-test-with-temp-text "* Head" (let ((org-columns-default-format "%ITEM")) (org-columns)) (aref org-columns-current-maxwidths 0)))) ;; Special case: width takes into account link narrowing in ITEM. (should (equal '("* 123" . 5) (org-test-with-temp-text "* [[https://orgmode.org][123]]" (let ((org-columns-default-format "%ITEM")) (org-columns)) (cons (get-char-property (point) 'org-columns-value-modified) (aref org-columns-current-maxwidths 0))))) ;; When a value is too wide for the current column, add ellipses. ;; Take into consideration length of `org-columns-ellipses'. (should (equal "123.. |" (org-test-with-temp-text "* H\n:PROPERTIES:\n:P: 123456\n:END:" (let ((org-columns-default-format "%5P") (org-columns-ellipses "..")) (org-columns)) (org-trim (get-char-property (point) 'display))))) (should (equal (if (= 1 (char-width ?…)) "1234… |" "123… |") (org-test-with-temp-text "* H\n:PROPERTIES:\n:P: 123456\n:END:" (let ((org-columns-default-format "%5P") (org-columns-ellipses "…")) (org-columns)) (org-trim (get-char-property (point) 'display)))))) (ert-deftest test-org-colview/columns-summary () "Test `org-columns' summary types." ;; {+} and {+;format} add numbers. (should (equal "3" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 1 :END: ** S1 :PROPERTIES: :A: 2 :END:" (let ((org-columns-default-format "%A{+}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) (should (equal "3.0" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 1 :END: ** S1 :PROPERTIES: :A: 2 :END:" (let ((org-columns-default-format "%A{+;%.1f}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) ;; {$} is a shortcut for {+;%.2f}. (should (equal "3.60" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 1.50 :END: ** S1 :PROPERTIES: :A: 2.10 :END:" (let ((org-columns-default-format "%A{$}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) ;; Obey to format string even in leaf values. (should (equal "1.0" (org-test-with-temp-text "* H :PROPERTIES: :A: 1 :END:" (let ((org-columns-default-format "%A{+;%.1f}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) ;; {:} sums times. Plain numbers are minutes. (should (equal "4:10" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 1:30 :END: ** S1 :PROPERTIES: :A: 2:40 :END:" (let ((org-columns-default-format "%A{:}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) (should (equal "1:32" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 1:30 :END: ** S1 :PROPERTIES: :A: 2 :END:" (let ((org-columns-default-format "%A{:}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) ;; {X}, {X/} and {X%} indicate checkbox status. (should (equal "[ ]" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: [ ] :END: ** S1 :PROPERTIES: :A: [ ] :END:" (let ((org-columns-default-format "%A{X}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) (should (equal "[-]" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: [ ] :END: ** S1 :PROPERTIES: :A: [X] :END:" (let ((org-columns-default-format "%A{X}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) (should (equal "[X]" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: [X] :END: ** S1 :PROPERTIES: :A: [X] :END:" (let ((org-columns-default-format "%A{X}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) (should (equal "[1/2]" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: [ ] :END: ** S1 :PROPERTIES: :A: [X] :END:" (let ((org-columns-default-format "%A{X/}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) (should (equal "[50%]" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: [ ] :END: ** S1 :PROPERTIES: :A: [X] :END:" (let ((org-columns-default-format "%A{X%}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) ;; {X/} handles recursive summaries. (should (equal "[1/2]" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: [ ] :END: ** S2 *** S21 :PROPERTIES: :A: [X] :END: *** S22 :PROPERTIES: :A: [X] :END:" (let ((org-columns-default-format "%A{X/}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) (should (equal "[1/2]" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: [X] :END: ** S2 *** S21 :PROPERTIES: :A: [ ] :END: *** S22 :PROPERTIES: :A: [ ] :END:" (let ((org-columns-default-format "%A{X/}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) ;; {X%} handles recursive summaries. (should (equal "[50%]" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: [ ] :END: ** S2 *** S21 :PROPERTIES: :A: [X] :END: *** S22 :PROPERTIES: :A: [X] :END:" (let ((org-columns-default-format "%A{X%}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) (should (equal "[50%]" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: [X] :END: ** S2 *** S21 :PROPERTIES: :A: [ ] :END: *** S22 :PROPERTIES: :A: [ ] :END:" (let ((org-columns-default-format "%A{X%}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) ;; {min} is the smallest number in column, {max} the largest one. ;; {mean} is the arithmetic mean of numbers in column. (should (equal "42" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 99 :END: ** S1 :PROPERTIES: :A: 42 :END:" (let ((org-columns-default-format "%A{min}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) (should (equal "99" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 99 :END: ** S1 :PROPERTIES: :A: 42 :END:" (let ((org-columns-default-format "%A{max}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) (should (equal "51.0" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 60 :END: ** S1 :PROPERTIES: :A: 42 :END:" (let ((org-columns-default-format "%A{mean}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) ;; {:min}, {:max} and {:mean} apply to time values. (should (equal "1:20" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 4:40 :END: ** S1 :PROPERTIES: :A: 1:20 :END:" (let ((org-columns-default-format "%A{:min}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) (should (equal "4:40" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 4:40 :END: ** S1 :PROPERTIES: :A: 1:20 :END:" (let ((org-columns-default-format "%A{:max}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) (should (equal "3:00" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 4:40 :END: ** S1 :PROPERTIES: :A: 1:20 :END:" (let ((org-columns-default-format "%A{:mean}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) ;; {@min}, {@max} and {@mean} apply to ages. (should (equal "0min" (org-test-at-time "<2014-03-04 Tue>" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: <2012-03-29 Thu> :END: ** S1 :PROPERTIES: :A: <2014-03-04 Tue> :END:" (let ((org-columns-default-format "%A{@min}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified))))) (should (equal "2d" (org-test-at-time "<2014-03-04 Tue>" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: <2014-03-03 Mon> :END: ** S1 :PROPERTIES: :A: <2014-03-02 Sun> :END:" (let ((org-columns-default-format "%A{@max}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified))))) (should (equal "1d 12h" (org-test-at-time "<2014-03-04 Tue>" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: <2014-03-03 Mon> :END: ** S1 :PROPERTIES: :A: <2014-03-02 Sun> :END:" (let ((org-columns-default-format "%A{@mean}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified))))) ;; If a time value is expressed as a duration, return a duration. ;; If any of them follows H:MM:SS pattern, use it too. Also handle ;; combinations of duration and H:MM:SS patterns. (should (equal "3d 4:20" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 3d 3h :END: ** S1 :PROPERTIES: :A: 1:20 :END:" (let ((org-columns-default-format "%A{:}") (org-duration-units '(("d" . 1440) ("h" . 60))) (org-duration-format '(("d" . nil) (special . h:mm)))) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) (should (equal "6:00:10" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 4:40:10 :END: ** S1 :PROPERTIES: :A: 1:20 :END:" (let ((org-columns-default-format "%A{:}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) (should (equal "3d 4:20" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 3d 3h :END: ** S1 :PROPERTIES: :A: 0d 1:20 :END:" (let ((org-columns-default-format "%A{:}") (org-duration-units '(("d" . 1440) ("h" . 60))) (org-duration-format '(("d" . nil) (special . h:mm)))) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) ;; @min, @max and @mean also accept regular duration. (should (equal "1d 10h" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 1d 10h 0min :END: ** S1 :PROPERTIES: :A: 5d 3h :END:" (let ((org-columns-default-format "%A{@min}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) ;; {est+} gives a low-high estimate using mean and standard ;; deviation. (should (equal "3-17" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 0-10 :END: ** S1 :PROPERTIES: :A: 0-10 :END:" (let ((org-columns-default-format "%A{est+}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) ;; When using {est+} summary, a single number is understood as ;; a degenerate range. (should (equal "4-4" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 4 :END: " (let ((org-columns-default-format "%A{est+}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) ;; Allow custom summary types. (should (equal "1|2" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 1 :END: ** S1 :PROPERTIES: :A: 2 :END:" (let ((org-columns-summary-types '(("custom" . (lambda (s _) (mapconcat #'identity s "|"))))) (org-columns-default-format "%A{custom}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) ;; Allow custom _collect_ for summary types. (should (equal "2" (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 1 :END: ** S1 :PROPERTIES: :A: 2 :A-OK: 1 :END:" (let ((org-columns-summary-types '(("custom" org-columns--summary-sum (lambda (p) (if (equal "1" (org-entry-get nil (format "%s-OK" p))) (org-entry-get nil p) ""))))) (org-columns-default-format "%A{custom}")) (org-columns)) (get-char-property (point) 'org-columns-value-modified)))) ;; Allow custom collect function to be used for different columns (should (equal '("2" "1") (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 1 :B: 1 :B-OK: 1 :END: ** S1 :PROPERTIES: :A: 2 :B: 2 :A-OK: 1 :END:" (let ((org-columns-summary-types '(("custom" org-columns--summary-sum (lambda (p) (if (equal "1" (org-entry-get nil (format "%s-OK" p))) (org-entry-get nil p) ""))))) (org-columns-default-format "%A{custom} %B{custom}")) (org-columns)) (list (get-char-property (point) 'org-columns-value-modified) (get-char-property (1+ (point)) 'org-columns-value-modified))))) ;; Allow multiple summary types applied to the same property. (should (equal '("42" "99") (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 99 :END: ** S1 :PROPERTIES: :A: 42 :END:" (let ((org-columns-default-format "%A{min} %A{max}")) (org-columns)) (list (get-char-property (point) 'org-columns-value-modified) (get-char-property (1+ (point)) 'org-columns-value-modified))))) ;; Allow mixing both summarized and non-summarized columns for ;; a property. However, the first column takes precedence and ;; updates the value. (should (equal '("1000" "42") (org-test-with-temp-text "* H :PROPERTIES: :A: 1000 :END: ** S1 :PROPERTIES: :A: 99 :END: ** S1 :PROPERTIES: :A: 42 :END:" (let ((org-columns-default-format "%A %A{min}")) (org-columns)) (list (get-char-property (point) 'org-columns-value-modified) (get-char-property (1+ (point)) 'org-columns-value-modified))))) (should (equal '("42" "42") (org-test-with-temp-text "* H :PROPERTIES: :A: 1000 :END: ** S1 :PROPERTIES: :A: 99 :END: ** S1 :PROPERTIES: :A: 42 :END:" (let ((org-columns-default-format "%A{min} %A")) (org-columns)) (list (get-char-property (point) 'org-columns-value-modified) (get-char-property (1+ (point)) 'org-columns-value-modified)))))) (ert-deftest test-org-colview/columns-new () "Test `org-columns-new' specifications." ;; Insert new column at the left of the current one. (should (equal '("FOO" "ITEM") (org-test-with-temp-text "* H" (let ((org-columns-default-format "%ITEM")) (org-columns)) (org-columns-new nil "FOO" "FOO" nil nil nil) (list (get-char-property (point) 'org-columns-key) (get-char-property (1+ (point)) 'org-columns-key))))) (should (equal '("ITEM" "FOO" "BAR") (org-test-with-temp-text "* H" (let ((org-columns-default-format "%ITEM %BAR")) (org-columns)) (forward-char) (org-columns-new nil "FOO" "FOO" nil nil nil) (list (get-char-property (1- (point)) 'org-columns-key) (get-char-property (point) 'org-columns-key) (get-char-property (1+ (point)) 'org-columns-key))))) ;; Update #+COLUMNS keyword if needed. (should (equal "#+COLUMNS: %FOO %ITEM" (org-test-with-temp-text "#+COLUMNS: %ITEM\n* H" (let ((org-columns-default-format "%ITEM")) (org-columns)) (org-columns-new nil "FOO" "FOO" nil nil nil) (goto-char (point-min)) (buffer-substring-no-properties (point) (line-end-position))))) (should (equal "#+COLUMNS: %ITEM %FOO %BAR" (org-test-with-temp-text "#+COLUMNS: %ITEM %BAR\n* H" (let ((org-columns-default-format "%ITEM %BAR")) (org-columns)) (forward-char) (org-columns-new nil "FOO" "FOO" nil nil nil) (goto-char (point-min)) (buffer-substring-no-properties (point) (line-end-position))))) ;; Mind case when updating #+COLUMNS. (should (equal "#+COLUMNS: %ITEM %Foo %BAR" (org-test-with-temp-text "#+COLUMNS: %ITEM %BAR\n* H" (let ((org-columns-default-format "%ITEM %BAR")) (org-columns)) (forward-char) (org-columns-new nil "Foo" "Foo" nil nil nil) (goto-char (point-min)) (buffer-substring-no-properties (point) (line-end-position))))) (should (equal "#+columns: %ITEM %Foo %BAR" (org-test-with-temp-text "#+columns: %ITEM %BAR\n* H" (let ((org-columns-default-format "%ITEM %BAR")) (org-columns)) (forward-char) (org-columns-new nil "Foo" "Foo" nil nil nil) (goto-char (point-min)) (buffer-substring-no-properties (point) (line-end-position))))) ;; Also update :COLUMNS: properties. (should (equal "%FOO %ITEM" (org-test-with-temp-text "* H\n:PROPERTIES:\n:COLUMNS: %ITEM\n:END:" (let ((org-columns-default-format "%ITEM")) (org-columns)) (org-columns-new nil "FOO" "FOO" nil nil nil) (org-entry-get nil "COLUMNS")))) ;; If no keyword nor any property is available, insert one. (should (string-match-p (regexp-quote "#+COLUMNS: %FOO %ITEM") (org-test-with-temp-text "* H" (let ((org-columns-default-format "%ITEM")) (org-columns)) (org-columns-new nil "FOO" "FOO" nil nil nil) (buffer-string))))) (ert-deftest test-org-colview/columns-update () "Test `org-columns-update' specifications." ;; Update display. (should (equal "12 |" (org-test-with-temp-text "* H :PROPERTIES: :A: 1 :END: " (let ((org-columns-default-format "%5A")) (org-columns)) (search-forward "1") (insert "2") (org-columns-update "A") (get-char-property (point-min) 'display)))) ;; Update is case-insensitive. (should (equal "12 |" (org-test-with-temp-text "* H :PROPERTIES: :A: 1 :END: " (let ((org-columns-default-format "%5A")) (org-columns)) (search-forward "1") (insert "2") (org-columns-update "a") (get-char-property (point-min) 'display)))) ;; Update stored values. (should (equal '("12" "12") (org-test-with-temp-text "* H :PROPERTIES: :A: 1 :END: " (let ((org-columns-default-format "%5A")) (org-columns)) (search-forward "1") (insert "2") (org-columns-update "A") (list (get-char-property (point-min) 'org-columns-value) (get-char-property (point-min) 'org-columns-value-modified))))) ;; When multiple columns are using the same property, value is ;; updated according to the specifications of the first one. (should (equal "2" (org-test-with-temp-text "* H :PROPERTIES: :A: 1 :END: ** S :PROPERTIES: :A: 2 :END:" (let ((org-columns-default-format "%A{min} %A")) (org-columns)) (org-columns-update "A") (org-entry-get nil "A")))) (should (equal "1" (org-test-with-temp-text "* H :PROPERTIES: :A: 1 :END: ** S :PROPERTIES: :A: 2 :END:" (let ((org-columns-default-format "%A %A{min}")) (org-columns)) (org-columns-update "A") (org-entry-get nil "A")))) ;; Ensure modifications propagate in upper levels even when multiple ;; summary types apply to the same property. (should (equal '("1" "22") (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 1 :END: ** S2 :PROPERTIES: :A: 2 :END:" (save-excursion (goto-char (point-min)) (let ((org-columns-default-format "%A{min} %A{max}")) (org-columns))) (insert "2") (org-columns-update "A") (list (get-char-property 1 'org-columns-value) (get-char-property 2 'org-columns-value-modified))))) ;; Ensure additional processing is done (e.g., ellipses, special ;; keywords fontification...). (should (equal "ve.. |" (org-test-with-temp-text "* H :PROPERTIES: :A: text :END: " (let ((org-columns-default-format "%4A") (org-columns-ellipses "..")) (org-columns)) (search-forward ":A: ") (insert "very long ") (org-columns-update "A") (get-char-property (point-min) 'display)))) ;; Values obtained from inline tasks are at the same level as those ;; obtained from children of the current node. (when (featurep 'org-inlinetask) (should (equal "2" (org-test-with-temp-text "* H *************** Inline task :PROPERTIES: :A: 2 :END: *************** END ** Children :PROPERTIES: :A: 3 :END: " (let ((org-columns-default-format "%A{min}") (org-columns-ellipses "..") (org-inlinetask-min-level 15)) (org-element-update-syntax) (org-columns)) (get-char-property (point-min) 'org-columns-value))))) ;; Handle `org-columns-modify-value-for-display-function', even with ;; multiple titles for the same property. (should (equal '("foo" "bar") (org-test-with-temp-text "* H" (let ((org-columns-default-format "%ITEM %ITEM(Name)") (org-columns-modify-value-for-display-function (lambda (title _value) (pcase title ("ITEM" "foo") ("Name" "bar") (_ "baz"))))) (org-columns)) (list (get-char-property 1 'org-columns-value-modified) (get-char-property 2 'org-columns-value-modified)))))) (ert-deftest test-org-colview/columns-move-row-down () "Test `org-columns-move-row-down' specifications." (should (equal "* H ** B ** A " (org-test-with-temp-text "* H ** A ** B " (let ((org-columns-default-format "%ITEM")) (org-columns) (next-line 1) (org-columns-move-row-down) (buffer-substring-no-properties (point-min) (point-max))))))) (ert-deftest test-org-colview/columns-move-row-up () "Test `org-columns-move-row-up' specifications." (should (equal "* H ** B ** A " (org-test-with-temp-text "* H ** A ** B " (let ((org-columns-default-format "%ITEM")) (org-columns) (next-line 2) (org-columns-move-row-up) (buffer-substring-no-properties (point-min) (point-max))))))) (ert-deftest test-org-colview/columns--move-row-stay-at-the-same-column () "After function call 'org-columns--move-row' point should stay at the same column." ;; `current-column' did not return _visual_ column prior to Emacs 29. (skip-unless (version<= "29" emacs-version)) (should (equal 35 (org-test-with-temp-text "* H ** A ** B " (org-columns) (next-line 1) (forward-char 2) (org-columns--move-row) (current-column))))) (ert-deftest test-org-colview/columns-move-row-down-with-subheading () "Test `org-columns-move-row-up' specifications with subheading." (should (equal "* H ** B ** A *** A1 " (org-test-with-temp-text "* H ** A *** A1 ** B " (let ((org-columns-default-format "%ITEM")) (org-columns) (next-line 1) (org-columns-move-row-down) (buffer-substring-no-properties (point-min) (point-max))))))) (ert-deftest test-org-colview/columns-move-left () "Test `org-columns-move-left' specifications." ;; Error when trying to move the left-most column. (should-error (org-test-with-temp-text "* H" (let ((org-columns-default-format "%ITEM")) (org-columns)) (org-columns-move-left))) ;; Otherwise, move column to left and update display. (should (equal '("2" "1") (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:B: 2\n:END:" (let ((org-columns-default-format "%A %B")) (org-columns)) (forward-char) (org-columns-move-left) (list (get-char-property (point) 'org-columns-value) (get-char-property (1+ (point)) 'org-columns-value))))) ;; Handle multiple columns with the same property. (should (equal '("2" "1") (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 1 :END: ** S1 :PROPERTIES: :A: 2 :END:" (let ((org-columns-default-format "%ITEM %A{min} %A{max}")) (org-columns)) (forward-char 2) (org-columns-move-left) (list (get-char-property (point) 'org-columns-value) (get-char-property (1+ (point)) 'org-columns-value))))) ;; Special case: do not update values even if move entails changing ;; them. (should (equal "1" (org-test-with-temp-text "* H :PROPERTIES: :A: 1 :END: ** S1 :PROPERTIES: :A: 99 :END:" (let ((org-columns-default-format "%A %A{max}")) (org-columns)) (forward-char) (org-columns-move-left) ;; Since the first column matching a given property ;; determines how a value is computed, the following ;; should return "99" instead. However, this behavior is ;; in practice surprising so we just ignore the value ;; change. It can be computed later. (org-entry-get (point) "A"))))) (ert-deftest test-org-colview/columns-move-right () "Test `org-columns-move-right' specifications." ;; Error when trying to move the right-most column. (should-error (org-test-with-temp-text "* H" (let ((org-columns-default-format "%ITEM")) (org-columns)) (org-columns-move-right))) ;; Otherwise, move column to left and update display. (should (equal '("2" "1") (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:B: 2\n:END:" (let ((org-columns-default-format "%A %B")) (org-columns)) (org-columns-move-right) (list (get-char-property (1- (point)) 'org-columns-value) (get-char-property (point) 'org-columns-value))))) ;; Handle multiple columns with the same property. (should (equal '("2" "1") (org-test-with-temp-text "* H ** S1 :PROPERTIES: :A: 1 :END: ** S1 :PROPERTIES: :A: 2 :END:" (let ((org-columns-default-format "%ITEM %A{min} %A{max}")) (org-columns)) (forward-char) (org-columns-move-right) (list (get-char-property (1- (point)) 'org-columns-value) (get-char-property (point) 'org-columns-value))))) ;; Special case: do not update values even if move entails changing ;; them. (should (equal "1" (org-test-with-temp-text "* H :PROPERTIES: :A: 1 :END: ** S1 :PROPERTIES: :A: 99 :END:" (let ((org-columns-default-format "%A %A{max}")) (org-columns)) (org-columns-move-right) ;; See `test-org-colview/columns-move-left' for an ;; explanation. (org-entry-get (point) "A"))))) (ert-deftest test-org-colview/columns-next-allowed-value () "Test `org-columns-next-allowed-value' specifications." ;; Cannot shift "ITEM" property. (should-error (org-test-with-temp-text "* H" (let ((org-columns-default-format "%ITEM")) (org-columns)) (org-columns-next-allowed-value))) ;; Throw an error when allowed values are not defined. (should-error (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:" (let ((org-columns-default-format "%A")) (org-columns)) (org-columns-next-allowed-value))) ;; Throw an error when there's only one value to select. (should-error (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:A_ALL: 1\n:END:" (let ((org-columns-default-format "%A")) (org-columns)) (org-columns-next-allowed-value))) ;; By default select the next allowed value. Where there is no more ;; value, start again from first possible one. (should (equal "2" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:A_ALL: 1 2 3\n:END:" (let ((org-columns-default-format "%A")) (org-columns)) (org-columns-next-allowed-value) (org-entry-get (point) "A")))) (should (equal "3" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 2\n:A_ALL: 1 2 3\n:END:" (let ((org-columns-default-format "%A")) (org-columns)) (org-columns-next-allowed-value) (org-entry-get (point) "A")))) (should (equal "1" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 3\n:A_ALL: 1 2 3\n:END:" (let ((org-columns-default-format "%A")) (org-columns)) (org-columns-next-allowed-value) (org-entry-get (point) "A")))) ;; PREVIOUS argument moves backward. (should (equal "1" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 2\n:A_ALL: 1 2 3\n:END:" (let ((org-columns-default-format "%A")) (org-columns)) (org-columns-next-allowed-value 'previous) (org-entry-get (point) "A")))) (should (equal "2" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 3\n:A_ALL: 1 2 3\n:END:" (let ((org-columns-default-format "%A")) (org-columns)) (org-columns-next-allowed-value 'previous) (org-entry-get (point) "A")))) (should (equal "3" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:A_ALL: 1 2 3\n:END:" (let ((org-columns-default-format "%A")) (org-columns)) (org-columns-next-allowed-value 'previous) (org-entry-get (point) "A")))) ;; Select Nth element with optional argument NTH. (should (equal "1" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 2\n:A_ALL: 1 2 3\n:END:" (let ((org-columns-default-format "%A")) (org-columns)) (org-columns-next-allowed-value nil 1) (org-entry-get (point) "A")))) ;; If NTH is negative, go backwards, 0 being the last one and -1 the ;; penultimate. (should (equal "3" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 2\n:A_ALL: 1 2 3\n:END:" (let ((org-columns-default-format "%A")) (org-columns)) (org-columns-next-allowed-value nil 0) (org-entry-get (point) "A")))) (should (equal "2" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 2\n:A_ALL: 1 2 3\n:END:" (let ((org-columns-default-format "%A")) (org-columns)) (org-columns-next-allowed-value nil -1) (org-entry-get (point) "A")))) ;; Throw an error if NTH is greater than the number of allowed ;; values. (should-error (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 2\n:A_ALL: 1 2 3\n:END:" (let ((org-columns-default-format "%A")) (org-columns)) (org-columns-next-allowed-value nil 4) (org-entry-get (point) "A"))) ;; Pathological case: when shifting the value alters the current ;; heading, make sure all columns are still at their correct ;; location. (should (equal '("H" "" "" "" "TODO") (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* H" (let ((org-columns-default-format "%ITEM %A %B %C %TODO")) (org-columns) (forward-char 4) (org-columns-next-allowed-value) (list (get-char-property (- (point) 4) 'org-columns-value) (get-char-property (- (point) 3) 'org-columns-value) (get-char-property (- (point) 2) 'org-columns-value) (get-char-property (- (point) 1) 'org-columns-value) (get-char-property (point) 'org-columns-value))))))) (should (equal '("H" "VERYLONGTODO") (let ((org-todo-keywords '((sequence "TODO" "VERYLONGTODO")))) (org-test-with-temp-text "* TODO H" (let ((org-columns-default-format "%ITEM %TODO")) (org-columns) (forward-char) (org-columns-next-allowed-value) (list (get-char-property (- (point) 1) 'org-columns-value) (get-char-property (point) 'org-columns-value)))))))) ;;; Dynamic block (defun test-org-colview/dblock-formatter (ipos table params) "User-defined columnview dblock formatting function." (goto-char ipos) (insert-before-markers "Hello columnview!" "\n") (insert-before-markers (format "table has %d rows" (length table)) "\n") (insert-before-markers (format "there are %d parameters" (/ (length params) 2)))) (ert-deftest test-org-colview/dblock () "Test the column view table." (should (equal "#+BEGIN: columnview | ITEM | |------| | H | #+END:" (org-test-with-temp-text "* H\n#+BEGIN: columnview\n#+END:" (let ((org-columns-default-format "%ITEM")) (org-update-dblock)) (buffer-substring-no-properties (point) (point-max))))) (should (equal "#+BEGIN: columnview | ITEM | A | |------+---| | H | 1 | #+END:" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:\n#+BEGIN: columnview\n#+END:" (let ((org-columns-default-format "%ITEM %A")) (org-update-dblock)) (buffer-substring-no-properties (point) (point-max))))) ;; Test column widths. (should (equal "#+BEGIN: columnview | <5> | | ITEM | |------| | H | #+END:" (org-test-with-temp-text "* H\n#+BEGIN: columnview\n#+END:" (let ((org-columns-default-format "%5ITEM")) (org-update-dblock)) (buffer-substring-no-properties (point) (point-max))))) ;; Properties are case insensitive. (should (equal "#+BEGIN: columnview | a | |---| | 1 | #+END:" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:\n#+BEGIN: columnview\n#+END:" (let ((org-columns-default-format "%a")) (org-update-dblock)) (buffer-substring-no-properties (point) (point-max))))) ;; Test titles given to columns. (should (equal "#+BEGIN: columnview | Name | Prop | |------+------| | H | 1 | #+END:" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:\n#+BEGIN: columnview\n#+END:" (let ((org-columns-default-format "%ITEM(Name) %A(Prop)")) (org-update-dblock)) (buffer-substring-no-properties (point) (point-max))))) ;; Test `:id' parameter (should (equal "#+BEGIN: columnview :id local | ITEM | |------| | H1 | | H1.1 | #+END: " (org-test-with-temp-text "* H1\n#+BEGIN: columnview :id local\n#+END:\n** H1.1\n* H2" (let ((org-columns-default-format "%ITEM")) (org-update-dblock)) (buffer-substring-no-properties (point) (outline-next-heading))))) (should (equal "#+BEGIN: columnview :id global | ITEM | |------| | H1 | | H1.1 | | H2 | #+END: " (org-test-with-temp-text "\n* H1\n#+BEGIN: columnview :id global\n#+END:\n** H1.1\n* H2" (let ((org-columns-default-format "%ITEM")) (org-update-dblock)) (buffer-substring-no-properties (point) (outline-next-heading))))) ;; Test `:hlines' parameter. (should (equal "#+BEGIN: columnview :hlines t :id global | ITEM | |------| | H | |------| | H2 | |------| | H2.1 | #+END:\n" (org-test-with-temp-text " * H #+BEGIN: columnview :hlines t :id global #+END: * H2 ** H2.1" (let ((org-columns-default-format "%ITEM")) (org-update-dblock)) (buffer-substring-no-properties (point) (outline-next-heading))))) (should (equal "#+BEGIN: columnview :hlines 1 :id global | ITEM | |------| | H | |------| | H2 | | H2.1 | #+END:\n" (org-test-with-temp-text " * H #+BEGIN: columnview :hlines 1 :id global #+END: * H2 ** H2.1" (let ((org-columns-default-format "%ITEM")) (org-update-dblock)) (buffer-substring-no-properties (point) (outline-next-heading))))) (should (equal "#+BEGIN: columnview :hlines 1 :id \"id\" | ITEM | |------| | H2 | | H2.1 | #+END: " (org-test-with-temp-text " * H #+BEGIN: columnview :hlines 1 :id \"id\" #+END: * H2 :PROPERTIES: :ID: id :END: ** H2.1" (let ((org-columns-default-format "%ITEM")) (org-update-dblock)) (buffer-substring-no-properties (point) (outline-next-heading))))) (should (equal "#+BEGIN: columnview :hlines 1 :id id | ITEM | |------| | H2 | | H2.1 | #+END: " (org-test-with-temp-text " * H #+BEGIN: columnview :hlines 1 :id id #+END: * H2 :PROPERTIES: :ID: id :END: ** H2.1" (let ((org-columns-default-format "%ITEM")) (org-update-dblock)) (buffer-substring-no-properties (point) (outline-next-heading))))) ;; Test `:indent' parameter. (should (equal "#+BEGIN: columnview :indent t | ITEM | |----------| | H1 | | \\_ H1.1 | #+END: " (org-test-with-temp-text "* H1\n#+BEGIN: columnview :indent t\n#+END:\n** H1.1" (let ((org-columns-default-format "%ITEM")) (org-update-dblock)) (buffer-substring-no-properties (point) (outline-next-heading))))) (should (equal "#+BEGIN: columnview :indent t | Prop | Name | |------+----------| | | H1 | | | \\_ H1.1 | #+END: " (org-test-with-temp-text "* H1\n#+BEGIN: columnview :indent t\n#+END:\n** H1.1" (let ((org-columns-default-format "%A(Prop) %ITEM(Name)")) (org-update-dblock)) (buffer-substring-no-properties (point) (outline-next-heading))))) ;; Test `:vlines' parameter. (should (equal "#+BEGIN: columnview :vlines t | | ITEM | A | |---+------+----| | | H | 1 | | / | <> | <> | #+END:" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:\n#+BEGIN: columnview :vlines t\n#+END:" (let ((org-columns-default-format "%ITEM %A")) (org-update-dblock)) (buffer-substring-no-properties (point) (point-max))))) ;; Test `:skip-empty-rows' parameter. (should (equal "#+BEGIN: columnview :skip-empty-rows t | ITEM | A | |------+---| | H1.1 | 1 | #+END: " (org-test-with-temp-text " * H1 #+BEGIN: columnview :skip-empty-rows t #+END: ** H1.1 :PROPERTIES: :A: 1 :END:" (let ((org-columns-default-format "%ITEM %A")) (org-update-dblock)) (buffer-substring-no-properties (point) (outline-next-heading))))) ;; Test `:exclude-tags' parameter. (should (equal "#+BEGIN: columnview :exclude-tags (\"excludeme\") | ITEM | A | |------+---| | H1 | | #+END: " (org-test-with-temp-text " * H1 #+BEGIN: columnview :exclude-tags (\"excludeme\") #+END: ** H1.1 :excludeme: :PROPERTIES: :A: 1 :END:" (let ((org-columns-default-format "%ITEM %A")) (org-update-dblock)) (buffer-substring-no-properties (point) (outline-next-heading))))) ;; Test `:format' parameter. (should (equal "#+BEGIN: columnview :format \"%ITEM(Name)\" | Name | |------| | H | #+END:" (org-test-with-temp-text "* H\n#+BEGIN: columnview :format \"%ITEM(Name)\"\n#+END:" (let ((org-columns-default-format "%ITEM")) (org-update-dblock)) (buffer-substring-no-properties (point) (point-max))))) ;; When inserting ITEM values, make sure to clean sensitive ;; contents, like unique targets or forbidden inline src-blocks. (should (equal "#+BEGIN: columnview | ITEM | |------| | H 1 | #+END:" (org-test-with-temp-text "* H <> 1\n#+BEGIN: columnview\n#+END:" (let ((org-columns-default-format "%ITEM")) (org-update-dblock)) (buffer-substring-no-properties (point) (point-max))))) (should (equal "#+BEGIN: columnview | ITEM | |------| | H 1 | #+END:" (org-test-with-temp-text "* H src_emacs-lisp{(+ 1 1)} 1\n#+BEGIN: columnview\n#+END:" (let ((org-columns-default-format "%ITEM")) (org-update-dblock)) (buffer-substring-no-properties (point) (point-max))))) ;; Active time stamps are displayed as inactive. (should (equal "#+BEGIN: columnview | ITEM | d | s | t | |------+------------------+------------------+------------------| | H | [2020-05-14 Thu] | [2020-05-11 Mon] | [2020-06-10 Wed] | #+END:" (org-test-with-temp-text "* H SCHEDULED: <2020-05-11 Mon> DEADLINE: <2020-05-14 Thu> <2020-06-10 Wed> #+BEGIN: columnview\n#+END:" (let ((org-columns-default-format "%ITEM %DEADLINE(d) %SCHEDULED(s) %TIMESTAMP(t)")) (org-update-dblock)) (buffer-substring-no-properties (point) (point-max))))) ;; custom formatting function (should (equal "#+BEGIN: columnview :formatter test-org-colview/dblock-formatter Hello columnview! table has 3 rows there are 4 parameters #+END:" (org-test-with-temp-text "* H\n#+BEGIN: columnview :formatter test-org-colview/dblock-formatter\n#+END:" (let ((org-columns-default-format "%ITEM")) (org-update-dblock)) (buffer-substring-no-properties (point) (point-max))))) ;; test headline linkification (should (equal "#+BEGIN: columnview :link t | ITEM | |------| | [[*H][H]] | #+END:" (org-test-with-temp-text "* H\n#+BEGIN: columnview :link t\n#+END:" (let ((org-columns-default-format "%ITEM")) (org-update-dblock)) (buffer-substring-no-properties (point) (point-max)))))) (provide 'test-org-colview) ;;; test-org-colview.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-ctags.el000066400000000000000000000160011500430433700220600ustar00rootroot00000000000000;;; test-org-ctags.el --- tests for org-ctags.el -*- lexical-binding: t -*- ;; Copyright (C) 2024 Max Nikulin ;; Authors: Max Nikulin ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: ;; Alternative implementation for `test-org-ctags/mock-command' ;; is required for cmd.exe. (unless (string-equal "-c" shell-command-switch) (signal 'missing-test-dependency "POSIX shell")) (require 'org-ctags) ;;;; Helpers: (defun test-org-ctags/mock-command (temp-file command-name) "Define shell function COMMAND-NAME wrining arguments to TEMP-FILE." ;; Failure exit code is used to prevent further `org-ctags' actions. (format "%s() { printf '%%s\\n' %s \"$@\" >%s 2>&1 ; false ; } ; %s" command-name command-name (shell-quote-argument temp-file) command-name)) (defun test-org-ctags/get-args (temp-file base magic) "Read list of strings from TEMP-FILE. If TEMP-FILE does not start from MAGIC then return its content as a string. Otherwise strip first line and trailing newline, replace BASE with \"TMPDIR\" string, return list of lines." (let* ((case-fold-search nil) (content (and (file-exists-p temp-file) (with-temp-buffer (insert-file-contents temp-file) (goto-char (point-min)) (when (looking-at magic) (while (search-forward base nil 'noerror) (replace-match "TMPDIR" 'fixedcase 'literal))) (goto-char (point-max)) (when (and (bolp) (> (point) 1)) (delete-char -1)) (buffer-string))))) (if (and content (string-prefix-p magic content)) (cdr (split-string content "\n")) content))) (defmacro test-org-ctags/with-fake-ctags (temp-dir subdir &rest body) "Run BODY with `org-ctags-path-to-ctags' set to a test function. Create a buffer backed by a file in the TEMP-DIR/SUBDIR directory." (declare (indent 2)) (let ((buffer (gensym "buffer")) (base (gensym "base")) (dir (gensym "dir")) (temp-file (gensym "temp-file"))) `(let* ((,base ,temp-dir) (,dir (concat ,base "/" ,subdir)) (,temp-file (concat ,dir "/ctags.txt")) (org-ctags-path-to-ctags (test-org-ctags/mock-command ,temp-file "ctags_mock")) ,buffer) (make-directory ,dir) (unwind-protect ;; `org-ctags' commands call `buffer-file-name'. (with-current-buffer (setq ,buffer (find-file-noselect ,temp-file)) (insert "Should be overwritten by org-ctags mock script") (save-buffer) ,@body (test-org-ctags/get-args ,temp-file ,base "ctags_mock\n")) (kill-buffer ,buffer) (delete-file ,temp-file) (delete-directory ,dir))))) ;;;; Comparator to have informative failures: (defun test-org-ctags/list-elements (lst &optional indicies) "Select INDICIES elements from LST list. INDICIES should be sorted in growing order." (if (not (and indicies (listp lst))) lst (let (selected (prev 0)) (dolist (i indicies (nreverse selected)) (setq lst (nthcdr (- i prev) lst)) (setq prev i) (push (car lst) selected))))) (defun test-org-ctags/list-elements-equal-p (expect actual indicies &rest _comments) "Call `equal' for lists EXPECT and INDICIES elements from ACTUAL. _COMMENTS should appear in failure message." (equal expect (test-org-ctags/list-elements actual indicies))) (defun test-org-ctags/list-elements-equal-explain (expect actual indicies &rest _comments) "`ert-eplainer' for `test-org-ctags/list-elements-equal-p'." (if (listp actual) (list 'selected-elements (test-org-ctags/list-elements actual indicies)) "Shell command failed")) (put 'test-org-ctags/list-elements-equal-p 'ert-explainer 'test-org-ctags/list-elements-equal-explain) ;;;; Tests: (ert-deftest test-org-ctags/create-tags-escape () "Test that `org-ctags-create-tags' escapes shell arguments." (let ((temp-dir (make-temp-file "test-org-ctags-" 'dir))) (unwind-protect (progn (should (test-org-ctags/list-elements-equal-p (list (format "--regex-orgmode=%s" org-ctags-tag-regexp)) (test-org-ctags/with-fake-ctags temp-dir "regexp" (org-ctags-create-tags)) '(2) "Regexp should be escaped.")) (should (test-org-ctags/list-elements-equal-p '("TMPDIR/regular/ctags.txt") (test-org-ctags/with-fake-ctags temp-dir "regular" (org-ctags-create-tags (concat temp-dir "/regular"))) '(7) "Wildcard should be expanded." "Directory passed as an argument.")) (should (test-org-ctags/list-elements-equal-p '("TMPDIR/space char/TAGS" "TMPDIR/space char/ctags.txt") (test-org-ctags/with-fake-ctags temp-dir "space char" (org-ctags-create-tags (concat temp-dir "/space char"))) '(4 7) "Space characters should not split arguments." "Directory passed as an argument.")) (should (test-org-ctags/list-elements-equal-p '("TMPDIR/apostrophe' sep '/TAGS" "TMPDIR/apostrophe' sep '/ctags.txt") (test-org-ctags/with-fake-ctags temp-dir "apostrophe' sep '" (org-ctags-create-tags)) '(4 7) "Apostrophes should be regular characters." "Path is derived from `default-directory'.")) (should (test-org-ctags/list-elements-equal-p '("TMPDIR/def-dir.$HOME/TAGS" "TMPDIR/def-dir.$HOME/ctags.txt") (test-org-ctags/with-fake-ctags temp-dir "def-dir.$HOME" (org-ctags-create-tags)) '(4 7) "$VARIABLES should not be expanded in directory names." "Path is derived from `default-directory'.")) (should (test-org-ctags/list-elements-equal-p '("TMPDIR/arg.$HOME/TAGS" "TMPDIR/arg.$HOME/ctags.txt") (test-org-ctags/with-fake-ctags temp-dir "arg.$HOME" (org-ctags-create-tags (concat temp-dir "/arg.$HOME"))) '(4 7) "$VARIABLES should not be expanded in directory names." "Directory passed as an argument"))) (delete-directory temp-dir)))) (provide 'test-org-ctags) ;;; test-org.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-datetree.el000066400000000000000000000242321500430433700225610ustar00rootroot00000000000000;;; test-org-datetree.el --- Tests for Org Datetree -*- lexical-binding: t; -*- ;; Copyright (C) 2015, 2019 Nicolas Goaziou ;; Author: Nicolas Goaziou ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (require 'org-datetree) (ert-deftest test-org-datetree/find-date-create () "Test `org-datetree-find-date-create' specifications." ;; When date is missing, create it. (let ((org-blank-before-new-entry '((heading . t)))) (should (string-match "\\`\\* 2012\n\n\\*\\* 2012-03 .*\n\n\\*\\*\\* 2012-03-29 .*\\'" (org-test-with-temp-text "" (let ((org-datetree-add-timestamp nil)) (org-datetree-find-date-create '(3 29 2012))) (org-trim (buffer-string))))) ;; Do not create new year node when one exists. (should (string-match "\\`\\* 2012\n\n\\*\\* 2012-03 .*\n\n\\*\\*\\* 2012-03-29 .*\\'" (org-test-with-temp-text "* 2012\n" (let ((org-datetree-add-timestamp nil)) (org-datetree-find-date-create '(3 29 2012))) (org-trim (buffer-string))))) ;; Do not create new month node when one exists. (should (string-match "\\`\\* 2012\n\n\\*\\* 2012-03 .*\n\n\\*\\*\\* 2012-03-29 .*\\'" (org-test-with-temp-text "* 2012\n\n** 2012-03 month" (let ((org-datetree-add-timestamp nil)) (org-datetree-find-date-create '(3 29 2012))) (org-trim (buffer-string))))) ;; Do not create new day node when one exists. (should (string-match "\\`\\* 2012\n\n\\*\\* 2012-03 .*\n\n\\*\\*\\* 2012-03-29 .*\\'" (org-test-with-temp-text "* 2012\n\n** 2012-03 month\n\n*** 2012-03-29 day" (let ((org-datetree-add-timestamp nil)) (org-datetree-find-date-create '(3 29 2012))) (org-trim (buffer-string))))) ;; Do not create new day node when one exists. (should (string-match "\\`\\* DONE 2012 :tag1:tag2:\n\n\\*\\* TODO 2012-03 .*\n\n\\*\\*\\* \\[#A\\] 2012-03-29 .*\\'" (org-test-with-temp-text "* DONE 2012 :tag1:tag2:\n\n** TODO 2012-03 month\n\n*** [#A] 2012-03-29 day :tag3:" (let ((org-datetree-add-timestamp nil)) (org-datetree-find-date-create '(3 29 2012))) (org-trim (buffer-string))))) ;; Sort new entry in right place. (should (string-match "\\`\\* 2012\n\n\\*\\* 2012-02 .*\n\n\\*\\*\\* 2012-02-01 .*\n\n\\*\\* 2012-03 .*\n\n\\*\\*\\* 2012-03-29 .*\\'" (org-test-with-temp-text "* 2012\n\n** 2012-03 month\n\n*** 2012-03-29 day" (let ((org-datetree-add-timestamp nil)) (org-datetree-find-date-create '(3 29 2012)) (org-datetree-find-date-create '(2 1 2012))) (org-trim (buffer-string))))) ;; When `org-datetree-add-timestamp' is non-nil, insert a timestamp ;; in entry. When set to `inactive', insert an inactive one. (should (string-match "\\`\\* 2012\n\n\\*\\* 2012-03 .*\n\n\\*\\*\\* \\(2012-03-29\\) .*\n[ \t]*<\\1.*?>\\'" (org-test-with-temp-text "* 2012\n" (let ((org-datetree-add-timestamp t)) (org-datetree-find-date-create '(3 29 2012))) (org-trim (buffer-string))))) (should (string-match "\\`\\* 2012\n\n\\*\\* 2012-03 .*\n\n\\*\\*\\* \\(2012-03-29\\) .*\n[ \t]*\\[\\1.*?\\]\\'" (org-test-with-temp-text "* 2012\n" (let ((org-datetree-add-timestamp 'inactive)) (org-datetree-find-date-create '(3 29 2012))) (org-trim (buffer-string))))) ;; Insert at top level, unless some node has DATE_TREE property. In ;; this case, date tree becomes one of its sub-trees. (should (string-match "\\* 2012" (org-test-with-temp-text "* Top" (let ((org-datetree-add-timestamp nil)) (org-datetree-find-date-create '(3 29 2012))) (org-trim (buffer-string))))) (should (string-match "\\*\\* H1.1\n:PROPERTIES:\n:DATE_TREE: t\n:END:\n\n\\*\\*\\* 2012" (org-test-with-temp-text "* H1\n\n** H1.1\n:PROPERTIES:\n:DATE_TREE: t\n:END:\n\n* H2" (let ((org-datetree-add-timestamp nil)) (org-datetree-find-date-create '(3 29 2012))) (org-trim (buffer-string))))) ;; Always leave point at beginning of day entry. (should (string-match "\\*\\*\\* 2012-03-29" (org-test-with-temp-text "* 2012\n\n** 2012-03 month\n\n*** 2012-03-29 day" (let ((org-datetree-add-timestamp nil)) (org-datetree-find-date-create '(3 29 2012))) (buffer-substring (point) (line-end-position))))) (should (string-match "\\*\\*\\* 2012-03-29" (org-test-with-temp-text "* 2012\n\n** 2012-03 month\n\n*** 2012-03-29 day" (let ((org-datetree-add-timestamp t)) (org-datetree-find-date-create '(3 29 2012))) (buffer-substring (point) (line-end-position))))))) (ert-deftest test-org-datetree/find-month-create () "Test `org-datetree-find-month-create' specifications." (let ((org-blank-before-new-entry '((heading . t)))) ;; When date is missing, create it with the entry under month. (should (string-match "\\`\\* 2012\n\n\\*\\* 2012-03 .*\\'" (org-test-with-temp-text "" (let ((org-datetree-add-timestamp nil)) (org-datetree-find-month-create '(3 29 2012))) (org-trim (buffer-string))))))) (ert-deftest test-org-datetree/find-iso-week-create () "Test `org-datetree-find-iso-date-create' specification." (let ((org-blank-before-new-entry '((heading . t)))) ;; When date is missing, create it. (should (string-match "\\`\\* 2015\n\n\\*\\* 2015-W01\n\n\\*\\*\\* 2014-12-31 .*\\'" (org-test-with-temp-text "" (let ((org-datetree-add-timestamp nil)) (org-datetree-find-iso-week-create '(12 31 2014))) (org-trim (buffer-string))))) ;; Do not create new year node when one exists. (should (string-match "\\`\\* 2015\n\n\\*\\* 2015-W01\n\n\\*\\*\\* 2014-12-31 .*\\'" (org-test-with-temp-text "* 2015\n" (let ((org-datetree-add-timestamp nil)) (org-datetree-find-iso-week-create '(12 31 2014))) (org-trim (buffer-string))))) ;; Do not create new month node when one exists. (should (string-match "\\`\\* 2015\n\n\\*\\* 2015-W01\n\n\\*\\*\\* 2014-12-31 .*\\'" (org-test-with-temp-text "* 2015\n\n** 2015-W01" (let ((org-datetree-add-timestamp nil)) (org-datetree-find-iso-week-create '(12 31 2014))) (org-trim (buffer-string))))) ;; Do not create new day node when one exists. (should (string-match "\\`\\* 2015\n\n\\*\\* 2015-W01\n\n\\*\\*\\* 2014-12-31 .*\\'" (org-test-with-temp-text "* 2015\n\n** 2015-W01\n\n*** 2014-12-31 day" (let ((org-datetree-add-timestamp nil)) (org-datetree-find-iso-week-create '(12 31 2014))) (org-trim (buffer-string))))) ;; Do not create new day node when one exists. (should (string-match "\\`\\* TODO \\[#B\\] 2015\n\n\\*\\* 2015-W01 :tag1:\n\n\\*\\*\\* 2014-12-31 .*\\'" (org-test-with-temp-text "* TODO [#B] 2015\n\n** 2015-W01 :tag1:\n\n*** 2014-12-31 day" (let ((org-datetree-add-timestamp nil)) (org-datetree-find-iso-week-create '(12 31 2014))) (org-trim (buffer-string))))) ;; Sort new entry in right place. (should (string-match "\\`\\* 2015\n\n\\*\\* 2015-W01\n\n\\*\\*\\* 2014-12-31 .*\n\n\\*\\* 2015-W36\n\n\\*\\*\\* 2015-09-01 .*\\'" (org-test-with-temp-text "* 2015" (let ((org-datetree-add-timestamp nil)) (org-datetree-find-iso-week-create '(9 1 2015)) (org-datetree-find-iso-week-create '(12 31 2014))) (org-trim (buffer-string))))) ;; When `org-datetree-add-timestamp' is non-nil, insert a timestamp ;; in entry. When set to `inactive', insert an inactive one. (should (string-match "\\`\\* 2015\n\n\\*\\* 2015-W01\n\n\\*\\*\\* \\(2014-12-31\\) .*\n[ \t]*<\\1.*?>\\'" (org-test-with-temp-text "* 2015\n" (let ((org-datetree-add-timestamp t)) (org-datetree-find-iso-week-create '(12 31 2014))) (org-trim (buffer-string))))) (should (string-match "\\`\\* 2015\n\n\\*\\* 2015-W01\n\n\\*\\*\\* \\(2014-12-31\\) .*\n[ \t]*\\[\\1.*?\\]\\'" (org-test-with-temp-text "* 2015\n" (let ((org-datetree-add-timestamp 'inactive)) (org-datetree-find-iso-week-create '(12 31 2014))) (org-trim (buffer-string))))) ;; Insert at top level, unless some node has WEEK_TREE property. In ;; this case, date tree becomes one of its sub-trees. (should (string-match "\\* 2015" (org-test-with-temp-text "* Top" (let ((org-datetree-add-timestamp nil)) (org-datetree-find-iso-week-create '(12 31 2014))) (org-trim (buffer-string))))) (should (string-match "\\*\\* H1.1\n:PROPERTIES:\n:WEEK_TREE: t\n:END:\n\n\\*\\*\\* 2015" (org-test-with-temp-text "* H1\n** H1.1\n:PROPERTIES:\n:WEEK_TREE: t\n:END:\n\n* H2" (let ((org-datetree-add-timestamp nil)) (org-datetree-find-iso-week-create '(12 31 2014))) (org-trim (buffer-string))))) ;; Always leave point at beginning of day entry. (should (string-match "\\*\\*\\* 2014-12-31" (org-test-with-temp-text "* 2015\n\n** 2015-W01\n\n*** 2014-12-31 day" (let ((org-datetree-add-timestamp nil)) (org-datetree-find-iso-week-create '(12 31 2014))) (buffer-substring (point) (line-end-position))))) (should (string-match "\\*\\*\\* 2014-12-31" (org-test-with-temp-text "* 2015\n\n** 2015-W01\n\n*** 2014-12-31 day" (let ((org-datetree-add-timestamp t)) (org-datetree-find-iso-week-create '(12 31 2014))) (buffer-substring (point) (line-end-position))))))) (provide 'test-org-datetree) ;;; test-org-datetree.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-duration.el000066400000000000000000000152761500430433700226210ustar00rootroot00000000000000;;; test-org-duration.el --- Tests for org-duration.el -*- lexical-binding: t; -*- ;; Copyright (C) 2017, 2019 Nicolas Goaziou ;; Author: Nicolas Goaziou ;; 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 . ;;; Code: (require 'org-duration) (ert-deftest test-org-duration/to-minutes () "Test `org-duration-to-minutes' specifications." ;; Raise an error for unknown duration format. (should-error (org-duration-to-minutes "1:2")) ;; Return number of minutes, as a float. (should (= (org-duration-to-minutes "1:01") 61)) (should (floatp (org-duration-to-minutes "1:01"))) ;; Handle various duration formats. (should (= (org-duration-to-minutes "1:20:30") 80.5)) (should (= (org-duration-to-minutes "2h 10min") 130)) (should (= (org-duration-to-minutes "1d 1:02") 1502)) (should (= (org-duration-to-minutes "2.5h") 150)) ;; Special case: a bare number is treated as minutes. (should (= (org-duration-to-minutes "2") 2)) (should (= (org-duration-to-minutes "2.5") 2.5)) (should (= (org-duration-to-minutes 1) 1)) ;; Special case: the empty string is 0.0. (should (= (org-duration-to-minutes "") 0.0)) ;; Support custom units. (should (= 4 (let ((org-duration-units '(("longmin" . 2))) org-duration--unit-re org-duration--full-re org-duration--mixed-re) (org-duration-set-regexps) (org-duration-to-minutes "2longmin")))) (should (= 61 (let ((org-duration-units '(("h" . 61))) org-duration--unit-re org-duration--full-re org-duration--mixed-re) (org-duration-set-regexps) (org-duration-to-minutes "1h")))) ;; When CANONICAL is non-nil, ignore custom units and only recognize ;; units defined in `org-duration-canonical-units'. (should (= 60 (let ((org-duration-units '(("h" . 61))) org-duration--unit-re org-duration--full-re org-duration--mixed-re) (org-duration-set-regexps) (org-duration-to-minutes "1h" t)))) (should-error (let ((org-duration-units '(("longmin" . 2))) org-duration--unit-re org-duration--full-re org-duration--mixed-re) (org-duration-set-regexps) (org-duration-to-minutes "2longmin" t)))) (ert-deftest test-org-duration/from-minutes () "Test `org-duration-from-minutes' specifications." ;; Format number of minutes according to `org-duration-format'. (should (equal "1:00" (let ((org-duration-format 'h:mm)) (org-duration-from-minutes 60)))) (should (equal "1:01:30" (let ((org-duration-format 'h:mm:ss)) (org-duration-from-minutes 61.5)))) (should (equal "1:01" (let ((org-duration-format 'h:mm)) (org-duration-from-minutes 61.5)))) ;; Handle required parameter in advanced format specifications. (should (equal "1h" (let ((org-duration-format '(("h" . nil) ("min" . nil)))) (org-duration-from-minutes 60)))) (should (equal "1h 0min" (let ((org-duration-format '(("h" . nil) ("min" . t)))) (org-duration-from-minutes 60)))) (should (equal "50min" (let ((org-duration-format '(("h" . nil) ("min" . nil)))) (org-duration-from-minutes 50)))) (should (equal "0h 50min" (let ((org-duration-format '(("h" . t) ("min" . t)))) (org-duration-from-minutes 50)))) ;; Handle mixed mode. (should (equal "1d 0:10" (let ((org-duration-format '(("d" . nil) (special . h:mm)))) (org-duration-from-minutes (+ (* 24 60) 10))))) (should (equal "1d 0:12:30" (let ((org-duration-format '(("d" . nil) (special . h:mm:ss)))) (org-duration-from-minutes (+ (* 24 60) 12.5))))) ;; Handle fractional duration. Parameter is the precision. (should (equal "1.5h" (let ((org-duration-format '(("h" . nil) (special . 1)))) (org-duration-from-minutes 90)))) (should (equal "1.50h" (let ((org-duration-format '(("h" . nil) (special . 2)))) (org-duration-from-minutes 90)))) ;; When using fractional duration, use first required unit or the ;; first with a non-zero integer part. If none is found, refer to ;; smallest unit specified in format. (should (equal "0.7h" (let ((org-duration-format '(("h" . t) ("min" . nil) (special . 1)))) (org-duration-from-minutes 40)))) (should (equal "40.0min" (let ((org-duration-format '(("h" . nil) ("min" . nil) (special . 1)))) (org-duration-from-minutes 40)))) (should (equal "0.5min" (let ((org-duration-format '(("h" . nil) ("min" . nil) (special . 1)))) (org-duration-from-minutes 0.5)))) ;; Handle compact form. (should (equal "0h50min" (let ((org-duration-format '(("h" . t) ("min" . t) compact))) (org-duration-from-minutes 50)))) (should (equal "1d0:10" (let ((org-duration-format '(("d" . nil) (special . h:mm) compact))) (org-duration-from-minutes (+ (* 24 60) 10)))))) (ert-deftest test-org-duration/p () "Test `org-duration-p' specifications." ;; Test all duration formats. (should (org-duration-p "3:12")) (should (org-duration-p "123:12")) (should (org-duration-p "1:23:45")) (should (org-duration-p "3d 3h 4min")) (should (org-duration-p "3d3h4min")) (should (org-duration-p "3d 13:35")) (should (org-duration-p "3d13:35")) (should (org-duration-p "2.35h")) ;; Handle custom units, but return nil for unknown units. (should-not (org-duration-p "1minute")) (should (let ((org-duration-units '(("minute" . 1))) org-duration--unit-re org-duration--full-re org-duration--mixed-re) (org-duration-set-regexps) (org-duration-p "2minute"))) ;; Tolerate white space between the number and the unit. (should (org-duration-p "2 h")) ;; Return nil for ill-formed H:MM:SS strings. (should-not (org-duration-p "3::12")) (should-not (org-duration-p "3:2")) (should-not (org-duration-p "3:12:4")) ;; Return nil in mixed mode if H:MM:SS part is not the last one. (should-not (org-duration-p "3d 13:35 13h"))) (ert-deftest test-org-duration/h:mm-only-p () "Test `org-duration-h:mm-only-p' specifications." (should (org-duration-h:mm-only-p '("123:31" "1:00"))) (should-not (org-duration-h:mm-only-p '("123:32" "1h"))) (should (eq 'h:mm:ss (org-duration-h:mm-only-p '("3:33" "1:23:45"))))) (provide 'test-org-duration) ;;; test-org-duration.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-element.el000066400000000000000000006100251500430433700224160ustar00rootroot00000000000000;;; test-org-element.el --- Tests for org-element.el -*- lexical-binding: t; -*- ;; Copyright (C) 2012-2015, 2019 Nicolas Goaziou ;; Author: Nicolas Goaziou ;; 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 . ;;; Code: (eval-and-compile (require 'cl-lib)) (require 'org-element) (require 'org) (require 'org-inlinetask) (defun org-test-parse-and-interpret (text) "Parse TEXT as Org syntax and interpret it. Return interpreted string." (with-temp-buffer (org-mode) (insert text) (org-element-interpret-data (org-element-parse-buffer)))) ;;; Test getters. (ert-deftest test-org-element/type () "Test `org-element-type' specifications." (should (eq 'plain-text (org-element-type "string"))) (should-not (org-element-type nil)) (should-not (org-element-type 1)) (should (eq 'dummy (org-element-type '(dummy)))) (should (eq 'dummy (org-element-type '(dummy nil 'foo)))) (should (eq 'dummy (org-element-type '(dummy (:a a :b b) 'foo)))) ;; anonymous node. (should-not (org-element-type '((dummy)))) (should (eq 'anonymous (org-element-type '((dummy)) t))) (should (eq 'anonymous (org-element-type '("string") t))) (should-not (org-element-type '(1 2) t))) (ert-deftest test-org-element/type-p () "Test `org-element-type-p' specifications." (should (org-element-type-p '(foo) 'foo)) (should (org-element-type-p '(foo) '(foo))) (should (org-element-type-p '(foo) '(foo bar))) (should-not (org-element-type-p '(foo) 'bar)) (should-not (org-element-type-p '(foo) '(bar baz))) (should (org-element-type-p "string" 'plain-text)) (should (org-element-type-p '((foo)) 'anonymous))) (ert-deftest test-org-element/org-element-property-raw () "Test `org-element-property-raw' specifications." ;; No properties. (dolist (element `( nil (headline nil) (headline nil (headline)) "string")) (should-not (org-element-property-raw :begin element)) (should (eq 'default (org-element-property-raw :begin element 'default))) (should-not (org-element-property-raw :begin1 element)) (should (eq 'default (org-element-property-raw :begin1 element 'default))) (dolist (prop '(:begin)) (should-not (org-element-property-raw prop element)) (should (eq 'default (org-element-property-raw prop element 'default)))) (dolist (prop '(:begin1)) (should-not (org-element-property-raw prop element)) (should (eq 'default (org-element-property-raw prop element 'default))))) ;; Only non-standard properties. (dolist (element `((headline (:begin1 1)) (headline (:begin1 1) (headline)) ,(propertize "string" :begin1 1))) (should-not (org-element-property-raw :begin element)) (should (eq 'default (org-element-property-raw :begin element 'default))) (should (= 1 (org-element-property-raw :begin1 element))) (should (= 1 (org-element-property-raw :begin1 element 'default))) (dolist (prop '(:begin)) (should-not (org-element-property-raw prop element)) (should (eq 'default (org-element-property-raw prop element 'default)))) (dolist (prop '(:begin1)) (should (= 1 (org-element-property-raw prop element))) (should (= 1 (org-element-property-raw prop element 'default))))) ;; Only standard properties. (dolist (element `((headline (:standard-properties ,(make-vector 10 'test))) (headline (:standard-properties ,(make-vector 10 'test)) (headline)))) (should (eq 'test (org-element-property-raw :begin element))) (should (eq 'test (org-element-property-raw :begin element 'default))) (should-not (org-element-property-raw :begin1 element)) (should (eq 'default (org-element-property-raw :begin1 element 'default))) (dolist (prop '(:begin)) (should (eq 'test (org-element-property-raw prop element))) (should (eq 'test (org-element-property-raw prop element 'default)))) (dolist (prop '(:begin1)) (should-not (org-element-property-raw prop element)) (should (eq 'default (org-element-property-raw prop element 'default))))) ;; Standard properties in the plist. (dolist (element `((headline (:begin 1)) (headline (:begin 1) (headline)) ,(propertize "string" :begin 1))) (should (= 1 (org-element-property-raw :begin element))) (should (= 1 (org-element-property-raw :begin element 'default))) (should-not (org-element-property-raw :begin1 element)) (should (eq 'default (org-element-property-raw :begin1 element 'default))) (dolist (prop '(:begin)) (should (= 1 (org-element-property-raw prop element))) (should (= 1 (org-element-property-raw prop element 'default)))) (dolist (prop '(:begin1)) (should-not (org-element-property-raw prop element)) (should (eq 'default (org-element-property-raw prop element 'default))))) ;; Standard properties mixed in the plist and standard array. (dolist (element `((headline (:standard-properties ,(make-vector 10 'test) :begin 1)) (headline (:begin 1 :standard-properties ,(make-vector 10 'test))) (headline (:standard-properties ,(make-vector 10 'test) :begin 1) (headline)))) (should (eq 'test (org-element-property-raw :begin element))) (should (eq 'test (org-element-property-raw :begin element 'default))) (should-not (org-element-property-raw :begin1 element)) (should (eq 'default (org-element-property-raw :begin1 element 'default))) (dolist (prop '(:begin)) (should (eq 'test (org-element-property-raw prop element))) (should (eq 'test (org-element-property-raw prop element 'default)))) (dolist (prop '(:begin1)) (should-not (org-element-property-raw prop element)) (should (eq 'default (org-element-property-raw prop element 'default))))) ;; General case. (dolist (element `((headline (:standard-properties ,(make-vector 10 'test) :begin1 1)) (headline (:begin1 1 :standard-properties ,(make-vector 10 'test))) (headline (:standard-properties ,(make-vector 10 'test) :begin1 1) (headline)))) (should (eq 'test (org-element-property-raw :begin element))) (should (eq 'test (org-element-property-raw :begin element 'default))) (should (= 1 (org-element-property-raw :begin1 element))) (should (= 1 (org-element-property-raw :begin1 element 'default))) (dolist (prop '(:begin)) (should (eq 'test (org-element-property-raw prop element))) (should (eq 'test (org-element-property-raw prop element 'default)))) (dolist (prop '(:begin1)) (should (= 1 (org-element-property-raw prop element))) (should (= 1 (org-element-property-raw prop element 'default)))))) (ert-deftest test-org-element/property () "Test resolving deferred properties." ;; Resolve `:deferred' property. (let ((el (org-element-create 'dummy `(:deferred ,(org-element-deferred-create t (lambda (el) (org-element-put-property el :foo 'bar) nil)))))) (should (eq 'bar (org-element-property :foo el))) (should-not (org-element-property :foo2 el))) ;; Deferred value. (let ((el (org-element-create 'dummy `(:foo ,(org-element-deferred-create nil (lambda (_) 'bar)))))) (should (eq 'bar (org-element-property :foo el)))) ;; Auto-undefer. (let ((el (org-element-create 'dummy `(:foo ,(org-element-deferred-create t (lambda (_) 'bar)))))) (should (eq 'bar (org-element-property :foo el))) (should (eq 'bar (org-element-property-raw :foo el)))) ;; Force undefer. (let ((el (org-element-create 'dummy `(:foo ,(org-element-deferred-create nil (lambda (_) 'bar)))))) (should (eq 'bar (org-element-property :foo el))) (should-not (eq 'bar (org-element-property-raw :foo el))) (should (eq 'bar (org-element-property :foo el nil 'force))) (should (eq 'bar (org-element-property-raw :foo el)))) ;; Test deferred alias. (let ((el (org-element-create 'dummy `( :foo 1 :bar ,(org-element-deferred-create-alias :foo))))) (should (equal 1 (org-element-property :foo el))) (should (equal 1 (org-element-property :bar el)))) ;; Test deferred list. (let ((el (org-element-create 'dummy `(:foo ,(org-element-deferred-create-list (list 1 2 (org-element-deferred-create nil (lambda (_) 3)))))))) (should (equal '(1 2 3) (org-element-property :foo el)))) ;; Test deferred property with side effects. (let ((el (org-element-create 'dummy `(:foo ,(org-element-deferred-create nil (lambda (el) (org-element-put-property el :foo 1) (throw :org-element-deferred-retry nil))))))) (should (eq 1 (org-element-property :foo el)))) ;; Test recursive undefer. (let ((el (org-element-create 'dummy `(:foo ,(org-element-deferred-create nil (lambda (el) (org-element-deferred-create nil (lambda (_) 1)))))))) (should (eq 1 (org-element-property :foo el))))) (ert-deftest test-org-element/property-2 () "Test `org-element-property-2' specifications." (let ((el (org-element-create 'dummy '(:foo bar)))) (should (eq (org-element-property :foo el) (org-element-property-2 el :foo))))) (ert-deftest test-org-element/parent () "Test `org-element-parent' specifications." (let ((el (org-element-create 'dummy '(:parent bar)))) (should (eq (org-element-property :parent el) (org-element-parent el))))) (ert-deftest test-org-element/properties-resolve () "Test `org-element-properties-resolve' specifications." (let ((el (org-element-create 'dummy `( :foo ,(org-element-deferred-create t (lambda (_) 1)) :bar ,(org-element-deferred-create nil (lambda (_) 2)) :deferred ,(org-element-deferred-create nil (lambda (el) (org-element-put-property el :baz 3))))))) ;; Resolve conditionally. (setq el (org-element-properties-resolve el)) (should (eq 1 (org-element-property-raw :foo el))) (should-not (eq 2 (org-element-property-raw :bar el))) (should (eq 2 (org-element-property :bar el))) (should (eq 3 (org-element-property-raw :baz el))) ;; Resolve unconditionally. (setq el (org-element-properties-resolve el 'force)) (should (eq 2 (org-element-property-raw :bar el))))) (ert-deftest test-org-element/secondary-p () "Test `org-element-secondary-p' specifications." ;; In a secondary string, return property name. (should (eq :title (org-test-with-temp-text "* Headline *object*" (org-element-map (org-element-parse-buffer) 'bold (lambda (object) (org-element-secondary-p object)) nil t)))) (should (eq :foo (org-element-secondary-p (let* ((el (org-element-create 'dummy '(:secondary (:foo)))) (child (org-element-create "string" `(:parent ,el)))) (org-element-put-property el :foo (list child)) child)))) ;; Outside a secondary string, return nil. (should-not (org-test-with-temp-text "Paragraph *object*" (org-element-map (org-element-parse-buffer) 'bold (lambda (object) (org-element-type (org-element-secondary-p object))) nil t))) (should-not (eq :foo (org-element-secondary-p (let* ((el (org-element-create 'dummy '(:secondary (:foo)))) (child (org-element-create "string" `(:parent ,el)))) (org-element-put-property el :bar (list child)) child))))) (ert-deftest test-org-element/class () "Test `org-element-class' specifications." ;; Regular tests. (should (eq 'element (org-element-class '(paragraph nil) nil))) (should (eq 'object (org-element-class '(target nil) nil))) ;; Special types. (should (eq 'element (org-element-class '(org-data nil) nil))) (should (eq 'object (org-element-class "text" nil))) (should (eq 'object (org-element-class '("secondary " "string") nil))) ;; Pseudo elements. (should (eq 'element (org-element-class '(foo nil) nil))) (should (eq 'element (org-element-class '(foo nil) '(center-block nil)))) (should (eq 'element (org-element-class '(foo nil) '(org-data nil)))) ;; Pseudo objects. (should (eq 'object (org-element-class '(foo nil) '(bold nil)))) (should (eq 'object (org-element-class '(foo nil) '(paragraph nil)))) (should (eq 'object (org-element-class '(foo nil) '("secondary")))) (should (eq 'object (let* ((datum '(foo nil)) (headline `(headline (:title (,datum) :secondary (:title))))) (org-element-put-property datum :parent headline) (org-element-class datum))))) ;;; Test `org-element-map' and `org-element-properties-map' (ert-deftest test-org-element/map () "Test `org-element-map'." ;; Can map to `plain-text' objects. (should (= 2 (org-test-with-temp-text "Some text \alpha #+BEGIN_CENTER Some other text #+END_CENTER" (let ((count 0)) (org-element-map (org-element-parse-buffer) 'plain-text (lambda (s) (when (string-match "text" s) (cl-incf count)))) count)))) ;; Applies to secondary strings (should (org-element-map '("some " (bold nil "bold") "text") 'bold 'identity)) ;; Enter secondary strings before entering contents. (should (equal "alpha" (org-element-property :name (org-test-with-temp-text "* Some \\alpha headline\n\\beta entity." (org-element-map (org-element-parse-buffer) 'entity 'identity nil t))))) ;; Apply NO-RECURSION argument. (should-not (org-test-with-temp-text "#+BEGIN_CENTER\n\\alpha\n#+END_CENTER" (org-element-map (org-element-parse-buffer) 'entity 'identity nil nil 'center-block))) ;; Use WITH-AFFILIATED argument. (should (equal '("1" "a" "2" "b") (org-test-with-temp-text "#+CAPTION[a]: 1\n#+CAPTION[b]: 2\nParagraph" (org-element-map (org-element-at-point) 'plain-text 'identity nil nil nil t))))) (ert-deftest test-org-element/ast-map () "Test `org-element-ast-map' specifications." ;; TYPES = t (should (equal '(plain-text plain-text bold) (org-element-ast-map (org-element-create 'anonymous nil "a" "b" (org-element-create 'bold)) t #'org-element-type))) ;; IGNORE (should (equal '(plain-text plain-text) (let ((bold (org-element-create 'bold))) (org-element-ast-map (org-element-create 'anonymous nil "a" "b" bold) t #'org-element-type (list bold))))) ;; FUN as a list form (org-test-with-temp-text "* H1\n* H2" (should (equal '("H1" "H2") (org-element-map (org-element-parse-buffer) t '(org-element-property :raw-value node))))) ;; Extra secondary properties. (should (equal '(bold bold) (org-element-ast-map (org-element-create 'dummy `(:foo ,(org-element-create 'bold)) (org-element-create 'bold)) 'bold #'org-element-type nil nil nil '(:foo)))) (should-not (equal '(bold bold) (org-element-ast-map (org-element-create 'dummy `(:foo ,(org-element-create 'bold)) (org-element-create 'bold)) 'bold #'org-element-type))) ;; No secondary. (should-not (equal '(bold bold) (org-element-ast-map (org-element-create 'dummy `(:secondary (:foo) :foo ,(org-element-create 'bold)) (org-element-create 'bold)) 'bold #'org-element-type nil nil nil nil 'no-secondary))) (should (equal '(bold bold) (org-element-ast-map (org-element-create 'dummy `(:secondary (:foo) :foo ,(org-element-create 'bold)) (org-element-create 'bold)) 'bold #'org-element-type))) ;; Deferred values. (should (equal '(dummy bold) (org-element-ast-map (org-element-create 'dummy `(:secondary (:foo) :foo ,(org-element-deferred-create nil (lambda (_) "a"))) (org-element-create 'bold)) t #'org-element-type nil nil nil nil nil 'no-undefer))) (should (equal '(dummy plain-text bold) (org-element-ast-map (org-element-create 'dummy `(:secondary (:foo) :foo ,(org-element-deferred-create nil (lambda (_) "a"))) (org-element-create 'bold)) t #'org-element-type)))) (ert-deftest test-org-element/properties-mapc () "Test `org-element-properties-mapc' specifications." (let ((el (org-element-create 'dummy `( :foo ,(org-element-deferred-create t (lambda (_) 1)) :bar 2)))) (should (catch :found (org-element-properties-mapc (lambda (_ val _) (when (org-element-deferred-p val) (throw :found t))) el))) (should (catch :found (org-element-properties-mapc (lambda (prop val _) (when (and (eq prop :foo) (eq 1 val)) (throw :found t))) el 'undefer))))) (ert-deftest test-org-element/properties-map () "Test `org-element-properties-map' specifications." ;; Check resolving deferred properties. (let ((el (org-element-create 'dummy `( :foo ,(org-element-deferred-create t (lambda (_) 1)) :bar 2)))) (should (equal '(2) (cdr (org-element-properties-map (lambda (_ val _) val) el)))) (should-not (equal '(1 2) (org-element-properties-map (lambda (_ val _) val) el))) (should (equal '(1 2) (org-element-properties-map (lambda (_ val _) val) el 'undefer)))) ;; Check functions with different arity. (let ((el (org-element-create 'dummy '(:foo 1 :bar 2 :baz 3)))) (should ;; Single argument. (equal '(1 2 3) (org-element-properties-map #'identity el))) ;; Two arguments. (should (equal '(1 2 nil) (org-element-properties-map (lambda (prop val) (unless (eq prop :baz) val)) el))) ;; Three arguments. (should (equal '(1 2 4) (org-element-properties-map (lambda (prop val node) (if (eq prop :baz) (1+ (org-element-property-raw :baz node)) val)) el))))) ;;; Test Setters (ert-deftest test-org-element/org-element-create () "Test `org-element-create' specifications." (should (pcase (org-element-create 'foo '(:a 1 :b 2)) (`(foo (:standard-properties ,_ :a 1 :b 2)) t))) (should (pcase (org-element-create 'foo '(:begin 10)) (`(foo (:standard-properties ,vec)) (= 10 (aref vec (org-element--property-idx :begin)))))) ;; Strings (should (equal "foo" (org-element-create "foo"))) (should (equal "foo" (org-element-create 'plain-text nil "foo"))) (should (get-text-property 0 :a (org-element-create 'plain-text '(:a 1) "foo"))) (should (get-text-property 0 :begin (org-element-create 'plain-text '(:begin 1) "foo"))) ;; Children (let ((children '("a" "b" (org-element-create 'foo)))) (should (equal (cddr (apply #'org-element-create 'bar nil children)) children)))) (ert-deftest test-org-element/put-property () "Test `org-element-put-property' specifications." ;; Standard test. (org-test-with-temp-text "* Headline\n *a*" (let ((tree (org-element-parse-buffer))) (org-element-put-property (org-element-map tree 'bold 'identity nil t) :test 1) (should (org-element-property :test (org-element-map tree 'bold 'identity nil t))))) ;; Put property on a string. (should (org-element-property :test (org-element-put-property "Paragraph" :test t))) ;; No properties. (let ((element (list 'heading nil)) vec) (setq vec (make-vector (length org-element--standard-properties) nil)) (aset vec 0 1) (should (equal (list 'heading (list :standard-properties vec)) (org-element-put-property element :begin 1)))) (let ((element (list 'heading nil))) (should (equal (list 'heading (list :begin1 1)) (org-element-put-property element :begin1 1)))) ;; Standard properties. (let ((element (list 'heading (list :standard-properties (make-vector (length org-element--standard-properties) 'foo))))) (should (= 1 (org-element-property-raw :begin (org-element-put-property element :begin 1))))) ;; Adding standard properties when other standard properties are defined manually in the plist. (let ((element (list 'heading (list :begin 1 :end 20 :foo 'foo)))) (should (= 2 (org-element-property-raw :begin (org-element-put-property element :begin 2)))) ;; Check setter. (cl-incf (org-element-property-raw :begin element)) (should (= 3 (org-element-property-raw :begin element))) (should (= 20 (org-element-property-raw :end element))) (should (eq 'foo (org-element-property-raw :foo element))))) (ert-deftest test-org-element/put-property-2 () "Test `org-element-put-property-2' specifications." (should (equal (org-element-put-property (org-element-create 'foo) :test 'value) (org-element-put-property-2 :test 'value (org-element-create 'foo))))) (ert-deftest test-org-element/set-contents () "Test `org-element-set-contents' specifications." ;; Accept multiple entries. (should (equal '("b" (italic nil "a")) (org-test-with-temp-text "* Headline\n *a*" (let ((tree (org-element-parse-buffer))) (org-element-set-contents (org-element-map tree 'bold 'identity nil t) "b" '(italic nil "a")) (org-element-contents (org-element-map tree 'bold 'identity nil t)))))) ;; Accept atoms and elements. (should (equal '("b") (org-test-with-temp-text "* Headline\n *a*" (let ((tree (org-element-parse-buffer))) (org-element-set-contents (org-element-map tree 'bold 'identity nil t) "b") (org-element-contents (org-element-map tree 'bold 'identity nil t)))))) (should (equal '((italic nil "b")) (org-test-with-temp-text "* Headline\n *a*" (let ((tree (org-element-parse-buffer))) (org-element-set-contents (org-element-map tree 'bold 'identity nil t) '(italic nil "b")) (org-element-contents (org-element-map tree 'bold 'identity nil t)))))) ;; Allow nil contents. (should-not (org-test-with-temp-text "* Headline\n *a*" (let ((tree (org-element-parse-buffer))) (org-element-set-contents (org-element-map tree 'bold 'identity nil t)) (org-element-contents (org-element-map tree 'bold 'identity nil t))))) ;; Set contents of anonymous elements. (should (equal '#1=((b (:parent #1#))) (let ((element '#1=((a (:parent #1#)) (b (:parent #1#))))) (org-element-set-contents element `(b (:parent ,element))) element)))) (ert-deftest test-org-element/adopt-elements () "Test `org-element-adopt' specifications." ;; Adopt an element. (should (equal '(plain-text italic) (org-test-with-temp-text "* Headline\n *a*" (let ((tree (org-element-parse-buffer))) (org-element-adopt (org-element-map tree 'bold 'identity nil t) '(italic nil "a")) (mapcar (lambda (blob) (org-element-type blob)) (org-element-contents (org-element-map tree 'bold 'identity nil t))))))) ;; Adopt a string. (should (equal '("a" "b") (org-test-with-temp-text "* Headline\n *a*" (let ((tree (org-element-parse-buffer))) (org-element-adopt (org-element-map tree 'bold 'identity nil t) "b") (org-element-contents (org-element-map tree 'bold 'identity nil t))))))) (ert-deftest test-org-element/extract-element () "Test `org-element-extract' specifications." ;; Extract a greater element. (should (eq 'org-data (org-test-with-temp-text "* Headline" (let* ((tree (org-element-parse-buffer)) (element (org-element-map tree 'headline 'identity nil t))) (org-element-extract element) (org-element-type tree))))) ;; Extract an element. (should-not (org-element-map (org-test-with-temp-text "Paragraph" (let* ((tree (org-element-parse-buffer)) (element (org-element-map tree 'paragraph 'identity nil t))) (org-element-extract element) tree)) 'paragraph 'identity)) ;; Extract an object, even in a secondary string. (should-not (org-element-map (org-test-with-temp-text "*bold*" (let* ((tree (org-element-parse-buffer)) (element (org-element-map tree 'bold 'identity nil t))) (org-element-extract element) tree)) 'bold 'identity)) (should-not (org-element-map (org-test-with-temp-text "* Headline *bold*" (let* ((tree (org-element-parse-buffer)) (element (org-element-map tree 'bold 'identity nil t))) (org-element-extract element) tree)) 'bold 'identity)) ;; Return value doesn't have any :parent set. (should-not (org-element-property :parent (org-test-with-temp-text "* Headline\n Paragraph with *bold* text." (let* ((tree (org-element-parse-buffer)) (element (org-element-map tree 'bold 'identity nil t))) (org-element-extract element)))))) (ert-deftest test-org-element/insert-before () "Test `org-element-insert-before' specifications." ;; Standard test. (should (equal '(italic entity bold) (org-test-with-temp-text "/some/ *paragraph*" (let* ((tree (org-element-parse-buffer)) (_paragraph (org-element-map tree 'paragraph #'identity nil t)) (bold (org-element-map tree 'bold 'identity nil t))) (org-element-insert-before '(entity (:name "\\alpha")) bold) (org-element-map tree '(bold entity italic) #'org-element-type nil))))) ;; Insert an object in a secondary string. (should (equal '(entity italic) (org-test-with-temp-text "* /A/\n Paragraph." (let* ((tree (org-element-parse-buffer)) (headline (org-element-map tree 'headline 'identity nil t)) (italic (org-element-map tree 'italic 'identity nil t))) (org-element-insert-before '(entity (:name "\\alpha")) italic) (org-element-map (org-element-property :title headline) '(entity italic) #'org-element-type)))))) (ert-deftest test-org-element/set () "Test `org-element-set' specifications." ;; Check if new element is inserted. (should (org-test-with-temp-text "* Headline\n*a*" (let* ((tree (org-element-parse-buffer)) (bold (org-element-map tree 'bold 'identity nil t))) (org-element-set bold '(italic nil "b")) (org-element-map tree 'italic 'identity)))) ;; Check if old element is removed. (should-not (org-test-with-temp-text "* Headline\n*a*" (let* ((tree (org-element-parse-buffer)) (bold (org-element-map tree 'bold 'identity nil t))) (org-element-set bold '(italic nil "b")) (org-element-map tree 'bold 'identity)))) ;; Check if :parent property is correctly set. (should (eq 'paragraph (org-test-with-temp-text "* Headline\n*a*" (let* ((tree (org-element-parse-buffer)) (bold (org-element-map tree 'bold 'identity nil t))) (org-element-set bold '(italic nil "b")) (org-element-type (org-element-property :parent (org-element-map tree 'italic 'identity nil t))))))) ;; Allow to replace strings with elements. (should (equal '("b") (org-test-with-temp-text "* Headline" (let* ((tree (org-element-parse-buffer)) (text (org-element-map tree 'plain-text 'identity nil t))) (org-element-set text (list 'bold nil "b")) (org-element-map tree 'plain-text 'identity))))) ;; Allow to replace elements with strings. (should (equal "a" (org-test-with-temp-text "* =verbatim=" (let* ((tree (org-element-parse-buffer)) (verb (org-element-map tree 'verbatim 'identity nil t))) (org-element-set verb "a") (org-element-map tree 'plain-text 'identity nil t))))) ;; Allow to replace strings with strings. (should (equal "b" (org-test-with-temp-text "a" (let* ((tree (org-element-parse-buffer)) (text (org-element-map tree 'plain-text 'identity nil t))) (org-element-set text "b") (org-element-map tree 'plain-text 'identity nil t))))) ;; Replace string inside anonymous element with another string. (let* ((parent (org-element-create 'anonymous nil "test")) (str (car (org-element-contents parent)))) (let ((return (org-element-set str "repl")) (new (car (org-element-contents parent)))) ;; Return the modified value. (should (eq return new)) (should (equal new "repl")) (should (eq (org-element-parent new) parent)))) ;; KEEP-PROPS (should (org-element-property :foo (org-element-set (org-element-create 'dummy '(:foo bar)) (org-element-create 'dummy '(:foo2 bar2)) '(:foo))))) (ert-deftest test-org-element/copy () "Test `org-element-copy' specifications." ;; Preserve type. (should (eq 'bold (org-test-with-temp-text "*bold*" (org-element-type (org-element-copy (org-element-context)))))) (should (eq 'plain-text (org-test-with-temp-text "*bold*" (org-element-type (org-element-map (org-element-parse-buffer) 'plain-text #'org-element-copy nil t))))) ;; Preserve properties except `:parent'. (should (= 7 (org-test-with-temp-text "*bold*" (org-element-property :end (org-element-copy (org-element-context)))))) (should-not (org-test-with-temp-text "*bold*" (org-element-property :parent (org-element-copy (org-element-context))))) (should-not (org-test-with-temp-text "*bold*" (org-element-property :parent (org-element-map (org-element-parse-buffer) 'plain-text #'org-element-copy nil t)))) ;; Copying nil returns nil. (should-not (org-element-copy nil)) ;; Return a copy secondary strings. (should (equal '("text") (org-element-copy '("text")))) (should-not (eq '("text") (org-element-copy '("text")))) ;; Do not alter the source. (org-test-with-temp-text "*bold*" (let* ((source (org-element-context)) (copy (org-element-copy source))) (should-not (org-element-parent copy)) (should (org-element-parent source))))) ;;; Test Parsers ;;;; Affiliated Keywords (ert-deftest test-org-element/affiliated-keywords-parser () "Test affiliated keywords parsing." ;; Read simple keywords. (should (equal "para" (org-element-property :name (org-test-with-temp-text "#+NAME: para\nParagraph" (org-element-at-point))))) (should (= 1 (org-element-property :begin (org-test-with-temp-text "#+NAME: para\nParagraph" (org-element-at-point))))) ;; Parse multiple keywords. (should (equal '("line1" "line2") (org-element-property :attr_ascii (org-test-with-temp-text "#+ATTR_ASCII: line1\n#+ATTR_ASCII: line2\nParagraph" (org-element-at-point))))) ;; Parse "parsed" keywords, unless granularity prevents it. (should (equal '(("caption")) (org-test-with-temp-text "#+CAPTION: caption\nParagraph" (car (org-element-property :caption (org-element-at-point)))))) (should (org-test-with-temp-text "#+CAPTION: *caption*\nParagraph" (org-element-map (org-element-map (org-element-parse-buffer) 'paragraph (lambda (e) (org-element-property :caption e)) nil t) 'bold #'org-element-type nil t))) (should-not (org-test-with-temp-text "#+CAPTION: *caption*\nParagraph" (org-element-map (org-element-map (org-element-parse-buffer 'element) 'paragraph (lambda (e) (org-element-property :caption e)) nil t) 'bold #'org-element-type nil t))) ;; Parse dual keywords. (should (equal '((("long") "short")) (org-test-with-temp-text "#+CAPTION[short]: long\nParagraph" (org-element-property :caption (org-element-at-point))))) ;; Allow multiple caption keywords. (should (equal '((("l1") "s1") (("l2") "s2")) (org-test-with-temp-text "#+CAPTION[s1]: l1\n#+CAPTION[s2]: l2\nParagraph" (org-element-property :caption (org-element-at-point))))) (should (equal '((nil "s1") (("l1"))) (org-test-with-temp-text "#+CAPTION[s1]:\n#+CAPTION: l1\nParagraph" (org-element-property :caption (org-element-at-point))))) ;; Corner case: orphaned keyword at the end of an element. (should (eq 'keyword (org-test-with-temp-text "- item\n #+name: name\nSome paragraph" (progn (search-forward "name") (org-element-type (org-element-at-point)))))) (should-not (org-test-with-temp-text "- item\n #+name: name\nSome paragraph" (progn (search-forward "Some") (org-element-property :name (org-element-at-point))))) ;; Corner case: orphaned keyword before comment. ;; Comments cannot have affiliated keywords. (should-not (org-test-with-temp-text "#+name: foo\n# bar" (progn (search-forward "bar") (org-element-property :name (org-element-at-point))))) ;; Headlines cannot have affiliated keywords. (should (org-test-with-temp-text "#+name: foo\n* Heading" (org-element-type-p (org-element-at-point) 'keyword))) ;; Clocks cannot have affiliated keywords. (should (org-test-with-temp-text "#+name: foo CLOCK: [2023-10-13 Fri 14:40]--[2023-10-13 Fri 14:51] => 0:11" (org-element-type-p (org-element-at-point) 'keyword))) ;; Inlinetasks cannot have affiliated keywords. (should (let ((org-inlinetask-min-level 4)) (org-test-with-temp-text "#+name: foo **** Inlinetask" (org-element-type-p (org-element-at-point) 'keyword))))) ;;;; Babel Call (ert-deftest test-org-element/babel-call-parser () "Test `babel-call' parsing." ;; Standard test. (should (eq 'babel-call (org-test-with-temp-text "#+CALL: test()" (org-element-type (org-element-at-point))))) ;; Ignore case. (should (eq 'babel-call (org-test-with-temp-text "#+call: test()" (org-element-type (org-element-at-point))))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text "#+CALL: test()\n " (= (org-element-property :end (org-element-at-point)) (point-max)))) ;; Parse call name. (should (equal "test" (org-test-with-temp-text "#+CALL: test()" (org-element-property :call (org-element-at-point))))) ;; Parse inside header. It may contain paired square brackets. (should (equal ":results output" (org-test-with-temp-text "#+CALL: test[:results output]()" (org-element-property :inside-header (org-element-at-point))))) (should (equal ":results output, a=table[1:2], b=2" (org-test-with-temp-text "#+CALL: test[:results output, a=table[1:2], b=2]()" (org-element-property :inside-header (org-element-at-point))))) ;; Parse arguments, which can be nested. However, stop at paired ;; parenthesis, even when, e.g.,end header contains some. (should (equal "n=4" (org-test-with-temp-text "#+CALL: test(n=4)" (org-element-property :arguments (org-element-at-point))))) (should (equal "test()" (org-test-with-temp-text "#+CALL: test(test())" (org-element-property :arguments (org-element-at-point))))) (should (equal "a=1" (org-test-with-temp-text "#+CALL: test(a=1) :post another-call()" (org-element-property :arguments (org-element-at-point))))) ;; Parse end header. (should (equal ":results html" (org-test-with-temp-text "#+CALL: test() :results html" (org-element-property :end-header (org-element-at-point)))))) ;;;; Bold (ert-deftest test-org-element/bold-parser () "Test `bold' parser." ;; Standard test. (should (org-test-with-temp-text "*bold*" (org-element-map (org-element-parse-buffer) 'bold #'identity nil t))) ;; Multi-line markup. (should (equal (org-element-contents (org-test-with-temp-text "*first line\nsecond line*" (org-element-map (org-element-parse-buffer) 'bold #'identity nil t))) '("first line\nsecond line")))) ;;;; Center Block (ert-deftest test-org-element/center-block-parser () "Test `center-block' parser." ;; Standard test. (should (org-test-with-temp-text "#+BEGIN_CENTER\nText\n#+END_CENTER" (org-element-map (org-element-parse-buffer) 'center-block 'identity))) ;; Ignore case. (should (org-test-with-temp-text "#+begin_center\nText\n#+end_center" (org-element-map (org-element-parse-buffer) 'center-block 'identity))) ;; Ignore incomplete block. (should-not (org-test-with-temp-text "#+BEGIN_CENTER" (org-element-map (org-element-parse-buffer) 'center-block 'identity nil t))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text "#+BEGIN_CENTER\nC\n#+END_CENTER\n " (= (org-element-property :end (org-element-at-point)) (point-max))))) ;;;; Citation (ert-deftest test-org-element/citation-parser () "Test `citation' parser" ;; Parse citations. They must contain at least a bare key. (should (eq 'citation (org-test-with-temp-text "[cite:@key]" (org-element-type (org-element-context))))) (should-not (eq 'citation (org-test-with-temp-text "[cite:text]" (org-element-type (org-element-context))))) ;; Citation may contain a style. (should (eq 'citation (org-test-with-temp-text "[cite/style:@key]" (org-element-type (org-element-context))))) (should (equal "style" (org-test-with-temp-text "[cite/style:@key]" (org-element-property :style (org-element-context))))) ;; Handle multi citations separated with semi-columns. (should (eq 'citation (org-test-with-temp-text "[cite:@a;@b;@c]" (org-element-type (org-element-context))))) (should (equal '("a" "b" "c") (org-test-with-temp-text "[cite:@a;@b;@c]" (org-element-map (org-element-parse-buffer) 'citation-reference (lambda (r) (org-element-property :key r)))))) (should (eq 'citation (org-test-with-temp-text "[cite:@a;-@b]" (org-element-type (org-element-context))))) (should (equal '("a" "b") (org-test-with-temp-text "[cite:@a;-@b]" (org-element-map (org-element-parse-buffer) 'citation-reference (lambda (r) (org-element-property :key r)))))) ;; Multi citations accept `:prefix' and `:suffix' properties. (should (equal '("common-prefix") (org-test-with-temp-text "[cite:common-prefix;@a]" (org-element-property :prefix (org-element-context))))) (should (equal '("common-suffix") (org-test-with-temp-text "[cite:@a;common-suffix]" (org-element-property :suffix (org-element-context))))) ;; White spaces right after "cite" tags are ignored. So are white ;; spaces at the end of the citation. (should (equal '("common-prefix ") (org-test-with-temp-text "[cite: common-prefix ;@a]" (org-element-property :prefix (org-element-context))))) (should (equal '(" common-suffix") (org-test-with-temp-text "[cite: @a; common-suffix ]" (org-element-property :suffix (org-element-context))))) ;; Allow citations in a table cell. (should (eq 'citation (org-test-with-temp-text "| [cite:@key] |" (org-element-type (org-element-context)))))) ;;;; Citation Reference (ert-deftest test-org-element/citation-reference-parser () "Test `citation' reference parser." ;; Parse bare keys. (should (eq 'citation-reference (org-test-with-temp-text "[cite:@key]" (org-element-type (org-element-context))))) ;; Bare keys can contain any word character, and some punctuation, ;; but not semicolon, square brackets, and space. (should (equal "_key" (org-test-with-temp-text "[cite:@_key]" (org-element-property :key (org-element-context))))) (should (eq 'citation-reference (org-test-with-temp-text "[cite:@a]" (org-element-type (org-element-context))))) (should (eq 'citation-reference (org-test-with-temp-text "[cite:@ö]" (org-element-type (org-element-context))))) (should (eq 'citation-reference (org-test-with-temp-text "[cite:@_]" (org-element-type (org-element-context))))) (should (equal "a:.#$%&-+?<>~/1" (org-test-with-temp-text "[cite:@a:.#$%&-+?<>~/1]" (org-element-property :key (org-element-context))))) (should-not (eq 'citation-reference (org-test-with-temp-text "[cite:@;]" (org-element-type (org-element-context))))) (should-not (equal "key" (org-test-with-temp-text "[cite:@[]]" (org-element-property :key (org-element-context))))) ;; References in citations accept optional `:prefix' and `:suffix' ;; properties. (should (equal '("pre ") (org-test-with-temp-text "[cite:pre @key]" (org-element-property :prefix (org-element-context))))) (should (equal '(" post") (org-test-with-temp-text "[cite:@key post]" (org-element-property :suffix (org-element-context))))) ;; White spaces between "cite" tag and prefix are ignored. (should (equal '("pre ") (org-test-with-temp-text "[cite: pre @key]" (org-element-property :prefix (org-element-context))))) ;; Semicolons do not belong to prefix or suffix. (should (equal '("pre ") (org-test-with-temp-text "[cite:@key1;pre @key2]" (org-element-property :prefix (org-element-context))))) (should (equal '(" post") (org-test-with-temp-text "[cite:@key1 post;@key2]" (org-element-property :suffix (org-element-context))))) (should (equal '("pre ") (org-test-with-temp-text "[cite:global prefix;pre @key1]" (org-element-property :prefix (org-element-context))))) (should (equal '(" post") (org-test-with-temp-text "[cite:@key1 post; global suffix]" (org-element-property :suffix (org-element-context)))))) ;;;; Clock (ert-deftest test-org-element/clock-parser () "Test `clock' parser." ;; Running clock. (let ((clock (org-test-with-temp-text "CLOCK: [2012-01-01 sun. 00:01]" (org-element-at-point)))) (should (eq (org-element-property :status clock) 'running)) (should (equal (org-element-property :raw-value (org-element-property :value clock)) "[2012-01-01 sun. 00:01]")) (should-not (org-element-property :duration clock))) ;; clock string should not be case-sensitive. (let ((clock (org-test-with-temp-text "Clock: [2012-01-01 sun. 00:01]" (org-element-at-point)))) (should (eq (org-element-property :status clock) 'running)) (should (equal (org-element-property :raw-value (org-element-property :value clock)) "[2012-01-01 sun. 00:01]")) (should-not (org-element-property :duration clock))) ;; Closed clock. (let ((clock (org-test-with-temp-text "CLOCK: [2012-01-01 sun. 00:01]--[2012-01-01 sun. 00:02] => 0:01" (org-element-at-point)))) (should (eq (org-element-property :status clock) 'closed)) (should (equal (org-element-property :raw-value (org-element-property :value clock)) "[2012-01-01 sun. 00:01]--[2012-01-01 sun. 00:02]")) (should (equal (org-element-property :duration clock) "0:01"))) ;; Closed clock without timestamp. (let ((clock (org-test-with-temp-text "CLOCK: => 0:11" (org-element-at-point)))) (should (eq (org-element-property :status clock) 'closed)) (should-not (org-element-property :value clock)) (should (equal (org-element-property :duration clock) "0:11")))) ;;;; Code (ert-deftest test-org-element/code-parser () "Test `code' parser." ;; Regular test. (should (org-test-with-temp-text "~code~" (org-element-map (org-element-parse-buffer) 'code #'identity))) ;; Multi-line markup. (should (equal (org-element-property :value (org-test-with-temp-text "~first line\nsecond line~" (org-element-map (org-element-parse-buffer) 'code #'identity nil t))) "first line\nsecond line"))) ;;;; Comment (ert-deftest test-org-element/comment-parser () "Test `comment' parser." ;; Regular comment. (should (eq 'comment (org-test-with-temp-text "# Comment" (org-element-type (org-element-at-point))))) ;; Inline comment. (should (eq 'comment (org-test-with-temp-text " # Comment" (org-element-type (org-element-at-point))))) ;; Preserve indentation. (should (equal "No blank\n One blank" (org-element-property :value (org-test-with-temp-text "# No blank\n# One blank" (org-element-at-point))))) ;; Comment with blank lines. (should (equal "First part\n\n\nSecond part" (org-element-property :value (org-test-with-temp-text "# First part\n# \n#\n# Second part" (org-element-at-point))))) ;; Do not mix comments and keywords. (should (eq 1 (org-test-with-temp-text "#+keyword: value\n# comment\n#+keyword: value" (length (org-element-map (org-element-parse-buffer) 'comment #'identity))))) (should (equal "comment" (org-test-with-temp-text "#+key: value\n# comment\n#+key: value" (org-element-property :value (org-element-at-point))))) ;; Correctly handle non-empty blank lines at the end of buffer. (should (org-test-with-temp-text "# A\n " (= (org-element-property :end (org-element-at-point)) (point-max))))) ;;;; Comment Block (ert-deftest test-org-element/comment-block-parser () "Test `comment-block' parser." ;; Standard test. (should (org-test-with-temp-text "#+BEGIN_COMMENT\nText\n#+END_COMMENT" (org-element-map (org-element-parse-buffer) 'comment-block 'identity))) ;; Ignore case. (should (org-test-with-temp-text "#+begin_comment\nText\n#+end_comment" (org-element-map (org-element-parse-buffer) 'comment-block 'identity))) ;; Ignore incomplete block. (should-not (org-test-with-temp-text "#+BEGIN_COMMENT" (org-element-map (org-element-parse-buffer) 'comment-block 'identity nil t))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text "#+BEGIN_COMMENT\nC\n#+END_COMMENT\n " (= (org-element-property :end (org-element-at-point)) (point-max))))) ;;;; Diary Sexp (ert-deftest test-org-element/diary-sexp-parser () "Test `diary-sexp' parser." ;; Standard test. (should (eq 'diary-sexp (org-test-with-temp-text "%%(org-anniversary 1956 5 14)(2) Arthur Dent is %d years old" (org-element-type (org-element-at-point))))) ;; Diary sexp must live at beginning of line (should-not (eq 'diary-sexp (org-test-with-temp-text " %%(org-bbdb-anniversaries)" (org-element-type (org-element-at-point))))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text "%%(org-bbdb-anniversaries)\n " (= (org-element-property :end (org-element-at-point)) (point-max))))) ;;;; Drawer (ert-deftest test-org-element/drawer-parser () "Test `drawer' parser." ;; Standard test. (should (org-test-with-temp-text ":TEST:\nText\n:END:" (org-element-map (org-element-parse-buffer) 'drawer 'identity))) ;; Ignore incomplete drawer. (should-not (org-test-with-temp-text ":TEST:" (org-element-map (org-element-parse-buffer) 'drawer 'identity nil t))) (should-not (org-test-with-temp-text ":END:" (org-element-map (org-element-parse-buffer) 'drawer 'identity nil t))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text ":TEST:\nC\n:END:\n " (= (org-element-property :end (org-element-at-point)) (point-max))))) ;;;; Dynamic Block (ert-deftest test-org-element/dynamic-block-parser () "Test `dynamic-block' parser." ;; Standard test. (should (org-test-with-temp-text "#+BEGIN: myblock :param1 val1 :param2 val2\nText\n#+END:" (org-element-map (org-element-parse-buffer) 'dynamic-block 'identity))) ;; Ignore case. (should (org-test-with-temp-text "#+begin: myblock :param1 val1 :param2 val2\nText\n#+end:" (org-element-map (org-element-parse-buffer) 'dynamic-block 'identity))) ;; Ignore incomplete block. (should-not (org-test-with-temp-text "#+BEGIN: myblock :param1 val1 :param2 val2" (org-element-map (org-element-parse-buffer) 'dynamic-block 'identity nil t))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text "#+BEGIN: myblock :param val1\nC\n#+END:\n " (= (org-element-property :end (org-element-at-point)) (point-max)))) ;; Block name is mandatory. (should-not (org-test-with-temp-text "#+BEGIN:\n\n#+END:\n" (org-element-type-p (org-element-at-point) 'dynamic-block)))) ;;;; Entity (ert-deftest test-org-element/entity-parser () "Test `entity' parser." ;; Without brackets. (should (org-test-with-temp-text "\\sin" (org-element-map (org-element-parse-buffer) 'entity 'identity))) ;; Special case: space-based entities. (should (equal "_ " ;; Space after entity must be a part of its name. (org-test-with-temp-text "\\_ Foo" (org-element-property :name (car (org-element-map (org-element-parse-buffer) 'entity 'identity)))))) (should-not ;; {} is not a part of whitespace entity name. (org-test-with-temp-text "\\_ {}Foo" (org-element-property :bracketsp (car (org-element-map (org-element-parse-buffer) 'entity 'identity))))) ;; With brackets. (should (org-element-property :use-brackets-p (org-test-with-temp-text "\\alpha{}text" (org-element-map (org-element-parse-buffer) 'entity 'identity nil t)))) ;; User-defined entity. (should (equal (org-element-property :name (let ((org-entities-user '(("test" "test" nil "test" "test" "test" "test")))) (org-test-with-temp-text "\\test" (org-element-map (org-element-parse-buffer) 'entity 'identity nil t)))) "test")) ;; Special case: entity at the end of a container. (should (eq 'entity (org-test-with-temp-text "*\\alpha \\beta*" (search-forward "be") (org-element-type (org-element-context)))))) ;;;; Example Block (ert-deftest test-org-element/example-block-parser () "Test `example-block' parser." ;; Standard test. (should (org-test-with-temp-text "#+BEGIN_EXAMPLE\nText\n#+END_EXAMPLE" (org-element-map (org-element-parse-buffer) 'example-block 'identity))) ;; Ignore incomplete block. (should-not (eq 'example-block (org-test-with-temp-text "#+BEGIN_EXAMPLE" (org-element-type (org-element-at-point))))) ;; Properly un-escape code. (should (equal "* Headline\n #+keyword:\nText\n" (org-test-with-temp-text "#+BEGIN_EXAMPLE\n,* Headline\n ,#+keyword:\nText\n#+END_EXAMPLE" (org-element-property :value (org-element-at-point))))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text "#+BEGIN_EXAMPLE\nC\n#+END_EXAMPLE\n " (= (org-element-property :end (org-element-at-point)) (point-max))))) (ert-deftest test-org-element/block-switches () "Test `example-block' and `src-block' switches parsing." (let ((org-coderef-label-format "(ref:%s)")) ;; 1. Test "-i" switch. (should-not (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n(+ 1 1)\n#+END_SRC" (org-element-property :preserve-indent (org-element-at-point)))) (should (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp -i\n(+ 1 1)\n#+END_SRC" (org-element-property :preserve-indent (org-element-at-point)))) (should-not (org-test-with-temp-text "#+BEGIN_EXAMPLE\nText.\n#+END_EXAMPLE" (org-element-property :preserve-indent (org-element-at-point)))) (should (org-test-with-temp-text "#+BEGIN_EXAMPLE -i\nText.\n#+END_EXAMPLE" (org-element-property :preserve-indent (org-element-at-point)))) ;; 2. "-n -r -k" combination should number lines, retain labels but ;; not use them in coderefs. (org-test-with-temp-text "#+BEGIN_EXAMPLE -n -r -k\nText.\n#+END_EXAMPLE" (let ((element (org-element-at-point))) (should (org-element-property :number-lines element)) (should (org-element-property :retain-labels element)) (should-not (org-element-property :use-labels element)))) (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp -n -r -k\n(+ 1 1)\n#+END_SRC" (let ((element (org-element-at-point))) (should (org-element-property :number-lines element)) (should (org-element-property :retain-labels element)) (should-not (org-element-property :use-labels element)))) ;; 3. "-n -r" combination should number-lines remove labels and not ;; use them in coderefs. (org-test-with-temp-text "#+BEGIN_EXAMPLE -n -r\nText.\n#+END_EXAMPLE" (let ((element (org-element-at-point))) (should (org-element-property :number-lines element)) (should-not (org-element-property :retain-labels element)) (should-not (org-element-property :use-labels element)))) (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp -n -r\n(+ 1 1)\n#+END_SRC" (let ((element (org-element-at-point))) (should (org-element-property :number-lines element)) (should-not (org-element-property :retain-labels element)) (should-not (org-element-property :use-labels element)))) ;; 4. "-n" or "+n" should number lines, retain labels and use them ;; in coderefs. (should (org-test-with-temp-text "#+BEGIN_EXAMPLE -n\nText.\n#+END_EXAMPLE" (let ((element (org-element-at-point))) (and (org-element-property :number-lines element) (org-element-property :retain-labels element) (org-element-property :use-labels element))))) (should (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp -n\n(+ 1 1)\n#+END_SRC" (let ((element (org-element-at-point))) (and (org-element-property :number-lines element) (org-element-property :retain-labels element) (org-element-property :use-labels element))))) (should (org-test-with-temp-text "#+BEGIN_EXAMPLE +n\nText.\n#+END_EXAMPLE" (let ((element (org-element-at-point))) (and (org-element-property :number-lines element) (org-element-property :retain-labels element) (org-element-property :use-labels element))))) (should (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp +n\n(+ 1 1)\n#+END_SRC" (let ((element (org-element-at-point))) (and (org-element-property :number-lines element) (org-element-property :retain-labels element) (org-element-property :use-labels element))))) ;; 5. No switch should not number lines, but retain labels and use ;; them in coderefs. (org-test-with-temp-text "#+BEGIN_EXAMPLE\nText.\n#+END_EXAMPLE" (let ((element (org-element-at-point))) (should (not (org-element-property :number-lines element))) (should (org-element-property :retain-labels element)) (should (org-element-property :use-labels element)))) (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n(+ 1 1)\n#+END_SRC" (let ((element (org-element-at-point))) (should (not (org-element-property :number-lines element))) (should (org-element-property :retain-labels element)) (should (org-element-property :use-labels element)))) ;; 6. "-r" switch only: do not number lines, remove labels, and ;; don't use labels in coderefs. (org-test-with-temp-text "#+BEGIN_EXAMPLE -r\nText.\n#+END_EXAMPLE" (let ((element (org-element-at-point))) (should (not (org-element-property :number-lines element))) (should (not (org-element-property :retain-labels element))) (should (not (org-element-property :use-labels element))))) (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp -r\n(+ 1 1)\n#+END_SRC" (let ((element (org-element-at-point))) (should (not (org-element-property :number-lines element))) (should (not (org-element-property :retain-labels element))) (should (not (org-element-property :use-labels element))))) ;; 7. Recognize coderefs with user-defined syntax. (should (equal "[ref:%s]" (org-test-with-temp-text "#+BEGIN_EXAMPLE -l \"[ref:%s]\"\nText [ref:text]\n#+END_EXAMPLE" (org-element-property :label-fmt (org-element-at-point))))) (should (equal "[ref:%s]" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp -l \"[ref:%s]\"\n(+ 1 1) [ref:text]\n#+END_SRC" (org-element-property :label-fmt (org-element-at-point))))))) ;;;; Export Block (ert-deftest test-org-element/export-block-parser () "Test `export-block' parser." ;; Standard test. (should (eq 'export-block (org-test-with-temp-text "#+BEGIN_EXPORT LATEX\nText\n#+END_EXPORT" (org-element-type (org-element-at-point))))) (should (equal "LATEX" (org-test-with-temp-text "#+BEGIN_EXPORT LATEX\nText\n#+END_EXPORT" (org-element-property :type (org-element-at-point))))) ;; Ignore case. (should (eq 'export-block (org-test-with-temp-text "#+begin_export latex\nText\n#+end_export" (org-element-type (org-element-at-point))))) ;; Ignore incomplete block. (should-not (eq 'export-block (org-test-with-temp-text "#+BEGIN_EXPORT" (org-element-type (org-element-at-point))))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text "#+BEGIN_EXPORT latex\nC\n#+END_EXPORT\n " (= (org-element-property :end (org-element-at-point)) (point-max)))) ;; Un-escape commas in `:value'. (should (equal "* H\n" (org-test-with-temp-text "#+BEGIN_EXPORT org\n,* H\n#+END_EXPORT\n " (org-element-property :value (org-element-at-point)))))) ;;;; Export Snippet (ert-deftest test-org-element/export-snippet-parser () "Test `export-snippet' parser." (should (equal '("backend" . "contents") (org-test-with-temp-text "@@backend:contents@@" (org-element-map (org-element-parse-buffer) 'export-snippet (lambda (snippet) (cons (org-element-property :back-end snippet) (org-element-property :value snippet))) nil t))))) ;;;; Fixed Width (ert-deftest test-org-element/fixed-width-parser () "Test fixed-width area parsing." ;; Preserve indentation. (should (equal "no blank\n one blank" (org-test-with-temp-text ": no blank\n: one blank" (org-element-property :value (org-element-at-point))))) ;; Fixed-width with empty lines. (should (equal "first part\n\n\nsecond part" (org-test-with-temp-text ": first part\n:\n: \n: second part" (org-element-property :value (org-element-at-point))))) ;; Parse indented fixed-width markers. (should (eq 'fixed-width (org-test-with-temp-text "Text\n : no blank\n : one blank" (org-element-type (org-element-at-point))))) ;; Distinguish fixed-width areas within a list and outside of it. (should (org-test-with-temp-text " - Item : fixed-width inside : fixed-width outside" (= (org-element-property :end (org-element-at-point)) (line-beginning-position 2)))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text ": A\n " (= (org-element-property :end (org-element-at-point)) (point-max))))) ;;;; Footnote Definition (ert-deftest test-org-element/footnote-definition-parser () "Test `footnote-definition' parser." (should (org-test-with-temp-text "[fn:1] Definition" (eq (org-element-type (org-element-at-point)) 'footnote-definition))) ;; Footnote with more contents. (should (= 29 (org-test-with-temp-text "[fn:1] Definition\n\n| a | b |" (org-element-property :end (org-element-at-point))))) ;; Test difference between :contents-end and :end property (should (< (org-test-with-temp-text "[fn:1] Definition\n\n\n" (org-element-property :contents-end (org-element-at-point))) (org-test-with-temp-text "[fn:1] Definition\n\n\n" (org-element-property :end (org-element-at-point))))) ;; Footnote starting with special syntax. (should-not (org-test-with-temp-text "[fn:1] - no item" (eq (org-element-type (org-element-at-point)) 'item))) ;; Correctly handle footnote starting with an empty line. (should (= 9 (org-test-with-temp-text "[fn:1]\n\n Body" (org-element-property :contents-begin (org-element-at-point))))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text "[fn:1] Definition\n " (= (org-element-property :end (org-element-at-point)) (point-max)))) ;; Footnote with attributes. (should (= 1 (org-test-with-temp-text "#+attr_latex: :offset 0in\n[fn:1] A footnote." (length (org-element-map (org-element-parse-buffer) 'footnote-definition #'identity))))) (should (org-test-with-temp-text "[fn:1] 1\n\n#+attr_latex: :offset 0in\n[fn:2] 2" (goto-char (org-element-property :end (org-element-at-point))) (looking-at "#"))) ;; An empty footnote has no contents. (should-not (org-test-with-temp-text "[fn:1]\n\n" (let ((footnote (org-element-at-point))) (or (org-element-property :contents-begin footnote) (org-element-property :contents-end footnote))))) ;; Parse `:pre-blank'. (should (= 0 (org-test-with-temp-text "[fn:1] A" (org-element-property :pre-blank (org-element-at-point))))) (should (= 1 (org-test-with-temp-text "[fn:1]\nA" (org-element-property :pre-blank (org-element-at-point))))) (should (= 2 (org-test-with-temp-text "[fn:1]\n\nA" (org-element-property :pre-blank (org-element-at-point)))))) ;;;; Footnotes Reference. (ert-deftest test-org-element/footnote-reference-parser () "Test `footnote-reference' parser." ;; Parse a standard reference. (should (org-test-with-temp-text "Text[fn:label]" (org-element-map (org-element-parse-buffer) 'footnote-reference 'identity))) ;; Parse an inline reference. (should (org-test-with-temp-text "Text[fn:test:def]" (org-element-map (org-element-parse-buffer) 'footnote-reference 'identity))) ;; Parse an anonymous reference. (should (org-test-with-temp-text "Text[fn::def]" (org-element-map (org-element-parse-buffer) 'footnote-reference 'identity))) ;; Parse inline references with syntax loaded characters. (should (eq 'footnote-reference (org-test-with-temp-text "Text[fn::(def]" (org-element-type (org-element-context))))) (should (eq 'footnote-reference (org-test-with-temp-text "Text[fn::\"def]" (org-element-type (org-element-context))))) ;; Parse nested footnotes. (should (= 2 (length (org-test-with-temp-text "Text[fn::def [fn:label]]" (org-element-map (org-element-parse-buffer) 'footnote-reference 'identity))))) ;; Parse adjacent footnotes. (should (org-test-with-temp-text "Text[fn:label1][fn:label2]" (= 2 (length (org-element-map (org-element-parse-buffer) 'footnote-reference 'identity))))) ;; Only properly closed footnotes are recognized as such. (should-not (org-test-with-temp-text "Text[fn:label" (org-element-map (org-element-parse-buffer) 'footnote-reference 'identity)))) ;;;; Headline (ert-deftest test-org-element/headline-todo-keyword () "Test todo keyword recognition." ;; Reference test. (org-test-with-temp-text "* TODO Headline" (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (should (org-element-property :todo-keyword (org-element-at-point))))) ;; Todo keyword is prefix of headlines first word. (org-test-with-temp-text "* TODOHeadline" (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (should-not (org-element-property :todo-keyword (org-element-at-point))))) (org-test-with-temp-text "* TODO" (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (should (org-element-property :todo-keyword (org-element-at-point))))) (org-test-with-temp-text "* :tag:" (should (member "tag" (org-element-property :tags (org-element-at-point))))) (org-test-with-temp-text "* COMMENT" (should (org-element-property :commentedp (org-element-at-point)))) (org-test-with-temp-text "* COMMENT title" (should (equal "title" (org-element-property :raw-value (org-element-at-point))))) (org-test-with-temp-text "* COMMENT:tag:" (should-not (org-element-property :commentedp (org-element-at-point))))) (ert-deftest test-org-element/headline-comment-keyword () "Test COMMENT keyword recognition." ;; Reference test. (org-test-with-temp-text "* Headline" (should-not (org-element-property :commentedp (org-element-at-point)))) ;; Standard position. (org-test-with-temp-text "* COMMENT Headline" (let ((headline (org-element-at-point))) (should (org-element-property :commentedp headline)) (should (equal (org-element-property :raw-value headline) "Headline")))) ;; Case sensitivity. (org-test-with-temp-text "* Comment Headline" (let ((headline (org-element-at-point))) (should-not (org-element-property :commentedp headline)) (should (equal (org-element-property :raw-value headline) "Comment Headline")))) ;; With another keyword. (org-test-with-temp-text "* TODO COMMENT Headline" (let* ((org-todo-keywords '((sequence "TODO" "DONE"))) (headline (org-element-at-point))) (should (org-element-property :commentedp headline)) (should (equal (org-element-property :raw-value headline) "Headline")))) ;; With the keyword only. (org-test-with-temp-text "* COMMENT" (let* ((headline (org-element-at-point))) (should (org-element-property :commentedp headline)) (should (equal (org-element-property :raw-value headline) ""))))) (ert-deftest test-org-element/headline-archive-tag () "Test ARCHIVE tag recognition." ;; Reference test. (should-not (org-test-with-temp-text "* Headline" (org-element-property :archivedp (org-element-at-point)))) ;; Single tag. (org-test-with-temp-text "* Headline :ARCHIVE:" (let ((headline (org-element-at-point))) (should (org-element-property :archivedp headline)))) ;; Multiple tags. (org-test-with-temp-text "* Headline :test:ARCHIVE:" (let ((headline (org-element-at-point))) (should (org-element-property :archivedp headline)))) ;; Tag is case-sensitive. (should-not (org-test-with-temp-text "* Headline :Archive:" (org-element-property :archivedp (org-element-at-point))))) (ert-deftest test-org-element/headline-properties () "Test properties from property drawer." ;; All properties from property drawer have their symbol upper ;; cased. (should (org-test-with-temp-text "* Headline\n:PROPERTIES:\n:foo: bar\n:END:" (org-element-property :FOO (org-element-at-point)))) (should-not (org-test-with-temp-text "* Headline\n:PROPERTIES:\n:foo: bar\n:END:" (org-element-property :foo (org-element-at-point)))) ;; Also parse properties associated in inlinetasks. (when (featurep 'org-inlinetask) (should (org-test-with-temp-text "*************** Inlinetask :PROPERTIES: :foo: bar :END: *************** END" (org-element-property :FOO (org-element-at-point))))) ;; Do not find property drawer in a verbatim area. (should-not (org-test-with-temp-text "* Headline #+BEGIN_EXAMPLE :PROPERTIES: :foo: bar :END: #+END_EXAMPLE" (org-element-property :FOO (org-element-at-point)))) ;; Do not use properties from a drawer associated to an inlinetask. (when (featurep 'org-inlinetask) (should-not (org-test-with-temp-text "* Headline *************** Inlinetask :PROPERTIES: :foo: bar :END: *************** END" (org-element-property :FOO (let ((org-inlinetask-min-level 15)) (org-element-at-point)))))) ;; Do not find incomplete drawers. (should-not (org-test-with-temp-text "* Headline\n:PROPERTIES:\n:foo: bar" (org-element-property :FOO (org-element-at-point))))) ;;;; Horizontal Rule (ert-deftest test-org-element/horizontal-rule-parser () "Test `horizontal-rule' parser." ;; Standard. (should (org-test-with-temp-text "-----" (org-element-map (org-element-parse-buffer) 'horizontal-rule 'identity))) ;; Indented. (should (org-test-with-temp-text " -----" (org-element-map (org-element-parse-buffer) 'horizontal-rule 'identity))) ;; Hyphen must be alone on the line. (should-not (org-test-with-temp-text "-----wrong" (org-element-map (org-element-parse-buffer) 'horizontal-rule 'identity))) ;; 4 hyphens is too small. (should-not (org-test-with-temp-text "----" (org-element-map (org-element-parse-buffer) 'horizontal-rule 'identity))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text "-----\n " (= (org-element-property :end (org-element-at-point)) (point-max))))) ;;;; Inline Babel Call (ert-deftest test-org-element/inline-babel-call-parser () "Test `inline-babel-call' parser." ;; Standard test. (should (eq 'inline-babel-call (org-test-with-temp-text "call_test()" (org-element-type (org-element-context))))) (should (eq 'inline-babel-call (org-test-with-temp-text "call_test[:results output](x=2)[:results html]" (org-element-type (org-element-context))))) ;; Parse call name. (should (equal "test" (org-test-with-temp-text "call_test[:results output](x=2)[:results html]" (org-element-property :call (org-element-context))))) ;; Parse inside header. (should (equal ":results output" (org-test-with-temp-text "call_test[:results output](x=2)[:results html]" (org-element-property :inside-header (org-element-context))))) ;; Parse arguments. (should (equal "x=2" (org-test-with-temp-text "call_test[:results output](x=2)[:results html]" (org-element-property :arguments (org-element-context))))) ;; Parse end header. (should (equal ":results html" (org-test-with-temp-text "call_test[:results output](x=2)[:results html]" (org-element-property :end-header (org-element-context))))) ;; Handle multi-line babel calls. (should (eq 'inline-babel-call (org-test-with-temp-text "call_test[:results\noutput](x=2)[:results html]" (org-element-type (org-element-context))))) (should (eq 'inline-babel-call (org-test-with-temp-text "call_test[:results output](x=2\ny=3)[:results html]" (org-element-type (org-element-context))))) (should (eq 'inline-babel-call (org-test-with-temp-text "call_test[:results output](x=2)[:results\nhtml]" (org-element-type (org-element-context))))) ;; Parse parameters containing round brackets. (should (eq 'inline-babel-call (org-test-with-temp-text "call_test[:var x='(1)](x=2)" (org-element-type (org-element-context)))))) ;;;; Inline Src Block (ert-deftest test-org-element/inline-src-block-parser () "Test `inline-src-block' parser." (should (org-test-with-temp-text "src_emacs-lisp{(+ 1 1)}" (org-element-map (org-element-parse-buffer) 'inline-src-block 'identity))) ;; With switches. (should (org-test-with-temp-text "src_emacs-lisp[:foo bar]{(+ 1 1)}" (org-element-map (org-element-parse-buffer) 'inline-src-block 'identity))) (should (org-test-with-temp-text "src_emacs-lisp[ :foo bar]{(+ 1 1)}" (org-element-map (org-element-parse-buffer) 'inline-src-block 'identity))) ;; Empty switches. (should (org-test-with-temp-text "src_emacs-lisp[]{(+ 1 1)}" (org-element-map (org-element-parse-buffer) 'inline-src-block 'identity))) ;; Invalid syntax. (should-not (org-test-with-temp-text "src_emacs-lisp[]foo{(+ 1 1)}" (org-element-map (org-element-parse-buffer) 'inline-src-block 'identity))) (should-not (org-test-with-temp-text "foosrc_emacs-lisp[]{(+ 1 1)}" (org-element-map (org-element-parse-buffer) 'inline-src-block 'identity))) ;; Invalid language name (should-not (org-test-with-temp-text "src_emacs-\tlisp{(+ 1 1)}" (org-element-map (org-element-parse-buffer) 'inline-src-block 'identity))) ;; Test parsing at the beginning of an item. (should (org-test-with-temp-text "- src_emacs-lisp{(+ 1 1)}" (org-element-map (org-element-parse-buffer) 'inline-src-block 'identity))) ;; Test parsing multi-line source blocks. (should (eq 'inline-src-block (org-test-with-temp-text "src_emacs-lisp{(+ 1\n 1)}" (org-element-type (org-element-context))))) (should (eq 'inline-src-block (org-test-with-temp-text "src_emacs-lisp[\n:foo bar]{(+ 1 1)}" (org-element-type (org-element-context))))) (should (eq 'inline-src-block (org-test-with-temp-text "src_emacs-lisp[:foo\nbar]{(+ 1 1)}" (org-element-type (org-element-context))))) ;; Besides curly brackets, ignore any other bracket type. (should (equal "[foo" (org-test-with-temp-text "src_emacs-lisp{[foo}" (org-element-property :value (org-element-context))))) (should (equal "foo]" (org-test-with-temp-text "src_emacs-lisp{foo]}" (org-element-property :value (org-element-context))))) (should (equal "(foo" (org-test-with-temp-text "src_emacs-lisp{(foo}" (org-element-property :value (org-element-context))))) (should (equal "foo)" (org-test-with-temp-text "src_emacs-lisp{foo)}" (org-element-property :value (org-element-context))))) ;; Parse parameters containing square brackets. (should (eq 'inline-src-block (org-test-with-temp-text "src_emacs-lisp[:var table=t[1,1]]{(+ 1 1)}" (org-element-type (org-element-context)))))) ;;;; Inlinetask (ert-deftest test-org-element/inlinetask-parser () "Test `inlinetask' parser." (when (featurep 'org-inlinetask) (let ((org-inlinetask-min-level 15)) ;; Regular inlinetask. (should (eq 'inlinetask (org-test-with-temp-text "*************** Task\nTest\n*************** END" (org-element-type (org-element-at-point))))) (should (eq 'inlinetask (org-test-with-temp-text "*************** Task\nTest\n*************** END" (org-element-type (org-element-at-point))))) ;; Degenerate inlinetask. (should (eq 'inlinetask (org-test-with-temp-text "*************** Task" (org-element-type (org-element-at-point))))) ;; Mixed inlinetasks. (should-not (org-test-with-temp-text " *************** Task *************** Task2 Contents *************** END" (forward-line) (goto-char (org-element-property :end (org-element-at-point))) (eobp))) ;; TODO keyword. (should (equal "TODO" (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "*************** TODO Task" (org-element-property :todo-keyword (org-element-at-point)))))) ;; Planning info. (should (org-test-with-temp-text " *************** Task DEADLINE: <2012-03-29 thu.> *************** END" (org-element-property :deadline (org-element-at-point)))) (should (eq 'planning (org-test-with-temp-text " *************** Task DEADLINE: <2012-03-29 thu.> *************** END" (org-element-type (org-element-at-point))))) (should-not (org-test-with-temp-text " *************** Task DEADLINE: <2012-03-29 thu.>" (org-element-property :deadline (org-element-at-point)))) (should-not (eq 'planning (org-test-with-temp-text " *************** Task DEADLINE: <2012-03-29 thu.>" (org-element-type (org-element-at-point))))) ;; Priority. (should (eq ?A (org-test-with-temp-text " *************** [#A] Task" (forward-line) (org-element-property :priority (org-element-at-point))))) ;; Tags. (should (equal '("test") (org-test-with-temp-text " *************** Task :test:" (forward-line) (org-element-property :tags (org-element-at-point))))) ;; Regular properties are accessed through upper case keywords. (should (org-test-with-temp-text " *************** Task :PROPERTIES: :foo: bar :END: *************** END" (forward-line) (org-element-property :FOO (org-element-at-point)))) (should-not (org-test-with-temp-text " *************** Task :PROPERTIES: :foo: bar :END: *************** END" (forward-line) (org-element-property :foo (org-element-at-point)))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text "*************** Task\n*************** END\n " (= (org-element-property :end (org-element-at-point)) (point-max))))))) ;;;; Italic (ert-deftest test-org-element/italic-parser () "Test `italic' parser." ;; Regular test. (should (org-test-with-temp-text "/italic/" (org-element-map (org-element-parse-buffer) 'italic #'identity nil t))) ;; Multi-line markup. (should (equal (org-element-contents (org-test-with-temp-text "/first line\nsecond line/" (org-element-map (org-element-parse-buffer) 'italic #'identity nil t))) '("first line\nsecond line")))) ;;;; Item (ert-deftest test-org-element/item-parser () "Test `item' parser." ;; Standard test. (should (org-test-with-temp-text "- item" (org-element-map (org-element-parse-buffer) 'item 'identity))) ;; Counter. (should (= 6 (org-element-property :counter (org-test-with-temp-text "6. [@6] item" (org-element-map (org-element-parse-buffer) 'item 'identity nil t))))) ;; Tag (should (equal '("tag") (org-element-property :tag (org-test-with-temp-text "- tag :: description" (org-element-map (org-element-parse-buffer) 'item 'identity nil t))))) ;; No tags in ordered lists. (should-not (org-element-property :tag (org-test-with-temp-text "1. tag :: description" (org-element-map (org-element-parse-buffer) 'item 'identity nil t)))) ;; Check-boxes (should (equal '(trans on off) (org-test-with-temp-text " - [-] item 1 - [X] item 1.1 - [ ] item 1.2" (org-element-map (org-element-parse-buffer) 'item (lambda (item) (org-element-property :checkbox item)))))) ;; Item starting with special syntax. (should (equal '(("- item")) (org-test-with-temp-text "- - item" (org-element-map (org-element-parse-buffer) 'paragraph 'org-element-contents)))) ;; Block in an item: ignore indentation within the block. (should (org-test-with-temp-text "- item\n #+begin_src emacs-lisp\n(+ 1 1)\n #+end_src" (= (org-element-property :end (org-element-at-point)) (point-max)))) ;; Parse `:pre-blank'. (should (= 0 (org-test-with-temp-text "- A" (org-element-property :pre-blank (org-element-at-point))))) (should (= 1 (org-test-with-temp-text "-\n A" (org-element-property :pre-blank (org-element-at-point))))) (should (= 2 (org-test-with-temp-text "-\n\n A" (org-element-property :pre-blank (org-element-at-point))))) ;; Last item in a list or sub-list has no `:post-blank' lines, since ;; those belong to the plain-list. (should (= 0 (org-test-with-temp-text "- A\n\n- B\n\nEnd list" (org-element-property :post-blank (org-element-at-point))))) (should (= 0 (org-test-with-temp-text "- A\n\n - B\n\n - C\n\n End sub-list" (org-element-property :post-blank (org-element-at-point))))) (should (= 0 (org-test-with-temp-text "1. foo\n 1. bar\n 2. baz\n\n2. lorem\nipsum" (org-element-property :post-blank (org-element-at-point)))))) ;;;; Keyword (ert-deftest test-org-element/keyword-parser () "Test `keyword' parser." ;; Standard test. (should (org-test-with-temp-text "#+KEYWORD: value" (org-element-map (org-element-parse-buffer) 'keyword 'identity))) ;; Keywords are case-insensitive. (should (org-test-with-temp-text "#+keyword: value" (org-element-map (org-element-parse-buffer) 'keyword 'identity))) ;; Affiliated keywords are not keywords. (should-not (org-test-with-temp-text "#+NAME: value Paragraph" (org-element-map (org-element-parse-buffer) 'keyword 'identity))) ;; Do not mix keywords with Babel calls and dynamic blocks. (should-not (org-test-with-temp-text "#+CALL: fun()" (org-element-map (org-element-parse-buffer) 'keyword 'identity))) (should-not (org-test-with-temp-text "#+BEGIN: my-fun\nBody\n#+END:" (org-element-map (org-element-parse-buffer) 'keyword 'identity))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text "#+KEYWORD: value\n " (= (org-element-property :end (org-element-at-point)) (point-max))))) ;;;; LaTeX Environment (ert-deftest test-org-element/latex-environment-parser () "Test `latex-environment' parser." (should (org-test-with-temp-text "\\begin{equation}\ne^{i\\pi}+1=0\n\\end{equation}" (org-element-map (org-element-parse-buffer) 'latex-environment 'identity))) ;; Allow nested environments. (should (equal "\\begin{outer} \\begin{inner} e^{i\\pi}+1=0 \\end{inner} \\end{outer}" (org-test-with-temp-text " \\begin{outer} \\begin{inner} e^{i\\pi}+1=0 \\end{inner} \\end{outer}" (org-element-property :value (org-element-map (org-element-parse-buffer) 'latex-environment 'identity nil t))))) ;; Allow environments with options and arguments. (should (eq 'latex-environment (org-test-with-temp-text "\\begin{theorem}[Euler]\ne^{i\\pi}+1=0\n\\end{theorem}" (org-element-type (org-element-at-point))))) (should (eq 'latex-environment (org-test-with-temp-text "\\begin{env}{arg}\nvalue\n\\end{env}" (org-element-type (org-element-at-point))))) ;; Allow environments without newline after \begin{.}. (should (eq 'latex-environment (org-test-with-temp-text "\\begin{env}{arg}something\nvalue\n\\end{env}" (org-element-type (org-element-at-point))))) ;; Allow one-line environments. (should (eq 'latex-environment (org-test-with-temp-text "\\begin{env}{arg}something\\end{env}" (org-element-type (org-element-at-point))))) ;; Should not allow different tags. (should-not (eq 'latex-environment (org-test-with-temp-text "\\begin{env*}{arg}something\\end{env}" (org-element-type (org-element-at-point))))) ;; LaTeX environments must be on separate lines. (should-not (eq 'latex-environment (org-test-with-temp-text "\\begin{env} x \\end{env} y" (org-element-type (org-element-at-point))))) (should-not (eq 'latex-environment (org-test-with-temp-text "y \\begin{env} x \\end{env}" (org-element-type (org-element-at-point))))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text "\\begin{env}\n\\end{env}\n " (= (org-element-property :end (org-element-at-point)) (point-max))))) ;;;; LaTeX Fragment (ert-deftest test-org-element/latex-fragment-parser () "Test `latex-fragment' parser." ;; Basic $...$ test. (should (eq 'latex-fragment (org-test-with-temp-text "$a$" (org-element-type (org-element-context))))) ;; Test valid characters after $...$ construct. (should-not (eq 'latex-fragment (org-test-with-temp-text "$a$a" (org-element-type (org-element-context))))) (should (eq 'latex-fragment (org-test-with-temp-text "$a$!" (org-element-type (org-element-context))))) (should (eq 'latex-fragment (org-test-with-temp-text "$a$," (org-element-type (org-element-context))))) (should (eq 'latex-fragment (org-test-with-temp-text "$a$\"" (org-element-type (org-element-context))))) (should (eq 'latex-fragment (org-test-with-temp-text "$a$)" (org-element-type (org-element-context))))) (should (eq 'latex-fragment (org-test-with-temp-text "$a$ " (org-element-type (org-element-context))))) (should (eq 'latex-fragment (org-test-with-temp-text "$a$'" (org-element-type (org-element-context))))) ;; Test forbidden characters inside $...$. (should-not (eq 'latex-fragment (org-test-with-temp-text "$.a$" (org-element-type (org-element-context))))) (should-not (eq 'latex-fragment (org-test-with-temp-text "$,a$" (org-element-type (org-element-context))))) (should-not (eq 'latex-fragment (org-test-with-temp-text "$;a$" (org-element-type (org-element-context))))) (should-not (eq 'latex-fragment (org-test-with-temp-text "$ a$" (org-element-type (org-element-context))))) (should-not (eq 'latex-fragment (org-test-with-temp-text "$a.$" (org-element-type (org-element-context))))) (should-not (eq 'latex-fragment (org-test-with-temp-text "$a,$" (org-element-type (org-element-context))))) (should-not (eq 'latex-fragment (org-test-with-temp-text "$a $" (org-element-type (org-element-context))))) ;; Test $$...$$. (should (eq 'latex-fragment (org-test-with-temp-text "$$a$$" (org-element-type (org-element-context))))) ;; Test \(...\). (should (eq 'latex-fragment (org-test-with-temp-text "\\(a\\)" (org-element-type (org-element-context))))) ;; Test \[...\]. (should (eq 'latex-fragment (org-test-with-temp-text "\\[a\\]" (org-element-type (org-element-context))))) ;; Test fragment at the beginning of an item. (should (eq 'latex-fragment (org-test-with-temp-text "- $x$" (org-element-type (org-element-context)))))) ;;;; Line Break (ert-deftest test-org-element/line-break-parser () "Test `line-break' parser." ;; Regular test. (should (org-test-with-temp-text "Text \\\\" (org-element-map (org-element-parse-buffer) 'line-break 'identity))) ;; Line break with trailing white spaces. (should (org-test-with-temp-text "Text \\\\ " (org-element-map (org-element-parse-buffer) 'line-break 'identity))) ;; Three backslashes are too much. (should-not (org-test-with-temp-text "Text \\\\\\" (org-element-map (org-element-parse-buffer) 'line-break 'identity)))) ;;;; Link (ert-deftest test-org-element/link-parser () "Test `link' parser." ;; Radio target. (should (equal "radio" (org-test-with-temp-text "<<>>A radio link" (org-update-radio-target-regexp) (org-element-property :type (org-element-map (org-element-parse-buffer) 'link #'identity nil t))))) (should (equal "radio" (org-test-with-temp-text "<<>><<>><<>>A radio link" (org-update-radio-target-regexp) (org-element-property :type (org-element-map (org-element-parse-buffer) 'link #'identity nil t))))) (should (equal "radio" (let ((org-target-link-regexp-limit 9)) (org-test-with-temp-text "<<>><<>><<>>A radio link" (org-update-radio-target-regexp) (org-element-property :type (org-element-map (org-element-parse-buffer) 'link #'identity nil t)))))) ;; Pathological case: radio target of length 1 at beginning of line ;; not followed by spaces. (should (org-test-with-temp-text "* <<>>\na-bug" (org-update-radio-target-regexp) (org-element-parse-buffer))) ;; Pathological case: radio target in an emphasis environment. (should (eq 'bold (org-test-with-temp-text "* <<>>\n*radio*" (org-update-radio-target-regexp) (org-element-type (org-element-context))))) (should (eq 'link (org-test-with-temp-text "* <<>>\n*radio*" (org-update-radio-target-regexp) (org-element-type (org-element-context))))) ;; Standard link. ;; ;; ... with description. (should (equal '("Orgmode.org") (org-test-with-temp-text "[[https://orgmode.org][Orgmode.org]]" (org-element-contents (org-element-map (org-element-parse-buffer) 'link 'identity nil t))))) ;; ... without description. (should (equal "https" (org-test-with-temp-text "[[https://orgmode.org]]" (org-element-property :type (org-element-map (org-element-parse-buffer) 'link 'identity nil t))))) ;; ... with expansion. (should (equal "//orgmode.org/worg" (org-test-with-temp-text "[[Org:worg]]" (let ((org-link-abbrev-alist '(("Org" . "https://orgmode.org/")))) (org-element-property :path (org-element-map (org-element-parse-buffer) 'link 'identity nil t)))))) ;; ... with translation. (should (equal "127.0.0.1" (org-test-with-temp-text "[[https://orgmode.org]]" (let ((org-link-translation-function (lambda (type _) (cons type "127.0.0.1")))) (org-element-property :path (org-element-map (org-element-parse-buffer) 'link #'identity nil t)))))) ;; ... custom-id link. (should (equal "custom-id" (org-test-with-temp-text "[[#some-id]]" (org-element-property :type (org-element-map (org-element-parse-buffer) 'link 'identity nil t))))) ;; ... coderef link. (should (equal "coderef" (org-test-with-temp-text "[[(reference)]]" (org-element-property :type (org-element-map (org-element-parse-buffer) 'link 'identity nil t))))) ;; ... fuzzy link. (should (equal "fuzzy" (org-test-with-temp-text "[[target-or-title]]" (org-element-property :type (org-element-map (org-element-parse-buffer) 'link 'identity nil t))))) ;; ... file-type link with search option. (should (equal '(("file" "projects.org" "*task title")) (org-test-with-temp-text "[[file:projects.org::*task title]]" (org-element-map (org-element-parse-buffer) 'link (lambda (l) (list (org-element-property :type l) (org-element-property :path l) (org-element-property :search-option l))))))) ;; ... file-type link with application... (should (equal '("file" "projects.org" "emacs") (org-test-with-temp-text "[[file+emacs:projects.org]]" (let ((l (org-element-context))) (list (org-element-property :type l) (org-element-property :path l) (org-element-property :application l)))))) ;; ... `:path' in a file-type link must be compatible with "file" ;; scheme in URI syntax, even if Org syntax isn't... (should (org-test-with-temp-text-in-file "" (let ((file (expand-file-name (buffer-file-name)))) (insert (format "[[file://%s]]" file)) (equal (org-element-property :path (org-element-context)) file)))) (should (org-test-with-temp-text-in-file "" (let ((file (expand-file-name (buffer-file-name)))) (insert (format "[[file:%s]]" file)) (equal (org-element-property :path (org-element-context)) file)))) ;; ... multi-line link. (should (equal "ls *.org" (org-test-with-temp-text "[[shell:ls\n*.org]]" (org-element-property :path (org-element-context))))) ;; Plain link. (should (org-test-with-temp-text "A link: https://orgmode.org" (org-element-map (org-element-parse-buffer) 'link 'identity))) ;; Angular link. Follow RFC 3986. (should (eq 'link (org-test-with-temp-text "A link: " (org-element-type (org-element-context))))) (should (equal "//orgmode.org" (org-test-with-temp-text "A link: " (org-element-property :path (org-element-context))))) ;; Link abbreviation. (should (equal "https" (org-test-with-temp-text "#+LINK: orgmode https://www.orgmode.org/\n[[orgmode:#docs]]" (progn (org-mode-restart) (goto-char (1- (point-max))) (org-element-property :type (org-element-context)))))) ;; Link abbreviation with spaces. (should (equal "https" (org-test-with-temp-text "#+LINK: \"Nu Html Checker\" https://validator.w3.org/nu/?doc=%h [[Nu Html Checker:test]]" (progn (org-mode-restart) (goto-char (1- (point-max))) (org-element-property :type (org-element-context)))))) ;; Link abbreviation in a secondary string. (should (equal "https" (org-test-with-temp-text "#+LINK: orgmode https://www.orgmode.org/\n* H [[orgmode:#docs]]" (progn (org-mode-restart) (org-element-map (org-element-parse-buffer) 'link (lambda (link) (org-element-property :type link)) nil t nil t)))))) ;;;; Macro (ert-deftest test-org-element/macro-parser () "Test `macro' parser." ;; Without arguments. (should (org-test-with-temp-text "{{{macro}}}" (org-element-map (org-element-parse-buffer) 'macro 'identity))) ;; With arguments. (should (org-test-with-temp-text "{{{macro(arg1,arg2)}}}" (org-element-map (org-element-parse-buffer) 'macro 'identity))) ;; Properly handle protected commas in arguments... (should (= 2 (length (org-test-with-temp-text "{{{macro(arg1\\,arg1,arg2)}}}" (org-element-property :args (org-element-context)))))) ;; ... even when last argument ends with a protected comma. (should (equal '("C-,") (org-test-with-temp-text "{{{macro(C-\\,)}}}" (org-element-property :args (org-element-context))))) ;; Allow to escape escaping character. (should (equal '("C-\\" "") (org-test-with-temp-text "{{{macro(C-\\\\,)}}}" (org-element-property :args (org-element-context))))) ;; No need to escape backslashes elsewhere. (should (equal '("\\") (org-test-with-temp-text "{{{macro(\\)}}}" (org-element-property :args (org-element-context)))))) ;;;; Node Property (ert-deftest test-org-element/node-property () "Test `node-property' parser." ;; Standard test. (should (equal '("abc" "value") (org-test-with-temp-text "* H\n:PROPERTIES:\n:abc: value\n:END:" (let ((element (org-element-at-point))) (list (org-element-property :key element) (org-element-property :value element)))))) ;; The insides of property blocks on document level are parsed the ;; same way as headline property blocks. I.e. the concept of ;; `node-property' apply also for properties in those blocks. (should (equal '("abc" "value") (org-test-with-temp-text ":PROPERTIES:\n:abc: value\n:END:" (let ((element (org-element-at-point))) (list (org-element-property :key element) (org-element-property :value element)))))) ;; Value should be trimmed. (should (equal "value" (org-test-with-temp-text "* H\n:PROPERTIES:\n:abc: value \n:END:" (org-element-property :value (org-element-at-point))))) ;; A node property requires to be wrapped within a property drawer. (should-not (eq 'node-property (org-test-with-temp-text ":abc: value" (org-element-type (org-element-at-point))))) ;; Accept empty properties. (should (equal '(("foo" "value") ("bar" "")) (org-test-with-temp-text "* H\n:PROPERTIES:\n:foo: value\n:bar:\n:END:" (org-element-map (org-element-parse-buffer) 'node-property (lambda (p) (list (org-element-property :key p) (org-element-property :value p)))))))) ;;;; Paragraph (ert-deftest test-org-element/paragraph-parser () "Test `paragraph' parser." ;; Standard test. (should (org-test-with-temp-text "Paragraph" (org-element-map (org-element-parse-buffer) 'paragraph 'identity nil t))) ;; Property find end of a paragraph stuck to another element. (should (eq ?# (org-test-with-temp-text "Paragraph\n# Comment" (org-element-map (org-element-parse-buffer) 'paragraph (lambda (p) (char-after (org-element-property :end p))) nil t)))) ;; Include ill-formed Keywords. (should (org-test-with-temp-text "#+wrong_keyword something" (org-element-map (org-element-parse-buffer) 'paragraph 'identity))) ;; Include incomplete-drawers. (should (org-test-with-temp-text ":TEST:\nParagraph" (let ((elem (org-element-at-point))) (and (eq (org-element-type elem) 'paragraph) (= (point-max) (org-element-property :end elem)))))) (should (org-test-with-temp-text "foo\n:end:\nbar" (let ((elem (org-element-at-point))) (and (eq (org-element-type elem) 'paragraph) (= (point-max) (org-element-property :end elem)))))) ;; Include incomplete blocks. (should (org-test-with-temp-text "#+BEGIN_CENTER\nParagraph" (let ((elem (org-element-at-point))) (and (eq (org-element-type elem) 'paragraph) (= (point-max) (org-element-property :end elem)))))) (should (org-test-with-temp-text "foo\n#+END_CENTER\nbar" (let ((elem (org-element-at-point))) (and (eq (org-element-type elem) 'paragraph) (= (point-max) (org-element-property :end elem)))))) ;; Include incomplete latex environments. (should (org-test-with-temp-text "\begin{equation}\nParagraph" (let ((elem (org-element-at-point))) (and (eq (org-element-type elem) 'paragraph) (= (point-max) (org-element-property :end elem)))))) (should (org-test-with-temp-text "Paragraph\n\begin{equation}" (let ((elem (org-element-at-point))) (and (eq (org-element-type elem) 'paragraph) (= (point-max) (org-element-property :end elem)))))) ;; Stop at affiliated keywords. (should (org-test-with-temp-text "Paragraph\n#+NAME: test\n| table |" (let ((elem (org-element-at-point))) (and (eq (org-element-type elem) 'paragraph) (not (org-element-property :name elem)) (= (org-element-property :end elem) (line-beginning-position 2)))))) (should (org-test-with-temp-text "Paragraph\n#+CAPTION[with short caption]: test\n| table |" (let ((elem (org-element-at-point))) (and (eq (org-element-type elem) 'paragraph) (not (org-element-property :name elem)) (= (org-element-property :end elem) (line-beginning-position 2)))))) ;; Do not steal affiliated keywords from container. (should (org-test-with-temp-text "#+ATTR_LATEX: test\n- item 1" (let ((elem (org-element-at-point))) (and (eq (org-element-type elem) 'paragraph) (not (org-element-property :attr_latex elem)) (/= (org-element-property :begin elem) 1))))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text "#+BEGIN_CENTER\nC\n#+END_CENTER\n " (= (org-element-property :end (org-element-at-point)) (point-max)))) (should (org-test-with-temp-text "#+BEGIN_CENTER\nC\n#+END_CENTER\n " (= (org-element-property :end (org-element-at-point)) (save-excursion (search-forward "END") (line-beginning-position)))))) ;;;; Plain List (ert-deftest test-org-element/plain-list-parser () "Test `plain-list' parser." (org-test-with-temp-text "- item" (should (org-element-map (org-element-parse-buffer) 'plain-list 'identity))) ;; Blank lines after a list or sub-list belongs to that list. (should (= 1 (org-test-with-temp-text "- A\n\n- B\n\nEnd list" (org-element-property :post-blank (org-element-at-point))))) (should (= 1 (org-test-with-temp-text "- A\n\n - B\n\n - C\n\n End sub-list" (org-element-property :post-blank (org-element-at-point))))) ;; Blank lines after the list only belong to outer plain list, ;; however. (should (equal '(t t) (org-test-with-temp-text " - outer - inner Outside list" (let ((endings (org-element-map (org-element-parse-buffer) 'plain-list (lambda (pl) (org-element-property :end pl))))) (list ;; Move to ending of outer list. (progn (goto-char (car endings)) (looking-at "Outside list")) ;; Move to ending of inner list. (progn (goto-char (nth 1 endings)) (looking-at "^$"))))))) ;; Correctly compute end of list if it doesn't end at a line ;; beginning. (should (org-test-with-temp-text "- list\n \n " (= (org-element-property :end (org-element-at-point)) (point-max)))) ;; Correctly compute list ending when list is before first headline. (dolist (org-element-use-cache '(t nil)) (org-test-with-temp-text "- list\n* Headline\n" (should (= (org-element-property :end (org-element-at-point)) 8))))) ;;;; Planning (ert-deftest test-org-element/planning-parser () "Test `planning' parser." ;; Test various keywords. (should (org-element-property :closed (org-test-with-temp-text "* H\nCLOSED: [2012-03-29 thu.]" (org-element-at-point)))) (should (org-element-property :deadline (org-test-with-temp-text "* H\nDEADLINE: <2012-03-29 thu.>" (org-element-at-point)))) (should (org-element-property :scheduled (org-test-with-temp-text "* H\nSCHEDULED: <2012-03-29 thu.>" (org-element-at-point)))) ;; Planning line only exists right after a headline. (should-not (eq 'planning (org-test-with-temp-text "DEADLINE: <2012-03-29 thu.>" (org-element-type (org-element-at-point))))) (should-not (eq 'planning (org-test-with-temp-text "* H\n# Comment\nDEADLINE: <2012-03-29 thu.>" (org-element-type (org-element-at-point))))) (should-not (eq 'planning (org-test-with-temp-text "* H\n\nDEADLINE: <2012-03-29 thu.>" (org-element-type (org-element-at-point)))))) ;;;; Property Drawer (ert-deftest test-org-element/property-drawer-parser () "Test `property-drawer' parser." ;; Standard test. (should (eq 'property-drawer (org-test-with-temp-text "* H\n:PROPERTIES:\n:prop: value\n:END:" (org-element-type (org-element-at-point))))) (should (eq 'property-drawer (org-test-with-temp-text "* H\nDEADLINE: <2014-03-04 tue.>\n:PROPERTIES:\n:prop: value\n:END:" (org-element-type (org-element-at-point))))) ;; Parse property drawer at the beginning of the document, possibly ;; after some initial comments. (should (eq 'property-drawer (org-test-with-temp-text ":PROPERTIES:\n:prop: value\n:END:" (org-element-type (org-element-at-point))))) (should (eq 'property-drawer (org-test-with-temp-text "# C\n# C\n:PROPERTIES:\n:prop: value\n:END:" (org-element-type (org-element-at-point))))) (should (eq 'property-drawer (org-test-with-temp-text "\n:PROPERTIES:\n:prop: value\n:END:" (org-element-type (org-element-at-point))))) ;; Allow properties without value and no property at all. (should (eq 'property-drawer (org-test-with-temp-text "* H\n:PROPERTIES:\n:prop:\n:END:" (org-element-type (org-element-at-point))))) (should (eq 'property-drawer (org-test-with-temp-text "* H\n:PROPERTIES:\n:END:" (org-element-type (org-element-at-point))))) ;; Ignore incomplete drawer, drawer at a wrong location or with ;; wrong contents. (should-not (eq 'property-drawer (org-test-with-temp-text "* H\n:PROPERTIES:\n:prop: value" (org-element-type (org-element-at-point))))) (should-not (eq 'property-drawer (org-test-with-temp-text "* H\nParagraph\n:PROPERTIES:\n:prop: value\n:END:" (org-element-type (org-element-at-point))))) (should-not (eq 'property-drawer (org-test-with-temp-text "* H\nParagraph\n:PROPERTIES:\nparagraph\n:END:" (org-element-type (org-element-at-point))))) (should-not (eq 'property-drawer (org-test-with-temp-text "* H\n\n:PROPERTIES:\n:prop: value\n:END:" (org-element-type (org-element-at-point))))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text "* H\n:PROPERTIES:\n:END:\n " (= (org-element-property :end (org-element-at-point)) (point-max))))) ;;;; Quote Block (ert-deftest test-org-element/quote-block-parser () "Test `quote-block' parser." ;; Regular test. (should (org-test-with-temp-text "#+BEGIN_QUOTE\nText\n#+END_QUOTE" (org-element-map (org-element-parse-buffer) 'quote-block 'identity))) ;; Ignore incomplete block. (should-not (org-test-with-temp-text "#+BEGIN_QUOTE" (org-element-map (org-element-parse-buffer) 'quote-block 'identity nil t))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text "#+BEGIN_QUOTE\nC\n#+END_QUOTE\n " (= (org-element-property :end (org-element-at-point)) (point-max))))) ;;;; Radio Target (ert-deftest test-org-element/radio-target-parser () "Test `radio-target' parser." ;; Standard test. (should (eq 'radio-target (org-test-with-temp-text "<<>>" (org-element-type (org-element-context))))) ;; Radio targets with objects. (should (eq 'radio-target (org-test-with-temp-text "<<>>" (org-element-type (org-element-context))))) ;; Radio targets starting with an object. (should (eq 'radio-target (org-test-with-temp-text "<<<\\alpha radio>>>" (org-element-type (org-element-context))))) ;; Radio targets cannot begin or end with white space. (should-not (eq 'radio-target (org-test-with-temp-text "<<< radio>>>" (org-element-type (org-element-context))))) (should-not (eq 'radio-target (org-test-with-temp-text "<<>>" (org-element-type (org-element-context)))))) ;;;; Section (ert-deftest test-org-element/section-parser () "Test `section' parser." ;; Standard test. (should (org-test-with-temp-text "* Headline\nText" (org-element-map (org-element-parse-buffer) 'section 'identity))) ;; There's a section before the first headline. (should (org-test-with-temp-text "Text" (org-element-map (org-element-parse-buffer) 'section 'identity))) ;; A section cannot be empty. (should-not (org-test-with-temp-text "* Headline 1\n* Headline 2" (org-element-map (org-element-parse-buffer) 'section 'identity))) ;; A section doesn't contain sub-trees. (should-not (org-test-with-temp-text "* Head\nText\n** Sub-Head" (org-element-map (org-element-map (org-element-parse-buffer) 'section 'identity nil t) 'headline 'identity)))) ;;;; Special Block (ert-deftest test-org-element/special-block-parser () "Test `special-block' parser." ;; Standard test. (should (equal "SPECIAL" (org-test-with-temp-text "#+BEGIN_SPECIAL\nText\n#+END_SPECIAL" (org-element-property :type (org-element-at-point))))) ;; Special blocks are case sensitive. (should (equal "CaSe" (org-test-with-temp-text "#+BEGIN_CaSe\nText\n#+END_CaSe" (org-element-property :type (org-element-at-point))))) ;; Special blocks can contain paragraphs. (should (eq 'paragraph (org-test-with-temp-text "#+BEGIN_SPECIAL\nText\n#+END_SPECIAL" (forward-line) (org-element-type (org-element-at-point))))) ;; Ignore incomplete block. (should-not (eq 'special-block (org-test-with-temp-text "#+BEGIN_SPECIAL" (org-element-type (org-element-at-point))))) ;; Allow special characters in type. (should (equal '(special-block "SPECIAL*") (org-test-with-temp-text "#+BEGIN_SPECIAL*\nContents\n#+END_SPECIAL*" (let ((element (org-element-at-point))) (list (org-element-type element) (org-element-property :type element)))))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text "#+BEGIN_SPECIAL\nC\n#+END_SPECIAL\n " (= (org-element-property :end (org-element-at-point)) (point-max)))) ;; When contents is empty, the parsed contents is nil. (should (org-test-with-temp-text "#+BEGIN_SPECIAL\n#+END_SPECIAL" (eq nil (org-element-contents (org-element-at-point))))) ;; Parse parameters if any, trimming blanks. (should (org-test-with-temp-text "#+BEGIN_SPECIAL* s p :w 3 \nContent.\n#+END_SPECIAL*" (equal "s p :w 3" (org-element-property :parameters (org-element-at-point))))) ;; When parameters is blank, `:parameters' is nil. (should (org-test-with-temp-text "#+BEGIN_SPECIAL* \t \nContent.\n#+END_SPECIAL*" (eq nil (org-element-property :parameters (org-element-at-point)))) )) ;;;; Src Block (ert-deftest test-org-element/src-block-parser () "Test `src-block' parser." ;; Regular tests. (should (org-test-with-temp-text "#+BEGIN_SRC org\nText\n#+END_SRC" (org-element-map (org-element-parse-buffer) 'src-block 'identity))) ;; Ignore incomplete block. (should-not (org-test-with-temp-text "#+BEGIN_SRC" (org-element-map (org-element-parse-buffer) 'src-block 'identity))) ;; Properly un-escape code. (should (equal "* Headline\n #+keyword\nText\n" (org-test-with-temp-text "#+BEGIN_SRC org\n,* Headline\n ,#+keyword\nText\n#+END_SRC" (org-element-property :value (org-element-at-point))))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\nC\n#+END_SRC\n " (= (org-element-property :end (org-element-at-point)) (point-max))))) ;;;; Statistics Cookie (ert-deftest test-org-element/statistics-cookie () "Test `statistics-cookie' parser." ;; With numbers. (should (org-test-with-temp-text "[1/2]" (org-element-map (org-element-parse-buffer) 'statistics-cookie 'identity))) ;; With percents. (should (org-test-with-temp-text "[33%]" (org-element-map (org-element-parse-buffer) 'statistics-cookie 'identity)))) ;;;; Strike Through (ert-deftest test-org-element/strike-through-parser () "Test `strike-through' parser." ;; Regular test. (should (org-test-with-temp-text "+strike-through+" (org-element-map (org-element-parse-buffer) 'strike-through #'identity))) ;; Multi-line markup. (should (equal (org-element-contents (org-test-with-temp-text "+first line\nsecond line+" (org-element-map (org-element-parse-buffer) 'strike-through #'identity nil t))) '("first line\nsecond line")))) ;;;; Subscript (ert-deftest test-org-element/subscript-parser () "Test `subscript' parser." ;; Without braces. (should (org-test-with-temp-text "a_b" (org-element-map (org-element-parse-buffer) 'subscript 'identity))) ;; With braces. (should (org-test-with-temp-text "a_{b}" (org-element-map (org-element-parse-buffer) 'subscript 'identity))) ;; Multiple subscripts in a paragraph. (should (= 2 (org-test-with-temp-text "a_b and c_d" (length (org-element-map (org-element-parse-buffer) 'subscript 'identity)))))) ;;;; Superscript (ert-deftest test-org-element/superscript-parser () "Test `superscript' parser." ;; Without braces. (should (org-test-with-temp-text "a^b" (org-element-map (org-element-parse-buffer) 'superscript 'identity))) ;; With braces. (should (org-test-with-temp-text "a^{b}" (org-element-map (org-element-parse-buffer) 'superscript 'identity))) ;; Multiple superscript in a paragraph. (should (= 2 (org-test-with-temp-text "a^b and c^d" (length (org-element-map (org-element-parse-buffer) 'superscript 'identity)))))) ;;;; Table (ert-deftest test-org-element/table-parser () "Test `table' parser." (should (org-test-with-temp-text "| a |" (org-element-map (org-element-parse-buffer) 'table 'identity))) ;; TBLFM keyword is case insensitive. (should (org-test-with-temp-text "| a |\n#+tblfm: test" (org-element-property :tblfm (org-element-map (org-element-parse-buffer) 'table 'identity nil t)))) ;; Handle multiple TBLFM lines. (should (= 2 (org-test-with-temp-text "| a |\n#+TBLFM: test1\n#+TBLFM: test2" (length (org-element-property :tblfm (org-element-map (org-element-parse-buffer) 'table 'identity nil t)))))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text "| a |\n " (= (org-element-property :end (org-element-at-point)) (point-max))))) ;;;; Table Cell (ert-deftest test-org-element/table-cell-parser () "Test `table-cell' parser." ;; Regular table cell. (should (org-test-with-temp-text "| a |" (org-element-map (org-element-parse-buffer) 'table-cell 'identity))) ;; Last vertical bar may be omitted. (should (org-test-with-temp-text "| a " (org-element-map (org-element-parse-buffer) 'table-cell 'identity)))) ;;;; Table Row (ert-deftest test-org-element/table-row-parser () "Test `table-row' parser." (should (equal '(standard rule) (org-test-with-temp-text "| a |\n|---|" (org-element-map (org-element-parse-buffer) 'table-row (lambda (row) (org-element-property :type row))))))) ;;;; Target (ert-deftest test-org-element/target-parser () "Test `target' parser." (should (org-test-with-temp-text "<>" (org-element-map (org-element-parse-buffer) 'target 'identity)))) ;;;; Timestamp (ert-deftest test-org-element/timestamp-parser () "Test `timestamp' parser." ;; Active timestamp. (should (org-test-with-temp-text "<2012-03-29 16:40>" (eq (org-element-property :type (org-element-context)) 'active))) (should-not (org-test-with-temp-text "<2012-03-29 Thu>" (let ((timestamp (org-element-context))) (or (org-element-property :hour-start timestamp) (org-element-property :minute-start timestamp))))) (should (equal '(2012 3 29 16 40) (org-test-with-temp-text "<2012-03-29 Thu 16:40>" (let ((object (org-element-context))) (list (org-element-property :year-start object) (org-element-property :month-start object) (org-element-property :day-start object) (org-element-property :hour-start object) (org-element-property :minute-start object)))))) ;; Inactive timestamp. (should (org-test-with-temp-text "[2012-03-29 Thu 16:40]" (eq (org-element-property :type (org-element-context)) 'inactive))) ;; Time range. (should (equal '(2012 3 29 16 40 7 30) (org-test-with-temp-text "<2012-03-29 Thu 7:30-16:40>" (let ((object (org-element-context))) (list (org-element-property :year-end object) (org-element-property :month-end object) (org-element-property :day-end object) (org-element-property :hour-end object) (org-element-property :minute-end object) (org-element-property :hour-start object) (org-element-property :minute-start object)))))) (should (eq 'active-range (org-test-with-temp-text "<2012-03-29 Thu 7:30-16:40>" (org-element-property :type (org-element-context))))) ;; Date range. (should (org-test-with-temp-text "[2012-03-29 Thu 16:40]--[2012-03-29 Thu 16:41]" (eq (org-element-property :type (org-element-context)) 'inactive-range))) (should-not (org-test-with-temp-text "[2011-07-14 Thu]--[2012-03-29 Thu]" (let ((timestamp (org-element-context))) (or (org-element-property :hour-end timestamp) (org-element-property :minute-end timestamp))))) ;; With repeater, repeater deadline, warning delay and combinations. (should (eq 'catch-up (org-test-with-temp-text "<2012-03-29 Thu ++1y>" (org-element-property :repeater-type (org-element-context))))) (should (equal '(catch-up 2 year) (org-test-with-temp-text "<2012-03-29 Thu ++1y/2y>" (let ((ts (org-element-context))) (list (org-element-property :repeater-type ts) (org-element-property :repeater-deadline-value ts) (org-element-property :repeater-deadline-unit ts)))))) (should (eq 'first (org-test-with-temp-text "<2012-03-29 Thu --1y>" (org-element-property :warning-type (org-element-context))))) (should (equal '(cumulate all) (org-test-with-temp-text "<2012-03-29 Thu +1y -1y>" (let ((ts (org-element-context))) (list (org-element-property :repeater-type ts) (org-element-property :warning-type ts)))))) (should (equal '(cumulate all 2 year) (org-test-with-temp-text "<2012-03-29 Thu +1y/2y -1y>" (let ((ts (org-element-context))) (list (org-element-property :repeater-type ts) (org-element-property :warning-type ts) (org-element-property :repeater-deadline-value ts) (org-element-property :repeater-deadline-unit ts)))))) ;; :range-type property (should (eq (org-test-with-temp-text "<2023-07-02 Sun>" (org-element-property :range-type (org-element-timestamp-parser))) nil)) (should (eq (org-test-with-temp-text "<2023-07-02 Sun 12:00>" (org-element-property :range-type (org-element-timestamp-parser))) nil)) (should (eq (org-test-with-temp-text "<2023-07-02 Sun 12:00-13:00>" (org-element-property :range-type (org-element-timestamp-parser))) 'timerange)) (should (eq (org-test-with-temp-text "<2023-07-02 Sun 12:00-12:00>" (org-element-property :range-type (org-element-timestamp-parser))) 'timerange)) (should (eq (org-test-with-temp-text "<2023-07-02 Sun>--<2023-07-02 Sun>" (org-element-property :range-type (org-element-timestamp-parser))) 'daterange)) (should (eq (org-test-with-temp-text "<2023-07-02 Sun>--<2023-07-03 Mon>" (org-element-property :range-type (org-element-timestamp-parser))) 'daterange)) (should (eq (org-test-with-temp-text "<2023-07-02 Sun 12:00>--<2023-07-02 Sun 12:00>" (org-element-property :range-type (org-element-timestamp-parser))) 'daterange)) (should (eq (org-test-with-temp-text "<2023-07-02 Sun 12:00>--<2023-07-03 Mon 13:00>" (org-element-property :range-type (org-element-timestamp-parser))) 'daterange)) (should (eq (org-test-with-temp-text "<2023-07-02 Sun 12:00>--<2023-07-02 Sun>" (org-element-property :range-type (org-element-timestamp-parser))) 'daterange)) (should (eq (org-test-with-temp-text "<2023-07-02 Sun 12:00>--<2023-07-03 Mon>" (org-element-property :range-type (org-element-timestamp-parser))) 'daterange)) (should (eq (org-test-with-temp-text "<2023-07-02 Sun 12:00>--<2023-07-02 Sun 13:00>" (org-element-property :range-type (org-element-timestamp-parser))) 'daterange)) (should (eq (org-test-with-temp-text "<2023-07-02 Sun 12:00 +5d>--<2023-07-02 Sun 13:00>" (org-element-property :range-type (org-element-timestamp-parser))) 'daterange))) ;;;; Underline (ert-deftest test-org-element/underline-parser () "Test `underline' parser." ;; Regular test. (should (org-test-with-temp-text "_underline_" (org-element-map (org-element-parse-buffer) 'underline #'identity))) ;; Multi-line markup. (should (equal (org-element-contents (org-test-with-temp-text "_first line\nsecond line_" (org-element-map (org-element-parse-buffer) 'underline #'identity nil t))) '("first line\nsecond line"))) ;; Nested underlines. (should (= 2 (org-test-with-temp-text "__test__" (length (org-element-map (org-element-parse-buffer) 'underline 'identity))))) ;; Starting after non-blank (should (eq 'underline (org-test-with-temp-text "(_underline_)" (org-element-type (org-element-context))))) (should-not (eq 'underline (org-test-with-temp-text "x_underline_)" (org-element-type (org-element-context)))))) ;;;; Verbatim (ert-deftest test-org-element/verbatim-parser () "Test `verbatim' parser." ;; Regular test. (should (org-test-with-temp-text "=verbatim=" (org-element-map (org-element-parse-buffer) 'verbatim #'identity))) ;; Multi-line markup. (should (equal (org-element-property :value (org-test-with-temp-text "=first line\nsecond line=" (org-element-map (org-element-parse-buffer) 'verbatim #'identity nil t))) "first line\nsecond line"))) ;;;; Verse Block (ert-deftest test-org-element/verse-block-parser () "Test `verse-block' parser." ;; Standard test. (should (org-test-with-temp-text "#+BEGIN_VERSE\nVerse block\n#+END_VERSE" (org-element-map (org-element-parse-buffer) 'verse-block 'identity))) ;; Ignore case. (should (org-test-with-temp-text "#+begin_verse\nVerse block\n#+end_verse" (org-element-map (org-element-parse-buffer) 'verse-block 'identity))) ;; Parse objects in verse blocks. (should (org-test-with-temp-text "#+BEGIN_VERSE\nVerse \\alpha\n#+END_VERSE" (org-element-map (org-element-parse-buffer) 'entity 'identity))) ;; Ignore incomplete verse block. (should-not (org-test-with-temp-text "#+BEGIN_VERSE" (org-element-map (org-element-parse-buffer) 'verse-block 'identity nil t))) ;; Handle non-empty blank line at the end of buffer. (should (org-test-with-temp-text "#+BEGIN_VERSE\nC\n#+END_VERSE\n " (= (org-element-property :end (org-element-at-point)) (point-max))))) ;;; Org data. (ert-deftest test-org-element/org-data-parser () "Test `org-data' parser." ;; Standard test. (org-test-with-temp-text "This is test." (let ((data (org-element-lineage (org-element-at-point) 'org-data))) (should (equal 1 (org-element-begin data))) (should (equal (point-max) (org-element-end data))))) ;; Parse top-level property drawer. (should (equal "bar" (org-test-with-temp-text ":PROPERTIES: :FOO: bar :END:" (org-element-property-inherited :FOO (org-element-at-point))))) ;; With leading comment line. (org-test-with-temp-text "# comment :PROPERTIES: :FOO: bar :END:" (should (equal "bar" (org-element-property-inherited :FOO (org-element-at-point))))) ;; Blank line on top. (should (equal "bar" (org-test-with-temp-text " :PROPERTIES: :FOO: bar :END:" (org-element-property-inherited :FOO (org-element-at-point)))))) ;;; Test Interpreters. (ert-deftest test-org-element/interpret-data () "Test `org-element-interpret-data' specifications." ;; Interpret simple affiliated keywords. (should (equal (org-element-interpret-data '(org-data nil (paragraph (:name "para") "Paragraph"))) "#+name: para\nParagraph\n")) ;; Interpret multiple affiliated keywords. (should (equal (org-element-interpret-data '(org-data nil (paragraph (:attr_ascii ("line1" "line2")) "Paragraph"))) "#+attr_ascii: line1\n#+attr_ascii: line2\nParagraph\n")) ;; Interpret parsed affiliated keywords. (should (equal (org-element-interpret-data '(org-data nil (paragraph (:caption (("caption"))) "Paragraph"))) "#+caption: caption\nParagraph\n")) ;; Interpret dual affiliated keywords. (should (equal (org-element-interpret-data '(org-data nil (paragraph (:caption ((("long") "short"))) "Paragraph"))) "#+caption[short]: long\nParagraph\n")) ;; Interpret multiple parsed dual keywords. (should (equal (org-element-interpret-data '(org-data nil (paragraph (:caption ((("l1") "s1") (("l2") "s2"))) "Paragraph"))) "#+caption[s1]: l1\n#+caption[s2]: l2\nParagraph\n")) ;; Pseudo objects and elements are transparent. (should (equal "A B" (org-trim (org-element-interpret-data '(paragraph nil (pseudo-object (:post-blank 1) "A") "B"))))) (should (equal "A\n\nB\n" (org-element-interpret-data '(center nil (pseudo-element (:post-blank 1) (paragraph nil "A")) (paragraph nil "B"))))) ;; Obey post-blank property in strings. (should (equal "A " (org-element-interpret-data (org-element-put-property "A" :post-blank 1))))) (ert-deftest test-org-element/center-block-interpreter () "Test center block interpreter." (should (equal (org-test-parse-and-interpret "#+BEGIN_CENTER\nTest\n#+END_CENTER") "#+begin_center\nTest\n#+end_center\n"))) (ert-deftest test-org-element/drawer-interpreter () "Test drawer interpreter." (should (equal (org-test-parse-and-interpret ":TEST:\nTest\n:END:") ":TEST:\nTest\n:END:\n"))) (ert-deftest test-org-element/dynamic-block-interpreter () "Test dynamic block interpreter." (should (equal (org-test-parse-and-interpret "#+BEGIN: myblock :parameter value1\nTest\n#+END:") "#+begin: myblock :parameter value1\nTest\n#+end:\n"))) (ert-deftest test-org-element/footnote-definition-interpreter () "Test footnote definition interpreter." (should (equal (org-test-parse-and-interpret "[fn:1] Test") "[fn:1] Test\n")) ;; Handle `:pre-blank' in definitions. (should (equal (org-test-parse-and-interpret "[fn:1]\nparagraph") "[fn:1]\nparagraph\n")) (should (equal (org-test-parse-and-interpret "[fn:1]\n\nparagraph") "[fn:1]\n\nparagraph\n"))) (ert-deftest test-org-element/headline-interpreter () "Test headline and section interpreters." ;; 1. Standard test. (should (equal (org-test-parse-and-interpret "* Headline") "* Headline\n")) ;; 2. With TODO keywords. (should (equal (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-parse-and-interpret "* TODO Headline")) "* TODO Headline\n")) ;; 3. With tags... ;; ;; 3.1. ... and a positive `org-tags-column' value. (should (equal (let ((org-tags-column 20)) (org-test-parse-and-interpret "* Headline :tag:")) "* Headline :tag:\n")) ;; 3.2. ... and a negative `org-tags-column' value. (should (equal (let ((org-tags-column -20)) (org-test-parse-and-interpret "* Headline :tag:")) "* Headline :tag:\n")) ;; 3.3. ... and a null `org-tags-column' value. (should (equal (let ((org-tags-column 0)) (org-test-parse-and-interpret "* Headline :tag:")) "* Headline :tag:\n")) ;; 4. With priority cookie. (should (equal (org-test-parse-and-interpret "* [#B] Headline") "* [#B] Headline\n")) ;; 5. With comment keyword. (should (equal (let ((org-comment-string "COMMENT")) (org-test-parse-and-interpret "* COMMENT Headline")) "* COMMENT Headline\n")) ;; 6. Keep same number of blank lines before body. (should (equal (org-test-parse-and-interpret "* Headline\n\n\nText after two blank lines.") "* Headline\n\n\nText after two blank lines.\n")) ;; 8. Preserve `org-odd-levels-only' state. (should (equal "* H\n*** H2\n" (let ((org-odd-levels-only t)) (org-test-parse-and-interpret "* H\n*** H2"))))) (ert-deftest test-org-element/inlinetask-interpreter () "Test inlinetask interpretation." (when (featurep 'org-inlinetask) (let ((org-inlinetask-min-level 15)) ;; 1. Regular inlinetask. (should (equal (org-test-parse-and-interpret "*************** Task\nTest\n*************** END") "*************** Task\nTest\n*************** end\n")) ;; 2. Degenerate inlinetask. (should (equal (org-test-parse-and-interpret "*************** Task") "*************** Task\n")) ;; 3. Prefer degenerate form when there are no contents. (should (equal (org-test-parse-and-interpret "*************** Task\n*************** end") "*************** Task\n")) ;; 4. With TODO keywords. (should (equal (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-parse-and-interpret "*************** TODO Task")) "*************** TODO Task\n")) ;; 5. With tags... ;; ;; 5.1. ... and a positive `org-tags-column' value. (should (equal (let ((org-tags-column 30)) (org-test-parse-and-interpret "*************** Task :tag:")) "*************** Task :tag:\n")) ;; 5.2. ... and a negative `org-tags-column' value. (should (equal (let ((org-tags-column -30)) (org-test-parse-and-interpret "*************** Task :tag:")) "*************** Task :tag:\n")) ;; 5.3. ... and a null `org-tags-column' value. (should (equal (let ((org-tags-column 0)) (org-test-parse-and-interpret "*************** Task :tag:")) "*************** Task :tag:\n")) ;; 6. With priority cookie. (should (equal (org-test-parse-and-interpret "*************** [#B] Task") "*************** [#B] Task\n"))))) (ert-deftest test-org-element/plain-list-interpreter () "Test plain-list and item interpreters." (let ((org-list-two-spaces-after-bullet-regexp nil)) ;; Unordered list. (should (equal (org-test-parse-and-interpret "- item 1") "- item 1\n")) ;; Description list. (should (equal (org-test-parse-and-interpret "- tag :: desc") "- tag :: desc\n")) ;; Ordered list. (should (equal (let ((org-plain-list-ordered-item-terminator t)) (org-test-parse-and-interpret "1. Item")) "1. Item\n")) (should (equal (let ((org-plain-list-ordered-item-terminator ?\))) (org-test-parse-and-interpret "1) Item")) "1) Item\n")) ;; Ordered list with counter. (should (equal (let ((org-plain-list-ordered-item-terminator t)) (org-test-parse-and-interpret "1. [@5] Item")) "5. [@5] Item\n")) ;; List with check-boxes. (should (equal (org-test-parse-and-interpret "- [-] Item 1\n - [X] Item 2\n - [ ] Item 3") "- [-] Item 1\n - [X] Item 2\n - [ ] Item 3\n")) ;; Item not starting with a paragraph. (should (equal (org-test-parse-and-interpret "-\n | a | b |") "- \n | a | b |\n")) ;; Handle `:pre-blank' in items. (should (equal (org-test-parse-and-interpret "-\n paragraph") "- \n paragraph\n")) (should (equal (org-test-parse-and-interpret "-\n\n paragraph") "- \n\n paragraph\n")) ;; Special case: correctly handle "*" bullets. (should (org-test-parse-and-interpret " * item")) ;; Special case: correctly handle empty items. (should (org-test-parse-and-interpret "-")))) (ert-deftest test-org-element/quote-block-interpreter () "Test quote block interpreter." (should (equal (org-test-parse-and-interpret "#+BEGIN_QUOTE\nTest\n#+END_QUOTE") "#+begin_quote\nTest\n#+end_quote\n"))) (ert-deftest test-org-element/special-block-interpreter () "Test special block interpreter." ;; No parameters (should (equal (org-test-parse-and-interpret "#+BEGIN_SPECIAL\nTest\n#+END_SPECIAL") "#+begin_SPECIAL\nTest\n#+end_SPECIAL\n")) ;; No content (should (equal (org-test-parse-and-interpret "#+BEGIN_SPECIAL\n#+END_SPECIAL") "#+begin_SPECIAL\n#+end_SPECIAL\n")) ;; Some parameters (should (equal (org-test-parse-and-interpret "#+BEGIN_special some parameters until EOL\nA very special content\n#+END_special") "#+begin_special some parameters until EOL\nA very special content\n#+end_special\n")) ;; No parameters (blanks only) (should (equal (org-test-parse-and-interpret "#+BEGIN_special \t \nA very special content\n#+END_special") "#+begin_special\nA very special content\n#+end_special\n")) ;; Some parameters with leading and trailing blanks, no content, and ;; a /special/ name. (should (equal (org-test-parse-and-interpret "#+BEGIN_speial :a :b \t :c \t \n#+END_speial") "#+begin_speial :a :b \t :c\n#+end_speial\n"))) (ert-deftest test-org-element/babel-call-interpreter () "Test Babel call interpreter." ;; Without argument. (should (equal (org-test-parse-and-interpret "#+CALL: test()") "#+call: test()\n")) ;; With argument. (should (equal (org-test-parse-and-interpret "#+CALL: test(x=2)") "#+call: test(x=2)\n")) ;; With header arguments. (should (equal (org-test-parse-and-interpret "#+CALL: test[:results output]() :results html") "#+call: test[:results output]() :results html\n"))) (ert-deftest test-org-element/clock-interpreter () "Test clock interpreter." ;; Running clock. (should (string-match "CLOCK: \\[2012-01-01 .* 00:01\\]" (org-test-parse-and-interpret "CLOCK: [2012-01-01 sun. 00:01]"))) ;; Closed clock. (should (string-match "CLOCK: \\[2012-01-01 .* 00:01\\]--\\[2012-01-01 .* 00:02\\] => 0:01" (org-test-parse-and-interpret " CLOCK: [2012-01-01 sun. 00:01]--[2012-01-01 sun. 00:02] => 0:01"))) ;; Closed clock without timestamp. (should (string-match "CLOCK: => 0:01" (org-test-parse-and-interpret "CLOCK: => 0:01")))) (ert-deftest test-org-element/comment-interpreter () "Test comment interpreter." ;; Regular comment. (should (equal (org-test-parse-and-interpret "# Comment") "# Comment\n")) ;; Inline comment. (should (equal (org-test-parse-and-interpret " # Comment") "# Comment\n")) ;; Preserve indentation. (should (equal (org-test-parse-and-interpret " # No blank\n# One blank") "# No blank\n# One blank\n"))) (ert-deftest test-org-element/comment-block-interpreter () "Test comment block interpreter." (should (equal (org-test-parse-and-interpret "#+BEGIN_COMMENT\nTest\n#+END_COMMENT") "#+begin_comment\nTest\n#+end_comment\n")) ;; Accept missing final newline in value. (should (equal "#+begin_comment\nTest\n#+end_comment\n" (org-element-interpret-data '(comment-block (:value "Test")))))) (ert-deftest test-org-element/diary-sexp () "Test diary-sexp interpreter." (should (equal (org-test-parse-and-interpret "%%(org-anniversary 1956 5 14)(2) Arthur Dent is %d years old") "%%(org-anniversary 1956 5 14)(2) Arthur Dent is %d years old\n"))) (ert-deftest test-org-element/example-block-interpreter () "Test example block interpreter." ;; Without switches. (should (equal "#+begin_example\nTest\n#+end_example\n" (let ((org-src-preserve-indentation t)) (org-test-parse-and-interpret "#+BEGIN_EXAMPLE\nTest\n#+END_EXAMPLE")))) ;; With switches. (should (equal "#+begin_example -n -k\n(+ 1 1)\n#+end_example\n" (let ((org-src-preserve-indentation t)) (org-test-parse-and-interpret "#+BEGIN_EXAMPLE -n -k\n(+ 1 1)\n#+END_EXAMPLE")))) ;; Preserve code escaping. (should (equal (let ((org-src-preserve-indentation t)) (org-test-parse-and-interpret "#+BEGIN_EXAMPLE\n,* Headline\n,#+KEYWORD: value\nText\n#+END_EXAMPLE")) "#+begin_example\n,* Headline\n,#+KEYWORD: value\nText\n#+end_example\n")) ;; Accept missing final newline in value. (should (equal "#+begin_example\nTest\n#+end_example\n" (let ((org-src-preserve-indentation t)) (org-element-interpret-data '(example-block (:value "Test")))))) ;; Handle indentation. (should (equal "#+begin_example\n Test\n#+end_example\n" (let ((org-src-preserve-indentation nil) (org-edit-src-content-indentation 2)) (org-test-parse-and-interpret "#+BEGIN_EXAMPLE\nTest\n#+END_EXAMPLE")))) (should (equal "#+begin_example\n Test\n#+end_example\n" (let ((org-src-preserve-indentation t) (org-edit-src-content-indentation 2)) (org-test-parse-and-interpret "#+BEGIN_EXAMPLE\n Test\n#+END_EXAMPLE"))))) (ert-deftest test-org-element/export-block-interpreter () "Test export block interpreter." (should (equal (org-test-parse-and-interpret "#+begin_export HTML\nTest\n#+end_export") "#+begin_export HTML\nTest\n#+end_export\n"))) (ert-deftest test-org-element/fixed-width-interpreter () "Test fixed width interpreter." ;; Standard test. (should (equal (org-test-parse-and-interpret ": Test") ": Test\n")) ;; Preserve indentation. (should (equal (org-test-parse-and-interpret ": 2 blanks\n: 1 blank") ": 2 blanks\n: 1 blank\n")) ;; Handle empty string. (should (equal (org-element-fixed-width-interpreter '(fixed-width (:value "")) nil) ":\n")) ;; Handle nil value. (should-not (org-element-fixed-width-interpreter '(fixed-width (:value nil)) nil))) (ert-deftest test-org-element/horizontal-rule-interpreter () "Test horizontal rule interpreter." (should (equal (org-test-parse-and-interpret "-------") "-----\n"))) (ert-deftest test-org-element/keyword-interpreter () "Test keyword interpreter." (should (equal (org-test-parse-and-interpret "#+KEYWORD: value") "#+keyword: value\n"))) (ert-deftest test-org-element/latex-environment-interpreter () "Test latex environment interpreter." (should (equal (org-test-parse-and-interpret "\\begin{equation}\n1+1=2\n\\end{equation}") "\\begin{equation}\n1+1=2\n\\end{equation}\n")) (should (equal (org-test-parse-and-interpret "\\begin{theorem}[me]\n1+1=2\n\\end{theorem}") "\\begin{theorem}[me]\n1+1=2\n\\end{theorem}\n"))) (ert-deftest test-org-element/planning-interpreter () "Test planning interpreter." (should (string-match "\\* Headline DEADLINE: <2012-03-29 .*?> SCHEDULED: <2012-03-29 .*?> CLOSED: \\[2012-03-29 .*?\\]" (org-test-parse-and-interpret "* Headline DEADLINE: <2012-03-29 thu.> SCHEDULED: <2012-03-29 thu.> CLOSED: [2012-03-29 thu.]")))) (ert-deftest test-org-element/property-drawer-interpreter () "Test property drawer interpreter." (should (equal (let ((org-property-format "%-10s %s")) (org-test-parse-and-interpret "* H\n:PROPERTIES:\n:prop: value\n:END:")) "* H\n:PROPERTIES:\n:prop: value\n:END:\n"))) (ert-deftest test-org-element/src-block-interpreter () "Test src block interpreter." ;; With arguments. (should (equal (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation nil)) (org-test-parse-and-interpret "#+BEGIN_SRC emacs-lisp :results silent\n(+ 1 1)\n#+END_SRC")) "#+begin_src emacs-lisp :results silent\n (+ 1 1)\n#+end_src\n")) ;; With switches. (should (equal (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation nil)) (org-test-parse-and-interpret "#+BEGIN_SRC emacs-lisp -n -k\n(+ 1 1)\n#+END_SRC")) "#+begin_src emacs-lisp -n -k\n (+ 1 1)\n#+end_src\n")) ;; Preserve code escaping. (should (equal (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation nil)) (org-test-parse-and-interpret "#+BEGIN_SRC org\n,* Headline\n,#+KEYWORD: value\nText\n#+END_SRC")) "#+begin_src org\n ,* Headline\n ,#+KEYWORD: value\n Text\n#+end_src\n")) ;; Do not apply `org-edit-src-content-indentation' when preserving ;; indentation. (should (equal (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation t)) (org-test-parse-and-interpret "#+BEGIN_SRC emacs-lisp\n(+ 1 1)\n#+END_SRC")) "#+begin_src emacs-lisp\n(+ 1 1)\n#+end_src\n")) (should (equal (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation nil)) (org-test-parse-and-interpret "#+BEGIN_SRC emacs-lisp -i\n(+ 1 1)\n#+END_SRC")) "#+begin_src emacs-lisp -i\n(+ 1 1)\n#+end_src\n")) ;; Accept missing final newline in value. (should (equal "#+begin_src emacs-lisp\n Test\n#+end_src\n" (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation nil)) (org-element-interpret-data '(src-block (:language "emacs-lisp" :value "Test"))))))) (ert-deftest test-org-element/table-interpreter () "Test table, table-row and table-cell interpreters." ;; 1. Simple table. (should (equal (org-test-parse-and-interpret "| a | b |\n| c | d |") "| a | b |\n| c | d |\n")) ;; 2. With horizontal rules. (should (equal (org-test-parse-and-interpret "| a | b |\n|---+---|\n| c | d |") "| a | b |\n|---+---|\n| c | d |\n")) ;; 3. With meta-data. (should (equal (org-test-parse-and-interpret "| / | < | > |\n| * | 1 | 2 |") "| / | < | > |\n| * | 1 | 2 |\n")) ;; 4. With a formula. (should (equal (org-test-parse-and-interpret "| 2 |\n| 4 |\n| 3 |\n#+TBLFM: @3=vmean(@1..@2)") "| 2 |\n| 4 |\n| 3 |\n#+TBLFM: @3=vmean(@1..@2)\n")) ;; 5. With multiple formulas. (should (equal (org-test-parse-and-interpret "| 2 |\n| 4 |\n| 3 |\n#+TBLFM: test1\n#+TBLFM: test2") "| 2 |\n| 4 |\n| 3 |\n#+TBLFM: test1\n#+TBLFM: test2\n"))) (ert-deftest test-org-element/timestamp-interpreter () "Test timestamp interpreter." ;; Active. (should (string-match "<2012-03-29 .* 16:40>" (org-test-parse-and-interpret "<2012-03-29 thu. 16:40>"))) (should (string-match "<2012-03-29 .* 16:40>" (org-element-timestamp-interpreter '(timestamp (:type active :year-start 2012 :month-start 3 :day-start 29 :hour-start 16 :minute-start 40)) nil))) (should (string-match "<2012-03-29 .* 16:40>" (org-element-timestamp-interpreter '(timestamp (:type active :year-start 2012 :month-start 3 :day-start 29 :hour-start 16 :minute-start 40 :year-end 2012 :month-end 3 :day-end 29 :hour-end 16 :minute-end 40)) nil))) ;; Inactive. (should (string-match "\\[2012-03-29 .* 16:40\\]" (org-test-parse-and-interpret "[2012-03-29 thu. 16:40]"))) (should (string-match "\\[2012-03-29 .* 16:40\\]" (org-element-timestamp-interpreter '(timestamp (:type inactive :year-start 2012 :month-start 3 :day-start 29 :hour-start 16 :minute-start 40)) nil))) ;; Active daterange. (should (string-match "<2012-03-29 .* 16:40>--<2012-03-29 .* 16:41>" (org-test-parse-and-interpret "<2012-03-29 thu. 16:40>--<2012-03-29 thu. 16:41>"))) ;;; No end time, dates are not equal (should ;; Expected result: "<2012-03-29 Thu 16:40>--<2012-03-30 Fri>" (string= (format "<%s>--<%s>" (format-time-string (cdr org-time-stamp-formats) (org-encode-time 0 40 16 29 03 2012)) (format-time-string (car org-time-stamp-formats) (org-encode-time 0 0 0 30 03 2012))) (org-element-timestamp-interpreter '(timestamp (:type active-range :year-start 2012 :month-start 3 :day-start 29 :hour-start 16 :minute-start 40 :year-end 2012 :month-end 3 :day-end 30)) nil))) ;;; No start time, dates are not equal (should ;; Expected result: "<2012-03-29 Thu>--<2012-03-30 Fri 16:40>" (string= (format "<%s>--<%s>" (format-time-string (car org-time-stamp-formats) (org-encode-time 0 0 0 29 03 2012)) (format-time-string (cdr org-time-stamp-formats) (org-encode-time 0 40 16 30 03 2012))) (org-element-timestamp-interpreter '(timestamp (:type active-range :year-start 2012 :month-start 3 :day-start 29 :hour-end 16 :minute-end 40 :year-end 2012 :month-end 3 :day-end 30)) nil))) (should (string-match "<2012-03-29 .* 16:40>--<2012-03-29 .* 16:40>" (org-element-timestamp-interpreter '(timestamp (:type active-range :year-start 2012 :month-start 3 :day-start 29 :hour-start 16 :minute-start 40 :year-end 2012 :month-end 3 :day-end 29 :hour-end 16 :minute-end 40)) nil))) (should (string-match "<2012-03-29 .* 16:40>--<2012-03-29 .* 16:41>" (org-element-timestamp-interpreter '(timestamp (:type active-range :year-start 2012 :month-start 3 :day-start 29 :hour-start 16 :minute-start 40 :year-end 2012 :month-end 3 :day-end 29 :hour-end 16 :minute-end 41)) nil))) ;; Inactive daterange. (should (string-match "\\[2012-03-29 .* 16:40\\]--\\[2012-03-29 .* 16:41\\]" (org-test-parse-and-interpret "[2012-03-29 thu. 16:40]--[2012-03-29 thu. 16:41]"))) (should (string-match "\\[2012-03-29 .* 16:40\\]--\\[2012-03-29 .* 16:41\\]" (org-element-timestamp-interpreter '(timestamp (:type inactive-range :year-start 2012 :month-start 3 :day-start 29 :hour-start 16 :minute-start 40 :year-end 2012 :month-end 3 :day-end 29 :hour-end 16 :minute-end 41)) nil))) ;; Active timerange (should (string-match "<2012-03-29 .* 16:40-16:41>" (org-test-parse-and-interpret "<2012-03-29 thu. 16:40-16:41>"))) ;; Diary. (should (equal (org-test-parse-and-interpret "<%%(diary-float t 4 2)>") "<%%(diary-float t 4 2)>\n")) ;; Diary with time. (should (equal (org-test-parse-and-interpret "<%%(diary-float t 4 2) 12:00>") "<%%(diary-float t 4 2) 12:00>\n")) (should (equal (org-test-parse-and-interpret "<%%(diary-cyclic 1 1 1 2020) 12:00-14:00>") "<%%(diary-cyclic 1 1 1 2020) 12:00-14:00>\n")) (org-test-with-temp-text "<%%(diary-float t 4 2) 12:00>" (let ((ts (org-element-context))) (should (org-element-type-p ts 'timestamp)) (should (eq 'diary (org-element-property :type ts))) (should (eq nil (org-element-property :range-type ts))) (should (equal 12 (org-element-property :hour-start ts))) (should (equal 0 (org-element-property :minute-start ts))) (should-not (org-element-property :hour-end ts)) (should-not (org-element-property :minute-end ts)))) (org-test-with-temp-text "<%%(diary-float t 4 2) 12:00-14:01>" (let ((ts (org-element-context))) (should (org-element-type-p ts 'timestamp)) (should (eq 'diary (org-element-property :type ts))) (should (eq 'timerange (org-element-property :range-type ts))) (should (equal 12 (org-element-property :hour-start ts))) (should (equal 0 (org-element-property :minute-start ts))) (should (equal 14 (org-element-property :hour-end ts))) (should (equal 1 (org-element-property :minute-end ts))))) ;; Timestamp with repeater interval, repeater deadline, with delay, with combinations. (should (string-match "<2012-03-29 .* \\+1y>" (org-test-parse-and-interpret "<2012-03-29 thu. +1y>"))) (should (string-match "<2012-03-29 .* \\+1y>" (org-element-timestamp-interpreter '(timestamp (:type active :year-start 2012 :month-start 3 :day-start 29 :repeater-type cumulate :repeater-value 1 :repeater-unit year)) nil))) (should (string-match "<2012-03-29 .* \\+1y/2y>" (org-element-timestamp-interpreter '(timestamp (:type active :year-start 2012 :month-start 3 :day-start 29 :repeater-type cumulate :repeater-value 1 :repeater-unit year :repeater-deadline-value 2 :repeater-deadline-unit year)) nil))) (should (string-match "<2012-03-29 .* -1y>" (org-element-timestamp-interpreter '(timestamp (:type active :year-start 2012 :month-start 3 :day-start 29 :warning-type all :warning-value 1 :warning-unit year)) nil))) (should (string-match "<2012-03-29 .* \\+1y -1y>" (org-element-timestamp-interpreter '(timestamp (:type active :year-start 2012 :month-start 3 :day-start 29 :warning-type all :warning-value 1 :warning-unit year :repeater-type cumulate :repeater-value 1 :repeater-unit year)) nil))) (should (string-match "<2012-03-29 .* \\+1y/2y -1y>" (org-element-timestamp-interpreter '(timestamp (:type active :year-start 2012 :month-start 3 :day-start 29 :warning-type all :warning-value 1 :warning-unit year :repeater-type cumulate :repeater-value 1 :repeater-unit year :repeater-deadline-value 2 :repeater-deadline-unit year)) nil))) ;; Timestamp range with repeater interval (should (string-match "<2012-03-29 .* \\+1y>--<2012-03-30 .* \\+1y>" (org-test-parse-and-interpret "<2012-03-29 Thu +1y>--<2012-03-30 Thu +1y>"))) (should (string-match "<2012-03-29 .* \\+1y>--<2012-03-30 .* \\+1y>" (org-element-timestamp-interpreter '(timestamp (:type active-range :year-start 2012 :month-start 3 :day-start 29 :year-end 2012 :month-end 3 :day-end 30 :repeater-type cumulate :repeater-value 1 :repeater-unit year)) nil))) ;; Tests for :range-type property ;;; Errors (should-error (org-element-timestamp-interpreter '(timestamp (:range-type timerange :type active :year-start 2023 :month-start 7 :day-start 10 :year-end 2023 :month-end 7 :day-end 10 :hour-start 17 :minute-start 30 :hour-end 17 :minute-end 30)) nil)) (should-error (org-element-timestamp-interpreter '(timestamp (:range-type daterange :type active :year-start 2023 :month-start 7 :day-start 10 :hour-start 17 :minute-start 30)) nil)) (should-error (org-element-timestamp-interpreter '(timestamp (:range-type timerange :type inactive :year-start 2023 :month-start 7 :day-start 10 :year-end 2023 :month-end 7 :day-end 10 :hour-start 17 :minute-start 30 :hour-end 17 :minute-end 30)) nil)) (should-error (org-element-timestamp-interpreter '(timestamp (:range-type daterange :type inactive :year-start 2023 :month-start 7 :day-start 10 :hour-start 17 :minute-start 30)) nil)) ;;; End part is nil (should ;; Expected result: "<2023-07-10 Mon>--<2023-07-10 Mon>" (string= (format "<%s>--<%s>" (format-time-string (car org-time-stamp-formats) (org-encode-time 0 0 0 10 7 2023)) (format-time-string (car org-time-stamp-formats) (org-encode-time 0 0 0 10 7 2023))) (org-element-timestamp-interpreter '(timestamp (:range-type daterange :type active-range :year-start 2023 :month-start 7 :day-start 10)) nil))) (should (string-match "<2023-07-10 .* 17:30-17:30>" (org-element-timestamp-interpreter '(timestamp (:range-type timerange :type active-range :year-start 2023 :month-start 7 :day-start 10 :hour-start 17 :minute-start 30)) nil))) (should ;; Expected result: "<2023-07-10 Mon 17:30>--<2023-07-10 Mon>" (string= (format "<%s>--<%s>" (format-time-string (cdr org-time-stamp-formats) (org-encode-time 0 30 17 10 7 2023)) (format-time-string (car org-time-stamp-formats) (org-encode-time 0 0 0 10 7 2023))) (org-element-timestamp-interpreter '(timestamp (:range-type daterange :type active-range :year-start 2023 :month-start 7 :day-start 10 :hour-start 17 :minute-start 30)) nil))) ;;; End is equal to start (should (string-match "<2023-07-10 .* 17:30-17:30>" (org-element-timestamp-interpreter '(timestamp (:range-type timerange :type active-range :year-start 2023 :month-start 7 :day-start 10 :year-end 2023 :month-end 7 :day-end 10 :hour-start 17 :minute-start 30 :hour-end 17 :minute-end 30)) nil))) (should (string-match "<2023-07-10 .* 17:30>--<2023-07-10 .* 17:30>" (org-element-timestamp-interpreter '(timestamp (:range-type daterange :type active-range :year-start 2023 :month-start 7 :day-start 10 :year-end 2023 :month-end 7 :day-end 10 :hour-start 17 :minute-start 30 :hour-end 17 :minute-end 30)) nil))) ;;;; End date is not equal to start date, but interpret the object as a timerange (:range-type 'timerange) (should (string-match "<2023-07-10 .* 17:30-18:30>" (org-element-timestamp-interpreter '(timestamp (:range-type timerange :type active-range :year-start 2023 :month-start 7 :day-start 10 :year-end 2023 :month-end 8 :day-end 10 :hour-start 17 :minute-start 30 :hour-end 18 :minute-end 30)) nil))) ;;;; End date is not equal to start date, interpret the object as a daterange (:range-type 'daterange) (should (string-match "<2023-07-10 .* 17:30>--<2023-08-10 .* 18:30>" (org-element-timestamp-interpreter '(timestamp (:range-type daterange :type active-range :year-start 2023 :month-start 7 :day-start 10 :year-end 2023 :month-end 8 :day-end 10 :hour-start 17 :minute-start 30 :hour-end 18 :minute-end 30)) nil)))) (ert-deftest test-org-element/verse-block-interpreter () "Test verse block interpretation." (should (equal (org-test-parse-and-interpret "#+BEGIN_VERSE\nTest\n#+END_VERSE") "#+begin_verse\nTest\n#+end_verse\n"))) (ert-deftest test-org-element/bold-interpreter () "Test bold interpreter." (should (equal (org-test-parse-and-interpret "*text*") "*text*\n"))) (ert-deftest test-org-element/citation-interpreter () "Test citation interpreter." (should (equal "[cite:@key]\n" (org-test-parse-and-interpret "[cite:@key]"))) (should (equal "[cite:-@key]\n" (org-test-parse-and-interpret "[cite:-@key]"))) (should (equal "[cite/style:@key]\n" (org-test-parse-and-interpret "[cite/style:@key]"))) (should (equal "[cite:pre @key]\n" (org-test-parse-and-interpret "[cite:pre @key]"))) (should (equal "[cite:@key post]\n" (org-test-parse-and-interpret "[cite:@key post]"))) (should (equal "[cite:@a ;b]\n" (org-test-parse-and-interpret "[cite: @a ;b]"))) (should (equal "[cite:@a;@b;@c]\n" (org-test-parse-and-interpret "[cite:@a;@b;@c]"))) (should (equal "[cite:common-pre ; @a]\n" (org-test-parse-and-interpret "[cite:common-pre ; @a]"))) (should (equal "[cite:@a ; common-post]\n" (org-test-parse-and-interpret "[cite:@a ; common-post]")))) (ert-deftest test-org-element/code-interpreter () "Test code interpreter." (should (equal (org-test-parse-and-interpret "~text~") "~text~\n"))) (ert-deftest test-org-element/entity-interpreter () "Test entity interpreter." ;; 1. Without brackets. (should (equal (org-test-parse-and-interpret "\\alpha text") "\\alpha text\n")) ;; 2. With brackets. (should (equal (org-test-parse-and-interpret "\\alpha{}text") "\\alpha{}text\n"))) (ert-deftest test-org-element/export-snippet-interpreter () "Test export snippet interpreter." (should (equal (org-test-parse-and-interpret "@@backend:contents@@") "@@backend:contents@@\n"))) (ert-deftest test-org-element/footnote-reference-interpreter () "Test footnote reference interpreter." ;; Regular reference. (should (equal (org-test-parse-and-interpret "Text[fn:1]") "Text[fn:1]\n")) ;; Named reference. (should (equal (org-test-parse-and-interpret "Text[fn:label]") "Text[fn:label]\n")) ;; Inline reference. (should (equal (org-test-parse-and-interpret "Text[fn:label:def]") "Text[fn:label:def]\n")) ;; Anonymous reference. (should (equal (org-test-parse-and-interpret "Text[fn::def]") "Text[fn::def]\n"))) (ert-deftest test-org-element/inline-babel-call-interpreter () "Test inline babel call interpreter." ;; Without arguments. (should (equal (org-test-parse-and-interpret "call_test()") "call_test()\n")) ;; With arguments. (should (equal (org-test-parse-and-interpret "call_test(x=2)") "call_test(x=2)\n")) ;; With header arguments. (should (equal (org-test-parse-and-interpret "call_test[:results output]()[:results html]") "call_test[:results output]()[:results html]\n"))) (ert-deftest test-org-element/inline-src-block-interpreter () "Test inline src block interpreter." ;; 1. Without header argument. (should (equal (org-test-parse-and-interpret "src_emacs-lisp{(+ 1 1)}") "src_emacs-lisp{(+ 1 1)}\n")) ;; 2. With header arguments. (should (equal (org-test-parse-and-interpret "src_emacs-lisp[:results silent]{(+ 1 1)}") "src_emacs-lisp[:results silent]{(+ 1 1)}\n"))) (ert-deftest test-org-element/italic-interpreter () "Test italic interpreter." (should (equal (org-test-parse-and-interpret "/text/") "/text/\n"))) (ert-deftest test-org-element/latex-fragment-interpreter () "Test latex fragment interpreter." (should (equal (org-test-parse-and-interpret "\\command{}") "\\command{}\n")) (should (equal (org-test-parse-and-interpret "$x$") "$x$\n")) (should (equal (org-test-parse-and-interpret "$x+y$") "$x+y$\n")) (should (equal (org-test-parse-and-interpret "$$x+y$$") "$$x+y$$\n")) (should (equal (org-test-parse-and-interpret "\\(x+y\\)") "\\(x+y\\)\n")) (should (equal (org-test-parse-and-interpret "\\[x+y\\]") "\\[x+y\\]\n"))) (ert-deftest test-org-element/line-break-interpreter () "Test line break interpreter." (should (equal (org-test-parse-and-interpret "First line \\\\ \nSecond line") "First line \\\\\nSecond line\n"))) (ert-deftest test-org-element/link-interpreter () "Test link interpreter." ;; Links targeted from a radio target. (should (equal (let ((org-target-link-regexp "radio-target")) (org-test-parse-and-interpret "a radio-target")) "a radio-target\n")) ;; Links without description. (should (equal (org-test-parse-and-interpret "[[https://orgmode.org]]") "[[https://orgmode.org]]\n")) ;; Links with a description, even one containing a link. (should (equal (org-test-parse-and-interpret "[[https://orgmode.org][Org mode]]") "[[https://orgmode.org][Org mode]]\n")) (should (equal (org-test-parse-and-interpret "[[https://orgmode.org][https://orgmode.org]]") "[[https://orgmode.org][https://orgmode.org]]\n")) ;; File links. (should (equal (org-test-parse-and-interpret "[[file+emacs:todo.org]]") "[[file+emacs:todo.org]]\n")) (should (equal (org-test-parse-and-interpret "[[file:todo.org::*task]]") "[[file:todo.org::*task]]\n")) (should (equal (org-test-parse-and-interpret "[[/tmp/todo.org::*task]]") "[[/tmp/todo.org::*task]]\n")) ;; Id links. (should (equal (org-test-parse-and-interpret "[[id:aaaa]]") "[[id:aaaa]]\n")) ;; Custom-id links. (should (equal (org-test-parse-and-interpret "[[#id]]") "[[#id]]\n")) ;; Code-ref links. (should (equal (org-test-parse-and-interpret "[[(ref)]]") "[[(ref)]]\n")) ;; Plain links. (should (equal (org-test-parse-and-interpret "https://orgmode.org") "https://orgmode.org\n")) ;; Angular links. (should (equal (org-test-parse-and-interpret "") "\n")) ;; Pathological case: link with a %-sign in description. (should (equal (org-test-parse-and-interpret "[[file://path][%s]]") "[[file://path][%s]]\n"))) (ert-deftest test-org-element/macro-interpreter () "Test macro interpreter." ;; 1. Without argument. (should (equal (org-test-parse-and-interpret "{{{test}}}") "{{{test}}}\n")) ;; 2. With arguments. (should (equal (org-test-parse-and-interpret "{{{test(arg1,arg2)}}}") "{{{test(arg1,arg2)}}}\n"))) (ert-deftest test-org-element/radio-target-interpreter () "Test radio target interpreter." (should (equal (org-test-parse-and-interpret "<<>>") "<<>>\n"))) (ert-deftest test-org-element/statistics-cookie-interpreter () "Test statistics cookie interpreter." ;; 1. Without percent (should (equal (org-test-parse-and-interpret "[0/1]") "[0/1]\n")) ;; 2. With percent. (should (equal (org-test-parse-and-interpret "[66%]") "[66%]\n"))) (ert-deftest test-org-element/strike-through-interpreter () "Test strike through interpreter." (should (equal (org-test-parse-and-interpret "+target+") "+target+\n"))) (ert-deftest test-org-element/subscript-interpreter () "Test subscript interpreter." ;; 1. Without brackets. (should (equal (org-test-parse-and-interpret "a_b") "a_b\n")) ;; 2. With brackets. (should (equal (org-test-parse-and-interpret "a_{b}") "a_{b}\n"))) (ert-deftest test-org-element/superscript-interpreter () "Test superscript interpreter." ;; 1. Without brackets. (should (equal (org-test-parse-and-interpret "a^b") "a^b\n")) ;; 2. With brackets. (should (equal (org-test-parse-and-interpret "a^{b}") "a^{b}\n"))) (ert-deftest test-org-element/target-interpreter () "Test target interpreter." (should (equal (org-test-parse-and-interpret "<>") "<>\n"))) (ert-deftest test-org-element/underline-interpreter () "Test underline interpreter." (should (equal (org-test-parse-and-interpret "_text_") "_text_\n"))) (ert-deftest test-org-element/verbatim-interpreter () "Test verbatim interpreter." (should (equal (org-test-parse-and-interpret "=text=") "=text=\n"))) ;;; Test Granularity (ert-deftest test-org-element/granularity () "Test granularity impact on buffer parsing." (org-test-with-temp-text "* Head 1 ** Head 2 #+BEGIN_CENTER Centered paragraph. #+END_CENTER Paragraph \\alpha." ;; 1.1. Granularity set to `headline' should parse every headline ;; in buffer, and only them. (let ((tree (org-element-parse-buffer 'headline))) (should (= 2 (length (org-element-map tree 'headline 'identity)))) (should-not (org-element-map tree 'paragraph 'identity))) ;; 1.2. Granularity set to `greater-element' should not enter ;; greater elements excepted headlines and sections. (let ((tree (org-element-parse-buffer 'greater-element))) (should (= 1 (length (org-element-map tree 'center-block 'identity)))) (should (= 1 (length (org-element-map tree 'paragraph 'identity)))) (should-not (org-element-map tree 'entity 'identity))) ;; 1.3. Granularity set to `element' should enter every ;; greater-element. (let ((tree (org-element-parse-buffer 'element))) (should (= 2 (length (org-element-map tree 'paragraph 'identity)))) (should-not (org-element-map tree 'entity 'identity))) ;; 1.4. Granularity set to `object' can see everything. (let ((tree (org-element-parse-buffer 'object))) (should (= 1 (length (org-element-map tree 'entity 'identity))))))) (ert-deftest test-org-element/secondary-string-parsing () "Test if granularity correctly toggles secondary strings parsing." ;; With a granularity bigger than `object', no secondary string ;; should be parsed. (should (stringp (org-test-with-temp-text "* Headline" (let ((headline (org-element-map (org-element-parse-buffer 'headline) 'headline #'identity nil 'first-match))) (org-element-property :title headline))))) (should (stringp (org-test-with-temp-text "* Headline\n- tag :: item" (let ((item (org-element-map (org-element-parse-buffer 'element) 'item #'identity nil 'first-match))) (org-element-property :tag item))))) (when (featurep 'org-inlinetask) (should (stringp (let ((org-inlinetask-min-level 15)) (org-test-with-temp-text "*************** Inlinetask" (let ((inlinetask (org-element-map (org-element-parse-buffer 'element) 'inlinetask #'identity nil 'first-match))) (org-element-property :title inlinetask))))))) ;; With a default granularity, secondary strings should be parsed. (should (listp (org-test-with-temp-text "* Headline" (let ((headline (org-element-map (org-element-parse-buffer) 'headline #'identity nil 'first-match))) (org-element-property :title headline))))) ;; `org-element-at-point' should never parse a secondary string. (should-not (listp (org-test-with-temp-text "* Headline" (org-element-property :title (org-element-at-point))))) ;; Preserve current local variables when parsing a secondary string. (should (let ((org-entities nil) (org-entities-user nil)) (org-test-with-temp-text " #+CAPTION: \\foo Text # Local Variables: # org-entities-user: ((\"foo\")) # End:" (let ((safe-local-variable-values '((org-entities-user . (("foo")))))) (hack-local-variables)) (org-element-map (org-element-parse-buffer) 'entity #'identity nil nil nil t))))) ;;; Test Visible Only Parsing (ert-deftest test-org-element/parse-buffer-visible () "Test `org-element-parse-buffer' with visible only argument." (should (equal '("H1" "H3" "H5") (org-test-with-temp-text "* H1\n** H2\n** H3 :visible:\n** H4\n** H5 :visible:" (org-occur ":visible:") (org-element-map (org-element-parse-buffer nil t) 'headline (lambda (hl) (org-element-property :raw-value hl)))))) (should (equal "Test" (let ((contents "Test")) (org-test-with-temp-text contents (add-text-properties 0 1 '(invisible t) contents) (org-element-map (org-element-parse-buffer nil t) 'plain-text #'org-no-properties nil t))))) (should (equal "Test" (let ((contents "Test")) (org-test-with-temp-text (concat "- " contents) (add-text-properties 0 1 '(invisible t) contents) (org-element-map (org-element-parse-buffer nil t) 'plain-text #'org-no-properties nil t)))))) ;;; Test `:parent' Property (ert-deftest test-org-element/parent-property () "Test `:parent' property." ;; Elements. (org-test-with-temp-text "#+BEGIN_CENTER\nText\n#+END_CENTER" (let* ((tree (org-element-parse-buffer)) (parent (org-element-property :parent (org-element-map tree 'paragraph 'identity nil t)))) (should parent) (should (eq (org-element-map tree 'center-block 'identity nil t) parent)))) ;; Objects. (org-test-with-temp-text "a_{/b/}" (let* ((tree (org-element-parse-buffer)) (parent (org-element-property :parent (org-element-map tree 'italic 'identity nil t)))) (should parent) (should (eq parent (org-element-map tree 'subscript 'identity nil t))))) ;; Secondary strings (org-test-with-temp-text "* /italic/" (let* ((tree (org-element-parse-buffer)) (parent (org-element-property :parent (org-element-map tree 'italic 'identity nil t)))) (should parent) (should (equal parent (org-element-map tree 'headline 'identity nil t)))))) ;;; Test Normalize Contents (ert-deftest test-org-element/normalize-contents () "Test `org-element-normalize-contents' specifications." ;; Remove maximum common indentation from element's contents. (should (equal (org-element-normalize-contents '(paragraph nil " Two spaces\n Three spaces")) '(paragraph nil "Two spaces\n Three spaces"))) (should (equal (org-element-normalize-contents '(paragraph nil " Two spaces\nNo space")) '(paragraph nil " Two spaces\nNo space"))) ;; Ignore objects within contents when computing maximum common ;; indentation. However, if contents start with an object, common ;; indentation is 0. (should (equal (org-element-normalize-contents '(paragraph nil " One " (emphasis nil "space") "\n Two spaces")) '(paragraph nil "One " (emphasis nil "space") "\n Two spaces"))) (should (equal (org-element-normalize-contents '(paragraph nil (verbatim nil "V") "No space\n Two\n Three")) '(paragraph nil (verbatim nil "V") "No space\n Two\n Three"))) ;; Ignore blank lines. (should (equal (org-element-normalize-contents '(paragraph nil " Two spaces\n\n \n Two spaces")) '(paragraph nil "Two spaces\n\n\nTwo spaces"))) (should (equal '(paragraph nil " Two spaces\n" (verbatim nil "V") "\n Two spaces") (org-element-normalize-contents '(paragraph nil " Two spaces\n " (verbatim nil "V") "\n Two spaces")))) (should (equal '(verse-block nil "line 1\n\nline 2") (org-element-normalize-contents '(verse-block nil " line 1\n\n line 2")))) ;; Recursively enter objects in order to compute common indentation. (should (equal (org-element-normalize-contents '(paragraph nil " Two spaces " (bold nil " and\n One space"))) '(paragraph nil " Two spaces " (bold nil " and\nOne space")))) ;; When optional argument is provided, ignore first line ;; indentation. (should (equal (org-element-normalize-contents '(paragraph nil "No space\n Two spaces\n Three spaces") t) '(paragraph nil "No space\nTwo spaces\n Three spaces"))) (should (equal (org-element-normalize-contents '(paragraph nil (verbatim nil "V") "No space\n Two\n Three") t) '(paragraph nil (verbatim nil "V") "No space\nTwo\n Three"))) ;; Corner case: do not ignore indentation of string right after ;; a line break. (should (equal (org-element-normalize-contents '(paragraph nil " 1 space" (line-break) " 2 spaces")) '(paragraph nil "1 space" (line-break) " 2 spaces")))) ;;; Test Navigation Tools. (ert-deftest test-org-element/at-point () "Test `org-element-at-point' specifications." ;; Return closest element containing point. (should (eq 'paragraph (org-test-with-temp-text "#+BEGIN_CENTER\nA\n#+END_CENTER" (progn (search-forward "A") (org-element-type (org-element-at-point)))))) ;; Point in other buffer. (should (eq 'paragraph (org-test-with-temp-text "#+BEGIN_CENTER\nA\n#+END_CENTER" (progn (search-forward "A") (org-element-type (let ((mk (point-marker))) (with-temp-buffer (org-element-at-point mk)))))))) ;; Correctly set `:parent' property. (should (eq 'center-block (org-test-with-temp-text "#+BEGIN_CENTER\nA\n#+END_CENTER" (progn (search-forward "A") (org-element-type (org-element-property :parent (org-element-at-point))))))) ;; Special case: at a blank line just below a headline, return that ;; headline. (should (equal "H1" (org-test-with-temp-text "* H1\n \n* H2\n" (forward-line) (org-element-property :title (org-element-at-point))))) ;; Special case: at the very beginning of a table, return `table' ;; object instead of `table-row'. (should (eq 'table (org-test-with-temp-text "| a | b |" (org-element-type (org-element-at-point))))) ;; Special case: at the very beginning of a list or sub-list, return ;; `plain-list' object instead of `item'. (should (eq 'plain-list (org-test-with-temp-text "- item" (org-element-type (org-element-at-point))))) ;; Special case: at the closing line of a greater element, be sure ;; to return it instead of the last element in its contents. (should (eq 'center-block (org-test-with-temp-text "#+BEGIN_CENTER\nParagraph\n#+END_CENTER" (progn (forward-line 2) (org-element-type (org-element-at-point)))))) ;; Special case: at a blank line between two items, be sure to ;; return item above instead of the last element of its contents. (should (eq 'item (org-test-with-temp-text "- Para1\n\n- Para2" (forward-line) (org-element-type (org-element-at-point))))) ;; Special case: at the last blank line in a plain list, return it ;; instead of the last item. (should (eq 'plain-list (org-test-with-temp-text "- Para1\n- Para2\n\nPara3" (progn (forward-line 2) (org-element-type (org-element-at-point)))))) ;; Special case: at the last blank line in a plain list at the end of ;; a headline, return the plain list, not the last item, and not the ;; headline. (should (eq 'plain-list (org-test-with-temp-text "* Headline\n- Para1\n- Para2\n\nPara3\n* Another headline" (progn (forward-line 3) (org-element-type (org-element-at-point)))))) ;; Special case: when a list ends at the end of buffer and there's ;; no final newline, return last element in last item. (should (eq 'paragraph (org-test-with-temp-text "- a" (end-of-line) (org-element-type (org-element-at-point))))) ;; Parse a list within a block itself contained in a list. (should (eq 'plain-list (org-test-with-temp-text "- outer\n #+begin_center\n - inner\n #+end_center" (search-forward "inner") (beginning-of-line) (org-element-type (org-element-at-point))))) ;; Do not error at eob on an empty line. (should (org-test-with-temp-text "* H\n" (forward-line) (or (org-element-at-point) t))) ;; Return greater element when outside contents. (should (eq 'drawer (org-test-with-temp-text ":DRAWER:\ntest\n:END:\n" (org-element-type (org-element-at-point))))) (should (eq 'drawer (org-test-with-temp-text ":DRAWER:\ntest\n:END:\n" (org-element-type (org-element-at-point))))) ;; Return greater element when at :contents-end. (should (eq 'drawer (org-test-with-temp-text ":DRAWER:\ntest\n:END:\n" (org-element-type (org-element-at-point)))))) (ert-deftest test-org-element/context () "Test `org-element-context' specifications." ;; Return closest object containing point. (should (eq 'underline (org-test-with-temp-text "Some *text with _underline_ text*" (org-element-type (org-element-context))))) ;; Find objects in secondary strings. (should (eq 'underline (org-test-with-temp-text "* Headline _with_ underlining" (org-element-type (org-element-context))))) ;; Find objects in objects. (should (eq 'macro (org-test-with-temp-text "| a | {{{macro}}} |" (org-element-type (org-element-context))))) (should (eq 'table-cell (org-test-with-temp-text "| a | b {{{macro}}} |" (org-element-type (org-element-context))))) ;; Find objects in item tags. (should (eq 'bold (org-test-with-temp-text "- *bold* ::" (org-element-type (org-element-context))))) (should-not (eq 'bold (org-test-with-temp-text "- *bold* ::" (org-element-type (org-element-context))))) (should-not (eq 'bold (org-test-with-temp-text "- *bold* ::\n" (org-element-type (org-element-context))))) ;; Do not find objects in table rules. (should (eq 'table-row (org-test-with-temp-text "| a | b |\n|---|---|\n| c | d |" (org-element-type (org-element-context))))) ;; Find objects in parsed affiliated keywords. (should (eq 'macro (org-test-with-temp-text "#+CAPTION: {{{macro}}}\n| a | b |" (org-element-type (org-element-context))))) (should (eq 'bold (org-test-with-temp-text "#+caption: *bold*\nParagraph" (org-element-type (org-element-context))))) ;; Find objects at the end of buffer. (should (eq 'bold (org-test-with-temp-text "*bold*" (goto-char (point-max)) (org-element-type (org-element-context))))) ;; Correctly set `:parent' property. (should (eq 'paragraph (org-test-with-temp-text "Some *bold* text" (org-element-type (org-element-property :parent (org-element-context)))))) ;; Between two objects, return the second one. (should (eq 'macro (org-test-with-temp-text "<>{{{test}}}" (org-element-type (org-element-context))))) ;; Test optional argument. (should (eq 'underline (org-test-with-temp-text "Some *text with _underline_ text*" (org-element-type (org-element-context (org-element-at-point)))))) ;; Special case: bold object at the beginning of a headline. (should (eq 'bold (org-test-with-temp-text "* *bold*" (org-element-type (org-element-context))))) ;; Special case: incomplete cell at the end of a table row. (should (eq 'table-cell (org-test-with-temp-text "|a|b|c" (org-element-type (org-element-context))))) ;; Special case: objects in inline footnotes. (should (eq 'link (org-test-with-temp-text "[fn::[[https://orgmode.org]]]" (org-element-type (org-element-context))))) ;; Special case: tags looking like a link. (should-not (eq 'link (org-test-with-temp-text "* Headline :file:tags:" (org-element-type (org-element-context))))) (should (eq 'link (org-test-with-temp-text "* Headline :file:tags: :real:tag:" (org-element-type (org-element-context))))) ;; Do not parse partial export snippets. (should-not (eq 'export-snippet (org-test-with-temp-text "@@latex:\n\nparagraph\n\n@@" (org-element-type (org-element-context)))))) ;;; Test Tools (ert-deftest test-org-element/lineage () "Test `org-element-lineage' specifications." ;; Regular tests. When applied to an element or object returned by ;; `org-element-at-point' or `org-element-context', the list is ;; limited to the current section. (should (equal '(paragraph center-block section headline headline org-data) (org-test-with-temp-text "* H1\n** H2\n#+BEGIN_CENTER\n*bold*\n#+END_CENTER" (mapcar #'car (org-element-lineage (org-element-context)))))) (should (equal '(paragraph center-block section headline headline org-data) (org-test-with-temp-text "* H1\n** H2\n#+BEGIN_CENTER\n*bold*\n#+END_CENTER" (mapcar #'car (org-element-lineage (org-element-map (org-element-parse-buffer) 'bold #'identity nil t)))))) ;; Test TYPES optional argument. (should (eq 'center-block (org-test-with-temp-text "* H1\n** H2\n#+BEGIN_CENTER\n*bold*\n#+END_CENTER" (org-element-type (org-element-lineage (org-element-context) 'center-block))))) (should-not (org-test-with-temp-text "* H1\n** H2\n#+BEGIN_CENTER\n*bold*\n#+END_CENTER" (org-element-lineage (org-element-context) '(example-block)))) ;; Test WITH-SELF optional argument. (should (equal '(bold paragraph center-block section headline headline org-data) (org-test-with-temp-text "* H1\n** H2\n#+BEGIN_CENTER\n*bold*\n#+END_CENTER" (mapcar #'car (org-element-lineage (org-element-context) nil t))))) ;; When TYPES and WITH-SELF are provided, the latter is also checked ;; against the former. (should (org-test-with-temp-text "* H1\n** H2\n#+BEGIN_CENTER\n*bold*\n#+END_CENTER" (org-element-lineage (org-element-context) '(bold) t)))) (ert-deftest test-org-element/lineage-map () "Test `org-element-lineage-map' specifications." (should (equal '(paragraph center-block section headline headline org-data) (org-test-with-temp-text "* H1\n** H2\n#+BEGIN_CENTER\n*bold*\n#+END_CENTER" (org-element-lineage-map (org-element-context) #'org-element-type)))) ;; WITH-SELF. (should (equal '(bold paragraph center-block section headline headline org-data) (org-test-with-temp-text "* H1\n** H2\n#+BEGIN_CENTER\n*bold*\n#+END_CENTER" (org-element-lineage-map (org-element-context) #'org-element-type nil t)))) ;; FUN as a Lisp form. (should (equal '("H2" "H1") (org-test-with-temp-text "* H1\n** H2\n#+BEGIN_CENTER\n*bold*\n#+END_CENTER" (org-element-lineage-map (org-element-context) '(org-element-property :raw-value node))))) ;; FIRST-MATCH (should (equal "H2" (org-test-with-temp-text "* H1\n** H2\n#+BEGIN_CENTER\n*bold*\n#+END_CENTER" (org-element-lineage-map (org-element-context) '(org-element-property :raw-value node) nil nil t))))) (ert-deftest test-org-element/property-inherited () "Test `org-element-property-inherited' specifications." ;; Property without self. (should (equal 'bar (org-element-property-inherited :foo (car (org-element-contents (org-element-create 'parent '(:foo bar) (org-element-create 'child '(:foo baz)))))))) ;; With self (should (equal 'baz (org-element-property-inherited :foo (car (org-element-contents (org-element-create 'parent '(:foo bar) (org-element-create 'child '(:foo baz))))) 'with-self))) ;; ACCUMULATE non-nil. (should (equal '(bar baz) (org-element-property-inherited :foo (car (org-element-contents (org-element-create 'parent '(:foo bar) (org-element-create 'child '(:foo baz))))) 'with-self 'accumulate))) ;; LITERAL-NIL. (should-not (org-element-property-inherited :foo (org-element-create 'child '(:foo "nil")) 'with-self nil t)) (should (org-element-property-inherited :foo (org-element-create 'child '(:foo "nil")) 'with-self)) ;; INCLUDE-NIL (should-not (org-element-property-inherited :foo (org-element-map (thread-last (org-element-create 'grandchild '(:foo baz)) (org-element-create 'child '(:foo nil)) (org-element-create 'parent '(:foo bar))) 'grandchild #'identity nil t) nil nil nil t)) (should (eq 'bar (org-element-property-inherited :foo (org-element-map (thread-last (org-element-create 'grandchild '(:foo baz)) (org-element-create 'child '(:foo nil)) (org-element-create 'parent '(:foo bar))) 'grandchild #'identity nil t)))) ;; INCLUDE-NIL in accumulated. (should (equal '(bar nil baz) (org-element-property-inherited :foo (org-element-map (thread-last (org-element-create 'grandchild '(:foo baz)) (org-element-create 'child '(:foo nil)) (org-element-create 'parent '(:foo bar))) 'grandchild #'identity nil t) 'with-self 'accumulate nil t))) ;; PROPERTY as a list. (should (equal '(bar value) (org-element-property-inherited '(:foo :extra) (car (org-element-contents (org-element-create 'parent '(:foo bar :extra value) (org-element-create 'child '(:foo baz))))) nil 'accumulate))) ;; Append list values (should (equal '(bar value value2) (org-element-property-inherited '(:foo :extra) (car (org-element-contents (org-element-create 'parent '(:foo bar :extra (value value2)) (org-element-create 'child '(:foo baz))))) nil 'accumulate)))) ;;; Test Cache. (ert-deftest test-org-element/cache-map () "Test `org-element-cache-map'." (org-test-with-temp-text "* headline\n:DRAWER:\nparagraph\n:END:\n* headline 2" (should (equal '(org-data headline section drawer paragraph headline) (org-element-cache-map #'car :granularity 'element)))) (should (equal '(org-data headline section drawer paragraph) (org-test-with-temp-text "* headline\n:DRAWER:\nparagraph\n:END:" (org-element-cache-map #'car :granularity 'element))))) (ert-deftest test-org-element/cache () "Test basic expectations and common pitfalls for cache." ;; Shift positions. (should (equal '(18 . 23) (org-test-with-temp-text "para1\n\npara2\n\npara3" (let ((org-element-use-cache t)) (save-excursion (goto-char (point-max)) (org-element-at-point)) (insert "add") (forward-line 4) (let ((element (org-element-at-point))) (cons (org-element-property :begin element) (org-element-property :end element))))))) ;; Partial shifting: when the contents of a greater element are ;; modified, only shift ending positions. (should (org-test-with-temp-text "#+BEGIN_CENTER\nPara1\n\nPara2\n\nPara3\n#+END_CENTER" (let ((org-element-use-cache t)) (save-excursion (search-forward "3") (org-element-at-point)) (search-forward "Para2") (insert " ") (let ((element (org-element-property :parent (org-element-at-point)))) (equal (cons (org-element-property :begin element) (org-element-property :end element)) (cons (point-min) (point-max))))))) ;; Re-parent shifted elements. (should (eq 'item (org-test-with-temp-text "- item\n\n\n para1\n para2" (let ((org-element-use-cache t)) (end-of-line) (org-element-at-point) (save-excursion (goto-char (point-max)) (org-element-at-point)) (forward-line) (delete-char 1) (goto-char (point-max)) (org-element-type (org-element-property :parent (org-element-at-point))))))) ;; Preserve local structures when re-parenting. (should (eq 'table (let ((org-element-use-cache t)) (org-test-with-temp-text "#+begin_center\nP0\n\n\n\n P1\n | a | b |\n | c | d |\n#+end_center" (save-excursion (search-forward "| c |") (org-element-at-point)) (insert "- item") (search-forward "| c |") (beginning-of-line) (org-element-type (org-element-property :parent (org-element-at-point))))))) (should-not (eq 'center-block (org-test-with-temp-text "#+begin_center\nP0\n\n\n\n P1\n | a | b |\n#+end_center" (let ((org-element-use-cache t)) (save-excursion (search-forward "| a |") (org-element-at-point)) (insert "- item") (search-forward "| a |") (beginning-of-line) (org-element-type (org-element-property :parent (org-element-at-point))))))) ;; When re-parenting, also propagate changes to list structures. (should (= 2 (org-test-with-temp-text "\n Para\n - item" (let ((org-element-use-cache t)) (org-element-at-point) (goto-char (point-min)) (insert "- Top\n") (search-forward "- item") (beginning-of-line) (length (org-element-property :structure (org-element-at-point))))))) ;; Modifying the last line of an element alters the element below. (should (org-test-with-temp-text "para1\n\npara2" (let ((org-element-use-cache t)) (goto-char (point-max)) (org-element-at-point) (forward-line -1) (insert "merge") (let ((element (org-element-at-point))) (equal (cons (org-element-property :begin element) (org-element-property :end element)) (cons (point-min) (point-max))))))) ;; Modifying the first line of an element alters the element above. (should (org-test-with-temp-text ": fixed-width\n:not-fixed-width" (let ((org-element-use-cache t)) (goto-char (point-max)) (org-element-at-point) (search-backward ":") (forward-char) (insert " ") (let ((element (org-element-at-point))) (equal (cons (org-element-property :begin element) (org-element-property :end element)) (cons (point-min) (point-max))))))) (org-test-with-temp-text ":DRAWER:\ntest\n:END:\n #\nParagraph" (let ((org-element-use-cache t)) (org-element-cache-map #'ignore :granularity 'element) (should (eq 'comment (org-element-type (org-element-at-point)))) (should (eq 0 (org-element-property :post-blank (org-element-at-point (point-min))))) (insert " ") (delete-char -1) (org-element-cache-map #'ignore :granularity 'element) (delete-char 1) (should (eq 1 (org-element-property :post-blank (org-element-at-point (point-min))))))) ;; Sensitive change: adding a line alters document structure both ;; above and below. (should (eq 'example-block (org-test-with-temp-text "#+BEGIN_EXAMPLE\nPara1\n\nPara2\n" (let ((org-element-use-cache t)) (goto-char (point-max)) (org-element-at-point) (insert "#+END_EXAMPLE") (search-backward "Para1") (org-element-type (org-element-at-point)))))) (should (eq 'example-block (org-test-with-temp-text "Para1\n\nPara2\n#+END_EXAMPLE" (let ((org-element-use-cache t)) (save-excursion (goto-char (point-max)) (org-element-at-point)) (insert "#+BEGIN_EXAMPLE\n") (search-forward "Para2") (org-element-type (org-element-at-point)))))) ;; Sensitive change: removing a line alters document structure both ;; above and below. (should (eq 'example-block (org-test-with-temp-text "# +BEGIN_EXAMPLE\nPara1\n\nPara2\n#+END_EXAMPLE" (let ((org-element-use-cache t)) (save-excursion (goto-char (point-max)) (org-element-at-point)) (forward-char) (delete-char 1) (search-forward "Para2") (org-element-type (org-element-at-point)))))) (should (eq 'example-block (org-test-with-temp-text "#+BEGIN_EXAMPLE\nPara1\n\nPara2\n# +END_EXAMPLE" (let ((org-element-use-cache t)) (save-excursion (goto-char (point-max)) (org-element-at-point)) (search-forward "# ") (delete-char -1) (search-backward "Para1") (org-element-type (org-element-at-point)))))) ;; Make sure that we do not generate intersecting elements. (should (eq 'paragraph (org-test-with-temp-text ":DRAWER:\nP1\n\n:END:\n#+END_EXAMPLE" (let ((org-element-use-cache t)) (org-element-at-point (point-max)) (org-element-at-point) (insert "#+BEGIN_EXAMPLE") (org-element-type (org-element-at-point)))))) ;; But yet correctly slurp obsolete elements inside a new element. (should (eq 'example-block (org-test-with-temp-text ":DRAWER:\nP1\n\nP2\n#+END_EXAMPLE\n:END:" (let ((org-element-use-cache t)) (org-element-at-point (point-max)) (save-excursion (re-search-forward "P2") (should (eq 'paragraph (org-element-type (org-element-at-point)))) (re-search-forward "END_") (should (eq 'paragraph (org-element-type (org-element-at-point))))) (insert "#+BEGIN_EXAMPLE") (re-search-forward "P2") (should (eq 'example-block (org-element-type (org-element-at-point)))) (re-search-forward "END_") (org-element-type (org-element-at-point)))))) ;; Test edits near :end of element (should-not (eq 'headline (org-test-with-temp-text "* H1\nP1\n*H2\n" (let ((org-element-use-cache t)) (org-element-cache-map #'ignore :granularity 'element) (insert "Blah") (org-element-type (org-element-at-point)))))) (should-not (eq 'headline (org-test-with-temp-text "* H1\nP1\n*H2\n" (let ((org-element-use-cache t)) (org-element-cache-map #'ignore :granularity 'element) (backward-delete-char 1) (org-element-type (org-element-at-point)))))) (org-test-with-temp-text "Paragraph.\n # comment" (let ((org-element-use-cache t)) (org-element-cache-map #'ignore :granularity 'element) (should (eq 'comment (org-element-type (org-element-at-point)))) (insert "not comment anymore") (org-element-cache-map #'ignore :granularity 'element) (should-not (eq 'comment (org-element-type (org-element-at-point)))) (should (eq (org-element-at-point) (org-element-at-point 1))))) (should (eq 'headline (org-test-with-temp-text "* H1\nP1\n* H2\n" (let ((org-element-use-cache t)) (org-element-cache-map #'ignore :granularity 'element) (insert "Blah\n") (org-element-type (org-element-at-point)))))) ;; Corner case: watch out drawers named "PROPERTIES" as they are ;; fragile, unlike to other drawers. (should (eq 'node-property (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:A\n:END:" (let ((org-element-use-cache t)) (org-element-at-point) (insert "+:") (org-element-type (org-element-at-point)))))) ;; Properly handle elements not altered by modifications but whose ;; parents were removed from cache. (should (org-test-with-temp-text "Paragraph\n\n\n\n#+begin_center\ncontents\n#+end_center" (let ((org-element-use-cache t) (parent-end (point-max))) (org-element-at-point) (save-excursion (search-backward "Paragraph") (forward-line 2) (insert "\n ")) (eq (org-element-property :end (org-element-property :parent (org-element-at-point))) (+ parent-end 3)))))) (ert-deftest test-org-element/cache-affiliated () "Test updating affiliated keywords." ;; Inserting a line right after other keywords. (let ((org-element-use-cache t)) (org-test-with-temp-text " #+caption: test #+name: test line" (org-element-cache-map #'ignore :granularity 'element) (should (eq 'keyword (org-element-type (org-element-at-point)))) (insert "1") (should (eq 2 (org-element-property :begin (org-element-at-point))))))) (ert-deftest test-org-element/cache-table () "Test handling edits in tables." ;; Unindented second row of the table should not be re-parented by ;; inserted item. (should (eq 'table (let ((org-element-use-cache t)) (org-test-with-temp-text "#+begin_center P0 P1 | a | b | | c | d | #+end_center" (save-excursion (search-forward "| c |") (org-element-at-point)) (insert "- item") (search-forward "| c |") (beginning-of-line) (org-element-type (org-element-at-point)))))) (should (eq 'table (let ((org-element-use-cache t)) (org-test-with-temp-text " - item 1 | a | b | | c | d | #+end_center" (save-excursion (search-forward "| c |") (org-element-at-point)) (delete-char 1) (search-forward "| c |") (beginning-of-line) (org-element-type (org-element-at-point)))))) (should (eq 'table-row (let ((org-element-use-cache t)) (org-test-with-temp-text " - item 1 | a | b | | c | d | #+end_center" (save-excursion (search-forward "| c |") (org-element-at-point)) (insert "\n") (search-forward "| c |") (beginning-of-line) (org-element-type (org-element-at-point))))))) (ert-deftest test-org-element/cache-headline () "Test basic expectations and common pitfalls for cached headings." ;; Appending to final headline in a subtree. (org-test-with-temp-text " * Heading Aliquam erat volutpat. *** Subheading ** Another ** Final :PROPERTIES: :ID: some :END: * Heading 2 ** End " (let ((org-element-use-cache t)) (org-element-at-point) (save-excursion (goto-char (point-max)) (org-element-at-point)) (insert ":CATEOGORY: cat\n") (search-backward "* Heading") (should (eq (org-element-property :end (org-element-at-point)) (save-excursion (search-forward "* Heading 2") (line-beginning-position)))) (search-forward "* Heading 2") (beginning-of-line) (insert "\n\n") (search-backward "* Heading") (should (eq (org-element-property :end (org-element-at-point)) (save-excursion (search-forward "* Heading 2") (line-beginning-position)))))) ;; Appending at eob. (org-test-with-temp-text " * Heading *** Sub-heading ** Another *** 1 ***** 2 ** 3 Aenean in sem ac leo mollis blandit. " (let ((org-element-use-cache t)) (org-element-at-point (point-max)) (insert "\n\nTest\n") (search-backward "* Heading") (should (eq (point-max) (org-element-property :end (org-element-at-point)))))) ;; Breaking headline at eob. (org-test-with-temp-text " * Heading *** Sub-heading " (let ((org-element-use-cache t)) (org-element-at-point (point-max)) (insert "* heading 2") (beginning-of-line) (should (eq (point-max) (org-element-property :end (org-element-at-point)))) (delete-char 1) (search-backward "* Heading") (should (eq (point-max) (org-element-property :end (org-element-at-point)))))) ;; Inserting low-level headline in-between. (org-test-with-temp-text " * Heading *** Sub-heading *** Sub-heading 2 *** Sub-heading 3 " (let ((org-element-use-cache t)) (org-element-at-point (point-max)) (insert "** heading 2") (search-forward "*** Sub-heading 2") (should (equal (org-element-property :parent (org-element-at-point)) (progn (search-backward "** heading 2") (org-element-at-point)))))) ;; Test when `org-element--cache-for-removal' modifies common parent ;; (`org-data' in this case) around changed region. (org-test-with-temp-text "blah :DRAWER: test :END: paragraph * headline" (let ((org-element-use-cache t)) (org-element-at-point (point-max)) (delete-region (point) (point-max)) (should (eq 'paragraph (org-element-type (org-element-at-point)))))) ;; Remove/re-introduce heading. (org-test-with-temp-text " * 1 ** 1-1 a ** 1-2 a " (let ((org-element-use-cache t)) (org-element-at-point (point-max)) (insert "FOO") (should (equal "1-1" (org-element-property :title (org-element-lineage (org-element-at-point) '(headline))))) (insert "\n") (should (equal "1" (org-element-property :title (org-element-lineage (org-element-at-point) '(headline)))))))) (ert-deftest test-org-element/cache-ignored-locals () "Test `org-element-ignored-local-variables' value. Anything holding element cache state must not be copied around buffers, as in `org-element-copy-buffer' or `org-export-copy-buffer'. Otherwise, we may encounter hard-to-debug errors when cache state is either not up-to-date or modified by side effect, influencing the original values." (mapatoms (lambda (var) (when (and (boundp var) (symbol-value var) (string-match-p "^org-element--cache" (symbol-name var)) (not (memq var '(org-element--cache-interrupt-C-g-max-count org-element--cache-map-statistics-threshold org-element--cache-variables org-element--cache-interrupt-C-g-count org-element--cache-interrupt-C-g org-element--cache-element-properties org-element--cache-sensitive-re org-element--cache-hash-size org-element--cache-non-modifying-commands org-element--cache-self-verify-frequency org-element--cache-diagnostics-level)))) (should (memq var org-element-ignored-local-variables)))))) (ert-deftest test-org-element/cache-get-key () "Test `org-element-cache-get-key' and `org-element-cache-store-key'." (org-test-with-temp-text "* Heading Paragraph with text Another paragraph." (org-element-cache-store-key (org-element-lineage (org-element-at-point) '(headline)) :robust-key 'val-robust 'robust) (org-element-cache-store-key (org-element-lineage (org-element-at-point) '(headline)) :fragile-key 'val-fragile) (insert "and more text.") (should (eq 'val-robust (org-element-cache-get-key (org-element-lineage (org-element-at-point) '(headline)) :robust-key))) (should (eq 'not-found (org-element-cache-get-key (org-element-lineage (org-element-at-point) '(headline)) :fragile-key 'not-found)))) ;; No length change in the altered. (org-test-with-temp-text "* Heading Paragraph with text Another paragraph." (org-element-cache-store-key (org-element-lineage (org-element-at-point) '(headline)) :robust-key 'val-robust 'robust) (org-element-cache-store-key (org-element-lineage (org-element-at-point) '(headline)) :fragile-key 'val-fragile) (search-forward "with") (org-combine-change-calls (match-beginning 0) (match-end 0) (replace-match "asdf")) (should (eq 'val-robust (org-element-cache-get-key (org-element-lineage (org-element-at-point) '(headline)) :robust-key))) (should (eq 'not-found (org-element-cache-get-key (org-element-lineage (org-element-at-point) '(headline)) :fragile-key 'not-found))))) (provide 'test-org-element) ;;; test-org-element.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-feed.el000066400000000000000000000071251500430433700216710ustar00rootroot00000000000000;;; test-org-feed.el --- Tests for org-feed.el -*- lexical-binding: t; -*- ;; Copyright (C) 2016, 2019 Michael Brand ;; Author: Michael Brand ;; 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 . ;;; Commentary: ;; Unit tests for Org Feed library. ;;; Code: (require 'org-feed) (ert-deftest test-org-feed/fill-template () "Test `org-feed-format-entry' template specifications." ;; When working on these tests consider to also change ;; `test-org-capture/fill-template'. ;; %(sexp) placeholder. (should (equal "success!" (org-feed-format-entry nil "%(concat \"success\" \"!\")" nil))) ;; %a placeholder. (should (equal "[[https://orgmode.org]]\n" (org-feed-format-entry '(:link "https://orgmode.org") "%a" nil))) ;; %t and %T placeholders. (should (equal (format-time-string (org-time-stamp-format nil nil)) (org-feed-format-entry nil "%t" nil))) (should (string-match-p "<2016-01-02 \\S-+>" (org-feed-format-entry '(:pubDate "Sat, 02 Jan 2016 12:00:00 +0000") "%t" nil))) (should (equal (format-time-string (org-time-stamp-format t nil)) (org-feed-format-entry nil "%T" nil))) (should (string-match-p "<2016-01-02 \\S-+ 12:00>" (org-feed-format-entry '(:pubDate "Sat, 02 Jan 2016 12:00:00 +0000") "%T" nil))) ;; %u and %U placeholders. (should (equal (format-time-string (org-time-stamp-format nil t)) (org-feed-format-entry nil "%u" nil))) (should (string-match-p "[2016-01-02 \\S-+]" (org-feed-format-entry '(:pubDate "Sat, 02 Jan 2016 12:00:00 +0000") "%u" nil))) (should (equal (format-time-string (org-time-stamp-format t t)) (org-feed-format-entry nil "%U" nil))) (should (string-match-p "[2016-01-02 \\S-+ 12:00]" (org-feed-format-entry '(:pubDate "Sat, 02 Jan 2016 12:00:00 +0000") "%U" nil))) ;; %h placeholder. Make sure sexp placeholders are not expanded ;; when they are inserted through this one. (should (equal "success!" (org-feed-format-entry '(:title "success!") "%h" nil))) (should (equal "%(concat \"no \" \"evaluation\")" (org-feed-format-entry '(:title "%(concat \"no \" \"evaluation\")") "%h" nil))) ;; Test %-escaping with \ character. (should (equal "%h" (org-feed-format-entry '(:title "success!") "\\%h" nil))) (should (equal "\\success!" (org-feed-format-entry '(:title "success!") "\\\\%h" nil))) (should (equal "\\%h" (org-feed-format-entry '(:title "success!") "\\\\\\%h" nil))) ;; More than one placeholder in the same template. (should (equal "success! success! success! success!" (org-feed-format-entry '(:title "success!") "%h %h %h %h" nil))) ;; %(sexp) placeholder with an input containing the traps %, ", ) ;; and \n all at once which is complicated to parse. (should (equal "5 % Less (See\n Item \"3)\" Somewhere)" (org-feed-format-entry '(:title "5 % less (see\n item \"3)\" somewhere)") "%(capitalize \"%h\")" nil)))) (provide 'test-org-feed) ;;; test-org-feed.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-fold.el000066400000000000000000000566661500430433700217300ustar00rootroot00000000000000;;; test-org-fold.el --- tests for org-fold.el -*- lexical-binding: t; -*- ;; Authors: Ihor Radchenko ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;; Org folding tests. ;;; Code: (eval-and-compile (require 'cl-lib)) (ert-deftest test-org-fold/hide-drawer-toggle () "Test `org-fold-hide-drawer-toggle' specifications." ;; Error when not at a drawer. (should-error (org-test-with-temp-text ":fake-drawer:\ncontents" (org-fold-hide-drawer-toggle 'off) (get-char-property (line-end-position) 'invisible))) (should-error (org-test-with-temp-text "#+begin_example\n:D:\nc\n:END:\n#+end_example" (org-fold-hide-drawer-toggle t))) ;; Hide drawer. (should (org-test-with-temp-text ":drawer:\ncontents\n:end:" (org-fold-show-all) (org-fold-hide-drawer-toggle) (get-char-property (line-end-position) 'invisible))) ;; Show drawer unconditionally when optional argument is `off'. (should-not (org-test-with-temp-text ":drawer:\ncontents\n:end:" (org-fold-hide-drawer-toggle) (org-fold-hide-drawer-toggle 'off) (get-char-property (line-end-position) 'invisible))) ;; Hide drawer unconditionally when optional argument is non-nil. (should (org-test-with-temp-text ":drawer:\ncontents\n:end:" (org-fold-hide-drawer-toggle t) (get-char-property (line-end-position) 'invisible))) ;; Do not hide drawer when called from final blank lines. (should-not (org-test-with-temp-text ":drawer:\ncontents\n:end:\n\n" (org-fold-show-all) (org-fold-hide-drawer-toggle) (goto-char (point-min)) (get-char-property (line-end-position) 'invisible))) ;; Don't leave point in an invisible part of the buffer when hiding ;; a drawer away. (should-not (org-test-with-temp-text ":drawer:\ncontents\n:end:" (org-fold-hide-drawer-toggle) (get-char-property (point) 'invisible)))) (ert-deftest test-org/hide-block-toggle () "Test `org-fold-hide-block-toggle' specifications." ;; Error when not at a block. (should-error (org-test-with-temp-text "#+BEGIN_QUOTE\ncontents" (org-fold-hide-block-toggle 'off) (get-char-property (line-end-position) 'invisible))) ;; Hide block. (should (org-test-with-temp-text "#+BEGIN_CENTER\ncontents\n#+END_CENTER" (org-fold-hide-block-toggle) (get-char-property (line-end-position) 'invisible))) (should (org-test-with-temp-text "#+BEGIN_EXAMPLE\ncontents\n#+END_EXAMPLE" (org-fold-hide-block-toggle) (get-char-property (line-end-position) 'invisible))) ;; Show block unconditionally when optional argument is `off'. (should-not (org-test-with-temp-text "#+BEGIN_QUOTE\ncontents\n#+END_QUOTE" (org-fold-hide-block-toggle) (org-fold-hide-block-toggle 'off) (get-char-property (line-end-position) 'invisible))) (should-not (org-test-with-temp-text "#+BEGIN_QUOTE\ncontents\n#+END_QUOTE" (org-fold-hide-block-toggle 'off) (get-char-property (line-end-position) 'invisible))) ;; Hide block unconditionally when optional argument is non-nil. (should (org-test-with-temp-text "#+BEGIN_QUOTE\ncontents\n#+END_QUOTE" (org-fold-hide-block-toggle t) (get-char-property (line-end-position) 'invisible))) (should (org-test-with-temp-text "#+BEGIN_QUOTE\ncontents\n#+END_QUOTE" (org-fold-hide-block-toggle) (org-fold-hide-block-toggle t) (get-char-property (line-end-position) 'invisible))) ;; Do not hide block when called from final blank lines. (should-not (org-test-with-temp-text "#+BEGIN_QUOTE\ncontents\n#+END_QUOTE\n\n" (org-fold-hide-block-toggle) (goto-char (point-min)) (get-char-property (line-end-position) 'invisible))) ;; Don't leave point in an invisible part of the buffer when hiding ;; a block away. (should-not (org-test-with-temp-text "#+BEGIN_QUOTE\ncontents\n#+END_QUOTE" (org-fold-hide-block-toggle) (get-char-property (point) 'invisible)))) (ert-deftest test-org-fold/hide-block-toggle-maybe () "Test `org-fold-hide-block-toggle' specifications." (should (org-test-with-temp-text "#+BEGIN: dynamic\nContents\n#+END:" (org-hide-block-toggle))) (should-error (org-test-with-temp-text "Paragraph" (org-hide-block-toggle)))) (ert-deftest test-org-fold/org-fold-hide-entry () "Test `org-fold-hide-entry' specifications." ;; Do nothing on empty heading with children. (should-not (org-test-with-temp-text "* HEADING ** subheading1 ** subheading2 " (org-fold-hide-entry) (org-invisible-p (line-end-position)))) ;; Text inside entry. Hide it. (should (org-test-with-temp-text "* HEADING Some text here ** subheading1 ** subheading2 " (org-fold-hide-entry) (org-invisible-p (line-end-position)))) ;; Heading at EOB. Do nothing. (should-not (org-test-with-temp-text "* HEADING" (org-fold-hide-entry) (org-invisible-p (line-end-position))))) (ert-deftest test-org-fold/show-set-visibility () "Test `org-fold-show-set-visibility' specifications." ;; Do not throw an error before first heading. (should (org-test-with-temp-text "Preamble\n* Headline" (org-fold-show-set-visibility 'tree) t)) ;; Test all visibility spans, both on headline and in entry. (let ((list-visible-lines (lambda (state headerp) (org-test-with-temp-text "* Grandmother (0) ** Uncle (1) *** Heir (2) ** Father (3) Ancestor text (4) *** Sister (5) Sibling text (6) *** Self (7) Match (8) **** First born (9) Child text (10) **** The other child (11) *** Brother (12) ** Aunt (13) " (org-cycle t) (search-forward (if headerp "Self" "Match")) (org-fold-show-set-visibility state) (goto-char (point-min)) (let (result (line 0)) (while (not (eobp)) (unless (org-invisible-p2) (push line result)) (cl-incf line) (forward-line)) (nreverse result)))))) (should (equal '(0 7) (funcall list-visible-lines 'minimal t))) (should (equal '(0 7 8) (funcall list-visible-lines 'minimal nil))) (should (equal '(0 7 8 9) (funcall list-visible-lines 'local t))) (should (equal '(0 7 8 9) (funcall list-visible-lines 'local nil))) (should (equal '(0 3 7) (funcall list-visible-lines 'ancestors t))) (should (equal '(0 3 7 8) (funcall list-visible-lines 'ancestors nil))) (should (equal '(0 3 7 8 9 10 11) (funcall list-visible-lines 'ancestors-full t))) (should (equal '(0 3 7 8 9 10 11) (funcall list-visible-lines 'ancestors-full nil))) (should (equal '(0 3 5 7 12) (funcall list-visible-lines 'lineage t))) (should (equal '(0 3 5 7 8 9 12) (funcall list-visible-lines 'lineage nil))) (should (equal '(0 1 3 5 7 12 13) (funcall list-visible-lines 'tree t))) (should (equal '(0 1 3 5 7 8 9 11 12 13) (funcall list-visible-lines 'tree nil))) (should (equal '(0 1 3 4 5 7 12 13) (funcall list-visible-lines 'canonical t))) (should (equal '(0 1 3 4 5 7 8 9 11 12 13) (funcall list-visible-lines 'canonical nil)))) ;; When point is hidden in a drawer or a block, make sure to make it ;; visible. (should-not (org-test-with-temp-text "#+BEGIN_QUOTE\nText\n#+END_QUOTE" (org-fold-hide-block-toggle) (search-forward "Text") (org-fold-show-set-visibility 'minimal) (org-invisible-p2))) (should-not (org-test-with-temp-text ":DRAWER:\nText\n:END:" (org-fold-hide-drawer-toggle) (search-forward "Text") (org-fold-show-set-visibility 'minimal) (org-invisible-p2))) (should-not (org-test-with-temp-text "#+BEGIN_QUOTE\n:DRAWER:\nText\n:END:\n#+END_QUOTE" (org-fold-hide-drawer-toggle) (forward-line -1) (org-fold-hide-block-toggle) (search-forward "Text") (org-fold-show-set-visibility 'minimal) (org-invisible-p2)))) (ert-deftest test-org-fold/copy-visible () "Test `org-copy-visible' specifications." ;;`org-unfontify-region', which is wired up to ;; `font-lock-unfontify-region-function', removes the invisible text ;; property, among other things. (cl-letf (((symbol-function 'org-unfontify-region) #'ignore)) (should (equal "Foo" (org-test-with-temp-text "Foo" (let ((kill-ring nil)) (org-copy-visible (point-min) (point-max)) (current-kill 0 t))))) ;; Skip invisible characters by text property. (should (equal "Foo" (org-test-with-temp-text #("Foo" 1 9 (invisible t)) (let ((kill-ring nil)) (org-copy-visible (point-min) (point-max)) (current-kill 0 t))))) ;; Skip invisible characters by overlay. (should (equal "Foo" (org-test-with-temp-text "Foo" (let ((o (make-overlay 2 10))) (overlay-put o 'invisible t)) (let ((kill-ring nil)) (org-copy-visible (point-min) (point-max)) (current-kill 0 t))))) ;; Handle invisible characters at the beginning and the end of the ;; buffer. (should (equal "Foo" (org-test-with-temp-text #("Foo" 0 8 (invisible t)) (let ((kill-ring nil)) (org-copy-visible (point-min) (point-max)) (current-kill 0 t))))) (should (equal "Foo" (org-test-with-temp-text #("Foo" 3 11 (invisible t)) (let ((kill-ring nil)) (org-copy-visible (point-min) (point-max)) (current-kill 0 t))))) ;; Handle multiple visible parts. (should (equal "abc" (org-test-with-temp-text #("aXbXc" 1 2 (invisible t) 3 4 (invisible t)) (let ((kill-ring nil)) (org-copy-visible (point-min) (point-max)) (current-kill 0 t))))) ;; Handle adjacent invisible parts. (should (equal "ab" (org-test-with-temp-text #("aXXb" 1 2 (invisible t) 2 3 (invisible org-link)) (let ((kill-ring nil)) (org-copy-visible (point-min) (point-max)) (current-kill 0 t))))) ;; Copies text based on what's actually visible, as defined by ;; `buffer-invisibility-spec'. (should (equal "aYb" (org-test-with-temp-text #("aXYb" 1 2 (invisible t) 2 3 (invisible org-test-copy-visible)) (let ((kill-ring nil)) (org-copy-visible (point-min) (point-max)) (current-kill 0 t))))))) (ert-deftest test-org-fold/set-visibility-according-to-property () "Test `org-set-visibility-according-to-property' specifications." ;; "folded" state. (should (org-test-with-temp-text " * a :PROPERTIES: :VISIBILITY: folded :END: ** b" (org-set-visibility-according-to-property) (invisible-p (point)))) (org-test-with-temp-text " #+STARTUP: overview * A ** AA ** AB *** ABA :PROPERTIES: :VISIBILITY: folded :END: **** ABAA **** ABAB **** ABAC ** AC * B " (org-set-regexps-and-options) (org-cycle-set-startup-visibility) (search-forward "A") (should-not (invisible-p (point))) (search-forward "AB") (should (invisible-p (point))) (search-forward "ABA") (should (invisible-p (point))) (search-forward "ABAB") (should (invisible-p (point))) (search-forward "AC") (should (invisible-p (point))) (search-forward "B") (should-not (invisible-p (point)))) ;; "children" state. (should (org-test-with-temp-text " * a :PROPERTIES: :VISIBILITY: children :END: ** b Contents ** c" (org-set-visibility-according-to-property) (invisible-p (point)))) (should (org-test-with-temp-text " * a :PROPERTIES: :VISIBILITY: children :END: ** b Contents *** c" (org-set-visibility-according-to-property) (invisible-p (point)))) ;; "content" state. (should (org-test-with-temp-text " * a :PROPERTIES: :VISIBILITY: content :END: ** b Contents *** c" (org-set-visibility-according-to-property) (invisible-p (point)))) (should (org-test-with-temp-text " * a :PROPERTIES: :VISIBILITY: content :END: ** b Contents *** c" (org-set-visibility-according-to-property) (not (invisible-p (point))))) ;; "showall" state. (should (org-test-with-temp-text " * a :PROPERTIES: :VISIBILITY: showall :END: ** b Contents *** c" (org-set-visibility-according-to-property) (not (invisible-p (point))))) (should (org-test-with-temp-text " * a :PROPERTIES: :VISIBILITY: showall :END: ** b Contents *** c" (org-set-visibility-according-to-property) (not (invisible-p (point))))) ;; When VISIBILITY properties are nested, do not alter parent ;; visibility unless necessary. (should (org-test-with-temp-text " * A :PROPERTIES: :VISIBILITY: folded :END: ** B :PROPERTIES: :VISIBILITY: folded :END:" (org-set-visibility-according-to-property) (invisible-p (point)))) (should (org-test-with-temp-text " * A :PROPERTIES: :VISIBILITY: folded :END: ** B :PROPERTIES: :VISIBILITY: content :END:" (org-set-visibility-according-to-property) (not (invisible-p (point)))))) (ert-deftest test-org-fold/visibility-show-branches () "Test visibility of inline archived subtrees." (org-test-with-temp-text "* Foo ** Bar :ARCHIVE: *** Baz " (org-kill-note-or-show-branches) (should (org-invisible-p (- (point-max) 2))))) (ert-deftest test-org-fold/org-cycle-narrowed-subtree () "Test cycling in narrowed buffer." (org-test-with-temp-text "* Heading 1 ** Child 1.1 ** Child 1.2 some text *** Sub-child 1.2.1 * Heading 2" (org-overview) (org-narrow-to-subtree) (org-cycle) (re-search-forward "Sub-child") (should (org-invisible-p)))) (ert-deftest test-org-fold/org-fold-reveal-broken-structure () "Test unfolding broken elements." (let ((org-fold-core-style 'text-properties)) (org-test-with-temp-text "* Heading 1 Text here" (org-overview) (re-search-forward "Text") (should (org-invisible-p)) (goto-char 1) (org-delete-char 1) (run-hooks 'post-command-hook) (re-search-forward "Text") (should-not (org-invisible-p))) (org-test-with-temp-text "* Heading 1 Text here" (org-overview) (re-search-forward "Text") (should (org-invisible-p)) (goto-char 1) (let ((last-command-event ?a)) (org-self-insert-command 1)) (run-hooks 'post-command-hook) (re-search-forward "Text") (should-not (org-invisible-p))) (org-test-with-temp-text "* Heading 1 :PROPERTIES: :ID: something :END: Text here" (org-cycle) (org-fold-hide-drawer-all) (re-search-forward "ID") (should (org-invisible-p)) (re-search-backward ":PROPERTIES:") (delete-char 1) (run-hooks 'post-command-hook) (re-search-forward "ID") (should-not (org-invisible-p))) (org-test-with-temp-text "* Heading 1 :PROPERTIES: :ID: something :END: Text here" (org-cycle) (org-fold-hide-drawer-all) (re-search-forward "ID") (should (org-invisible-p)) (re-search-forward ":END:") (delete-char -1) (run-hooks 'post-command-hook) (re-search-backward "ID") (should-not (org-invisible-p))) (org-test-with-temp-text "* Heading 1 #+begin_src emacs-lisp (+ 1 2) #+end_src Text here" (org-cycle) (org-fold-hide-drawer-all) (re-search-forward "end") (should (org-invisible-p)) (delete-char -1) (run-hooks 'post-command-hook) (re-search-backward "2") (should-not (org-invisible-p))))) (ert-deftest test-org-fold/re-hide-edits-inside-fold () "Test edits inside folded regions." (org-test-with-temp-text "* Heading 1 Text here" (org-overview) (org-set-property "TEST" "1") (re-search-forward "TEST") (should (org-invisible-p))) (org-test-with-temp-text "* Heading 1 Text here" (org-overview) (insert " and extra heading text") (re-search-backward "heading") (should-not (org-invisible-p))) (org-test-with-temp-text "* Heading 1 Text here" (org-overview) (insert " and extra text") (re-search-backward "extra") (should (org-invisible-p)))) (defmacro test-org-fold-with-default-template (&rest body) "Run `org-test-with-temp-text' using default folded template." (declare (indent 0)) `(let ((org-link-descriptive t)) (org-test-with-temp-text "#+STARTUP: showeverything * Folded heading Folded Paragraph inside heading. * Unfolded heading :FOLDED-DRAWER: Folded Paragraph inside drawer. :END: Unfolded Paragraph. #+begin_src emacs-lisp (message \"Folded block\") #+end_src [[hiddenlink][link]] " (org-cycle) (search-forward "FOLDED-DRAWER") (org-hide-drawer-toggle t) (search-forward "begin_src") (org-hide-block-toggle t) (goto-char 1) ,@body))) (ert-deftest test-org-fold/org-catch-invisible-edits () "Test invisible edits handling." ;; Disable delay in `org-fold-check-before-invisible-edit'. (cl-letf (((symbol-function 'sit-for) #'ignore)) (dolist (org-fold-core-style '(text-properties overlays)) (dolist (org-fold-catch-invisible-edits '(nil error smart show show-and-error)) (dolist (kind '(insert delete-backward delete nil)) (message "Testing invisible edits: %S:%S:%S" org-fold-core-style org-fold-catch-invisible-edits kind) ;; Edits outside invisible. (test-org-fold-with-default-template (search-forward "Unfolded Paragraph") (message "Outside invisible") (org-fold-check-before-invisible-edit kind) (should-not (org-invisible-p))) ;; Edits inside invisible region. (test-org-fold-with-default-template (dolist (txt '("Folded Paragraph inside heading" "Folded Paragraph inside drawer" "Folded block")) (search-forward txt) (message "Inside invisible %S" txt) (pcase org-fold-catch-invisible-edits (`nil (org-fold-check-before-invisible-edit kind) (should (org-invisible-p))) (`show (org-fold-check-before-invisible-edit kind) (should-not (org-invisible-p))) ((or `smart `show-and-error) (should-error (org-fold-check-before-invisible-edit kind)) (should-not (org-invisible-p))) (`error (should-error (org-fold-check-before-invisible-edit kind)) (should (org-invisible-p))))) (search-forward "hiddenlink") (message "Inside hidden link") (org-fold-check-before-invisible-edit kind) (should (org-invisible-p))) ;; Edits at the left border. (test-org-fold-with-default-template (dolist (txt '("Folded heading" ":FOLDED-DRAWER:" "#+begin_src emacs-lisp")) (search-forward txt) (message "Left of folded %S" txt) (pcase org-fold-catch-invisible-edits (`nil (org-fold-check-before-invisible-edit kind) (should (org-invisible-p (1+ (point))))) (`show (org-fold-check-before-invisible-edit kind) (should-not (org-invisible-p (1+ (point))))) (`smart (if (memq kind '(insert delete-backward)) (org-fold-check-before-invisible-edit kind) (should-error (org-fold-check-before-invisible-edit kind))) (should-not (org-invisible-p (1+ (point))))) (`show-and-error (should-error (org-fold-check-before-invisible-edit kind)) (should-not (org-invisible-p (1+ (point))))) (`error (should-error (org-fold-check-before-invisible-edit kind)) (should (org-invisible-p (1+ (point))))))) (search-forward "hiddenlink") (search-forward "lin") (message "Left border of ]] in link") (org-fold-check-before-invisible-edit kind) (should (org-invisible-p (1+ (point))))) ;; Edits at the right border. (test-org-fold-with-default-template (dolist (txt '("Folded Paragraph inside heading." ":END:" "#+end_src")) (search-forward txt) (message "After %S" txt) (pcase org-fold-catch-invisible-edits (`nil (org-fold-check-before-invisible-edit kind) (should (org-invisible-p (1- (point))))) (`show (org-fold-check-before-invisible-edit kind) (should-not (org-invisible-p (1- (point))))) ((or `smart `show-and-error) (should-error (org-fold-check-before-invisible-edit kind)) (should-not (org-invisible-p (1- (point))))) (`error (should-error (org-fold-check-before-invisible-edit kind)) (should (org-invisible-p (1- (point))))))) (search-forward "hiddenlink") (search-forward "link]]") (message "Right border of ]] in link") (org-fold-check-before-invisible-edit kind) (should (org-invisible-p (1- (point)))))))))) (ert-deftest test-org-fold/org-fold-display-inline-images () "Test inline images displaying when cycling." (skip-unless (not noninteractive)) (let* ((org-cycle-inline-images-display t) (images-dir (expand-file-name "examples/images/" org-test-dir)) (org-logo-image (expand-file-name "Org mode logo mono-color.png" images-dir))) ;; `org-cycle' -(state)-> `'children' display child inline images. ;; TODO: ;; `org-cycle' -(state)-> `'subtree' display subtrees inline images. ;; TODO: ;; `org-cycle' -(state)-> `'folded' remove inline image overlays. (org-test-with-temp-text (format "* Heading 1 [[file:%s]] ** Subheading 1 [[file:%s]] ** Subheading 2 [[file:%s]]" org-logo-image org-logo-image org-logo-image) (org-overview) (org-show-subtree) (org-fold-subtree t) (run-hook-with-args 'org-cycle-hook 'folded) (should-not org-inline-image-overlays) (should-not (cl-every (lambda (ov) (overlay-get ov 'org-image-overlay)) (overlays-in (point-min) (point-max)))) (org-show-subtree) (run-hook-with-args 'org-cycle-hook 'subtree) (should org-inline-image-overlays) (should (cl-every (lambda (ov) (overlay-get ov 'org-image-overlay)) (overlays-in (point-min) (point-max))))))) (provide 'test-org-fold) ;;; test-org-fold.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-footnote.el000066400000000000000000000466711500430433700226340ustar00rootroot00000000000000;;; test-org-footnote.el --- Tests for org-footnote.el -*- lexical-binding: t; -*- ;; Copyright (C) 2012-2015, 2019 Nicolas Goaziou ;; Author: Nicolas Goaziou ;; 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 . ;;; Code: (require 'org-footnote) (ert-deftest test-org-footnote/new-anon () "Test `org-footnote-new' specifications." ;; `org-footnote-auto-label' is `anonymous'. (should (string-match-p "Test\\[fn::\\]" (org-test-with-temp-text "Test" (let ((org-footnote-auto-label 'anonymous) (org-footnote-section nil)) (org-footnote-new)) (buffer-string))))) (ert-deftest test-org-footnote/new () "Test `org-footnote-new' specifications." ;; `org-footnote-auto-label' is t. (should (string-match-p "Test\\[fn:1\\]\n+\\[fn:1\\]" (org-test-with-temp-text "Test" (let ((org-footnote-auto-label t) (org-footnote-section nil)) (org-footnote-new)) (buffer-string)))) ;; `org-footnote-auto-label' is `random'. (should (string-match-p "Test\\[fn:\\(.+?\\)\\]\n+\\[fn:\\1\\]" (org-test-with-temp-text "Test" (let ((org-footnote-auto-label 'random) (org-footnote-section nil)) (org-footnote-new)) (buffer-string)))) ;; Error at beginning of line. (should-error (org-test-with-temp-text "Test" (org-footnote-new))) ;; Error at keywords. (should-error (org-test-with-temp-text "#+TITLE: value" (org-footnote-new))) (should-error (org-test-with-temp-text "#+CAPTION: \nParagraph" (org-footnote-new))) ;; Allow new footnotes in blank lines at the beginning of the ;; document. (should (string-match-p " \\[fn:1\\]" (org-test-with-temp-text " " (let ((org-footnote-auto-label t)) (org-footnote-new)) (buffer-string)))) ;; In an headline or inlinetask, point must be either on the ;; heading itself or on the blank lines below. (should (org-test-with-temp-text "* H" (org-footnote-new) t)) (should (org-test-with-temp-text "* H\n \nParagraph" (org-footnote-new) t)) (should-error (org-test-with-temp-text "* H" (org-footnote-new) t)) (should-error (org-test-with-temp-text "* H :tag:" (org-footnote-new) t)) ;; Allow new footnotes within recursive objects, but not in links. (should (string-match-p " \\*bold\\[fn:1\\]\\*" (org-test-with-temp-text " *bold*" (let ((org-footnote-auto-label t)) (org-footnote-new)) (buffer-string)))) (should-error (org-test-with-temp-text " [[https://orgmode.org][Org mode]]" (org-footnote-new))) ;; Allow new footnotes in blank lines after an element or white ;; spaces after an object. (should (string-match-p " \\[fn:1\\]" (org-test-with-temp-text "#+BEGIN_EXAMPLE\nA\n#+END_EXAMPLE\n " (let ((org-footnote-auto-label t)) (org-footnote-new)) (buffer-string)))) (should (string-match-p " \\*bold\\*\\[fn:1\\]" (org-test-with-temp-text " *bold*" (let ((org-footnote-auto-label t)) (org-footnote-new)) (buffer-string)))) ;; Allow new footnotes at the start of a footnote definition. (should (string-match-p "\\[fn:1\\]\\[fn:2\\]" (org-test-with-temp-text "[fn:1]" (let ((org-footnote-auto-label t)) (org-footnote-new)) (buffer-string)))) (should (string-match-p "\\[fn:1\\] \\[fn:2\\]" (org-test-with-temp-text "[fn:1] " (let ((org-footnote-auto-label t)) (org-footnote-new)) (buffer-string)))) (should (string-match-p "\\[fn:1\\]\\[fn:2\\]" (org-test-with-temp-text "[fn:1] \nParagraph" (let ((org-footnote-auto-label t)) (org-footnote-new)) (buffer-string)))) (should-error (org-test-with-temp-text "[fn:1]" (let ((org-footnote-auto-label t)) (org-footnote-new)) (buffer-string))) (should-error (org-test-with-temp-text "[fn:1]" (let ((org-footnote-auto-label t)) (org-footnote-new)) (buffer-string))) ;; Allow new footnotes in table cells. (should (string-match-p " \\[fn:1\\]" (org-test-with-temp-text "| |" (let ((org-footnote-auto-label t)) (org-footnote-new)) (buffer-string)))) (should (string-match-p "|\\[fn:1\\]" (org-test-with-temp-text "| |" (let ((org-footnote-auto-label t)) (org-footnote-new)) (buffer-string)))) (should (string-match-p " \\[fn:1\\]" (org-test-with-temp-text "| |" (let ((org-footnote-auto-label t)) (org-footnote-new)) (buffer-string)))) (should (string-match-p " \\[fn:1\\]" (org-test-with-temp-text "| contents |" (let ((org-footnote-auto-label t)) (org-footnote-new)) (buffer-string)))) ;; When creating a new footnote, move to its definition. (should (string= "[fn:1]" (org-test-with-temp-text "Text" (let ((org-footnote-auto-label t) (org-footnote-auto-adjust nil)) (org-footnote-new)) (buffer-substring-no-properties (line-beginning-position) (point))))) ;; Re-order and re-label footnotes properly when ;; `org-footnote-auto-adjust' is non-nil. (should (string= "[fn:1] 1\n\n[fn:2] \n\n[fn:3] 2\n" (org-test-with-temp-text "Text[fn:1]TextText[fn:2]\n\n[fn:1] 1\n\n[fn:2] 2" (let ((org-footnote-auto-label t) (org-footnote-auto-adjust t) (org-footnote-section nil)) (org-footnote-new)) (buffer-substring-no-properties (line-beginning-position -1) (line-beginning-position 4))))) ;; Do not alter file local variables when inserting new definition ;; label. (should (equal "Paragraph[fn:1] \[fn:1] # Local Variables: # foo: t # End:" (org-test-with-temp-text "Paragraph\n# Local Variables:\n# foo: t\n# End:" (let ((org-footnote-section nil)) (org-footnote-new)) (buffer-string)))) (should (equal "Paragraph[fn:1] * Footnotes \[fn:1] # Local Variables: # foo: t # End:" (org-test-with-temp-text "Paragraph\n# Local Variables:\n# foo: t\n# End:" (let ((org-footnote-section "Footnotes")) (org-footnote-new)) (buffer-string)))) (should (equal "Para[fn:1] * Footnotes :properties: :custom_id: id :end: \[fn:1]" (org-test-with-temp-text "Para\n* Footnotes\n:properties:\n:custom_id: id\n:end:" (let ((org-footnote-section "Footnotes")) (org-footnote-new)) (org-trim (buffer-string)))))) (ert-deftest test-org-footnote/delete () "Test `org-footnote-delete' specifications." ;; Regular test. (should (equal "Paragraph" (org-test-with-temp-text "Paragraph[fn:1]\n\n[fn:1] Definition" (org-footnote-delete) (org-trim (buffer-string))))) ;; Remove multiple definitions and references. (should (equal "Paragraph and another" (org-test-with-temp-text "Paragraph[fn:1] and another[fn:1] \[fn:1] def \[fn:1] def" (org-footnote-delete) (org-trim (buffer-string))))) ;; Delete inline footnotes and all references. (should (equal "Para and" (org-test-with-temp-text "Para[fn:label:def] and[fn:label]" (org-footnote-delete) (org-trim (buffer-string))))) ;; Delete anonymous footnotes. (should (equal "Para" (let ((org-footnote-section nil)) (org-test-with-temp-text "Para[fn::def]" (org-footnote-delete) (org-trim (buffer-string)))))) ;; With an argument, delete footnote with specified label. (should (equal "Paragraph[fn:1] and another\n\n[fn:1] def" (let ((org-footnote-section nil)) (org-test-with-temp-text "Paragraph[fn:1] and another[fn:2]\n\n[fn:1] def\n\n[fn:2] def2" (org-footnote-delete "2") (org-trim (buffer-string)))))) ;; Error when no argument is specified at point is not at a footnote ;; reference. (should-error (org-test-with-temp-text "Para[fn:1]\n\n[fn:1] Def" (org-footnote-delete))) ;; Correctly delete footnotes with multiple paragraphs. (should (equal "Para\n\n\nOutside footnote." (let ((org-footnote-section nil)) (org-test-with-temp-text "Para[fn:1]\n\n[fn:1] para1\n\npara2\n\n\nOutside footnote." (org-footnote-delete "1") (org-trim (buffer-string)))))) ;; Remove blank lines above the footnote but preserve those after ;; it. (should (equal "Text\n\n\nOther text." (let ((org-footnote-section nil)) (org-test-with-temp-text "Text[fn:1]\n\n[fn:1] Definition.\n\n\nOther text." (org-footnote-delete "1") (buffer-string))))) ;; Preserve file local variables when deleting a footnote. (should (equal "Paragraph\n# Local Variables:\n# foo: t\n# End:" (org-test-with-temp-text "Paragraph[fn:1]\n[fn:1] Def 1\n# Local Variables:\n# foo: t\n# End:" (let ((org-footnote-section nil)) (org-footnote-delete "1")) (buffer-string))))) (ert-deftest test-org-footnote/goto-definition () "Test `org-footnote-goto-definition' specifications." ;; Error on unknown definitions. (should-error (org-test-with-temp-text "No footnote definition" (org-footnote-goto-definition "1"))) ;; Error when trying to reach a definition outside narrowed part of ;; buffer. (should-error (org-test-with-temp-text "Some text\n[fn:1] Definition." (narrow-to-region (point-min) (point)) (org-footnote-goto-definition "1"))) (should-error (org-test-with-temp-text "[fn:1] Definition.\nSome text" (narrow-to-region (point) (point-max)) (org-footnote-goto-definition "1"))) ;; Otherwise, move at the beginning of the definition, including ;; anonymous footnotes. (should (equal " Definition." (org-test-with-temp-text "Some text\n[fn:1] Definition." (org-footnote-goto-definition "1") (buffer-substring (point) (point-max))))) (should (equal "definition]" (org-test-with-temp-text "Some text[fn:label:definition]" (org-footnote-goto-definition "label") (buffer-substring (point) (point-max)))))) (ert-deftest test-org-footnote/goto-previous-reference () "Test `org-footnote-goto-previous-reference' specifications." ;; Error on unknown reference. (should-error (org-test-with-temp-text "No footnote reference" (org-footnote-goto-previous-reference "1"))) ;; Error when trying to reach a reference outside narrowed part of ;; buffer. (should-error (org-test-with-temp-text "Some text\nReference[fn:1]." (narrow-to-region (point-min) (point)) (org-footnote-goto-previous-reference "1"))) ;; Otherwise, move to closest reference from point. (should (org-test-with-temp-text "First reference[fn:1]\nReference[fn:1]." (org-footnote-goto-previous-reference "1") (= (line-end-position) (point-max)))) (should (org-test-with-temp-text "First reference[fn:1]\nReference[fn:1]." (org-footnote-goto-previous-reference "1") (= (line-beginning-position) (point-min))))) (ert-deftest test-org-footnote/sort () "Test `org-footnote-sort' specifications." ;; Reorder definitions with a nil `org-footnote-section'. In this ;; case each definition is written at the end of the section ;; containing its first reference. (should (equal " Text[fn:1][fn:2] \[fn:1] Def 1 \[fn:2] Def 2 " (org-test-with-temp-text " Text[fn:1][fn:2] \[fn:2] Def 2 \[fn:1] Def 1" (let ((org-footnote-section nil)) (org-footnote-sort)) (buffer-string)))) (should (equal " * H1 Text[fn:1] \[fn:1] Def 1 * H2 Text[fn:2] \[fn:2] Def 2 " (org-test-with-temp-text " * H1 Text[fn:1] * H2 Text[fn:2] \[fn:1] Def 1 \[fn:2] Def 2 " (let ((org-footnote-section nil)) (org-footnote-sort)) (buffer-string)))) ;; Reorder definitions with a non-nil `org-footnote-section'. (should (equal " Text[fn:1][fn:2] * Footnotes \[fn:1] Def 1 \[fn:2] Def 2 " (org-test-with-temp-text " Text[fn:1][fn:2] \[fn:2] Def 2 \[fn:1] Def 1" (let ((org-footnote-section "Footnotes")) (org-footnote-sort)) (buffer-string)))) ;; When `org-footnote-section' is non-nil, clear previous footnote ;; sections. (should (equal " Text[fn:1] * Headline * Other headline * Footnotes \[fn:1] Def 1 " (org-test-with-temp-text " Text[fn:1] * Footnotes \[fn:1] Def 1 * Headline ** Footnotes * Other headline" (let ((org-footnote-section "Footnotes")) (org-footnote-sort)) (buffer-string)))) ;; Ignore anonymous footnotes. (should (equal " Text[fn:1][fn::inline][fn:2] \[fn:1] Def 1 \[fn:2] Def 2 " (org-test-with-temp-text " Text[fn:1][fn::inline][fn:2] \[fn:2] Def 2 \[fn:1] Def 1" (let ((org-footnote-section nil)) (org-footnote-sort)) (buffer-string)))) ;; Ignore inline footnotes. (should (equal " Text[fn:1][fn:label:inline][fn:2] \[fn:1] Def 1 \[fn:2] Def 2 " (org-test-with-temp-text " Text[fn:1][fn:label:inline][fn:2] \[fn:2] Def 2 \[fn:1] Def 1" (let ((org-footnote-section nil)) (org-footnote-sort)) (buffer-string)))) ;; Handle (deeply) nested footnotes. (should (equal " Text[fn:1][fn:3] \[fn:1] Def 1[fn:2] \[fn:2] Def 2 \[fn:3] Def 3 " (org-test-with-temp-text " Text[fn:1][fn:3] \[fn:1] Def 1[fn:2] \[fn:3] Def 3 \[fn:2] Def 2 " (let ((org-footnote-section nil)) (org-footnote-sort)) (buffer-string)))) (should (equal " Text[fn:1][fn:4] \[fn:1] Def 1[fn:2] \[fn:2] Def 2[fn:3] \[fn:3] Def 3 \[fn:4] Def 4 " (org-test-with-temp-text " Text[fn:1][fn:4] \[fn:1] Def 1[fn:2] \[fn:3] Def 3 \[fn:2] Def 2[fn:3] \[fn:4] Def 4 " (let ((org-footnote-section nil)) (org-footnote-sort)) (buffer-string)))) ;; When multiple (nested) references are used, make sure to insert ;; definition only once. (should (equal " * Section 1 Text[fn:1] \[fn:1] Def 1 * Section 2 Text[fn:1]" (org-test-with-temp-text " * Section 1 Text[fn:1] \[fn:1] Def 1 * Section 2 Text[fn:1]" (let ((org-footnote-section nil)) (org-footnote-sort)) (buffer-string)))) (should (equal " Text[fn:1][fn:4] \[fn:1] Def 1[fn:2][fn:3] \[fn:2] Def 2[fn:3] \[fn:3] Def 3 \[fn:4] Def 4 " (org-test-with-temp-text " Text[fn:1][fn:4] \[fn:1] Def 1[fn:2][fn:3] \[fn:3] Def 3 \[fn:2] Def 2[fn:3] \[fn:4] Def 4 " (let ((org-footnote-section nil)) (org-footnote-sort)) (buffer-string)))) ;; Insert un-referenced definitions at the end. (should (equal "Text[fn:9] \[fn:9] B \[fn:1] A " (org-test-with-temp-text "Text[fn:9]\n\n[fn:1] A\n[fn:9] B" (let ((org-footnote-section nil)) (org-footnote-sort)) (buffer-string)))) ;; When sorting, preserve file local variables. (should (equal " Paragraph[fn:1][fn:2] \[fn:1] Def 1 \[fn:2] Def 2 # Local Variables: # foo: t # End:" (org-test-with-temp-text " Paragraph[fn:1][fn:2] \[fn:2] Def 2 \[fn:1] Def 1 # Local Variables: # foo: t # End:" (let ((org-footnote-section nil)) (org-footnote-sort)) (buffer-string))))) (ert-deftest test-org-footnote/renumber-fn:N () "Test `org-footnote-renumber-fn:N' specifications." ;; Renumber (inline) references and definitions. (should (equal "Test[fn:1]" (org-test-with-temp-text "Test[fn:99]" (org-footnote-renumber-fn:N) (buffer-string)))) (should (equal "Test[fn:1]\n\n[fn:1] 99" (org-test-with-temp-text "Test[fn:99]\n\n[fn:99] 99" (org-footnote-renumber-fn:N) (buffer-string)))) (should (equal "Test[fn:1:99]" (org-test-with-temp-text "Test[fn:99:99]" (org-footnote-renumber-fn:N) (buffer-string)))) ;; No-op if there's no numbered footnote. (should (equal "Test[fn:label]\n\n[fn:label] Def" (org-test-with-temp-text "Test[fn:label]\n\n[fn:label] Def" (org-footnote-renumber-fn:N) (buffer-string)))) ;; Definitions without a reference get the highest numbers. (should (equal "Test[fn:1]\n[fn:1] 1\n[fn:2] 99" (org-test-with-temp-text "Test[fn:1]\n[fn:1] 1\n[fn:99] 99" (org-footnote-renumber-fn:N) (buffer-string)))) ;; Sort labels in sequence. Anonymous footnotes are ignored. (should (equal "Test[fn:1][fn:2:def][fn:3]" (org-test-with-temp-text "Test[fn:4][fn:3:def][fn:2]" (org-footnote-renumber-fn:N) (buffer-string)))) (should (equal "Test[fn:1][fn::def][fn:2]" (org-test-with-temp-text "Test[fn:4][fn::def][fn:2]" (org-footnote-renumber-fn:N) (buffer-string))))) (ert-deftest test-org-footnote/normalize () "Test `org-footnote-normalize' specifications." ;; Normalize regular, inline and anonymous references. (should (equal "Test[fn:1]\n\n[fn:1] def\n" (org-test-with-temp-text "Test[fn:label]\n[fn:label] def" (let ((org-footnote-section nil)) (org-footnote-normalize)) (buffer-string)))) (should (equal "Test[fn:1]\n\n[fn:1] def\n" (org-test-with-temp-text "Test[fn:label:def]" (let ((org-footnote-section nil)) (org-footnote-normalize)) (buffer-string)))) (should (equal "Test[fn:1]\n\n[fn:1] def\n" (org-test-with-temp-text "Test[fn::def]" (let ((org-footnote-section nil)) (org-footnote-normalize)) (buffer-string)))) ;; Normalization includes sorting. (should (equal "Test[fn:1][fn:2]\n\n[fn:1] def2\n\n[fn:2] def\n" (org-test-with-temp-text "Test[fn:2][fn:1]\n\n[fn:2] def2\n[fn:1] def" (let ((org-footnote-section nil)) (org-footnote-normalize)) (buffer-string)))) (should (equal "Test[fn:1][fn:2]\n\n[fn:1] def\n\n[fn:2] inline\n" (org-test-with-temp-text "Test[fn:2][fn::inline]\n[fn:2] def\n" (let ((org-footnote-section nil)) (org-footnote-normalize)) (buffer-string)))) (should (equal "Test[fn:1][fn:3] \[fn:1] def[fn:2] \[fn:2] inline \[fn:3] last " (org-test-with-temp-text "Test[fn:lab1][fn:lab2]\n[fn:lab1] def[fn::inline]\n[fn:lab2] last" (let ((org-footnote-section nil)) (org-footnote-normalize)) (buffer-string)))) ;; When normalizing an inline reference, fill paragraph whenever the ;; `org-footnote-fill-after-inline-note-extraction' is non-nil. (should (equal "Test[fn:1] Next\n\n[fn:1] def\n" (org-test-with-temp-text "Test[fn::def]\nNext" (let ((org-footnote-section nil) (org-footnote-fill-after-inline-note-extraction t)) (org-footnote-normalize)) (buffer-string)))) ;; Insert un-referenced definitions at the end. (should (equal "Test[fn:1]\nNext\n\n[fn:1] def\n\n[fn:2] A\n" (org-test-with-temp-text "Test[fn::def]\nNext\n[fn:unref] A" (let ((org-footnote-section nil)) (org-footnote-normalize)) (buffer-string)))) ;; Preserve file local variables when normalizing. (should (equal " Paragraph[fn:1][fn:2] \[fn:1] Def 1 \[fn:2] Def 2 # Local Variables: # foo: t # End:" (org-test-with-temp-text " Paragraph[fn:foo][fn:bar] \[fn:bar] Def 2 \[fn:foo] Def 1 # Local Variables: # foo: t # End:" (let ((org-footnote-section nil)) (org-footnote-normalize)) (buffer-string))))) (provide 'test-org-footnote) ;;; test-org-footnote.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-info.el000066400000000000000000000143531500430433700217220ustar00rootroot00000000000000;;; test-org-info.el --- Tests for "org-info.el" -*- lexical-binding: t; -*- ;; Copyright (C) 2017, 2019 Nicolas Goaziou ;; Author: Nicolas Goaziou ;; 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 . ;;; Code: (ert-deftest test-org-info/export () "Test `org-info-export' specifications." ;; Export to HTML. Without node, refer to "Top". (should (equal (org-info-export "filename#node" nil 'html) "filename#node")) (should (equal (org-info-export "filename" nil 'html) "filename")) ;; Directory index. Top anchor actually should not be added, ;; but it should be rather rare case to add special code path. (should (equal (org-info-export "dir" nil 'html) "dir")) ;; When exporting to HTML, ensure node names are expanded according ;; to (info "(texinfo) HTML Xref Node Name Expansion"). (should (equal "_005f" (let ((name (org-info-export "#_" nil 'html))) (and (string-match "#\\(.*\\)\"" name) (match-string 1 name))))) (should (equal "_002d" (let ((name (org-info-export "#-" nil 'html))) (and (string-match "#\\(.*\\)\"" name) (match-string 1 name))))) (should (equal "A-node" (let ((name (org-info-export "#A node" nil 'html))) (and (string-match "#\\(.*\\)\"" name) (match-string 1 name))))) (should (equal "A-node-_002d_002d_002d-with-_005f_0027_0025" (let ((name (org-info-export "#A node --- with _'%" nil 'html))) (and (string-match "#\\(.*\\)\"" name) (match-string 1 name))))) ;; Export to Texinfo. Without a node name, refer to "Top". (should (equal (org-info-export "filename" nil 'texinfo) "@ref{Top,,,filename,}")) (should (equal (org-info-export "filename#node" nil 'texinfo) "@ref{node,,,filename,}")) ;; "Top" is preserved, "::" as node separator. (should (equal "@ref{Top,,,emacs,}" (org-info-export "emacs::Top" nil 'texinfo))) ;; Description. (should (equal "@ref{Top,Emacs,,emacs,}" (org-info-export "emacs" "Emacs" 'texinfo))) (should (equal "@ref{Destructuring with pcase Patterns,pcase-let,,emacs,}" (org-info-export "emacs#Destructuring with pcase Patterns" "pcase-let" 'texinfo)))) (ert-deftest test-org-info/link-file-node () "Test parse info links by `org-info--link-file-node'." (should (equal '("success" . "Hash Separator") (org-info--link-file-node "success#Hash Separator"))) ;; Other separators. (should (equal '("success" . "Single Colon Separator") (org-info--link-file-node "success:Single Colon Separator"))) (should (equal '("success" . "Double Colon Separator") (org-info--link-file-node "success::Double Colon Separator"))) (should (equal '("success" . "Hash Colon Separator") (org-info--link-file-node "success#:Hash Colon Separator"))) ;; Partial specification. (should (equal '("nodeless" . "Top") (org-info--link-file-node "nodeless"))) (should (equal '("dir" . "Top") (org-info--link-file-node ""))) (should (equal '("dir" . "Top") (org-info--link-file-node nil))) ;; Feel free to change behavior of underspecified links, ;; the case is added to check that it does not signal some error. (should (equal '("dir" . "broken") (org-info--link-file-node "#broken"))) ;; Trailing separator. (should (equal '("trailing-hash" . "Top") (org-info--link-file-node "trailing-hash#"))) (should (equal '("trailing-single-colon" . "Top") (org-info--link-file-node "trailing-single-colon:"))) (should (equal '("trailing-double-colon" . "Top") (org-info--link-file-node "trailing-double-colon::"))) (should (equal '("trailing-hash-colon" . "Top") (org-info--link-file-node "trailing-hash-colon#:"))) ;; Trim spaces. (should (equal '("trim" . "Spaces") (org-info--link-file-node " trim # Spaces \t")))) (ert-deftest test-org-info/description-as-command () "Test `org-info-description-as-command'." (let ((cases '(("info file" "info:file") ("info strip-top-hash" "info:strip-top-hash#Top") ("info strip-top-single-colon" "info:strip-top-single-colon:Top") ("info strip-top-double-colon" "info:strip-top-double-colon::Top") ("info \"(pass) Hash\"" "info:pass#Hash") ("info \"(pass) Double Colon\"" "info:pass:: Double Colon") ("info \"(info) Advanced\"" "info:info:Advanced") ("info \"(dir)\"" "info:") ;; It actually works as "(dir) Top", test that no errors is signalled. ("info \"(dir) Invalid\"" "info::Invalid") (nil "http://orgmode.org/index.html#Not-info-link")))) (dolist (expectation-input cases) (let ((expectation (car expectation-input)) (input (cadr expectation-input))) (should (equal expectation (org-info-description-as-command input nil)))))) (let ((cases '(("Override link" "info:ignored#Link" "Override link") ("Fallback description" "http://not.info/link" "Fallback description") ("Link is nil" nil "Link is nil")))) (dolist (expectation-input-desc cases) (let ((expectation (car expectation-input-desc)) (input (cadr expectation-input-desc)) (desc (nth 2 expectation-input-desc))) (should (equal expectation (org-info-description-as-command input desc))))))) (provide 'test-org-info) ;;; test-org-info.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-inlinetask.el000066400000000000000000000067741500430433700231400ustar00rootroot00000000000000;;; test-org-inlinetask.el --- Tests for org-inlinetask.el -*- lexical-binding: t; -*- ;; Copyright (c) Marco Wahl ;; Authors: Marco Wahl ;; 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 . ;;; Comments: ;; Tests for org-inlinetask.el. ;;; Code: (require 'org-inlinetask) ;;; Test movement (ert-deftest test-org-inlinetask/org-inlinetask-goto-end () ;; Goto end. (should (equal (let ((org-inlinetask-min-level 5) (org-adapt-indentation t)) (org-test-with-temp-text "** H ***** I ***** END foo" (org-inlinetask-goto-end) (insert "") (buffer-string))) "** H ***** I ***** END foo")) ;; Goto end. End is buffer end. (should (equal (let ((org-inlinetask-min-level 5) (org-adapt-indentation t)) (org-test-with-temp-text "** H ***** I ***** END" (org-inlinetask-goto-end) (insert "") (buffer-string))) "** H ***** I ***** END")) ;; Goto end. Starting somewhere. (should (equal (let ((org-inlinetask-min-level 5) (org-adapt-indentation t)) (org-test-with-temp-text "** H ***** I ***** END ***** I ***** END" (org-inlinetask-goto-end) (insert "") (buffer-string))) "** H ***** I ***** END ***** I ***** END")) (should (equal (let ((org-inlinetask-min-level 5) (org-adapt-indentation t)) (org-test-with-temp-text "** H ***** I inside ***** END ***** I ***** END" (org-inlinetask-goto-end) (insert "") (buffer-string))) "** H ***** I inside ***** END ***** I ***** END"))) (ert-deftest test-org-inlinetask/inlinetask-within-plain-list () "Fold inlinetasks in plain-lists. Report: http://lists.gnu.org/archive/html/emacs-orgmode/2017-12/msg00502.html" (should (org-test-with-temp-text "* Test - x - a *************** List folding stopped here *************** END - b " (org-cycle-internal-local) (invisible-p (1- (search-forward "- b")))))) (ert-deftest test-org-inlinetask/folding-directly-consecutive-tasks/0 () "Fold directly consecutive inlinetasks." (should (org-test-with-temp-text "* Test - x - a *************** List folding stopped here *************** END *************** List folding stopped here *************** END - b " (org-cycle-internal-local) (invisible-p (1- (search-forward "- b")))))) (ert-deftest test-org-inlinetask/folding-directly-consecutive-tasks/1 () "Fold directly consecutive inlinetasks." (should (org-test-with-temp-text "* Test *************** p1 p2 *************** END *************** p3 p4 *************** END " (org-flag-subtree t) (org-cycle) (and (not (invisible-p (1- (search-forward "p1")))) (invisible-p (1- (search-forward "p2"))) (not (invisible-p (1- (search-forward "p3")))) (invisible-p (1- (search-forward "p4"))))))) (provide 'test-org-inlinetask) ;;; test-org-inlinetask.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-lint.el000066400000000000000000000501071500430433700217320ustar00rootroot00000000000000;;; test-org-lint.el --- Tests for Org Lint -*- lexical-binding: t; -*- ;; Copyright (C) 2016, 2019 Nicolas Goaziou ;; Author: Nicolas Goaziou ;; 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 . ;;; Code: (require 'org-footnote) (require 'org-lint) (ert-deftest test-org-lint/add-checker () "Test `org-lint-add-checker'." ;; Name should be a non-nil symbol. (should-error (org-lint-add-checker nil "Nil check" #'ignore)) (should-error (org-lint-add-checker 2 "Odd check" #'ignore)) ;; Check function should be valid. (should-error (org-lint-add-checker 'check "check" (gensym))) ;; Checkers must be named uniquely. (should (= 1 (let ((org-lint--checkers nil)) (org-lint-add-checker 'check "check" #'ignore) (length org-lint--checkers)))) (should-not (= 2 (let ((org-lint--checkers nil)) (org-lint-add-checker 'check "check" #'ignore) (org-lint-add-checker 'check "other check" #'ignore) (length org-lint--checkers))))) (ert-deftest test-org-lint/duplicate-custom-id () "Test `org-lint-duplicate-custom-id' checker." (should (org-test-with-temp-text " * H1 :PROPERTIES: :CUSTOM_ID: foo :END: * H2 :PROPERTIES: :CUSTOM_ID: foo :END:" (org-lint '(duplicate-custom-id)))) (should-not (org-test-with-temp-text " * H1 :PROPERTIES: :CUSTOM_ID: foo :END: * H2 :PROPERTIES: :CUSTOM_ID: bar :END:" (org-lint '(duplicate-custom-id))))) (ert-deftest test-org-lint/duplicate-name () "Test `org-lint-duplicate-name' checker." (should (org-test-with-temp-text " #+name: foo Paragraph1 #+name: foo Paragraph 2" (org-lint '(duplicate-name)))) (should-not (org-test-with-temp-text " #+name: foo Paragraph1 #+name: bar Paragraph 2" (org-lint '(duplicate-name))))) (ert-deftest test-org-lint/duplicate-target () "Test `org-lint-duplicate-target' checker." (should (org-test-with-temp-text "<> <>" (org-lint '(duplicate-target)))) (should-not (org-test-with-temp-text "<> <>" (org-lint '(duplicate-target))))) (ert-deftest test-org-lint/duplicate-footnote-definition () "Test `org-lint-duplicate-footnote-definition' checker." (should (org-test-with-temp-text " \[fn:1] Definition 1 \[fn:1] Definition 2" (org-lint '(duplicate-footnote-definition)))) (should-not (org-test-with-temp-text " \[fn:1] Definition 1 \[fn:2] Definition 2" (org-lint '(duplicate-footnote-definition))))) (ert-deftest test-org-lint/orphaned-affiliated-keywords () "Test `org-lint-orphaned-affiliated-keywords' checker." (should (org-test-with-temp-text "#+name: foo" (org-lint '(orphaned-affiliated-keywords))))) (ert-deftest test-org-lint/deprecated-export-blocks () "Test `org-lint-deprecated-export-blocks' checker." (should (org-test-with-temp-text " #+begin_latex ... #+end_latex" (org-lint '(deprecated-export-blocks))))) (ert-deftest test-org-lint/deprecated-header-syntax () "Test `org-lint-deprecated-header-syntax' checker." (should (org-test-with-temp-text "#+property: cache yes" (org-lint '(deprecated-header-syntax)))) (should (org-test-with-temp-text " * H :PROPERTIES: :cache: yes :END:" (org-lint '(deprecated-header-syntax))))) (ert-deftest test-org-lint/missing-language-in-src-block () "Test `org-lint-missing-language-in-src-block' checker." (should (org-test-with-temp-text " #+begin_src ... #+end_src" (org-lint '(missing-language-in-src-block))))) (ert-deftest test-org-lint/missing-backend-in-export-block () "Test `org-lint-missing-backend-in-export-block' checker." (should (org-test-with-temp-text " #+begin_export ... #+end_export" (org-lint '(missing-backend-in-export-block))))) (ert-deftest test-org-lint/invalid-babel-call-block () "Test `org-lint-invalid-babel-call-block' checker." (should (org-test-with-temp-text "#+call:" (org-lint '(invalid-babel-call-block)))) (should (org-test-with-temp-text "#+call: foo() [:exports code]" (org-lint '(invalid-babel-call-block))))) (ert-deftest test-org-lint/deprecated-category-setup () "Test `org-lint-deprecated-category-setup' checker." (should (org-test-with-temp-text "#+category: foo\n#+category: bar" (org-lint '(deprecated-category-setup))))) (ert-deftest test-org-lint/invalid-coderef-link () "Test `org-lint-invalid-coderef-link' checker." (should (org-test-with-temp-text "[[(unknown)]]" (org-lint '(invalid-coderef-link)))) (should-not (org-test-with-temp-text "[[(foo)]] #+begin_src emacs-lisp -l \"; ref:%s\" \(+ 1 1) ; ref:foo #+end_src" (org-lint '(invalid-coderef-link))))) (ert-deftest test-org-lint/invalid-custom-id-link () "Test `org-lint-invalid-custom-id-link' checker." (should (org-test-with-temp-text "[[#unknown]]" (org-lint '(invalid-custom-id-link)))) (should-not (org-test-with-temp-text "[[#foo]] * H :PROPERTIES: :CUSTOM_ID: foo :END:" (org-lint '(invalid-custom-id-link))))) (ert-deftest test-org-lint/invalid-fuzzy-link () "Test `org-lint-invalid-fuzzy-link' checker." (should (org-test-with-temp-text "[[*unknown]]" (org-lint '(invalid-fuzzy-link)))) (should-not (org-test-with-temp-text "[[*foo]]\n* foo" (org-lint '(invalid-fuzzy-link)))) (should (org-test-with-temp-text "[[unknown]]" (org-lint '(invalid-fuzzy-link)))) (should-not (org-test-with-temp-text "[[foo]]\n#+name: foo\nParagraph" (org-lint '(invalid-fuzzy-link)))) (should-not (org-test-with-temp-text "[[foo]]\n<>" (org-lint '(invalid-fuzzy-link))))) (ert-deftest test-org-lint/special-property-in-properties-drawer () "Test `org-lint-special-property-in-properties-drawer' checker." (should (org-test-with-temp-text " * H :PROPERTIES: :TODO: foo :END:" (org-lint '(special-property-in-properties-drawer))))) (ert-deftest test-org-lint/obsolete-properties-drawer () "Test `org-lint-obsolete-properties-drawer' checker." (should-not (org-test-with-temp-text " * H :PROPERTIES: :SOMETHING: foo :END:" (org-lint '(obsolete-properties-drawer)))) (should-not (org-test-with-temp-text " * H SCHEDULED: <2012-03-29> :PROPERTIES: :SOMETHING: foo :END:" (org-lint '(obsolete-properties-drawer)))) (should-not (org-test-with-temp-text ":PROPERTIES: :SOMETHING: foo :END:" (org-lint '(obsolete-properties-drawer)))) (should-not (org-test-with-temp-text "# Comment :PROPERTIES: :SOMETHING: foo :END:" (org-lint '(obsolete-properties-drawer)))) (should (org-test-with-temp-text " * H Paragraph :PROPERTIES: :SOMETHING: foo :END:" (org-lint '(obsolete-properties-drawer)))) (should (org-test-with-temp-text " * H :PROPERTIES: This is not a node property :END:" (org-lint '(obsolete-properties-drawer)))) (should (org-test-with-temp-text "Paragraph :PROPERTIES: :FOO: bar :END:" (org-lint '(obsolete-properties-drawer))))) (ert-deftest test-org-lint/invalid-effort-property () "Test `org-lint-invalid-effort-property' checker." (should (org-test-with-temp-text "* H\n:PROPERTIES:\n:EFFORT: something\n:END:" (org-lint '(invalid-effort-property)))) (should-not (org-test-with-temp-text "* H\n:PROPERTIES:\n:EFFORT: 1:23\n:END:" (org-lint '(invalid-effort-property))))) (ert-deftest test-org-lint/link-to-local-file () "Test `org-lint-link-to-local-file' checker." (should (org-test-with-temp-text "[[file:/Idonotexist.org]]" (org-lint '(link-to-local-file))))) (ert-deftest test-org-lint/non-existent-setupfile-parameter () "Test `org-lint-non-existent-setupfile-parameter' checker." (should (org-test-with-temp-text "#+setupfile: Idonotexist.org" (org-lint '(non-existent-setupfile-parameter)))) (should-not (org-test-with-temp-text "#+setupfile: https://I.do/not.exist.org" (org-lint '(non-existent-setupfile-parameter))))) (ert-deftest test-org-lint/wrong-include-link-parameter () "Test `org-lint-wrong-include-link-parameter' checker." (should (org-test-with-temp-text "#+include:" (org-lint '(wrong-include-link-parameter)))) (should (org-test-with-temp-text "#+include: Idonotexist.org" (org-lint '(wrong-include-link-parameter)))) (should (org-test-with-temp-text-in-file "" (let ((file (buffer-file-name))) (org-test-with-temp-text (format "#+include: \"%s::#foo\"" file) (org-lint '(wrong-include-link-parameter)))))) (should-not (org-test-with-temp-text-in-file "* foo" (let ((file (buffer-file-name))) (org-test-with-temp-text (format "#+include: \"%s::*foo\"" file) (org-lint '(wrong-include-link-parameter))))))) (ert-deftest test-org-lint/obsolete-include-markup () "Test `org-lint-obsolete-include-markup' checker." (should (org-test-with-temp-text-in-file "" (let ((file (buffer-file-name))) (org-test-with-temp-text (format "#+include: \"%s\" html" file) (org-lint '(obsolete-include-markup)))))) (should-not (org-test-with-temp-text-in-file "" (let ((file (buffer-file-name))) (org-test-with-temp-text (format "#+include: \"%s\" export html" file) (org-lint '(obsolete-include-markup))))))) (ert-deftest test-org-lint/unknown-options-item () "Test `org-lint-unknown-options-item' checker." (should (org-test-with-temp-text "#+options: foobarbaz:t" (org-lint '(unknown-options-item)))) (should (org-test-with-temp-text "#+options: H:" (org-lint '(unknown-options-item))))) (ert-deftest test-org-lint/invalid-macro-argument-and-template () "Test `org-lint-invalid-macro-argument-and-template' checker." (should (org-test-with-temp-text "{{{undefined()}}}" (org-lint '(invalid-macro-argument-and-template)))) (should (org-test-with-temp-text "#+macro: wrongsignature $1 $2\n{{{wrongsignature(1, 2, 3)}}}" (org-lint '(invalid-macro-argument-and-template)))) (should (org-test-with-temp-text "#+macro:" (org-lint '(invalid-macro-argument-and-template)))) (should (org-test-with-temp-text "#+macro: missingtemplate" (org-lint '(invalid-macro-argument-and-template)))) (should (org-test-with-temp-text "#+macro: unusedplaceholders $1 $3" (org-lint '(invalid-macro-argument-and-template)))) (should-not (org-test-with-temp-text "#+macro: valid $1 $2\n{{{valid(1, 2)}}}" (org-lint '(invalid-macro-argument-and-template)))) (should (org-test-with-temp-text "{{{keyword}}}" (org-lint '(invalid-macro-argument-and-template)))) (should (org-test-with-temp-text "{{{keyword(one, too many)}}}" (org-lint '(invalid-macro-argument-and-template))))) (ert-deftest test-org-lint/undefined-footnote-reference () "Test `org-lint-undefined-footnote-reference' checker." (should (org-test-with-temp-text "Text[fn:1]" (org-lint '(undefined-footnote-reference)))) (should-not (org-test-with-temp-text "Text[fn:1]\n[fn:1] Definition" (org-lint '(undefined-footnote-reference)))) (should-not (org-test-with-temp-text "Text[fn:1:inline reference]" (org-lint '(undefined-footnote-reference)))) (should-not (org-test-with-temp-text "Text[fn:1:inline reference] [fn:1]" (org-lint '(undefined-footnote-reference)))) (should-not (org-test-with-temp-text "Text[fn::anonymous reference]" (org-lint '(undefined-footnote-reference))))) (ert-deftest test-org-lint/unreferenced-footnote-definition () "Test `org-lint-unreferenced-footnote-definition' checker." (should (org-test-with-temp-text "[fn:1] Definition" (org-lint '(unreferenced-footnote-definition)))) (should-not (org-test-with-temp-text "Text[fn:1]\n[fn:1] Definition" (org-lint '(unreferenced-footnote-definition))))) (ert-deftest test-org-lint/mismatched-planning-repeaters () "Test `org-lint-mismatched-planning-repeaters' checker." (should (org-test-with-temp-text "* H DEADLINE: <2023-03-26 Sun +2w> SCHEDULED: <2023-03-26 Sun +1w>" (org-lint '(mismatched-planning-repeaters))))) (ert-deftest test-org-lint/misplaced-planning-info () "Test `org-lint-misplaced-planning-info' checker." (should (org-test-with-temp-text "SCHEDULED: <2012-03-29 thu.>" (org-lint '(misplaced-planning-info)))) (should (org-test-with-temp-text " * H Text SCHEDULED: <2012-03-29 thu.>" (org-lint '(misplaced-planning-info)))) (should-not (org-test-with-temp-text " * H SCHEDULED: <2012-03-29 thu.>" (org-lint '(misplaced-planning-info))))) (ert-deftest test-org-lint/incomplete-drawer () "Test `org-lint-incomplete-drawer' checker." (should (org-test-with-temp-text ":DRAWER:" (org-lint '(incomplete-drawer)))) (should (org-test-with-temp-text ":DRAWER:\n:ODD:\n:END:" (org-lint '(incomplete-drawer)))) (should-not (org-test-with-temp-text ":DRAWER:\n:END:" (org-lint '(incomplete-drawer))))) (ert-deftest test-org-lint/indented-diary-sexp () "Test `org-lint-indented-diary-sexp' checker." (should (org-test-with-temp-text " %%(foo)" (org-lint '(indented-diary-sexp)))) (should-not (org-test-with-temp-text "%%(foo)" (org-lint '(indented-diary-sexp))))) (ert-deftest test-org-lint/invalid-block () "Test `org-lint-invalid-block' checker." (should (org-test-with-temp-text "#+begin_foo" (org-lint '(invalid-block)))) (should-not (org-test-with-temp-text "#+begin_foo\n#+end_foo" (org-lint '(invalid-block))))) (ert-deftest test-org-lint/invalid-keyword-syntax () "Test `org-lint-invalid-keyword-syntax' checker." (should (org-test-with-temp-text "#+keyword" (org-lint '(invalid-keyword-syntax)))) (should-not (org-test-with-temp-text "#+keyword:" (org-lint '(invalid-keyword-syntax))))) (ert-deftest test-org-lint/extraneous-element-in-footnote-section () "Test `org-lint-extraneous-element-in-footnote-section' checker." (should (org-test-with-temp-text "* Footnotes\nI'm not a footnote definition" (let ((org-footnote-section "Footnotes")) (org-lint '(extraneous-element-in-footnote-section))))) (should-not (org-test-with-temp-text "* Footnotes\n[fn:1] I'm a footnote definition" (let ((org-footnote-section "Footnotes")) (org-lint '(extraneous-element-in-footnote-section)))))) (ert-deftest test-org-lint/quote-section () "Test `org-lint-quote-section' checker." (should (org-test-with-temp-text "* QUOTE H" (org-lint '(quote-section)))) (should (org-test-with-temp-text "* COMMENT QUOTE H" (org-lint '(quote-section))))) (ert-deftest test-org-lint/file-application () "Test `org-lint-file-application' checker." (should (org-test-with-temp-text "[[file+emacs:foo.org]]" (org-lint '(file-application))))) (ert-deftest test-org-lint/percenc-encoding-link-escape () "Test `org-lint-percent-encoding-link-escape' checker." (should (org-test-with-temp-text "[[A%20B]]" (org-lint '(percent-encoding-link-escape)))) (should (org-test-with-temp-text "[[%5Bfoo%5D]]" (org-lint '(percent-encoding-link-escape)))) (should (org-test-with-temp-text "[[A%2520B]]" (org-lint '(percent-encoding-link-escape)))) (should-not (org-test-with-temp-text "[[A B]]" (org-lint '(percent-encoding-link-escape)))) (should-not (org-test-with-temp-text "[[A%30B]]" (org-lint '(percent-encoding-link-escape)))) (should-not (org-test-with-temp-text "[[A%20%30B]]" (org-lint '(percent-encoding-link-escape)))) (should-not (org-test-with-temp-text "" (org-lint '(percent-encoding-link-escape)))) (should-not (org-test-with-temp-text "[[A B%]]" (org-lint '(percent-encoding-link-escape))))) (ert-deftest test-org-lint/wrong-header-argument () "Test `org-lint-wrong-header-argument' checker." (should (org-test-with-temp-text "#+call: foo() barbaz yes" (org-lint '(wrong-header-argument)))) (should (org-test-with-temp-text "#+call: foo() :barbaz yes" (org-lint '(wrong-header-argument)))) (should (org-test-with-temp-text "call_foo[barbaz yes]()" (org-lint '(wrong-header-argument)))) (should (org-test-with-temp-text "call_foo[:barbaz yes]()" (org-lint '(wrong-header-argument)))) (should (org-test-with-temp-text "#+property: header-args barbaz yes" (org-lint '(wrong-header-argument)))) (should (org-test-with-temp-text "#+property: header-args :barbaz yes" (org-lint '(wrong-header-argument)))) (should (org-test-with-temp-text " * H :PROPERTIES: :HEADER-ARGS: barbaz yes :END:" (org-lint '(wrong-header-argument)))) (should (org-test-with-temp-text " * H :PROPERTIES: :HEADER-ARGS: :barbaz yes :END:" (org-lint '(wrong-header-argument)))) (should (org-test-with-temp-text " #+header: :barbaz yes #+begin_src emacs-lisp \(+ 1 1) #+end_src" (org-lint '(wrong-header-argument)))) (should (org-test-with-temp-text "src_emacs-lisp[barbaz yes]{}" (org-lint '(wrong-header-argument)))) (should (org-test-with-temp-text "src_emacs-lisp[:barbaz yes]{}" (org-lint '(wrong-header-argument))))) (ert-deftest test-org-lint/wrong-header-value () "Test `org-lint-wrong-header-value' checker." (should (org-test-with-temp-text " #+header: :cache maybe #+begin_src emacs-lisp \(+ 1 1) #+end_src" (org-lint '(wrong-header-value)))) (should (org-test-with-temp-text " #+header: :exports both none #+begin_src emacs-lisp \(+ 1 1) #+end_src" (org-lint '(wrong-header-value)))) (should-not (org-test-with-temp-text " #+header: :cache yes #+begin_src emacs-lisp \(+ 1 1) #+end_src" (org-lint '(wrong-header-value))))) (ert-deftest test-org/spurious-colons () "Test `org-list-spurious-colons' checker." (should-not (org-test-with-temp-text "* H :tag:tag2:" (org-lint '(spurious-colons)))) (should (org-test-with-temp-text "* H :tag::tag2:" (org-lint '(spurious-colons)))) (should (org-test-with-temp-text "* H :tag::" (org-lint '(spurious-colons))))) (ert-deftest test-org-lint/non-existent-bibliography () "Test `org-lint-non-existent-bibliography' checker." (should (org-test-with-temp-text "#+bibliography: Idonotexist.bib" (org-lint '(non-existent-bibliography))))) (ert-deftest test-org-lint/missing-print-bibliography () "Test `org-lint-missing-print-bibliography' checker." (should (org-test-with-temp-text "[cite:@foo]" (org-lint '(missing-print-bibliography)))) (should-not (org-test-with-temp-text "[cite:@foo]\n#+print_bibliography:" (org-lint '(missing-print-bibliography)))) (should-not (org-test-with-temp-text "" (org-lint '(missing-print-bibliography))))) (ert-deftest test-org-lint/invalid-cite-export-declaration () "Test `org-lint-invalid-cite-export-declaration' checker." (should (org-test-with-temp-text "#+cite_export: " (org-lint '(invalid-cite-export-declaration)))) (should (org-test-with-temp-text "#+cite_export: 2" (org-lint '(invalid-cite-export-declaration)))) (should (org-test-with-temp-text "#+cite_export: basic bar baz qux" (org-lint '(invalid-cite-export-declaration)))) (should (org-test-with-temp-text "#+cite_export: basic \"bar" (org-lint '(invalid-cite-export-declaration)))) (should (org-test-with-temp-text "#+cite_export: unknown" (org-lint '(invalid-cite-export-declaration)))) (should-not (org-test-with-temp-text "#+cite_export: basic" (org-lint '(invalid-cite-export-declaration))))) (ert-deftest test-org-lint/incomplete-citation () "Test `org-lint-incomplete-citation' checker." (should (org-test-with-temp-text "[cite:foo]" (org-lint '(incomplete-citation)))) (should (org-test-with-temp-text "[cite:@foo" (org-lint '(incomplete-citation)))) (should-not (org-test-with-temp-text "[cite:@foo]" (org-lint '(incomplete-citation))))) (provide 'test-org-lint) ;;; test-org-lint.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-list.el000066400000000000000000001517351500430433700217500ustar00rootroot00000000000000;;; test-org-list.el --- Tests for org-list.el -*- lexical-binding: t; -*- ;; Copyright (C) 2012, 2013, 2014, 2018, 2019 Nicolas Goaziou ;; Author: Nicolas Goaziou ;; 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 . ;;; Code: (require 'org-list) (require 'org) (ert-deftest test-org-list/list-ending () "Test if lists end at the right place." ;; With two blank lines. (org-test-with-temp-text "- item\n\n\n Text" (goto-line 4) (should-not (org-in-item-p))) ;; With text less indented than top items. (org-test-with-temp-text "- item\nText" (goto-line 2) (should-not (org-in-item-p))) ;; Though, blank lines and text indentation is ignored in blocks. (org-test-with-temp-text "- item\n #+begin_quote\n\n\nText at column 0\n #+end_quote\n Text" (goto-line 7) (should (org-in-item-p)))) (ert-deftest test-org-list/list-navigation () "Test list navigation specifications." (org-test-with-temp-text " - item A - item B - item 1 - item 1.1 - item 1.2 - item 1.3 - item 2 - item X - item Y" (let ((org-list-use-circular-motion nil)) ;; 1. Test `org-next-item'. ;; ;; 1.1. Should return an error if at last item in ;; a list/sub-list, unless `org-list-use-circular-motion' ;; is non-nil. (goto-line 9) (should-error (org-next-item)) (let ((org-list-use-circular-motion t)) (should (progn (org-next-item) t))) (goto-line 14) (should-error (org-next-item)) (let ((org-list-use-circular-motion t)) (should (progn (org-next-item) t))) ;; 1.2. Should jump over sub-lists. (goto-line 6) (org-next-item) (should (looking-at "- item 2")) ;; 1.3. Shouldn't move to another list. (goto-line 3) (should-error (org-next-item)) (should-not (looking-at "- item 1")) ;; 1.4. Should move to the list/sub-list first item when ;; `org-list-use-circular-motion' is non-nil. (let ((org-list-use-circular-motion t)) (goto-line 10) (org-next-item) (should (looking-at "- item 1")) (goto-line 9) (org-next-item) (should (looking-at " - item 1.1"))) ;; 2. Test `org-previous-item'. ;; ;; 2.1. Should return an error if at first item in ;; a list/sub-list, unless `org-list-use-circular-motion is ;; non-nil. (goto-line 7) (should-error (org-previous-item)) (let ((org-list-use-circular-motion t)) (should (progn (org-previous-item) t))) (goto-line 13) (should-error (org-previous-item)) (let ((org-list-use-circular-motion t)) (should (progn (org-previous-item) t))) ;; 2.2. Should ignore sub-lists. (goto-line 10) (org-previous-item) (should (looking-at "- item 1")) ;; 2.3. Shouldn't move to another list. (goto-line 6) (should-error (org-previous-item)) (should-not (looking-at "- item B")) ;; 2.4. Should move to the list/sub-list last item when ;; `org-list-use-circular-motion' is non-nil. (let ((org-list-use-circular-motion t)) (goto-line 6) (org-previous-item) (should (looking-at "- item 2")) (goto-line 7) (org-previous-item) (should (looking-at " - item 1.3")))))) (ert-deftest test-org-list/cycle-bullet () "Test `org-cycle-list-bullet' specifications." ;; Error when not at an item. (should-error (org-test-with-temp-text "Paragraph" (org-cycle-list-bullet))) ;; Cycle through "-", "+", "*", "1.", "1)". (org-test-with-temp-text " - item" (org-cycle-list-bullet) (should (looking-at "[ \t]+\\+")) (org-cycle-list-bullet) (should (looking-at "[ \t]+\\*")) (let ((org-plain-list-ordered-item-terminator t)) (org-cycle-list-bullet)) (should (looking-at "[ \t]+1\\.")) (let ((org-plain-list-ordered-item-terminator t)) (org-cycle-list-bullet)) (should (looking-at "[ \t]+1)"))) ;; Argument is a valid bullet: cycle to that bullet directly. (should (equal "1. item" (org-test-with-temp-text "- item" (let ((org-plain-list-ordered-item-terminator t)) (org-cycle-list-bullet "1.") (buffer-string))))) ;; Argument is an integer N: cycle to the Nth allowed bullet. (should (equal "+ item" (org-test-with-temp-text "1. item" (let ((org-plain-list-ordered-item-terminator t)) (org-cycle-list-bullet 1) (buffer-string))))) ;; Argument is `previous': cycle backwards. (should (equal "- item" (org-test-with-temp-text "+ item" (let ((org-plain-list-ordered-item-terminator t)) (org-cycle-list-bullet 'previous) (buffer-string))))) ;; Do not cycle to "*" bullets when item is at column 0. (should (equal "1. item" (org-test-with-temp-text "+ item" (let ((org-plain-list-ordered-item-terminator t)) (org-cycle-list-bullet) (buffer-string))))) ;; Do not cycle to numbered bullets in a description list. (should-not (equal "1. tag :: item" (org-test-with-temp-text "+ tag :: item" (let ((org-plain-list-ordered-item-terminator t)) (org-cycle-list-bullet) (buffer-string))))) ;; Do not cycle to ordered item terminators if they are not allowed ;; in `org-plain-list-ordered-item-terminator'. (should (equal " 1) item" (org-test-with-temp-text " * item" (let ((org-plain-list-ordered-item-terminator 41)) (org-cycle-list-bullet) (buffer-string))))) ;; When `org-list-allow-alphabetical' is non-nil, cycle to alpha bullets. (should (equal "a. item" (org-test-with-temp-text "1) item" (let ((org-plain-list-ordered-item-terminator t) (org-list-allow-alphabetical t)) (org-cycle-list-bullet) (buffer-string))))) ;; Do not cycle to alpha bullets when list has more than 26 ;; elements. (should-not (equal "a. item 1" (org-test-with-temp-text "1) item 1 2) item 2 3) item 3 4) item 4 5) item 5 6) item 6 7) item 7 8) item 8 9) item 9 10) item 10 11) item 11 12) item 12 13) item 13 14) item 14 15) item 15 16) item 16 17) item 17 18) item 18 19) item 19 20) item 20 21) item 21 22) item 22 23) item 23 24) item 24 25) item 25 26) item 26 27) item 27" (let ((org-plain-list-ordered-item-terminator t) (org-list-allow-alphabetical t)) (org-cycle-list-bullet) (buffer-substring (point) (line-end-position)))))) ;; Preserve point position while cycling. (org-test-with-temp-text "- this is test - asd - asd - this is * headline " (should (= (point) 36)) (dotimes (_ 10) (org-cycle-list-bullet) (should (= 1 (- (point) (line-beginning-position)))))) (org-test-with-temp-text " - this is test + asd - asd + this is * headline " (should (= (point) 37)) (dotimes (_ 10) (org-cycle-list-bullet) (should (= 2 (- (point) (line-beginning-position)))))) (org-test-with-temp-text " - this is test + asd - asd + this is * headline " (should (= (point) 38)) (dotimes (_ 10) (org-cycle-list-bullet) (should (= 3 (- (point) (line-beginning-position)))))) (org-test-with-temp-text " - this is test - asd - asd - this is * headline " (should (= (point) 39)) (dotimes (i 5) (org-cycle-list-bullet) (should (if (or (< i 2) (= i 4)) (should (= 4 (- (point) (line-beginning-position)))) (should (= 5 (- (point) (line-beginning-position))))))))) (ert-deftest test-org-list/indent-item () "Test `org-indent-item' specifications." ;; Error when not at an item. (org-test-with-temp-text "Paragraph." (should-error (org-indent-item))) ;; Error when trying to move first item of a list. (should-error (org-test-with-temp-text " - Item 1 - Item 2" (forward-line) (org-indent-item))) (should-error (org-test-with-temp-text " - Item 1 - Item 2" (forward-line) (let ((org-list-automatic-rules nil)) (org-indent-item)))) ;; Indent a single item, not its children. (should (equal " - Item 1 - Item 2 - Item 2.1" (org-test-with-temp-text " - Item 1 - Item 2 - Item 2.1" (let (org-list-demote-modify-bullet) (org-indent-item)) (buffer-string)))) ;; Follow `org-list-demote-modify-bullet' specifications. (should (equal " - Item 1 + Item 2" (org-test-with-temp-text " - Item 1 - Item 2" (let ((org-list-demote-modify-bullet '(("-" . "+")))) (org-indent-item)) (buffer-string)))) (should (equal " - [ ] list item 1 + [ ] list item 2" (org-test-with-temp-text " - [ ] list item 1 - [ ] list item 2" (let ((org-list-demote-modify-bullet '(("-" . "+")))) (org-indent-item) (buffer-string))))) (should (equal " 1. Item 1 + Item 2" (org-test-with-temp-text " 1. Item 1 2. Item 2" (let ((org-plain-list-ordered-item-terminator t) (org-list-demote-modify-bullet '(("1." . "+")))) (org-indent-item)) (buffer-string)))) (should (equal " a. Item 1 - Item 2" (org-test-with-temp-text " a. Item 1 b. Item 2" (let ((org-plain-list-ordered-item-terminator t) (org-list-allow-alphabetical t) (org-list-demote-modify-bullet '(("A." . "a.") ("a." . "-")))) (org-indent-item)) (buffer-string)))) ;; When a region is selected, indent every item within. (should (equal " - Item 1 - Item 2 - Item 3 " (org-test-with-temp-text " - Item 1 - Item 2 - Item 3 " (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (let (org-list-demote-modify-bullet) (org-indent-item)) (buffer-string)))) ;; When point is right after empty item, do not move point. (should (= 13 (org-test-with-temp-text " - item - ::" (org-indent-item) (point)))) ;; Preserve space after point upon promoting level. (org-test-with-temp-text " - item - ::" (org-indent-item) (should (looking-at-p " \t")))) (ert-deftest test-org-list/indent-item-tree () "Test `org-indent-item-tree' specifications." ;; 1. Error when not at an item. (org-test-with-temp-text "Paragraph." (should-error (org-indent-item-tree))) ;; 2. Indent item along with its children. (org-test-with-temp-text " - Item 1 - Item 2 - Item 2.1" (search-forward "- Item 2") (let (org-list-demote-modify-bullet) (org-indent-item-tree)) (should (equal (buffer-string) " - Item 1 - Item 2 - Item 2.1"))) ;; 3. Special case: When indenting top item, move the whole list. (org-test-with-temp-text " - Item 1 - Item 2" (search-forward "- Item 1") (let (org-list-demote-modify-bullet org-odd-levels-only) (org-indent-item-tree)) (should (equal (buffer-string) " - Item 1 - Item 2"))) ;; 4. Follow `org-list-demote-modify-bullet' specifications. ;; ;; 4.1. With unordered lists. (org-test-with-temp-text " - Item 1 - Item 2 + Item 2.1" (search-forward "- Item 2") (let ((org-list-demote-modify-bullet '(("-" . "+") ("+" . "-")))) (org-indent-item-tree)) (should (equal (buffer-string) " - Item 1 + Item 2 - Item 2.1"))) ;; 4.2. and ordered lists. (org-test-with-temp-text " 1. Item 1 2. Item 2 + Item 2.1" (search-forward "2. Item 2") (let ((org-plain-list-ordered-item-terminator t) (org-list-demote-modify-bullet '(("1." . "+") ("+" . "1.")))) (org-indent-item-tree)) (should (equal (buffer-string) " 1. Item 1 + Item 2 1. Item 2.1"))) ;; 5. When a region is selected, indent every item within. (org-test-with-temp-text " - Item 1 - Item 2 - Item 2.1 - Item 3 - Item 3.1 " (search-forward "- Item 2") (beginning-of-line) (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (let (org-list-demote-modify-bullet) (org-indent-item-tree)) (should (equal (buffer-string) " - Item 1 - Item 2 - Item 2.1 - Item 3 - Item 3.1 ")))) (ert-deftest test-org-list/outdent-item () "Test `org-outdent-item' specifications." ;; 1. Error when not at an item. (org-test-with-temp-text "Paragraph." (should-error (org-outdent-item))) ;; 2. Error when trying to move first item of a list. (org-test-with-temp-text " - Item 1 - Item 2" (forward-line) (should-error (org-outdent-item))) ;; 3. Error when trying to outdent an item without its children. (org-test-with-temp-text " - Item 1 - Item 1.1 - Item 1.1.1" (search-forward "- Item 1.1") (should-error (org-outdent-item))) ;; 4. Error when trying to outdent before top item. (org-test-with-temp-text " - Item 1 - Item 2" (search-forward "- Item 2") (should-error (org-outdent-item))) ;; 5. When a region is selected, outdent every item within. (org-test-with-temp-text " - Item 1 - Item 2 - Item 3 " (search-forward "- Item 2") (beginning-of-line) (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (let (org-list-demote-modify-bullet) (org-outdent-item)) (should (equal (buffer-string) " - Item 1 - Item 2 - Item 3 ")))) (ert-deftest test-org-list/outdent-item-tree () "Test `org-outdent-item-tree' specifications." ;; 1. Error when not at an item. (org-test-with-temp-text "Paragraph." (should-error (org-outdent-item-tree))) ;; 2. Error when trying to outdent before top item. (org-test-with-temp-text " - Item 1 - Item 2" (search-forward "- Item 2") (should-error (org-outdent-item-tree))) ;; 3. Outdent item along with its children. (org-test-with-temp-text " - Item 1 - Item 2 - Item 2.1" (search-forward "- Item 2") (org-outdent-item-tree) (should (equal (buffer-string) " - Item 1 - Item 2 - Item 2.1"))) ;; 3. Special case: When outdenting top item, move the whole list. (org-test-with-temp-text " - Item 1 - Item 2" (search-forward "- Item 1") (let (org-odd-levels-only) (org-outdent-item-tree)) (should (equal (buffer-string) " - Item 1 - Item 2"))) ;; 5. When a region is selected, outdent every item within. (org-test-with-temp-text " - Item 1 - Item 2 - Item 2.1 - Item 3 - Item 3.1 " (search-forward "- Item 2") (beginning-of-line) (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (org-outdent-item-tree) (should (equal (buffer-string) " - Item 1 - Item 2 - Item 2.1 - Item 3 - Item 3.1 ")))) (ert-deftest test-org-list/cycle-item-identation () "Test `org-list-cycle-item-indentation' specifications." ;; Refuse to indent non-empty items. (should-not (org-test-with-temp-text "- item - item2" (org-cycle-item-indentation))) ;; First try to indent item. (should (equal "- item\n - sub-item\n - " (org-test-with-temp-text "- item\n - sub-item\n - " (org-cycle-item-indentation) (buffer-string)))) ;; If first indentation is not possible, outdent item. (should (equal "- item\n- " (org-test-with-temp-text "- item\n - " (org-cycle-item-indentation) (buffer-string)))) ;; Throw an error when item cannot move either way. (should-error (org-test-with-temp-text "- " (org-cycle-item-indentation))) ;; On repeated commands, cycle through all the indented positions, ;; then through all the outdented ones, then move back to initial ;; position. (should (equal '(4 6 0 2) (org-test-with-temp-text "- i0\n - i1\n - s1\n - " (let ((indentations nil)) (org-cycle-item-indentation) (dotimes (_ 3) (let ((last-command 'org-cycle-item-indentation)) (push (current-indentation) indentations) (org-cycle-item-indentation))) (reverse (cons (current-indentation) indentations)))))) ;; Refuse to indent the first item in a sub-list. Also refuse to ;; outdent an item with a next sibling. (should-error (org-test-with-temp-text "- item\n - \n - sub-item 2" (org-cycle-item-indentation))) ;; When cycling back into initial position, preserve bullet type. (should (equal "1. item\n - " (org-test-with-temp-text "1. item\n - " (org-cycle-item-indentation) (let ((last-command 'org-cycle-item-indentation)) (org-cycle-item-indentation)) (buffer-string)))) (should (equal "1. item\n - tag :: " (org-test-with-temp-text "1. item\n - tag :: " (org-cycle-item-indentation) (let ((last-command 'org-cycle-item-indentation)) (org-cycle-item-indentation)) (buffer-string)))) ;; When starting at top level, never outdent. (should (org-test-with-temp-text "- item\n- " (org-cycle-item-indentation) (let ((last-command 'org-cycle-item-indentation)) (org-cycle-item-indentation)) (buffer-string)))) (ert-deftest test-org-list/move-item-down () "Test `org-move-item-down' specifications." ;; Standard test. (should (equal "- item 2\n- item 1" (org-test-with-temp-text "- item 1\n- item 2" (org-move-item-down) (buffer-string)))) ;; Keep same column in item. (should (org-test-with-temp-text "- item 1\n- item 2" (org-move-item-down) (looking-at "em 1"))) ;; Move sub-items. (org-test-with-temp-text "- item 1\n - sub-item 1\n- item 2" (org-move-item-down) (should (equal (buffer-string) "- item 2\n- item 1\n - sub-item 1"))) ;; Preserve blank lines. (should (equal "- item 2\n\n- item 1" (org-test-with-temp-text "- item 1\n\n- item 2" (org-move-item-down) (buffer-string)))) ;; Error when trying to move the last item... (should-error (org-test-with-temp-text "- item 1\n- item 2" (forward-line) (org-move-item-down))) ;; ... unless `org-list-use-circular-motion' is non-nil. In this ;; case, move to the first item. (should (equal "- item 3\n- item 1\n- item 2\n" (org-test-with-temp-text "- item 1\n- item 2\n- item 3" (let ((org-list-use-circular-motion t)) (org-move-item-down)) (buffer-string)))) ;; Preserve item visibility. (should (equal (make-list 2 'org-fold-outline) (let ((org-fold-core-style 'text-properties)) (org-test-with-temp-text "* Headline\n- item 1\n body 1\n- item 2\n body 2" (let ((org-cycle-include-plain-lists t)) (org-cycle) (search-forward "- item 2") (org-cycle)) (search-backward "- item 1") (org-move-item-down) (forward-line) (list (org-fold-get-folding-spec) (progn (search-backward " body 2") (org-fold-get-folding-spec))))))) (should (equal '(outline outline) (let ((org-fold-core-style 'overlays)) (org-test-with-temp-text "* Headline\n- item 1\n body 1\n- item 2\n body 2" (let ((org-cycle-include-plain-lists t)) (org-cycle) (search-forward "- item 2") (org-cycle)) (search-backward "- item 1") (org-move-item-down) (forward-line) (list (org-fold-get-folding-spec) (progn (search-backward " body 2") (org-fold-get-folding-spec))))))) ;; Preserve children visibility. (org-test-with-temp-text "* Headline - item 1 - sub-item 1 sub-body 1 - item 2 - sub-item 2 sub-body 2" (let ((org-cycle-include-plain-lists t)) (search-forward "- sub-item 1") (org-cycle) (search-forward "- sub-item 2") (org-cycle)) (search-backward "- item 1") (org-move-item-down) (search-forward "sub-body 1") (should (org-invisible-p2)) (search-backward "sub-body 2") (should (org-invisible-p2)))) (ert-deftest test-org-list/move-item-down-contents-visibility () "Preserve contents visibility." (org-test-with-temp-text " - item 1 #+BEGIN_CENTER Text1 #+END_CENTER - item 2 #+BEGIN_CENTER Text2 #+END_CENTER" (org-fold-hide-block-all) (let ((invisible-property-1 (progn (search-forward "Text1") (get-char-property (point) 'invisible))) (invisible-property-2 (progn (search-forward "Text2") (get-char-property (point) 'invisible)))) (goto-char (point-min)) (search-forward "- item 1") (org-move-item-down) (search-forward "Text1") (should (eq invisible-property-1 (get-char-property (point) 'invisible))) (search-backward "Text2") (should (eq invisible-property-2 (get-char-property (point) 'invisible)))))) (ert-deftest test-org-list/move-item-up () "Test `org-move-item-up' specifications." ;; Standard test. (org-test-with-temp-text "- item 1\n- item 2" (forward-line) (org-move-item-up) (should (equal (buffer-string) "- item 2\n- item 1"))) ;; Keep same column in item. (org-test-with-temp-text "- item 1\n- item 2" (forward-line) (forward-char 4) (org-move-item-up) (should (looking-at "em 2"))) ;; Move sub-items. (org-test-with-temp-text "- item 1\n- item 2\n - sub-item 2" (forward-line) (org-move-item-up) (should (equal (buffer-string) "- item 2\n - sub-item 2\n- item 1"))) ;; Preserve blank lines. (should (equal "- item 2\n\n- item 1" (org-test-with-temp-text "- item 1\n\n- item 2" (search-forward "- item 2") (org-move-item-up) (buffer-string)))) ;; Error when trying to move the first item... (org-test-with-temp-text "- item 1\n- item 2" (should-error (org-move-item-up))) ;; ... unless `org-list-use-circular-motion' is non-nil. In this ;; case, move to the first item. (should (equal "- item 2\n- item 3\n- item 1" (org-test-with-temp-text "- item 1\n- item 2\n- item 3" (let ((org-list-use-circular-motion t)) (org-move-item-up)) (buffer-string)))) ;; Preserve item visibility. (org-test-with-temp-text "* Headline\n- item 1\n body 1\n- item 2\n body 2" (let ((org-cycle-include-plain-lists t)) (search-forward "- item 1") (org-cycle) (search-forward "- item 2") (org-cycle)) (org-move-item-up) (forward-line) (should (org-invisible-p2)) (search-forward " body 1") (should (org-invisible-p2))) ;; Preserve children visibility. (org-test-with-temp-text "* Headline - item 1 - sub-item 1 sub-body 1 - item 2 - sub-item 2 sub-body 2" (let ((org-cycle-include-plain-lists t)) (search-forward "- sub-item 1") (org-cycle) (search-forward "- sub-item 2") (org-cycle)) (search-backward "- item 2") (org-move-item-up) (search-forward "sub-body 2") (should (org-invisible-p2)) (search-forward "sub-body 1") (should (org-invisible-p2)))) (ert-deftest test-org-list/move-item-up-contents-visibility () (org-test-with-temp-text " - item 1 #+BEGIN_CENTER Text1 #+END_CENTER - item 2 #+BEGIN_CENTER Text2 #+END_CENTER" (org-hide-block-all) (let ((invisible-property-1 (progn (search-forward "Text1") (get-char-property (point) 'invisible))) (invisible-property-2 (progn (search-forward "Text2") (get-char-property (point) 'invisible)))) (goto-char (point-min)) (search-forward "- item 2") (org-move-item-up) (search-forward "Text2") (should (eq invisible-property-2 (get-char-property (point) 'invisible))) (search-forward "Text1") (should (eq invisible-property-1 (get-char-property (point) 'invisible)))))) (ert-deftest test-org-list/insert-item () "Test item insertion." ;; Blank lines specifications. ;; ;; Non-nil `org-blank-before-new-entry': insert a blank line. (should (org-test-with-temp-text "- a" (let ((org-blank-before-new-entry '((plain-list-item . t)))) (end-of-line) (org-insert-item) (forward-line -1) (looking-at "$")))) ;; Nil `org-blank-before-new-entry': do not insert a blank line. (should-not (org-test-with-temp-text "- a" (let ((org-blank-before-new-entry '((plain-list-item . nil)))) (end-of-line) (org-insert-item) (forward-line -1) (looking-at "$")))) ;; `org-blank-before-new-entry' set to auto: if there's no blank ;; line already in the sole item, do not insert one. (should-not (org-test-with-temp-text "- a" (let ((org-blank-before-new-entry '((plain-list-item . auto)))) (end-of-line) (org-insert-item) (forward-line -1) (looking-at "$")))) ;; `org-blank-before-new-entry' set to `auto': if there's a blank ;; line in the sole item, insert another one. (should (org-test-with-temp-text "- a\n\n b" (let ((org-blank-before-new-entry '((plain-list-item . auto)))) (org-insert-item) (forward-line -1) (looking-at "$")))) ;; `org-blank-before-new-entry' set to `auto': if the user specified ;; a blank line, preserve it. (should (org-test-with-temp-text "- a\n\n" (let ((org-blank-before-new-entry '((plain-list-item . auto)))) (org-insert-item) (forward-line -1) (looking-at "$")))) ;; `org-blank-before-new-entry' set to `auto': if some items in list ;; are already separated by blank lines, insert one. (should (org-test-with-temp-text "- a\n\n- b" (let ((org-blank-before-new-entry '((plain-list-item . auto)))) (org-insert-item) (forward-line -1) (looking-at "$")))) (should (org-test-with-temp-text "- a\n\n- b" (let ((org-blank-before-new-entry '((plain-list-item . auto)))) (org-insert-item) (forward-line) (looking-at "$")))) (should (org-test-with-temp-text "- a\n #+BEGIN_EXAMPLE\n\n x\n #+END_EXAMPLE" (let ((org-blank-before-new-entry '((plain-list-item . auto)))) (org-insert-item) (forward-line -1) (looking-at "$")))) ;; When called before or on the bullet, insert new item before ;; current one. (should (equal "- \n- item" (org-test-with-temp-text "- item" (org-insert-item) (buffer-string)))) (should (equal "- \n- item" (org-test-with-temp-text "- item" (org-insert-item) (buffer-string)))) ;; When called at the very end of the list, insert new item as ;; a sibling of the very last one. (should (equal "- A\n\n - B\n\n - " (org-test-with-temp-text "- A\n\n - B\n\n" (org-insert-item) (buffer-string)))) (should (equal "- A\n\n - B\n\n - " (org-test-with-temp-text "- A\n\n - B\n\n " (org-insert-item) (buffer-string)))) ;; When called on tag in a descriptive list, insert new item before ;; current one too. (should (equal "- :: \n- tag :: item" (org-test-with-temp-text "- tag :: item" (org-insert-item) (buffer-string)))) (should (equal "- :: \n- tag :: item" (org-test-with-temp-text "- tag :: item" (org-insert-item) (buffer-string)))) ;; Further, it splits the line or add a blank new item after it, ;; according to `org-M-RET-may-split-line'. (should (equal "- it\n- em" (org-test-with-temp-text "- item" (let ((org-M-RET-may-split-line '((default . t)))) (org-insert-item)) (buffer-string)))) (should (equal "- item\n- " (org-test-with-temp-text "- item" (let ((org-M-RET-may-split-line '((default . nil)))) (org-insert-item)) (buffer-string)))) ;; Re-order automatically. (should (equal "1. A\n\n2. \n\n3. \n\n4. B" (org-test-with-temp-text "1. A\n\n2. \n\n3. B" (org-insert-item) (buffer-string)))) (should (equal "1. a\n2. \n b\n3. c" (org-test-with-temp-text "1. a\n b\n2. c" (org-insert-item) (buffer-string)))) ;; Preserve list visibility when inserting an item. (should (equal `(org-fold-outline org-fold-outline) (let ((org-fold-core-style 'text-properties)) (org-test-with-temp-text "- A\n - B\n- C\n - D" (let ((org-cycle-include-plain-lists t)) (org-cycle) (forward-line 2) (org-cycle) (org-insert-item) (list (org-fold-get-folding-spec nil (line-beginning-position 0)) (org-fold-get-folding-spec nil (line-end-position 2)))))))) (should (equal '(outline outline) (let ((org-fold-core-style 'overlays)) (org-test-with-temp-text "- A\n - B\n- C\n - D" (let ((org-cycle-include-plain-lists t)) (org-cycle) (forward-line 2) (org-cycle) (org-insert-item) (list (get-char-property (line-beginning-position 0) 'invisible) (get-char-property (line-end-position 2) 'invisible))))))) ;; Test insertion in area after a sub-list. In particular, if point ;; is right at the end of the previous sub-list, still insert ;; a sub-item in that list. (should (= 2 (org-test-with-temp-text "- item\n - sub-list\n resume item" (org-insert-item) (current-indentation)))) (should (= 0 (org-test-with-temp-text "- item\n - sub-list\n resume item" (org-insert-item) (current-indentation)))) ;; Test splitting with blanks around. (should (equal "- A\n B\n- C\n - D\n- [ ] E" (org-test-with-temp-text "- A\n B C\n - D\n- [ ] E" (org-insert-item) (buffer-string))))) (ert-deftest test-org-list/send-item () "Test `org-list-send-item' specifications." ;; Move to beginning (should (equal "- item3\n- item1\n- item2\n" (org-test-with-temp-text "- item1\n- item2\n- item3\n" (org-list-send-item (caar (last (org-list-struct))) 'begin (org-list-struct)) (buffer-string)))) ;; Move to beginning with child item (should (equal "- item3\n - item4\n- item1\n- item2\n" (org-test-with-temp-text "- item1\n- item2\n- item3\n - item4\n" (org-list-send-item (car (nth 2 (org-list-struct))) 'begin (org-list-struct)) (buffer-string)))) ;; Move to end (should (equal "- item2\n- item3\n - item4\n- item1\n - item1child\n" (org-test-with-temp-text "- item1\n - item1child\n- item2\n- item3\n - item4\n" (org-list-send-item (car (nth 0 (org-list-struct))) 'end (org-list-struct)) (buffer-string)))) ;; Move to item number by string should move the item before the specified one (should (equal "- item2\n- item1\n - item1child\n- item3\n- item4\n- item5\n" (org-test-with-temp-text "- item1\n - item1child\n- item2\n- item3\n- item4\n- item5\n" (org-list-send-item (car (nth 0 (org-list-struct))) "3" (org-list-struct)) (buffer-string)))) ;; Move to item number by position should move the item before the specified one (should (equal "- item2\n- item1\n - item1child\n- item3\n- item4\n- item5\n" (org-test-with-temp-text "- item1\n - item1child\n- item2\n- item3\n- item4\n- item5\n" (re-search-forward "item3") (org-list-send-item (car (nth 0 (org-list-struct))) (point-at-bol) (org-list-struct)) (buffer-string)))) ;; Delete (should (equal "- item1\n - item1child\n- item2\n- item4\n- item5\n" (org-test-with-temp-text "- item1\n - item1child\n- item2\n- item3\n- item4\n- item5\n" (re-search-forward "item3") (org-list-send-item (point-at-bol) 'delete (org-list-struct)) (buffer-string)))) ;; Kill (let ((kill-ring nil)) (org-test-with-temp-text "- item1\n - item1child\n- item2\n- item3\n - item3child\n- item4\n- item5\n" (re-search-forward "item3") (org-list-send-item (point-at-bol) 'kill (org-list-struct)) (should (equal "- item1\n - item1child\n- item2\n- item4\n- item5\n" (buffer-string))) (should (equal "item3\n - item3child" (current-kill 0 t)))))) (ert-deftest test-org-list/repair () "Test `org-list-repair' specifications." ;; Repair indentation. (should (equal "- item\n - child" (org-test-with-temp-text "- item\n - child" (let ((org-list-indent-offset 0)) (org-list-repair)) (buffer-string)))) ;; Repair bullets and numbering. (should (equal "- a\n- b" (org-test-with-temp-text "- a\n+ b" (let ((org-list-indent-offset 0)) (org-list-repair)) (buffer-string)))) (should (equal "1. a\n2. b" (org-test-with-temp-text "1. a\n1. b" (let ((org-list-indent-offset 0) (org-plain-list-ordered-item-terminator t)) (org-list-repair)) (buffer-string)))) ;; Repair check-boxes. (should (equal "- [X] item\n - [X] child" (org-test-with-temp-text "- [ ] item\n - [X] child" (let ((org-list-indent-offset 0)) (org-list-repair)) (buffer-string)))) ;; Special case: do not move contents of an item within its child. ;; Yet, preserve indentation differences within contents. (should (equal "- item\n - child\n within item" (org-test-with-temp-text "- item\n - child\n within item" (let ((org-list-indent-offset 0)) (org-list-repair)) (buffer-string)))) (should (equal "- item\n - child\n within item\n indented" (org-test-with-temp-text "- item\n - child\n within item\n indented" (let ((org-list-indent-offset 0)) (org-list-repair)) (buffer-string))))) (ert-deftest test-org-list/update-checkbox-count () "Test `org-update-checkbox-count' specifications." ;; From a headline. (should (string-match "\\[0/1\\]" (org-test-with-temp-text "* [/]\n- [ ] item" (org-update-checkbox-count) (buffer-string)))) (should (string-match "\\[1/1\\]" (org-test-with-temp-text "* [/]\n- [X] item" (org-update-checkbox-count) (buffer-string)))) (should (string-match "\\[100%\\]" (org-test-with-temp-text "* [%]\n- [X] item" (org-update-checkbox-count) (buffer-string)))) ;; From a list or a sub-list. (should (string-match "\\[0/1\\]" (org-test-with-temp-text "- [/]\n - [ ] item" (org-update-checkbox-count) (buffer-string)))) (should (string-match "\\[1/1\\]" (org-test-with-temp-text "- [/]\n - [X] item" (org-update-checkbox-count) (buffer-string)))) (should (string-match "\\[100%\\]" (org-test-with-temp-text "- [%]\n - [X] item" (org-update-checkbox-count) (buffer-string)))) (should (string-match "\\[1/1\\]" (org-test-with-temp-text "- [ ] item 1\n- [ ] item 2 [/]\n - [X] sub 1" (org-update-checkbox-count) (buffer-string)))) ;; Count do not apply to sub-lists unless count is not hierarchical. ;; This state can be achieved with COOKIE_DATA node property set to ;; "recursive". (should (string-match "\\[1/1\\]" (org-test-with-temp-text "- [/]\n - item\n - [X] sub-item" (let ((org-checkbox-hierarchical-statistics nil)) (org-update-checkbox-count)) (buffer-string)))) (should (string-match "\\[1/1\\]" (org-test-with-temp-text " * H :PROPERTIES: :COOKIE_DATA: recursive :END: - [/] - item - [X] sub-item" (org-update-checkbox-count) (buffer-string)))) (should (string-match "\\[0/0\\]" (org-test-with-temp-text "- [/]\n - item\n - [ ] sub-item" (org-update-checkbox-count) (buffer-string)))) ;; With optional argument ALL, update all buffer. (should (= 2 (org-test-with-temp-text "* [/]\n- [X] item\n* [/]\n- [X] item" (org-update-checkbox-count t) (count-matches "\\[1/1\\]")))) ;; Ignore boxes in drawers, blocks or inlinetasks when counting from ;; outside. (should (string-match "\\[2/2\\]" (org-test-with-temp-text " - [/] - [X] item1 :DRAWER: - [X] item :END: - [X] item2" (let ((org-checkbox-hierarchical-statistics nil)) (org-update-checkbox-count)) (buffer-string))))) ;;; API (ert-deftest test-org-list/at-radio-list-p () "Test `org-at-radio-list-p' specifications." (should (org-test-with-temp-text "#+attr_org: :radio t\n- foo" (org-at-radio-list-p))) (should (org-test-with-temp-text "#+attr_org: :radio t\n- foo\n- bar" (org-at-radio-list-p))) (should (org-test-with-temp-text "#+ATTR_ORG: :radio t\n- foo" (org-at-radio-list-p))) (should (org-test-with-temp-text "#+attr_org: :radio bar\n- foo" (org-at-radio-list-p))) (should-not (org-test-with-temp-text "#+attr_org: :radio nil\n- foo" (org-at-radio-list-p))) (should-not (org-test-with-temp-text "- foo" (org-at-radio-list-p))) (should-not (org-test-with-temp-text "#+attr_org: :radio t\n- foo\n bar" (org-at-radio-list-p))) (should-not (org-test-with-temp-text "#+attr_org: :radio t\n#+begin_example\n- foo\n#+end_example" (org-at-radio-list-p)))) ;;; Miscellaneous (ert-deftest test-org-list/toggle-item () "Test `org-toggle-item' specifications." ;; Convert normal lines to items. (should (equal "- line" (org-test-with-temp-text "line" (org-toggle-item nil) (buffer-string)))) ;; Convert items to normal lines. (should (equal "line" (org-test-with-temp-text "- line" (org-toggle-item nil) (buffer-string)))) ;; Convert headlines to items. (should (equal "- line" (org-test-with-temp-text "* line" (org-toggle-item nil) (buffer-string)))) ;; When converting a headline to a list item, TODO keywords become ;; checkboxes. (should (equal "- [X] line" (org-test-with-temp-text "* DONE line" (org-toggle-item nil) (buffer-string)))) (should (equal "- [ ] line" (org-test-with-temp-text "* TODO line" (org-toggle-item nil) (buffer-string)))) ;; When turning headlines into items, make sure planning info line ;; and properties drawers are removed. This also includes empty ;; lines following them. (should (equal "- H\n" (org-test-with-temp-text "* H\nSCHEDULED: <2012-03-29 Thu>" (org-toggle-item nil) (buffer-string)))) (should (equal "- H\n" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:" (org-toggle-item nil) (buffer-string)))) (should (equal "- H\nText" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:\n\n\nText" (org-toggle-item nil) (buffer-string)))) ;; When no region is marked and point is on a blank line ;; only operate on current line. (should (equal " \n* H :tag:" (org-test-with-temp-text " \n* H :tag:" (org-toggle-item nil) (buffer-string)))) ;; When a region is marked and first line is a headline, all ;; headlines are turned into items. (should (equal "- H1\n - H2" (org-test-with-temp-text "* H1\n** H2" (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (org-toggle-item nil) (buffer-string)))) (should (equal "- [ ] H1\n - [ ] H2" (org-test-with-temp-text "* TODO H1\n** TODO H2" (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (org-toggle-item nil) (buffer-string)))) ;; When turning headlines into items, make sure headings contents ;; are kept within items. (should (equal "- H1\n Text" (org-test-with-temp-text "* H1\nText" (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (org-toggle-item nil) (buffer-string)))) ;; When a region is marked and first line is an item, all items are ;; turned into normal lines. (should (equal "1\n 2" (org-test-with-temp-text "- 1\n - 2" (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (org-toggle-item nil) (buffer-string)))) (should (equal "1\n2" (org-test-with-temp-text "- 1\n2" (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (org-toggle-item nil) (buffer-string)))) ;; When a region is marked and first line is an item, all normal ;; lines are turned into items. (should (equal "- line 1\n- line 2" (org-test-with-temp-text "line 1\nline 2" (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (org-toggle-item nil) (buffer-string)))) (should (equal "- line 1\n- line 2" (org-test-with-temp-text "line 1\n- line 2" (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (org-toggle-item nil) (buffer-string)))) ;; When headings contain footnote definitions, move the definition ;; out of the list. Footnote definitions cannot be indented. (should (equal "- Main headline - Headline 1 bbbbbbbb [fn:1] - Headline 2 [fn:1] cccccccccccccccc " (org-test-with-temp-text "* Main headline ** Headline 1 bbbbbbbb [fn:1] [fn:1] cccccccccccccccc * Headline 2" (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (org-toggle-item t) (buffer-string)))) ;; Footnote definitions that did not have trailing double blank line ;; must not slurp the following element. (should (equal "- Head 1 - Head 2 [fn:1] cccccccccccccccc Paragraph outside footnote definitions." (org-test-with-temp-text "* Head 1 [fn:1] cccccccccccccccc * Head 2 Paragraph outside footnote definitions." (transient-mark-mode 1) (push-mark (point) t t) (search-forward "Head 2") (org-toggle-item t) (buffer-string)))) ;; Move footnote definitions past pre-existing items after. (should (equal "- Line 1 Line 2 - next item [fn:1] definition " (org-test-with-temp-text "Line 1 Line 2 [fn:1] definition - next item" (transient-mark-mode 1) (push-mark (point) t t) (search-forward "definition") (org-toggle-item t) (buffer-string)))) ;; When argument ARG is non-nil, change the whole region into ;; a single item. (should (equal "- line 1\n line 2" (org-test-with-temp-text "line 1\nline 2" (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (org-toggle-item t) (buffer-string))))) (ert-deftest test-org-list/sort () "Test `org-sort-list'." ;; Sort alphabetically. (let ((original-string-collate-lessp (symbol-function 'string-collate-lessp))) (cl-letf (((symbol-function 'string-collate-lessp) (lambda (s1 s2 &optional _locale ignore-case) (funcall original-string-collate-lessp s1 s2 "C" ignore-case)))) (should (equal "- abc\n- def\n- XYZ\n" (org-test-with-temp-text "- def\n- XYZ\n- abc\n" (org-sort-list nil ?a) (buffer-string)))) (should (equal "- XYZ\n- def\n- abc\n" (org-test-with-temp-text "- def\n- XYZ\n- abc\n" (org-sort-list nil ?A) (buffer-string)))) ;; Sort alphabetically (with case). (should (equal "- C\n- a\n- b\n" (org-test-with-temp-text "- b\n- C\n- a\n" (org-sort-list t ?a) (buffer-string)))) (should (equal "- b\n- a\n- C\n" (org-test-with-temp-text "- b\n- C\n- a\n" (org-sort-list t ?A) (buffer-string)))))) ;; Sort numerically. (should (equal "- 1\n- 2\n- 10\n" (org-test-with-temp-text "- 10\n- 1\n- 2\n" (org-sort-list nil ?n) (buffer-string)))) (should (equal "- 10\n- 2\n- 1\n" (org-test-with-temp-text "- 10\n- 1\n- 2\n" (org-sort-list nil ?N) (buffer-string)))) ;; Sort by checked status. (should (equal "- [ ] xyz\n- [ ] def\n- [X] abc\n" (org-test-with-temp-text "- [X] abc\n- [ ] xyz\n- [ ] def\n" (org-sort-list nil ?x) (buffer-string)))) (should (equal "- [X] abc\n- [ ] xyz\n- [ ] def\n" (org-test-with-temp-text "- [X] abc\n- [ ] xyz\n- [ ] def\n" (org-sort-list nil ?X) (buffer-string)))) ;; Sort by time stamp. (should (equal "- <2017-05-08 Mon>\n- <2017-05-09 Tue>\n- <2018-05-09 Wed>\n" (org-test-with-temp-text "- <2018-05-09 Wed>\n- <2017-05-09 Tue>\n- <2017-05-08 Mon>\n" (org-sort-list nil ?t) (buffer-string)))) (should (equal "- <2018-05-09 Wed>\n- <2017-05-09 Tue>\n- <2017-05-08 Mon>\n" (org-test-with-temp-text "- <2018-05-09 Wed>\n- <2017-05-09 Tue>\n- <2017-05-08 Mon>\n" (org-sort-list nil ?T) (buffer-string)))) ;; Sort by custom function. (should (equal "- b\n- aa\n- ccc\n" (org-test-with-temp-text "- ccc\n- b\n- aa\n" (org-sort-list nil ?f (lambda () (length (buffer-substring (point-at-bol) (point-at-eol)))) #'<) (buffer-string)))) (should (equal "- ccc\n- aa\n- b\n" (org-test-with-temp-text "- ccc\n- b\n- aa\n" (org-sort-list nil ?F (lambda () (length (buffer-substring (point-at-bol) (point-at-eol)))) #'<) (buffer-string))))) ;;; List transformations (ert-deftest test-org-list/to-generic () "Test `org-list-to-generic' specifications." ;; Test `:ustart' and `:uend' parameters. (should (equal "begin\na" (org-test-with-temp-text "- a" (org-list-to-generic (org-list-to-lisp) '(:ustart "begin"))))) (should-not (equal "begin\na" (org-test-with-temp-text "1. a" (org-list-to-generic (org-list-to-lisp) '(:ustart "begin"))))) (should (equal "a\nend" (org-test-with-temp-text "- a" (org-list-to-generic (org-list-to-lisp) '(:uend "end"))))) (should-not (equal "a\nend" (org-test-with-temp-text "1. a" (org-list-to-generic (org-list-to-lisp) '(:uend "end"))))) (should (equal "begin l1\na\nbegin l2\nb\nend l2\nend l1" (org-test-with-temp-text "- a\n - b" (org-list-to-generic (org-list-to-lisp) (list :ustart (lambda (l) (format "begin l%d" l)) :uend (lambda (l) (format "end l%d" l))))))) ;; Test `:ostart' and `:oend' parameters. (should (equal "begin\na" (org-test-with-temp-text "1. a" (org-list-to-generic (org-list-to-lisp) '(:ostart "begin"))))) (should-not (equal "begin\na" (org-test-with-temp-text "- a" (org-list-to-generic (org-list-to-lisp) '(:ostart "begin"))))) (should (equal "a\nend" (org-test-with-temp-text "1. a" (org-list-to-generic (org-list-to-lisp) '(:oend "end"))))) (should-not (equal "a\nend" (org-test-with-temp-text "- a" (org-list-to-generic (org-list-to-lisp) '(:oend "end"))))) (should (equal "begin l1\na\nbegin l2\nb\nend l2\nend l1" (org-test-with-temp-text "1. a\n 1. b" (org-list-to-generic (org-list-to-lisp) (list :ostart (lambda (l) (format "begin l%d" l)) :oend (lambda (l) (format "end l%d" l))))))) ;; Test `:dstart' and `:dend' parameters. (should (equal "begin\ntaga" (org-test-with-temp-text "- tag :: a" (org-list-to-generic (org-list-to-lisp) '(:dstart "begin"))))) (should-not (equal "begin\na" (org-test-with-temp-text "- a" (org-list-to-generic (org-list-to-lisp) '(:dstart "begin"))))) (should (equal "taga\nend" (org-test-with-temp-text "- tag :: a" (org-list-to-generic (org-list-to-lisp) '(:dend "end"))))) (should-not (equal "a\nend" (org-test-with-temp-text "- a" (org-list-to-generic (org-list-to-lisp) '(:dend "end"))))) (should (equal "begin l1\ntag1a\nbegin l2\ntag2b\nend l2\nend l1" (org-test-with-temp-text "- tag1 :: a\n - tag2 :: b" (org-list-to-generic (org-list-to-lisp) (list :dstart (lambda (l) (format "begin l%d" l)) :dend (lambda (l) (format "end l%d" l))))))) ;; Test `:dtstart', `:dtend', `:ddstart' and `:ddend' parameters. (should (equal ">tag" :dtend "<"))))) (should (equal "tag>a<" (org-test-with-temp-text "- tag :: a" (org-list-to-generic (org-list-to-lisp) '(:ddstart ">" :ddend "<"))))) ;; Test `:istart' and `:iend' parameters. (should (equal "starta" (org-test-with-temp-text "- a" (org-list-to-generic (org-list-to-lisp) '(:istart "start"))))) (should (equal "level1 a\nlevel2 b" (org-test-with-temp-text "- a\n - b" (org-list-to-generic (org-list-to-lisp) '(:istart (lambda (type l) (format "level%d "l))))))) (should (equal "a\nblevel2level1" (org-test-with-temp-text "- a\n - b" (org-list-to-generic (org-list-to-lisp) '(:iend (lambda (type l) (format "level%d" l))))))) ;; Test `:icount' parameter. (should (equal "counta" (org-test-with-temp-text "1. [@3] a" (org-list-to-generic (org-list-to-lisp) '(:icount "count"))))) (should-not (equal "counta" (org-test-with-temp-text "1. a" (org-list-to-generic (org-list-to-lisp) '(:icount "count"))))) (should (equal "counta" (org-test-with-temp-text "1. [@3] a" (org-list-to-generic (org-list-to-lisp) '(:icount "count" :istart "start"))))) (should (equal "level:1, counter:3 a" (org-test-with-temp-text "1. [@3] a" (org-list-to-generic (org-list-to-lisp) '(:icount (lambda (type l c) (format "level:%d, counter:%d " l c))))))) ;; Test `:isep' parameter. (should (equal "a\n--\nb" (org-test-with-temp-text "- a\n- b" (org-list-to-generic (org-list-to-lisp) '(:isep "--"))))) (should-not (equal "a\n--\nb" (org-test-with-temp-text "- a\n - b" (org-list-to-generic (org-list-to-lisp) '(:isep "--"))))) (should (equal "a\n- 1 -\nb" (org-test-with-temp-text "- a\n- b" (org-list-to-generic (org-list-to-lisp) '(:isep (lambda (type depth) (format "- %d -" depth))))))) ;; Test `:ifmt' parameter. (should (equal ">> a <<" (org-test-with-temp-text "1. [@3] a" (org-list-to-generic (org-list-to-lisp) '(:ifmt (lambda (type c) (format ">> %s <<" c))))))) ;; Test `:cbon', `:cboff', `:cbtrans' (should (equal "!a" (org-test-with-temp-text "- [X] a" (org-list-to-generic (org-list-to-lisp) '(:cbon "!"))))) (should-not (equal "!a" (org-test-with-temp-text "- [X] a" (org-list-to-generic (org-list-to-lisp) '(:cboff "!" :cbtrans "!"))))) (should (equal "!a" (org-test-with-temp-text "- [ ] a" (org-list-to-generic (org-list-to-lisp) '(:cboff "!"))))) (should-not (equal "!a" (org-test-with-temp-text "- [ ] a" (org-list-to-generic (org-list-to-lisp) '(:cbon "!" :cbtrans "!"))))) (should (equal "!a" (org-test-with-temp-text "- [-] a" (org-list-to-generic (org-list-to-lisp) '(:cbtrans "!"))))) (should-not (equal "!a" (org-test-with-temp-text "- [-] a" (org-list-to-generic (org-list-to-lisp) '(:cbon "!" :cboff "!"))))) ;; Test `:splice' parameter. (should (equal "a" (org-test-with-temp-text "- a" (org-list-to-generic (org-list-to-lisp) '(:ustart "begin" :uend "end" :splice t))))) ;; No error on empty lists. (should (org-test-with-temp-text "-" (org-list-to-generic (org-list-to-lisp) nil)))) (ert-deftest test-org-list/to-html () "Test `org-list-to-html' specifications." (should (equal "
    \n
  • a
  • \n
" (org-test-with-temp-text "- a" (org-list-to-html (org-list-to-lisp) nil))))) (ert-deftest test-org-list/to-latex () "Test `org-list-to-latex' specifications." (should (equal "\\begin{itemize}\n\\item a\n\\end{itemize}" (org-test-with-temp-text "- a" (org-list-to-latex (org-list-to-lisp) nil))))) (ert-deftest test-org-list/to-texinfo () "Test `org-list-to-texinfo' specifications." (should (equal "@itemize\n@item\na\n@end itemize" (org-test-with-temp-text "- a" (org-list-to-texinfo (org-list-to-lisp) nil))))) (ert-deftest test-org-list/to-org () "Test `org-list-to-org' specifications." ;; Un-ordered list. (should (equal "- a" (org-test-with-temp-text "- a" (org-list-to-org (org-list-to-lisp) nil)))) ;; Ordered list. (should (equal "1. a" (org-test-with-temp-text "1. a" (org-list-to-org (org-list-to-lisp) nil)))) ;; Descriptive list. (should (equal "- a :: b" (org-test-with-temp-text "- a :: b" (org-list-to-org (org-list-to-lisp) nil)))) ;; Nested list. (should (equal "- a\n - b" (org-test-with-temp-text "- a\n - b" (org-list-to-org (org-list-to-lisp) nil)))) ;; Item spanning over multiple lines. (should (equal "- a\n b" (org-test-with-temp-text "- a\n b" (org-list-to-org (org-list-to-lisp) nil)))) ;; Item with continuation text after a sub-list. (should (equal "- a\n - b\n c" (org-test-with-temp-text "- a\n - b\n c" (org-list-to-org (org-list-to-lisp) nil))))) (provide 'test-org-list) ;;; test-org-list.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-macro.el000066400000000000000000000402741500430433700220710ustar00rootroot00000000000000;;; test-org-macro.el --- Tests for org-macro.el -*- lexical-binding: t; -*- ;; Copyright (C) 2013, 2014, 2019 Nicolas Goaziou ;; Author: Nicolas Goaziou ;; 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 . ;;; Code: ;;; Macros (ert-deftest test-org-macro/initialize-templates () "Test `org-macro-initialize-templates'." ;; No code is executed during loading of Org mode files." (should (org-test-with-temp-text "#+MACRO: title (eval (eval-and-compile (error \"CVE-2024-30202\")))" (progn (org-macro-initialize-templates) t))) (org-test-with-temp-text "#+MACRO: title (eval (eval-and-compile (error \"CVE-2024-30202\")))" (progn (org-mode) t))) (ert-deftest test-org/macro-replace-all () "Test `org-macro-replace-all' specifications." ;; Standard test. (should (equal "#+MACRO: A B\n1 B 3" (org-test-with-temp-text "#+MACRO: A B\n1 {{{A}}} 3" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-string)))) ;; Macro with arguments. (should (equal "#+MACRO: macro $1 $2\nsome text" (org-test-with-temp-text "#+MACRO: macro $1 $2\n{{{macro(some,text)}}}" (progn (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-string))))) ;; Macro with "eval". (should (equal "3" (org-test-with-temp-text "#+MACRO: add (eval (+ (string-to-number $1) (string-to-number $2))) {{{add(1,2)}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (point) (line-end-position))))) ;; Nested macros. (should (equal "#+MACRO: in inner\n#+MACRO: out {{{in}}} outer\ninner outer" (org-test-with-temp-text "#+MACRO: in inner\n#+MACRO: out {{{in}}} outer\n{{{out}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-string)))) ;; Error out when macro expansion is circular. (should-error (org-test-with-temp-text "#+MACRO: mac1 {{{mac2}}}\n#+MACRO: mac2 {{{mac1}}}\n{{{mac1}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates))) ;; Macros in setup file. (should (string-match "success success\\'" (org-test-with-temp-text (format "#+MACRO: other-macro success #+SETUPFILE: \"%sexamples/macro-templates.org\" {{{included-macro}}} {{{other-macro}}}" org-test-dir) (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-string)))) ;; Macro expansion ignores narrowing. (should (string-match "expansion" (org-test-with-temp-text "#+MACRO: macro expansion\n{{{macro}}}\nContents" (narrow-to-region (point) (point-max)) (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (org-with-wide-buffer (buffer-string))))) ;; Macros in a commented tree are not expanded. (should (string-match-p "{{{macro}}}" (org-test-with-temp-text "#+MACRO: macro expansion\n* COMMENT H\n{{{macro}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (org-with-wide-buffer (buffer-string))))) (should (string-match-p "{{{macro}}}" (org-test-with-temp-text "#+MACRO: macro expansion\n* COMMENT H1\n** H2\n{{{macro}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (org-with-wide-buffer (buffer-string)))))) (ert-deftest test-org-macro/property () "Test {{{property}}} macro." ;; With only one argument, retrieve property from current headline. ;; Otherwise, the second argument is a search option to get the ;; property from another headline. (should (equal "1" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:\n{{{property(A)}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) (should (equal "1" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:\n{{{property(A,)}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) (should (equal "1" (org-test-with-temp-text "* H1\n:PROPERTIES:\n:A: 1\n:END:\n* H2\n{{{property(A,*H1)}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) (should-error (org-test-with-temp-text "* H1\n:PROPERTIES:\n:A: 1\n:END:\n* H2\n{{{property(A,*???)}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates)))) (ert-deftest test-org-macro/n () "Test {{{n}}} macro." ;; Standard test with default counter. (should (equal "1 2" (org-test-with-temp-text "{{{n}}} {{{n}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) (should (equal "1 2" (org-test-with-temp-text "{{{n()}}} {{{n}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) ;; Test alternative counters. (should (equal "1 1 1 2" (org-test-with-temp-text "{{{n}}} {{{n(c1)}}} {{{n(c2)}}} {{{n(c1)}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) ;; Second argument set a counter to a given value. A non-numeric ;; value resets the counter to 1. (should (equal "9 10" (org-test-with-temp-text "{{{n(c,9)}}} {{{n(c)}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) (should (equal "9 1" (org-test-with-temp-text "{{{n(c,9)}}} {{{n(c,reset)}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) ;; Check that reset happens when the second argument is neither "-" ;; nor a number. (should (equal "9 1 1 1" (org-test-with-temp-text (concat "{{{n(c,9)}}} {{{n(c,reiniciar)}}}" " {{{n(c,réinitialiser)}}} {{{n(c,zurückstellen)}}}") (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) ;; Tolerate spaces in first argument. (should (equal "1 2 3 4" (org-test-with-temp-text "{{{n(c)}}} {{{n(c )}}} {{{n( c)}}} {{{n( c )}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) ;; Tolerate spaces when second argument is an integer. (should (equal "2 3 5 7" (org-test-with-temp-text (concat "{{{n(c,2)}}} {{{n(c, 3)}}}" " {{{n(c,5 )}}} {{{n(c, 7 )}}}") (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) ;; Tolerate spaces when second argument is the hold argument. (should (equal "7 7 8 8 9 9" (org-test-with-temp-text (concat "{{{n(,7)}}} {{{n(, -)}}}" " {{{n}}} {{{n(,- )}}} {{{n}}} {{{n(, - )}}}") (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) ;; Tolerate spaces when second argument is used to reset the counter. (should (equal "9 1 1 1 1" (org-test-with-temp-text (concat "{{{n(c,9)}}} {{{n(c,reset)}}} {{{n(c, reset)}}}" " {{{n(c,reset )}}} {{{n(c, reset )}}}") (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) ;; Second argument also applies to default counter. (should (equal "9 10 1" (org-test-with-temp-text "{{{n(,9)}}} {{{n}}} {{{n(,reset)}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) ;; An empty second argument is equivalent to no argument. (should (equal "2 3" (org-test-with-temp-text "{{{n(c,2)}}} {{{n(c,)}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) ;; Hold value at reset value of 1 if the counter hasn't yet started. (should (equal "1" (org-test-with-temp-text "{{{n(,-)}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) ;; Increment counter following a hold. (should (equal "1 1 2" (org-test-with-temp-text "{{{n}}} {{{n(,-)}}} {{{n}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) ;; Hold counter value following a counter value set. (should (equal "1 10 10" (org-test-with-temp-text "{{{n}}} {{{n(,10)}}} {{{n(,-)}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) ;; Hold counter value in a multiple-counter situation. (should (equal "1.1 1.2 1.3" (org-test-with-temp-text "{{{n}}}.{{{n(c)}}} {{{n(,-)}}}.{{{n(c)}}} {{{n(,-)}}}.{{{n(c)}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) ;; Hold counter values on one or multiple counters at the same time. (should (equal "1.1 1.2 2.2 2.2" (org-test-with-temp-text (concat "{{{n}}}.{{{n(c)}}} {{{n(,-)}}}.{{{n(c)}}}" " {{{n}}}.{{{n(c,-)}}} {{{n(,-)}}}.{{{n(c,-)}}}") (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (line-end-position)))))) (ert-deftest test-org-macro/keyword () "Test {{{keyword}}} macro." ;; Replace macro with keyword's value. (should (equal "value" (org-test-with-temp-text "#+keyword: value\n{{{keyword(KEYWORD)}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (point-max)))))) (ert-deftest test-org-macro/author () "Test {{{author}}} macro." ;; Return AUTHOR keyword value. (should (equal "me" (org-test-with-temp-text "#+author: me\n{{{author}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (point-max))))) ;; Return AUTHOR keyword value. (should (equal "author 1 author 2" (org-test-with-temp-text "#+author: author 1\n#+author: author 2\n{{{author}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (point-max))))) ;; When AUTHOR keyword is missing, return the empty string. (should (equal "" (org-test-with-temp-text "{{{author}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (point-max)))))) (ert-deftest test-org-macro/email () "Test {{{email}}} macro." ;; Return EMAIL keyword value. (should (equal "me@home" (org-test-with-temp-text "#+email: me@home\n{{{email}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (point-max))))) ;; When EMAIL keyword is missing, return the empty string. (should (equal "" (org-test-with-temp-text "{{{email}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (point-max)))))) (ert-deftest test-org-macro/title () "Test {{{title}}} macro." ;; Return TITLE keyword value. (should (equal "Foo!" (org-test-with-temp-text "#+title: Foo!\n{{{title}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (point-max))))) ;; When TITLE keyword is missing, return the empty string. (should (equal "" (org-test-with-temp-text "{{{title}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (point-max))))) ;; When multiple TITLE keywords are used, concatenate them. (should (equal "Foo Bar!" (org-test-with-temp-text "#+title: Foo\n#+title: Bar!\n{{{title}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties (line-beginning-position) (point-max)))))) (ert-deftest test-org-macro/escape-arguments () "Test `org-macro-escape-arguments' specifications." ;; Regular tests. (should (equal "a" (org-macro-escape-arguments "a"))) (should (equal "a,b" (org-macro-escape-arguments "a" "b"))) ;; Handle empty arguments. (should (equal "a,,b" (org-macro-escape-arguments "a" "" "b"))) ;; Properly escape commas and backslashes preceding them. (should (equal "a\\,b" (org-macro-escape-arguments "a,b"))) (should (equal "a\\\\,b" (org-macro-escape-arguments "a\\" "b"))) (should (equal "a\\\\\\,b" (org-macro-escape-arguments "a\\,b")))) (ert-deftest test-org-macro/extract-arguments () "Test `org-macro-extract-arguments' specifications." ;; Regular tests. (should (equal '("a") (org-macro-extract-arguments "a"))) (should (equal '("a" "b") (org-macro-extract-arguments "a,b"))) ;; Handle empty arguments. (should (equal '("a" "" "b") (org-macro-extract-arguments "a,,b"))) ;; Handle escaped commas and backslashes. (should (equal '("a,b") (org-macro-extract-arguments "a\\,b"))) (should (equal '("a\\" "b") (org-macro-extract-arguments "a\\\\,b"))) (should (equal '("a\\,b") (org-macro-extract-arguments "a\\\\\\,b")))) (provide 'test-org-macro) ;;; test-org-macro.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-macs.el000066400000000000000000000135201500430433700217050ustar00rootroot00000000000000;;; test-org-macs.el --- Tests for Org Macs library -*- lexical-binding: t; -*- ;; Copyright (C) 2017, 2019 Nicolas Goaziou ;; Author: Nicolas Goaziou ;; 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 . ;;; Code: ;;; String manipulation (ert-deftest test-org/split-string () "Test `org-split-string' specifications." ;; Regular test. (should (equal '("a" "b") (org-split-string "a b" " "))) ;; Empty parts are not removed. (should (equal '("a" "" "b") (org-split-string "a||b" "|"))) ;; However, empty parts at beginning or end of string are removed. (should (equal '("a" "b") (org-split-string "|a|b|" "|"))) ;; Pathological case: call on an empty string. Since empty parts ;; are not removed, it shouldn't return nil. (should (equal '("") (org-split-string ""))) ;; SEPARATORS, when non-nil, is a regexp. In particular, do not ;; match more than specified. (should-not (equal '("a" "b") (org-split-string "a b" " "))) ;; When nil, SEPARATORS matches any number of blank characters. (should (equal '("a" "b") (org-split-string "a \t\nb")))) (ert-deftest test-org/string-width () "Test `org-string-width' specifications." (should (= 1 (org-string-width "a"))) (should (= 0 (org-string-width ""))) ;; Ignore invisible characters. (should (= 0 (org-string-width #("a" 0 1 (invisible t))))) (should (= 1 (org-string-width #("ab" 0 1 (invisible t))))) (should (= 1 (org-string-width #("ab" 1 2 (invisible t))))) (should (= 3 (org-string-width #("abcde" 1 2 (invisible t) 3 4 (invisible t))))) ;; Check if `invisible' value really means invisibility. (should (= 0 (let ((buffer-invisibility-spec t)) (org-string-width #("a" 0 1 (invisible foo)))))) (should (= 0 (let ((buffer-invisibility-spec '(foo))) (org-string-width #("a" 0 1 (invisible foo)))))) (should (= 0 (let ((buffer-invisibility-spec '((foo . t)))) (org-string-width #("a" 0 1 (invisible foo)))))) (should (= 1 (let ((buffer-invisibility-spec '(bar))) (org-string-width #("a" 0 1 (invisible foo)))))) ;; Check `display' property. (should (= 3 (org-string-width #("a" 0 1 (display "abc"))))) (should (= 5 (org-string-width #("1a3" 1 2 (display "abc"))))) ;; `display' string can also contain invisible characters. (should (= 4 (org-string-width #("123" 1 2 (display #("abc" 1 2 (invisible t))))))) ;; Test `space' property in `display'. (should (= 2 (org-string-width #(" " 0 1 (display (space :width 2)))))) ;; Test `wrap-prefix' property. (should (= 2 (org-string-width #("ab" 0 2 (wrap-prefix " "))))) ;; Test `line-prefix' property. (should (= 2 (org-string-width #("ab" 0 2 (line-prefix " ")))))) ;;; Regexp (ert-deftest test-org/in-regexp () "Test `org-in-regexp' specifications." ;; Standard tests. (should (org-test-with-temp-text "xx abc xx" (org-in-regexp "abc"))) (should-not (org-test-with-temp-text "xx abc xx" (org-in-regexp "abc"))) ;; Return non-nil even with multiple matching regexps in the same ;; line. (should (org-test-with-temp-text "abc xx abc xx" (org-in-regexp "abc"))) ;; With optional argument NLINES, check extra lines around point. (should-not (org-test-with-temp-text "A\nB\nC" (org-in-regexp "A\nB\nC"))) (should (org-test-with-temp-text "A\nB\nC" (org-in-regexp "A\nB\nC" 1))) (should-not (org-test-with-temp-text "A\nB\nC" (org-in-regexp "A\nB\nC" 1))) ;; When optional argument VISUALLY is non-nil, return nil if at ;; regexp boundaries. (should (org-test-with-temp-text "xx abc xx" (org-in-regexp "abc"))) (should-not (org-test-with-temp-text "xx abc xx" (org-in-regexp "abc" nil t)))) ;;; Template (ert-deftest test-org/fill-template () "Test `org-fill-template'" (should (string= "working" (org-fill-template "%var-long" '(("var" . "broken") ("var-long" . "working")))))) ;;; Time (ert-deftest test-org-matcher-time () "Test `org-matcher-time'." (let ((system-time-locale "en_US")) (org-test-at-time "<2021-01-11 Mon 13:00>" (should (equal (list 0 0 13 11 1 2021) (butlast (decode-time (org-matcher-time "")) 3))) (should (equal (list 0 0 0 14 1 2021) (butlast (decode-time (org-matcher-time "<+3d>")) 3))) (should (equal (list 0 0 0 9 1 2021) (butlast (decode-time (org-matcher-time "<-2d>")) 3))) (should (equal (list 0 0 0 18 1 2021) (butlast (decode-time (org-matcher-time "<+1w>")) 3))) (should (equal (list 0 0 17 11 1 2021) (butlast (decode-time (org-matcher-time "<+4h>")) 3))) (should (equal (list 0 0 11 11 1 2021) (butlast (decode-time (org-matcher-time "<-2h>")) 3))) (should (equal (list 0 0 3 12 1 2021) (butlast (decode-time (org-matcher-time "<+14h>")) 3)))))) (provide 'test-org-macs) ;;; test-org-macs.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-num.el000066400000000000000000000307361500430433700215710ustar00rootroot00000000000000;;; test-org-num.el --- Tests for Org Num library -*- lexical-binding: t; -*- ;; Copyright (C) 2018 Nicolas Goaziou ;; Author: Nicolas Goaziou ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (require 'org-num) (ert-deftest test-org-num/face () "Test `org-num-face' parameter." (should (equal '(foo) (org-test-with-temp-text "* H1" (let ((org-num-face 'foo)) (org-num-mode 1)) (mapcar (lambda (o) (get-text-property 0 'face (overlay-get o 'after-string))) (overlays-in (point-min) (point-max))))))) (ert-deftest test-org-num/format-function () "Test `org-num-format-function' parameter." (should (equal '("foo" "foo") (org-test-with-temp-text "* H1\n** H2" (let ((org-num-format-function (lambda (_) "foo"))) (org-num-mode 1)) (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max)))))) ;; Preserve face, when set. (should (equal-including-properties '(#("foo" 0 3 (face bar))) (org-test-with-temp-text "* H1" (let ((org-num-format-function (lambda (_) (org-add-props "foo" nil 'face 'bar)))) (org-num-mode 1)) (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max)))))) ;; Set face override `org-num-face'. (should (equal-including-properties '(#("foo" 0 3 (face bar))) (org-test-with-temp-text "* H1" (let ((org-num-face 'baz) (org-num-format-function (lambda (_) (org-add-props "foo" nil 'face 'bar)))) (org-num-mode 1)) (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))))))) (ert-deftest test-org-num/max-level () "Test `org-num-max-level' option." (should (equal (sort '("1.1 " "1 ") #'string-lessp) (org-test-with-temp-text "* H1\n** H2\n*** H3" (let ((org-num-max-level 2)) (org-num-mode 1)) (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp))))) (ert-deftest test-org-num/skip-numbering () "Test various skip numbering parameters." ;; Skip commented headlines. (should (equal (sort '(nil "1 ") #'string-lessp) (org-test-with-temp-text "* H1\n* COMMENT H2" (let ((org-num-skip-commented t)) (org-num-mode 1)) (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))) (should (equal (sort '("2 " "1 ") #'string-lessp) (org-test-with-temp-text "* H1\n* COMMENT H2" (let ((org-num-skip-commented nil)) (org-num-mode 1)) (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))) ;; Skip commented sub-trees. (should (equal (sort '(nil nil) #'string-lessp) (org-test-with-temp-text "* COMMENT H1\n** H2" (let ((org-num-skip-commented t)) (org-num-mode 1)) (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))) ;; Skip footnotes sections. (should (equal (sort '(nil "1 ") #'string-lessp) (org-test-with-temp-text "* H1\n* FN" (let ((org-num-skip-footnotes t) (org-footnote-section "FN")) (org-num-mode 1)) (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))) (should (equal (sort '("2 " "1 ") #'string-lessp) (org-test-with-temp-text "* H1\n* FN" (let ((org-num-skip-footnotes nil) (org-footnote-section "FN")) (org-num-mode 1)) (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))) ;; Skip tags, recursively. (should (equal (sort '(nil "1 ") #'string-lessp) (org-test-with-temp-text "* H1\n* H2 :foo:" (let ((org-num-skip-tags '("foo"))) (org-num-mode 1)) (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))) (should (equal (sort '(nil nil) #'string-lessp) (org-test-with-temp-text "* H1 :foo:\n** H2" (let ((org-num-skip-tags '("foo"))) (org-num-mode 1)) (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))) ;; Skip unnumbered sections. (should (equal (sort '(nil "1 ") #'string-lessp) (org-test-with-temp-text "* H1\n* H2\n:PROPERTIES:\n:UNNUMBERED: t\n:END:" (let ((org-num-skip-unnumbered t)) (org-num-mode 1)) (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))) (should (equal (sort '("2 " "1 ") #'string-lessp) (org-test-with-temp-text "* H1\n* H2\n:PROPERTIES:\n:UNNUMBERED: t\n:END:" (let ((org-num-skip-unnumbered nil)) (org-num-mode 1)) (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))) (should (equal (sort '("2 " "1 ") #'string-lessp) (org-test-with-temp-text "* H1\n* H2\n:PROPERTIES:\n:UNNUMBERED: nil\n:END:" (let ((org-num-skip-unnumbered t)) (org-num-mode 1)) (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))) ;; Skip unnumbered sub-trees. (should (equal (sort '(nil nil) #'string-lessp) (org-test-with-temp-text "* H1\n:PROPERTIES:\n:UNNUMBERED: t\n:END:\n** H2" (let ((org-num-skip-unnumbered t)) (org-num-mode 1)) (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))) ;; Do not choke on empty headlines. (should (equal (sort '("1 ") #'string-lessp) (org-test-with-temp-text "* " (let ((org-num-skip-commented t)) (org-num-mode 1)) (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))) (should (equal (sort '("1 ") #'string-lessp) (org-test-with-temp-text "* " (let ((org-num-skip-unnumbered t)) (org-num-mode 1)) (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))) (should (equal (sort '("1 ") #'string-lessp) (org-test-with-temp-text "* " (let ((org-num-skip-footnotes t)) (org-num-mode 1)) (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp))))) (ert-deftest test-org-num/update () "Test numbering update after a buffer modification." ;; Headlines created at BEG. (should (equal "1 " (org-test-with-temp-text "X* H" (org-num-mode 1) (delete-char 1) (overlay-get (car (overlays-at (line-beginning-position))) 'after-string)))) (should (equal "1 " (org-test-with-temp-text "*\n H" (org-num-mode 1) (delete-char 1) (overlay-get (car (overlays-at (line-beginning-position))) 'after-string)))) (should (equal "1 " (org-test-with-temp-text "*bold*" (org-num-mode 1) (insert " ") (overlay-get (car (overlays-at (line-beginning-position))) 'after-string)))) ;; Headlines created at END. (should (equal (sort '("1 ") #'string-lessp) (org-test-with-temp-text "X H" (org-num-mode 1) (insert "\n*") (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))) (should (equal (sort '("1 ") #'string-lessp) (org-test-with-temp-text "X* H" (org-num-mode 1) (insert "\n") (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))) ;; Headlines created between BEG and END. (should (equal (sort '("1.1 " "1 ") #'string-lessp) (org-test-with-temp-text "" (org-num-mode 1) (insert "\n* H\n** H2") (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))) ;; Change level of a headline. (should (equal (sort '("0.1 ") #'string-lessp) (org-test-with-temp-text "* H" (org-num-mode 1) (insert "*") (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))) (should (equal (sort '("1 ") #'string-lessp) (org-test-with-temp-text "** H" (org-num-mode 1) (delete-char 1) (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))) ;; Alter skip state. (should (equal (sort '("1 ") #'string-lessp) (org-test-with-temp-text "* H :foo:" (let ((org-num-skip-tags '("foo"))) (org-num-mode 1) (delete-char 1)) (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))) (should (equal (sort '(nil) #'string-lessp) (org-test-with-temp-text "* H :fo:" (let ((org-num-skip-tags '("foo"))) (org-num-mode 1) (insert "o")) (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))) ;; Invalidate an overlay and insert new headlines. (should (equal (sort '("1.2 " "1.1 " "1 ") #'string-lessp) (org-test-with-temp-text "* H\n:PROPERTIES:\n:UNNUMBERED: t\n:END:" (let ((org-num-skip-unnumbered t)) (org-num-mode 1) (insert "\n** H2\n** H3\n") (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp))))) ;; Invalidate two overlays: current headline and next one. (should (equal (sort '("1 ") #'string-lessp) (org-test-with-temp-text "* H\n:PROPERTIES:\n:UNNUMBERED: t\n:END:\n** H2" (let ((org-num-skip-unnumbered t)) (org-num-mode 1) (delete-region (point) (line-beginning-position 3)) (sort (mapcar (lambda (o) (overlay-get o 'after-string)) (overlays-in (point-min) (point-max))) #'string-lessp)))))) (provide 'test-org-num) ;;; org-test-num.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-pcomplete.el000066400000000000000000000116551500430433700227610ustar00rootroot00000000000000;;; test-org-pcomplete.el --- test pcomplete integration -*- lexical-binding: t; -*- ;; Copyright (C) 2015-2016, 2019 Alexey Lebedeff ;; Authors: Alexey Lebedeff ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Comments: ;;; Code: (require 'org) (ert-deftest test-org-pcomplete/clocktable () "Test completion of clock table parameters." (should (equal "#+begin: clocktable :scope" (org-test-with-temp-text "#+begin: clocktable :sco" (pcomplete) (buffer-string))))) (ert-deftest test-org-pcomplete/drawer () "Test drawer completion." (should (equal "* Foo\n:PROPERTIES:" (org-test-with-temp-text "* Foo\n:" (pcomplete) (buffer-string)))) (should (equal ":DRAWER:\nContents\n:END:\n* Foo\n:DRAWER:" (org-test-with-temp-text ":DRAWER:\nContents\n:END:\n* Foo\n:D" (pcomplete) (buffer-string))))) (ert-deftest test-org-pcomplete/entity () "Test entity completion." (should (equal "\\alpha" (org-test-with-temp-text "\\alp" (pcomplete) (buffer-string)))) (should (equal "\\frac12" (org-test-with-temp-text "\\frac1" (pcomplete) (buffer-string))))) (ert-deftest test-org-pcomplete/keyword () "Test keyword and block completion." (should (string-prefix-p "#+startup: " (org-test-with-temp-text "#+start" (pcomplete) (buffer-string)) t)) (should (string-prefix-p "#+begin_center" (org-test-with-temp-text "#+begin_ce" (pcomplete) (buffer-string)) t))) (ert-deftest test-org-pcomplete/src-block () "Test Babel source block header arguments completion." (should (string-prefix-p "#+begin_src emacs-lisp" (org-test-with-temp-text "#+begin_src emac" (pcomplete) (buffer-string)))) (should (string-prefix-p "#+begin_src emacs-lisp :session" (org-test-with-temp-text "#+begin_src emacs-lisp :sess" (pcomplete) (buffer-string))))) (ert-deftest test-org-pcomplete/link () "Test link completion" (should (equal "[[org:" (org-test-with-temp-text "[[o" (let ((org-link-abbrev-alist '(("org" . "https://orgmode.org/")))) (pcomplete)) (buffer-string)))) (should-not (equal "[org:" (org-test-with-temp-text "[[o" (let ((org-link-abbrev-alist '(("org" . "https://orgmode.org/")))) (pcomplete)) (buffer-string))))) (ert-deftest test-org-pcomplete/prop () "Test property completion." (should (equal " * a :PROPERTIES: :pname:\s :END: * b :PROPERTIES: :pname: pvalue :END: " (org-test-with-temp-text " * a :PROPERTIES: :pna :END: * b :PROPERTIES: :pname: pvalue :END: " (pcomplete) (buffer-string))))) (ert-deftest test-org-pcomplete/search-heading () "Test search heading completion." (should (equal "* Foo\n[[*Foo" (org-test-with-temp-text "* Foo\n[[*" (pcomplete) (buffer-string))))) (ert-deftest test-org-pcomplete/tag () "Test tag completion." ;; Complete at end of line, according to `org-current-tag-alist'. (should (equal "* H :foo:" (org-test-with-temp-text "* H :" (let ((org-current-tag-alist '(("foo")))) (pcomplete)) (buffer-string)))) (should (equal "* H :foo:bar:" (org-test-with-temp-text "* H :foo:b" (let ((org-current-tag-alist '(("bar")))) (pcomplete)) (buffer-string)))) ;; If `org-current-tag-alist' is non-nil, complete against tags in ;; buffer. (should (equal "* H1 :bar:\n* H2 :bar:" (org-test-with-temp-text "* H1 :bar:\n* H2 :" (let ((org-current-tag-alist nil)) (pcomplete)) (buffer-string)))) ;; Do not complete in the middle of a line. (should (equal "* H :notag: :real:tags:" (org-test-with-temp-text "* H :notag: :real:tags:" (let ((org-current-tag-alist '(("foo")))) (pcomplete)) (buffer-string)))) ;; Complete even when there's a match on the line. (should (equal "* foo: :foo:" (org-test-with-temp-text "* foo: :" (let ((org-current-tag-alist '(("foo")))) (pcomplete)) (buffer-string))))) (ert-deftest test-org-pcomplete/todo () "Test TODO completion." (should (equal "* TODO" (org-test-with-temp-text "* T" (pcomplete) (buffer-string))))) (provide 'test-org-pcomplete) ;;; test-org-pcomplete.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-protocol.el000066400000000000000000000267721500430433700226400ustar00rootroot00000000000000;;; test-org-protocol.el --- tests for org-protocol.el -*- lexical-binding: t; -*- ;; Copyright (c) Sacha Chua ;; Authors: Sacha Chua ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (require 'cl-lib) (require 'org-protocol) (require 'org-capture) (ert-deftest test-org-protocol/org-protocol-parse-parameters () "Test `org-protocol-parse-parameters' specifications." ;; Ignore lists (let ((data (org-protocol-parse-parameters '(:url "abc" :title "def") nil))) (should (string= (plist-get data :url) "abc")) (should (string= (plist-get data :title) "def"))) ;; Parse new-style links (let ((data (org-protocol-parse-parameters "url=abc&title=def" t))) (should (string= (plist-get data :url) "abc")) (should (string= (plist-get data :title) "def"))) ;; Parse new-style complex links (let* ((url (concat "template=p&" "url=https%3A%2F%2Forgmode.org%2Forg.html%23capture-protocol&" "title=The%20Org%20Manual&" "body=9.4.2%20capture%20protocol")) (data (org-protocol-parse-parameters url t))) (should (string= (plist-get data :template) "p")) (should (string= (plist-get data :url) "https://orgmode.org/org.html#capture-protocol")) (should (string= (plist-get data :title) "The Org Manual")) (should (string= (plist-get data :body) "9.4.2 capture protocol"))) ;; Parse old-style links (let ((data (org-protocol-parse-parameters "abc/def" nil '(:url :title)))) (should (string= (plist-get data :url) "abc")) (should (string= (plist-get data :title) "def"))) ;; Parse old-style links even without keys (let ((data (org-protocol-parse-parameters "b/abc/def" nil))) (should (equal data '("b" "abc" "def")))) ;; Parse old-style links with key/val pairs (let ((data (org-protocol-parse-parameters "b/abc/extrakey/extraval" nil '(:param1 :param2)))) (should (string= (plist-get data :param1) "b")) (should (string= (plist-get data :param2) "abc")) (should (string= (plist-get data :extrakey) "extraval")))) (ert-deftest test-org-protocol/org-protocol-store-link () "Test `org-protocol-store-link' specifications." ;; Old link style (let ((uri "/some/directory/org-protocol:/store-link:/URL/TITLE")) (should (null (org-protocol-check-filename-for-protocol uri (list uri) nil))) (should (equal (car org-stored-links) '("URL" "TITLE")))) ;; URL encoded (let ((uri (format "/some/directory/org-protocol:/store-link:/%s/TITLE" (url-hexify-string "http://example.com")))) (should (null (org-protocol-check-filename-for-protocol uri (list uri) nil))) (should (equal (car org-stored-links) '("http://example.com" "TITLE")))) ;; Handle multiple slashes, old link style (let ((uri "/some/directory/org-protocol://store-link://URL2//TITLE2")) (should (null (org-protocol-check-filename-for-protocol uri (list uri) nil))) (should (equal (car org-stored-links) '("URL2" "TITLE2")))) ;; New link style (let ((uri "/some/directory/org-protocol://store-link?url=URL3&title=TITLE3")) (should (null (org-protocol-check-filename-for-protocol uri (list uri) nil))) (should (equal (car org-stored-links) '("URL3" "TITLE3")))) ;; Do not decode "+" in old-style link (let ((uri "/org-protocol:/store-link:/one+one/plus+preserved")) (should (null (org-protocol-check-filename-for-protocol uri (list uri) nil))) (should (equal (car org-stored-links) '("one+one" "plus+preserved")))) ;; Decode "+" to space in new-style link (let ((uri "/org-protocol:/store-link/?url=one+two&title=plus+is+space")) (should (null (org-protocol-check-filename-for-protocol uri (list uri) nil))) (should (equal (car org-stored-links) '("one two" "plus is space"))))) (ert-deftest test-org-protocol/org-protocol-store-link-file () "store-link: `org-protocol-sanitize-uri' could distort URL." :expected-result :failed (let ((uri "/org-protocol:/store-link:/file%3A%2F%2F%2Fetc%2Fmailcap/Triple%20Slash")) (should (null (org-protocol-check-filename-for-protocol uri (list uri) nil))) (should (equal (car org-stored-links) '("file:///etc/mailcap" "Triple Slash")))) (let ((uri "/org-protocol:/store-link?url=file%3A%2F%2F%2Fetc%2Fmailcap&title=Triple%20Slash")) (should (null (org-protocol-check-filename-for-protocol uri (list uri) nil))) (should (equal (car org-stored-links) '("file:///etc/mailcap" "Triple Slash"))))) (ert-deftest test-org-protocol/org-protocol-capture () "Test `org-protocol-capture' specifications." (let* ((org-protocol-default-template-key "t") (temp-file-name (make-temp-file "org-protocol-test")) (org-capture-templates `(("t" "Test" entry (file ,temp-file-name) "** TODO\n\n%i\n\n%a\n" :kill-buffer t) ("x" "With params" entry (file ,temp-file-name) "** SOMEDAY\n\n%i\n\n%a\n" :kill-buffer t) ("X" "Just the template" entry (file ,temp-file-name) "** Hello World\n\n%i\n\nGoodbye World\n" :kill-buffer t))) (test-urls '( ;; Old style: ;; - multiple slashes ("/some/directory/org-protocol:/capture:/URL/TITLE" . "** TODO\n\n\n\n[[URL][TITLE]]\n") ;; - body specification ("/some/directory/org-protocol:/capture:/URL/TITLE/BODY" . "** TODO\n\nBODY\n\n[[URL][TITLE]]\n") ;; - template ("/some/directory/org-protocol:/capture:/x/URL/TITLE/BODY" . "** SOMEDAY\n\nBODY\n\n[[URL][TITLE]]\n") ;; - query parameters, not sure how to include them in template ("/some/directory/org-protocol:/capture:/x/URL/TITLE/BODY/from/example" . "** SOMEDAY\n\nBODY\n\n[[URL][TITLE]]\n") ;; New style: ;; - multiple slashes ("/some/directory/org-protocol:/capture?url=NEWURL&title=TITLE" . "** TODO\n\n\n\n[[NEWURL][TITLE]]\n") ;; - body specification ("/some/directory/org-protocol:/capture?url=NEWURL&title=TITLE&body=BODY" . "** TODO\n\nBODY\n\n[[NEWURL][TITLE]]\n") ;; - template ("/some/directory/org-protocol:/capture?template=x&url=NEWURL&title=TITLE&body=BODY" . "** SOMEDAY\n\nBODY\n\n[[NEWURL][TITLE]]\n") ;; - no url specified ("/some/directory/org-protocol:/capture?template=x&title=TITLE&body=BODY" . "** SOMEDAY\n\nBODY\n\nTITLE\n") ;; - no title specified ("/some/directory/org-protocol:/capture?template=x&url=NEWURL&body=BODY" . "** SOMEDAY\n\nBODY\n\n[[NEWURL][NEWURL]]\n") ;; - just the template ("/some/directory/org-protocol:/capture?template=X" . "** Hello World\n\n\n\nGoodbye World\n") ;; - query parameters, not sure how to include them in template ("/some/directory/org-protocol:/capture?template=x&url=URL&title=TITLE&body=BODY&from=example" . "** SOMEDAY\n\nBODY\n\n[[URL][TITLE]]\n") ;; - "+" is not decoded to space in old-style URIs ("/org-protocol:/capture:/t/https%3A%2F%2Forgmode.org%2Fsome+thing/org+mode/Body+plus" . "** TODO\n\nBody+plus\n\n[[https://orgmode.org/some+thing][org+mode]]\n") ;; - decode "+" to space ("/org-protocol:/capture?template=t&url=URL&title=Mailing+list&body=Body+no+plus" . "** TODO\n\nBody no plus\n\n[[URL][Mailing list]]\n") ))) ;; Old link style (mapc (lambda (test-case) (let ((uri (car test-case))) (org-protocol-check-filename-for-protocol uri (list uri) nil) (should (string= (buffer-string) (cdr test-case))) (org-capture-kill))) test-urls) (delete-file temp-file-name))) (ert-deftest test-org-protocol/org-protocol-capture-file () "capture: `org-protocol-sanitize-uri' could distort URL." :expected-result :failed (let* ((org-protocol-default-template-key "t") (temp-file-name (make-temp-file "org-protocol-test")) (org-capture-templates `(("t" "Test" plain (file ,temp-file-name) "%a\n%i\n" :kill-buffer t)))) (let ((uri "/org-protocol:/capture:/t/file%3A%2F%2F%2Fetc%2Fmailcap/Triple%20Slash/Body")) (should (null (org-protocol-check-filename-for-protocol uri (list uri) nil))) (should (string= (buffer-string) "[[file:///etc/mailcap][Triple Slash]]\nBody"))) (let ((uri "/org-protocol:/capture?template=t&url=file%3A%2F%2F%2Fetc%2Fmailcap&title=Triple%20Slash&body=Body")) (should (null (org-protocol-check-filename-for-protocol uri (list uri) nil))) (should (string= (buffer-string) "[[file:///etc/mailcap][Triple Slash]]\nBody"))))) (ert-deftest test-org-protocol/org-protocol-open-source () "Test org-protocol://open-source links." (let* ((temp-file-name1 (make-temp-file "org-protocol-test1")) (temp-file-name2 (make-temp-file "org-protocol-test2")) (org-protocol-project-alist `((test1 :base-url "http://example.com/" :online-suffix ".html" :working-directory ,(file-name-directory temp-file-name1)) (test2 :base-url "http://another.example.com/" :online-suffix ".js" :working-directory ,(file-name-directory temp-file-name2)) (test3 :base-url "https://blog-example.com/" :working-directory ,(file-name-directory temp-file-name2) :online-suffix ".html" :working-suffix ".md" :rewrites (("\\(https://blog-example.com/[0-9]+/[0-9]+/[0-9]+/\\)" . ".md"))))) (test-cases (list ;; Old-style URLs (cons (concat "/some/directory/org-protocol:/open-source:/" (url-hexify-string (concat "http://example.com/" (file-name-nondirectory temp-file-name1) ".html"))) temp-file-name1) (cons (concat "/some/directory/org-protocol:/open-source:/" (url-hexify-string (concat "http://another.example.com/" (file-name-nondirectory temp-file-name2) ".js"))) temp-file-name2) ;; New-style URLs (cons (concat "/some/directory/org-protocol:/open-source?url=" (url-hexify-string (concat "http://example.com/" (file-name-nondirectory temp-file-name1) ".html"))) temp-file-name1) (cons (concat "/some/directory/org-protocol:/open-source?url=" (url-hexify-string (concat "http://another.example.com/" (file-name-nondirectory temp-file-name2) ".js"))) temp-file-name2)))) (mapc (lambda (test-case) (should (string= (org-protocol-check-filename-for-protocol (car test-case) (list (car test-case)) nil) (cdr test-case)))) test-cases) (delete-file temp-file-name1) (delete-file temp-file-name2))) (defun test-org-protocol/org-protocol-greedy-handler (fname) ;; fname should be a list of parsed items (should (listp fname)) nil) (ert-deftest test-org-protocol/org-protocol-with-greedy-handler () "Check that greedy handlers are called with all the filenames." (let ((org-protocol-protocol-alist '(("protocol-a" :protocol "greedy" :function test-org-protocol/org-protocol-greedy-handler :kill-client t :greedy t)))) ;; Neither of these should signal errors (let ((uri "/some/dir/org-protocol://greedy?a=b&c=d") (uri2 "/some/dir/org-protocol://greedy?e=f&g=h")) (org-protocol-check-filename-for-protocol uri (list uri uri2) nil)))) ;; TODO: Verify greedy protocol handling (provide 'test-org-protocol) ;;; test-org-protocol.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-src.el000066400000000000000000000444741500430433700215650ustar00rootroot00000000000000;;; test-org-src.el --- tests for org-src.el -*- lexical-binding: t; -*- ;; Copyright (C) 2012-2015, 2019 Le Wang ;; Author: Le Wang ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (require 'org-test "../testing/org-test") (ert-deftest test-org-src/basic () "Editing regular block works, with point on source block." (org-test-with-temp-text " #+begin_src emacs-lisp (message hello) #+end_src " (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation nil)) (org-edit-special) (insert "blah") (org-edit-src-exit) (should (equal (buffer-string) " #+begin_src emacs-lisp blah(message hello) #+end_src ")) (should (looking-at-p "(message hello)"))))) (ert-deftest test-org-src/point-outside-block () "Editing with point before/after block signals expected error." (org-test-with-temp-text " #+begin_src emacs-lisp (message hello) #+end_src " (goto-line 1) (should-error (org-edit-special)) (goto-char (point-max)) (should-error (org-edit-special)))) (ert-deftest test-org-src/undo () "Undo-ing an edit buffer should not go back to empty state." (org-test-with-temp-text " #+begin_src emacs-lisp (message hello) #+end_src " (org-edit-special) (should-error (undo)) (org-edit-src-exit))) (ert-deftest test-org-src/empty-block () "Editing empty block." (org-test-with-temp-text " #+begin_src emacs-lisp #+end_src " (let ((org-edit-src-content-indentation 0) (org-src-preserve-indentation nil)) (org-edit-special) (insert "blah") (org-edit-src-exit) (should (equal (buffer-string) " #+begin_src emacs-lisp blah #+end_src ")) (should (equal (buffer-substring (line-beginning-position) (point)) "blah"))))) (ert-deftest test-org-src/blank-line-block () "Editing block with just a blank line." (org-test-with-temp-text-in-file " #+begin_src emacs-lisp #+end_src " (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation nil)) (goto-line 2) (org-edit-special) (insert "blah") (org-edit-src-exit) (should (equal (buffer-string) " #+begin_src emacs-lisp blah #+end_src "))))) (ert-deftest test-org-src/preserve-tabs () "Editing block preserve tab characters." ;; With `org-src-preserve-indentation' set to nil. (should (equal " #+begin_src emacs-lisp This is a tab:\t. #+end_src" (org-test-with-temp-text " #+begin_src emacs-lisp This is a tab:\t. #+end_src" (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation nil)) (org-edit-special) (org-edit-src-exit) (buffer-string))))) ;; With `org-src-preserve-indentation' set to t. (should (equal " #+begin_src emacs-lisp This is a tab:\t. #+end_src" (org-test-with-temp-text " #+begin_src emacs-lisp This is a tab:\t. #+end_src" (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation t)) (org-edit-special) (org-edit-src-exit) (buffer-string)))))) (ert-deftest test-org-src/preserve-empty-lines () "Editing block preserves empty lines." (should (equal " #+begin_src emacs-lisp The following line is empty abc #+end_src" (org-test-with-temp-text " #+begin_src emacs-lisp The following line is empty abc #+end_src" (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation nil)) (org-edit-special) (org-edit-src-exit) (buffer-string))))) (should (equal " #+begin_src emacs-lisp The following line is empty abc #+end_src" (org-test-with-temp-text " #+begin_src emacs-lisp The following line is empty abc #+end_src" (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation nil)) (org-edit-special) (org-edit-src-exit) (buffer-string)))))) (ert-deftest test-org-src/coderef-format () "Test `org-src-coderef-format' specifications." ;; Regular tests in a src block, an example block and an edit ;; buffer. (should (equal "foo" (let ((org-coderef-label-format "foo")) (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n0\n#+END_SRC" (org-src-coderef-format))))) (should (equal "foo" (let ((org-coderef-label-format "foo")) (org-test-with-temp-text "#+BEGIN_EXAMPLE\n0\n#+END_EXAMPLE" (org-src-coderef-format))))) (should (equal "foo" (let ((org-coderef-label-format "foo") result) (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n0\n#+END_SRC" (org-edit-special) (setq result (org-src-coderef-format)) (org-edit-src-exit) result)))) ;; When a local variable in the source buffer is available, use it. (should (equal "bar" (let ((org-coderef-label-format "foo")) (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n0\n#+END_SRC" (setq-local org-coderef-label-format "bar") (org-src-coderef-format))))) (should (equal "bar" (let ((org-coderef-label-format "foo") result) (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n0\n#+END_SRC" (setq-local org-coderef-label-format "bar") (org-edit-special) (setq result (org-src-coderef-format)) (org-edit-src-exit) result)))) ;; Use provided local format even if in an edit buffer. (should (equal "bar" (let ((org-coderef-label-format "foo")) (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp -l \"bar\"\n0\n#+END_SRC" (org-src-coderef-format))))) (should (equal "bar" (let ((org-coderef-label-format "foo") result) (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp -l \"bar\"\n0\n#+END_SRC" (org-edit-special) (setq result (org-src-coderef-format)) (org-edit-src-exit) result)))) ;; Local format has precedence over local variables. (should (equal "bar" (let ((org-coderef-label-format "foo")) (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp -l \"bar\"\n0\n#+END_SRC" (setq-local org-coderef-label-format "foo") (org-src-coderef-format))))) (should (equal "bar" (let ((org-coderef-label-format "foo") result) (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp -l \"bar\"\n0\n#+END_SRC" (setq-local org-coderef-label-format "foo") (org-edit-special) (setq result (org-src-coderef-format)) (org-edit-src-exit) result)))) ;; When optional argument provides a coderef format string, use it. (should (equal "bar" (let ((org-coderef-label-format "foo") (element (org-element-create 'src-block '(:label-fmt "bar")))) (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n0\n#+END_SRC" (org-src-coderef-format element))))) (should (equal "baz" (let ((org-coderef-label-format "foo") (element (org-element-create 'src-block '(:label-fmt "baz")))) (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp -l \"bar\"\n0\n#+END_SRC" (setq-local org-coderef-label-format "foo") (org-src-coderef-format element))))) ;; If it doesn't provide any label format string, fall back to ;; regular checks. (should (equal "foo" (let ((org-coderef-label-format "foo") (element (org-element-create 'src-block))) (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n0\n#+END_SRC" (org-src-coderef-format element))))) (should (equal "bar" (let ((org-coderef-label-format "foo") (element (org-element-create 'src-block))) (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp -l \"bar\"\n0\n#+END_SRC" (setq-local org-coderef-label-format "foo") (org-src-coderef-format element)))))) (ert-deftest test-org-src/coderef-regexp () "Test `org-src-coderef-regexp' specifications." ;; Regular test. (should (string-match-p (org-src-coderef-regexp "; ref:%s") "#+BEGIN_SRC emacs-lisp\n0; ref:label\n#+END_SRC")) ;; Ignore white space around the coderef. (should (string-match-p (org-src-coderef-regexp "; ref:%s") "#+BEGIN_SRC emacs-lisp\n0 ; ref:label\n#+END_SRC")) (should (string-match-p (org-src-coderef-regexp "; ref:%s") "#+BEGIN_SRC emacs-lisp\n0 ; ref:label \n#+END_SRC")) ;; Only match regexp at the end of the line. (should-not (string-match-p (org-src-coderef-regexp "; ref:%s") "#+BEGIN_SRC emacs-lisp\n0; ref:label (+ 1 2)\n#+END_SRC")) ;; Do not match an empty label. (should-not (string-match-p (org-src-coderef-regexp "; ref:%s") "#+BEGIN_SRC emacs-lisp\n0; ref:\n#+END_SRC")) ;; When optional argument LABEL is provided, match given label only. (should (string-match-p (org-src-coderef-regexp "; ref:%s" "label") "#+BEGIN_SRC emacs-lisp\n0; ref:label\n#+END_SRC")) (should-not (string-match-p (org-src-coderef-regexp "; ref:%s" "label2") "#+BEGIN_SRC emacs-lisp\n0; ref:label\n#+END_SRC"))) (ert-deftest test-org-src/indented-blocks () "Test editing indented blocks." ;; Editing a block should preserve its global indentation, unless ;; `org-src-preserve-indentation' is non-nil. (should (equal "- Item\n #+BEGIN_SRC emacs-lisp\n Foo\n #+END_SRC" (org-test-with-temp-text "- Item\n #+BEGIN_SRC emacs-lisp\n (+ 1 1)\n #+END_SRC" (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation nil)) (org-edit-special) (erase-buffer) (insert "Foo") (org-edit-src-exit) (buffer-string))))) (should (equal "- Item\n #+BEGIN_SRC emacs-lisp\n Foo\n #+END_SRC" (org-test-with-temp-text "- Item\n #+BEGIN_SRC emacs-lisp\n (+ 1 1)\n #+END_SRC" (let ((org-src-preserve-indentation t)) (org-edit-special) (erase-buffer) (insert " Foo") (org-edit-src-exit) (buffer-string))))) ;; Global indentation does not obey `indent-tabs-mode' from the ;; original buffer. (should-not (string-match-p "\t" (org-test-with-temp-text " - Item #+BEGIN_SRC emacs-lisp (progn (function argument1 argument2)) #+END_SRC" (setq-local indent-tabs-mode t) (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation nil)) (org-edit-special) (org-edit-src-exit) (buffer-string))))) ;; Tab character is preserved (should (string-match-p "\targument2" (org-test-with-temp-text " - Item #+BEGIN_SRC emacs-lisp (progn\n (function argument1\n \targument2)) #+END_SRC" (setq-local indent-tabs-mode nil) (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation nil)) (org-edit-special) (org-edit-src-exit) (buffer-string))))) ;; Indentation does not obey `tab-width' from org buffer. (should (string-match-p "^ \targument2" (org-test-with-temp-text " #+BEGIN_SRC emacs-lisp (progn (list argument1\n \targument2)) #+END_SRC" (setq-local indent-tabs-mode t) (setq-local tab-width 4) (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation nil)) (org-edit-special) (setq-local indent-tabs-mode t) (setq-local tab-width 8) (lisp-indent-line) (org-edit-src-exit) (buffer-string))))) ;; Tab characters are displayed with `tab-width' from the native ;; edit buffer. (should (equal 10 (org-test-with-temp-text " #+BEGIN_SRC emacs-lisp (progn (list argument1\n \targument2)) #+END_SRC" (setq-local indent-tabs-mode t) (setq-local tab-width 4) (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation nil)) (font-lock-ensure) ;; `current-column' will not work with older versions of emacs ;; before commit 4243747b1b8: Fix 'current-column' in the ;; presence of display strings (if (<= emacs-major-version 28) (+ (progn (backward-char) (length (get-text-property (point) 'display))) (current-column)) (current-column)))))) ;; The initial tab characters respect org's `tab-width'. (should (equal 10 (org-test-with-temp-text " #+BEGIN_SRC emacs-lisp \t(progn \t (list argument1\n\t\targument2)) #+END_SRC" (setq-local indent-tabs-mode t) (setq-local tab-width 2) (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation nil)) (font-lock-ensure) (if (<= emacs-major-version 28) (+ (progn (backward-char) (length (get-text-property (point) 'display))) (current-column)) (current-column))))))) (ert-deftest test-org-src/indented-latex-fragments () "Test editing multiline indented LaTeX fragment." (should (equal "- Item $abc\n efg$" (org-test-with-temp-text "- Item $abc\n efg$" (org-edit-special) (org-edit-src-exit) (buffer-string))))) (ert-deftest test-org-src/footnote-references () "Test editing footnote references." ;; Error when there is no definition to edit. (should-error (org-test-with-temp-text "A footnote[fn:1]" (org-edit-special))) ;; Error when trying to edit an anonymous footnote. (should-error (org-test-with-temp-text "A footnote[fn::edit me!]" (org-edit-special))) ;; Edit a regular definition. (should (equal "[fn:1] Definition" (org-test-with-temp-text "A footnote[fn:1]\n[fn:1] Definition" (org-edit-special) (prog1 (buffer-string) (org-edit-src-exit))))) ;; Label should be protected against editing. (should (org-test-with-temp-text "A footnote[fn:1]\n[fn:1] Definition" (org-edit-special) (prog1 (get-text-property 0 'read-only (buffer-string)) (org-edit-src-exit)))) (should (org-test-with-temp-text "A footnote[fn:1]\n[fn:1] Definition" (org-edit-special) (prog1 (get-text-property 5 'read-only (buffer-string)) (org-edit-src-exit)))) ;; Edit a regular definition. (should (equal "A footnote[fn:1][fn:2]\n[fn:1] D1\n\n[fn:2] D2" (org-test-with-temp-text "A footnote[fn:1][fn:2]\n[fn:1] D1\n\n[fn:2] D2" (org-edit-special) (org-edit-src-exit) (buffer-string)))) ;; Edit an inline definition. (should (equal "[fn:1:definition]" (org-test-with-temp-text "An inline[fn:1] footnote[fn:1:definition]" (org-edit-special) (prog1 (buffer-string) (org-edit-src-exit))))) ;; Label and closing square bracket should be protected against ;; editing. (should (org-test-with-temp-text "An inline[fn:1] footnote[fn:1:definition]" (org-edit-special) (prog1 (get-text-property 0 'read-only (buffer-string)) (org-edit-src-exit)))) (should (org-test-with-temp-text "An inline[fn:1] footnote[fn:1:definition]" (org-edit-special) (prog1 (get-text-property 5 'read-only (buffer-string)) (org-edit-src-exit)))) (should (org-test-with-temp-text "An inline[fn:1] footnote[fn:1:definition]" (org-edit-special) (prog1 (get-text-property 16 'read-only (buffer-string)) (org-edit-src-exit)))) ;; Do not include trailing white spaces when displaying the inline ;; footnote definition. (should (equal "[fn:1:definition]" (org-test-with-temp-text "An inline[fn:1] footnote[fn:1:definition] and some text" (org-edit-special) (prog1 (buffer-string) (org-edit-src-exit))))) ;; Preserve local variables when editing a footnote definition. (should (eq 'bar (org-test-with-temp-text "A footnote[fn:1]\n[fn:1] Definition" (setq-local foo 'bar) (org-edit-special) (prog1 foo (org-edit-src-exit)))))) ;;; Code escaping (ert-deftest test-org-src/escape-code-in-string () "Test `org-escape-code-in-string' specifications." ;; Escape lines starting with "*" or "#+". (should (equal ",*" (org-escape-code-in-string "*"))) (should (equal ",#+" (org-escape-code-in-string "#+"))) ;; Escape lines starting with ",*" and ",#+". Number of leading ;; commas does not matter. (should (equal ",,*" (org-escape-code-in-string ",*"))) (should (equal ",,#+" (org-escape-code-in-string ",#+"))) (should (equal ",,,*" (org-escape-code-in-string ",,*"))) (should (equal ",,,#+" (org-escape-code-in-string ",,#+"))) ;; Indentation does not matter. (should (equal " ,*" (org-escape-code-in-string " *"))) (should (equal " ,#+" (org-escape-code-in-string " #+"))) ;; Do nothing on other cases. (should (equal "a" (org-escape-code-in-string "a"))) (should (equal "#" (org-escape-code-in-string "#"))) (should (equal "," (org-escape-code-in-string ",")))) (ert-deftest test-org-src/unescape-code-in-string () "Test `org-unescape-code-in-string' specifications." ;; Unescape lines starting with ",*" or ",#+". Number of leading ;; commas does not matter. (should (equal "*" (org-unescape-code-in-string ",*"))) (should (equal "#+" (org-unescape-code-in-string ",#+"))) (should (equal ",*" (org-unescape-code-in-string ",,*"))) (should (equal ",#+" (org-unescape-code-in-string ",,#+"))) ;; Indentation does not matter. (should (equal " *" (org-unescape-code-in-string " ,*"))) (should (equal " #+" (org-unescape-code-in-string " ,#+"))) ;; Do nothing on other cases. (should (equal "a" (org-unescape-code-in-string "a"))) (should (equal "#" (org-unescape-code-in-string "#"))) (should (equal "," (org-unescape-code-in-string ",")))) (provide 'test-org-src) ;;; test-org-src.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-table.el000066400000000000000000003237711500430433700220650ustar00rootroot00000000000000;;; test-org-table.el --- tests for org-table.el -*- lexical-binding: t; -*- ;; Copyright (c) David Maus ;; Authors: David Maus, Michael Brand ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;;; Comments: ;; Template test file for Org tests. Many tests are also a howto ;; example collection as a user documentation, more or less all those ;; using `org-test-table-target-expect'. See also the doc string of ;; `org-test-table-target-expect'. ;;; Code: (require 'org-table) ; `org-table-make-reference' (require 'ox) (ert-deftest test-org-table/simple-formula/no-grouping/no-title-row () "Simple sum without grouping rows, without title row." (org-test-table-target-expect " | 2 | | 4 | | 8 | | replace | " " | 2 | | 4 | | 8 | | 14 | " 1 ;; Calc formula "#+TBLFM: @>$1 = vsum(@<..@>>)" ;; Lisp formula "#+TBLFM: @>$1 = '(+ @<..@>>); N")) (ert-deftest test-org-table/simple-formula/no-grouping/with-title-row () "Simple sum without grouping rows, with title row." (org-test-table-target-expect " | foo | |---------| | 2 | | 4 | | 8 | | replace | " " | foo | |-----| | 2 | | 4 | | 8 | | 14 | " 1 ;; Calc formula "#+TBLFM: @>$1 = vsum(@I..@>>)" ;; Lisp formula "#+TBLFM: @>$1 = '(+ @I..@>>); N")) (ert-deftest test-org-table/simple-formula/with-grouping/no-title-row () "Simple sum with grouping rows, how not to do." ;; The first example has a problem, see the second example in this ;; ert-deftest. (org-test-table-target-expect " | 2 | | 4 | | 8 | |---------| | replace | " " | 2 | | 4 | | 8 | |----| | 14 | " 1 ;; Calc formula "#+TBLFM: $1 = vsum(@<..@>>)" ;; Lisp formula "#+TBLFM: $1 = '(+ @<..@>>); N") ;; The problem is that the first three rows with the summands are ;; considered the header and therefore column formulas are not ;; applied on them as shown below. Also export behaves unexpected. ;; See next ert-deftest how to group rows right. (org-test-table-target-expect " | 2 | header | | 4 | header | | 8 | header | |---------+---------| | replace | replace | " " | 2 | header | | 4 | header | | 8 | header | |----+--------| | 14 | 28 | " 2 ;; Calc formula "#+TBLFM: @>$1 = vsum(@<..@>>) :: $2 = 2 * $1" ;; Lisp formula "#+TBLFM: @>$1 = '(+ @<..@>>); N :: $2 = '(* 2 $1); N")) (ert-deftest test-org-table/simple-formula/with-grouping/with-title-row () "Simple sum with grouping rows, how to do it right." ;; Always add a top row with the column names separated by hline to ;; get the desired header when you want to group rows. (org-test-table-target-expect " | foo | bar | |---------+---------| | 2 | replace | | 4 | replace | | 8 | replace | |---------+---------| | replace | replace | " " | foo | bar | |-----+-----| | 2 | 4 | | 4 | 8 | | 8 | 16 | |-----+-----| | 14 | 28 | " 2 ;; Calc formula "#+TBLFM: @>$1 = vsum(@I..@>>) :: $2 = 2 * $1" ;; Lisp formula "#+TBLFM: @>$1 = '(+ @I..@>>); N :: $2 = '(* 2 $1); N")) (defconst references/target-normal " | 0 | 1 | replace | replace | replace | replace | replace | replace | | z | 1 | replace | replace | replace | replace | replace | replace | | | 1 | replace | replace | replace | replace | replace | replace | | | | replace | replace | replace | replace | replace | replace | " "Normal numbers and non-numbers for Lisp and Calc formula.") (defconst references/target-special " | nan | 1 | replace | replace | replace | replace | replace | replace | | uinf | 1 | replace | replace | replace | replace | replace | replace | | -inf | 1 | replace | replace | replace | replace | replace | replace | | inf | 1 | replace | replace | replace | replace | replace | replace | " "Special numbers for Calc formula.") (ert-deftest test-org-table/references/mode-string-EL () "Basic: Assign field reference, sum of field references, sum and len of simple range reference (no row) and complex range reference (with row). Mode string EL." ;; Empty fields are kept during parsing field but lost as list ;; elements within Lisp formula syntactically when used literally ;; and not enclosed with " within fields, see last columns with len. (org-test-table-target-expect references/target-normal ;; All the #ERROR show that for Lisp calculations N has to be used. " | 0 | 1 | 0 | 1 | 1 | 1 | 2 | 2 | | z | 1 | z | #ERROR | #ERROR | #ERROR | 2 | 2 | | | 1 | | 1 | 1 | 1 | 1 | 1 | | | | | 0 | 0 | 0 | 0 | 0 | " 1 (concat "#+TBLFM: $3 = '(identity \"$1\"); EL :: $4 = '(+ $1 $2); EL :: " "$5 = '(+ $1..$2); EL :: $6 = '(+ @0$1..@0$2); EL :: " "$7 = '(length '($1..$2)); EL :: $8 = '(length '(@0$1..@0$2)); EL")) ;; Empty fields are kept during parsing field _and_ as list elements ;; within Lisp formula syntactically even when used literally when ;; enclosed with " within fields, see last columns with len. (org-test-table-target-expect " | \"0\" | \"1\" | repl | repl | repl | repl | repl | repl | | \"z\" | \"1\" | repl | repl | repl | repl | repl | repl | | \"\" | \"1\" | repl | repl | repl | repl | repl | repl | | \"\" | \"\" | repl | repl | repl | repl | repl | repl | " " | \"0\" | \"1\" | \"0\" | 1 | #ERROR | #ERROR | 2 | 2 | | \"z\" | \"1\" | \"z\" | 1 | #ERROR | #ERROR | 2 | 2 | | \"\" | \"1\" | \"\" | 1 | #ERROR | #ERROR | 2 | 2 | | \"\" | \"\" | \"\" | 0 | #ERROR | #ERROR | 2 | 2 | " 1 (concat "#+TBLFM: $3 = '(concat \"\\\"\" $1 \"\\\"\"); EL :: " "$4 = '(+ (string-to-number $1) (string-to-number $2)); EL :: " "$5 = '(+ $1..$2); EL :: $6 = '(+ @0$1..@0$2); EL :: " "$7 = '(length '($1..$2)); EL :: $8 = '(length '(@0$1..@0$2)); EL"))) (ert-deftest test-org-table/references/mode-string-E () "Basic: Assign field reference, sum of field references, sum and len of simple range reference (no row) and complex range reference (with row). Mode string E." (let ((lisp (concat "#+TBLFM: $3 = '(identity $1); E :: $4 = '(+ $1 $2); E :: " "$5 = '(+ $1..$2); E :: $6 = '(+ @0$1..@0$2); E :: " "$7 = '(length '($1..$2)); E :: $8 = '(length '(@0$1..@0$2)); E")) (calc (concat "#+TBLFM: $3 = $1; E :: $4 = $1 + $2; E :: " "$5 = vsum($1..$2); E :: $6 = vsum(@0$1..@0$2); E :: " "$7 = vlen($1..$2); E :: $8 = vlen(@0$1..@0$2); E"))) (org-test-table-target-expect references/target-normal ;; All the #ERROR show that for Lisp calculations N has to be used. " | 0 | 1 | 0 | #ERROR | #ERROR | #ERROR | 2 | 2 | | z | 1 | z | #ERROR | #ERROR | #ERROR | 2 | 2 | | | 1 | | #ERROR | #ERROR | #ERROR | 2 | 2 | | | | | #ERROR | #ERROR | #ERROR | 2 | 2 | " 1 lisp) (org-test-table-target-expect references/target-normal " | 0 | 1 | 0 | 1 | 1 | 1 | 2 | 2 | | z | 1 | z | z + 1 | z + 1 | z + 1 | 2 | 2 | | | 1 | nan | nan | nan | nan | 2 | 2 | | | | nan | nan | nan | nan | 2 | 2 | " 1 calc) (org-test-table-target-expect references/target-special " | nan | 1 | nan | nan | nan | nan | 2 | 2 | | uinf | 1 | uinf | uinf | uinf | uinf | 2 | 2 | | -inf | 1 | -inf | -inf | -inf | -inf | 2 | 2 | | inf | 1 | inf | inf | inf | inf | 2 | 2 | " 1 calc))) (ert-deftest test-org-table/references/mode-string-EN () "Basic: Assign field reference, sum of field references, sum and len of simple range reference (no row) and complex range reference (with row). Mode string EN." (let ((lisp (concat "#+TBLFM: $3 = '(identity $1); EN :: $4 = '(+ $1 $2); EN :: " "$5 = '(+ $1..$2); EN :: $6 = '(+ @0$1..@0$2); EN :: " "$7 = '(length '($1..$2)); EN :: " "$8 = '(length '(@0$1..@0$2)); EN")) (calc (concat "#+TBLFM: $3 = $1; EN :: $4 = $1 + $2; EN :: " "$5 = vsum($1..$2); EN :: $6 = vsum(@0$1..@0$2); EN :: " "$7 = vlen($1..$2); EN :: $8 = vlen(@0$1..@0$2); EN"))) (org-test-table-target-expect references/target-normal " | 0 | 1 | 0 | 1 | 1 | 1 | 2 | 2 | | z | 1 | 0 | 1 | 1 | 1 | 2 | 2 | | | 1 | 0 | 1 | 1 | 1 | 2 | 2 | | | | 0 | 0 | 0 | 0 | 2 | 2 | " 1 lisp calc) (org-test-table-target-expect references/target-special " | nan | 1 | 0 | 1 | 1 | 1 | 2 | 2 | | uinf | 1 | 0 | 1 | 1 | 1 | 2 | 2 | | -inf | 1 | 0 | 1 | 1 | 1 | 2 | 2 | | inf | 1 | 0 | 1 | 1 | 1 | 2 | 2 | " 1 calc))) (ert-deftest test-org-table/references/mode-string-L () "Basic: Assign field reference, sum of field references, sum and len of simple range reference (no row) and complex range reference (with row). Mode string L." (org-test-table-target-expect references/target-normal ;; All the #ERROR show that for Lisp calculations N has to be used. " | 0 | 1 | 0 | 1 | 1 | 1 | 2 | 2 | | z | 1 | z | #ERROR | #ERROR | #ERROR | 2 | 2 | | | 1 | | 1 | 1 | 1 | 1 | 1 | | | | | 0 | 0 | 0 | 0 | 0 | " 1 (concat "#+TBLFM: $3 = '(identity \"$1\"); L :: $4 = '(+ $1 $2); L :: " "$5 = '(+ $1..$2); L :: $6 = '(+ @0$1..@0$2); L :: " "$7 = '(length '($1..$2)); L :: $8 = '(length '(@0$1..@0$2)); L"))) (ert-deftest test-org-table/references/mode-string-none () "Basic: Assign field reference, sum of field references, sum and len of simple range reference (no row) and complex range reference (with row). No mode string." (let ((lisp (concat "#+TBLFM: $3 = '(identity $1) :: $4 = '(+ $1 $2) :: " "$5 = '(+ $1..$2) :: $6 = '(+ @0$1..@0$2) :: " "$7 = '(length '($1..$2)) :: $8 = '(length '(@0$1..@0$2))")) (calc (concat "#+TBLFM: $3 = $1 :: $4 = $1 + $2 :: " "$5 = vsum($1..$2) :: $6 = vsum(@0$1..@0$2) :: " "$7 = vlen($1..$2) :: $8 = vlen(@0$1..@0$2)"))) (org-test-table-target-expect references/target-normal ;; All the #ERROR show that for Lisp calculations N has to be used. " | 0 | 1 | 0 | #ERROR | #ERROR | #ERROR | 2 | 2 | | z | 1 | z | #ERROR | #ERROR | #ERROR | 2 | 2 | | | 1 | | #ERROR | #ERROR | #ERROR | 1 | 1 | | | | | #ERROR | 0 | 0 | 0 | 0 | " 1 lisp) (org-test-table-target-expect references/target-normal " | 0 | 1 | 0 | 1 | 1 | 1 | 2 | 2 | | z | 1 | z | z + 1 | z + 1 | z + 1 | 2 | 2 | | | 1 | 0 | 1 | 1 | 1 | 1 | 1 | | | | 0 | 0 | 0 | 0 | 0 | 0 | " 1 calc) (org-test-table-target-expect references/target-special " | nan | 1 | nan | nan | nan | nan | 2 | 2 | | uinf | 1 | uinf | uinf | uinf | uinf | 2 | 2 | | -inf | 1 | -inf | -inf | -inf | -inf | 2 | 2 | | inf | 1 | inf | inf | inf | inf | 2 | 2 | " 1 calc))) (ert-deftest test-org-table/references/mode-string-N () "Basic: Assign field reference, sum of field references, sum and len of simple range reference (no row) and complex range reference (with row). Mode string N." (let ((lisp (concat "#+TBLFM: $3 = '(identity $1); N :: $4 = '(+ $1 $2); N :: " "$5 = '(+ $1..$2); N :: $6 = '(+ @0$1..@0$2); N :: " "$7 = '(length '($1..$2)); N :: $8 = '(length '(@0$1..@0$2)); N")) (calc (concat "#+TBLFM: $3 = $1; N :: $4 = $1 + $2; N :: " "$5 = vsum($1..$2); N :: $6 = vsum(@0$1..@0$2); N :: " "$7 = vlen($1..$2); N :: $8 = vlen(@0$1..@0$2); N"))) (org-test-table-target-expect references/target-normal " | 0 | 1 | 0 | 1 | 1 | 1 | 2 | 2 | | z | 1 | 0 | 1 | 1 | 1 | 2 | 2 | | | 1 | 0 | 1 | 1 | 1 | 1 | 1 | | | | 0 | 0 | 0 | 0 | 0 | 0 | " 1 lisp calc) (org-test-table-target-expect references/target-special " | nan | 1 | 0 | 1 | 1 | 1 | 2 | 2 | | uinf | 1 | 0 | 1 | 1 | 1 | 2 | 2 | | -inf | 1 | 0 | 1 | 1 | 1 | 2 | 2 | | inf | 1 | 0 | 1 | 1 | 1 | 2 | 2 | " 1 calc))) (ert-deftest test-org-table/mode-string-u () "Basic: verify that mode string u results in units simplification mode applied to Calc formulas." (org-test-table-target-expect " | 1.5 A/B | 2.0 B | | " " | 1.5 A/B | 2.0 B | 3. A | " 1 "#+TBLFM: $3=$1*$2;u")) (ert-deftest test-org-table/lisp-return-value () "Basic: Return value of Lisp formulas." (org-test-table-target-expect " | | nil | (list) | '() | |-------------------------+-------------+--------+-----| | type-of, no L | replace (r) | r | r | | type-of identity, no L | r | r | r | | identity, no L | r | r | r | |-------------------------+-------------+--------+-----| | type-of \"@1\" | r | r | r | | type-of (identity \"@1\") | r | r | r | | identity \"@1\" | r | r | r | |-------------------------+-------------+--------+-----| | type-of @1 | r | r | r | | type-of (identity @1) | r | r | r | | identity @1 | r | r | r | " " | | nil | (list) | '() | |-------------------------+--------+--------+--------| | type-of, no L | string | string | string | | type-of identity, no L | string | string | string | | identity, no L | nil | (list) | '() | |-------------------------+--------+--------+--------| | type-of \"@1\" | string | string | string | | type-of (identity \"@1\") | string | string | string | | identity \"@1\" | nil | (list) | '() | |-------------------------+--------+--------+--------| | type-of @1 | symbol | symbol | symbol | | type-of (identity @1) | symbol | symbol | symbol | | identity @1 | nil | nil | nil | " 1 (concat "#+TBLFM: @2$<<..@2$> = '(type-of @1) :: " "@3$<<..@3$> = '(type-of (identity @1)) :: " "@4$<<..@4$> = '(identity @1) :: @5$<<..@>$> = '(@0$1); L"))) (ert-deftest test-org-table/compare () "Basic: Compare field references in Calc." (org-test-table-target-expect " | | 0 | z | | nan | uinf | -inf | inf | |------+------+------+------+------+------+------+------| | 0 | repl | repl | repl | repl | repl | repl | repl | | z | repl | repl | repl | repl | repl | repl | repl | | | repl | repl | repl | repl | repl | repl | repl | | nan | repl | repl | repl | repl | repl | repl | repl | | uinf | repl | repl | repl | repl | repl | repl | repl | | -inf | repl | repl | repl | repl | repl | repl | repl | | inf | repl | repl | repl | repl | repl | repl | repl | " " | | 0 | z | | nan | uinf | -inf | inf | |------+---+---+---+-----+------+------+-----| | 0 | x | | | | | | | | z | | x | | | | | | | | | | x | | | | | | nan | | | | x | | | | | uinf | | | | | x | | | | -inf | | | | | | x | | | inf | | | | | | | x | " 1 ;; Compare field reference ($1) with field reference (@1) "#+TBLFM: @<<$<<..@>$> = if(\"$1\" == \"@1\", x, string(\"\")); E" ;; Compare field reference ($1) with absolute term (concat "#+TBLFM: " "$2 = if(\"$1\" == \"(0)\" , x, string(\"\")); E :: " "$3 = if(\"$1\" == \"(z)\" , x, string(\"\")); E :: " "$4 = if(\"$1\" == \"nan\" , x, string(\"\")); E :: " "$5 = if(\"$1\" == \"(nan)\" , x, string(\"\")); E :: " "$6 = if(\"$1\" == \"(uinf)\", x, string(\"\")); E :: " "$7 = if(\"$1\" == \"(-inf)\", x, string(\"\")); E :: " "$8 = if(\"$1\" == \"(inf)\" , x, string(\"\")); E")) ;; Check field reference converted from an empty field: Despite this ;; field reference will not end up in a result, Calc evaluates it. ;; Make sure that also then there is no Calc error. (org-test-table-target-expect " | 0 | replace | | z | replace | | | replace | | nan | replace | " " | 0 | 1 | | z | z + 1 | | | | | nan | nan | " 1 "#+TBLFM: $2 = if(\"$1\" == \"nan\", string(\"\"), $1 + 1); E")) (ert-deftest test-org-table/empty-field () "Examples how to deal with empty fields." ;; Test if one field is empty, else do a calculation (org-test-table-target-expect " | -1 | replace | | 0 | replace | | | replace | " " | -1 | 0 | | 0 | 1 | | | | " 1 ;; Calc formula "#+TBLFM: $2 = if(\"$1\" == \"nan\", string(\"\"), $1 + 1); E" ;; Lisp formula "#+TBLFM: $2 = '(if (eq \"$1\" \"\") \"\" (1+ $1)); L") ;; Test if several fields are empty, else do a calculation (org-test-table-target-expect " | 1 | 2 | replace | | 4 | | replace | | | 8 | replace | | | | replace | " " | 1 | 2 | 3 | | 4 | | | | | 8 | | | | | | " 1 ;; Calc formula (concat "#+TBLFM: $3 = if(\"$1\" == \"nan\" || \"$2\" == \"nan\", " "string(\"\"), $1 + $2); E") ;; Lisp formula (concat "#+TBLFM: $3 = '(if (or (eq \"$1\" \"\") (eq \"$2\" \"\")) " "\"\" (+ $1 $2)); L")) ;; $2: Use $1 + 0.5 if $1 available, else only reformat $2 if $2 available (org-test-table-target-expect " | 1.5 | 0 | | 3.5 | | | | 5 | | | | " " | 1.5 | 2.0 | | 3.5 | 4.0 | | | 5.0 | | | | " 1 ;; Calc formula (concat "#+TBLFM: $2 = if(\"$1\" == \"nan\", " "if(\"$2\" == \"nan\", string(\"\"), $2 +.0), $1 + 0.5); E f-1") ;; Lisp formula not implemented yet ) ;; Empty fields in simple and complex range reference (org-test-table-target-expect " | | | | | repl | repl | repl | repl | repl | repl | | | | 5 | 7 | repl | repl | repl | repl | repl | repl | | 1 | 3 | 5 | 7 | repl | repl | repl | repl | repl | repl | " " | | | | | | | | | 0 | 0 | | | | 5 | 7 | | | 6 | 6 | 3 | 3 | | 1 | 3 | 5 | 7 | 4 | 4 | 4 | 4 | 4 | 4 | " 1 ;; Calc formula (concat "#+TBLFM: " "$5 = if(typeof(vmean($1..$4)) == 12, " "string(\"\"), vmean($1..$4)); E :: " "$6 = if(typeof(vmean(@0$1..@0$4)) == 12, " "string(\"\"), vmean(@0$1..@0$4)); E :: " "$7 = if(\"$1..$4\" == \"[]\", string(\"\"), vmean($1..$4)) :: " "$8 = if(\"@0$1..@0$4\" == \"[]\", string(\"\"), vmean(@0$1..@0$4)) :: " "$9 = vmean($1..$4); EN :: " "$10 = vmean(@0$1..@0$4); EN") ;; Lisp formula (concat "#+TBLFM: " "$5 = '(let ((l '($1..$4))) (if (member \"\" l) \"\" " "(/ (apply '+ (mapcar 'string-to-number l)) (length l)))); E :: " "$6 = '(let ((l '(@0$1..@0$4))) (if (member \"\" l) \"\" " "(/ (apply '+ (mapcar 'string-to-number l)) (length l)))); E :: " "$7 = '(let ((l '($1..$4))) " "(if l (/ (apply '+ l) (length l)) \"\")); N :: " "$8 = '(let ((l '(@0$1..@0$4))) " "(if l (/ (apply '+ l) (length l)) \"\")); N :: " "$9 = '(/ (+ $1..$4) (length '($1..$4))); EN :: " "$10 = '(/ (+ @0$1..@0$4) (length '(@0$1..@0$4))); EN"))) (ert-deftest test-org-table/copy-field () "Experiments on how to copy one field into another field. See also `test-org-table/remote-reference-access'." (let ((target " | 0 | replace | | a b | replace | | c d | replace | | | replace | | 2012-12 | replace | | [2012-12-31 Mon] | replace | ")) ;; Lisp formula to copy literally (org-test-table-target-expect target " | 0 | 0 | | a b | a b | | c d | c d | | | | | 2012-12 | 2012-12 | | [2012-12-31 Mon] | [2012-12-31 Mon] | " 1 "#+TBLFM: $2 = '(identity $1)") ;; Calc formula to copy quite literally (org-test-table-target-expect target " | 0 | 0 | | a b | a b | | c d | c d | | | | | 2012-12 | 2012-12 | | [2012-12-31 Mon] | [2012-12-31 Mon] | " 1 (concat "#+TBLFM: $2 = if(\"$1\" == \"nan\", " "string(\"\"), string(subvec(\"$1\", 2, vlen(\"$1\")))); E")) ;; Calc formula simple (org-test-table-target-expect target " | 0 | 0 | | a b | a b | | c d | c d | | | | | 2012-12 | 2000 | | [2012-12-31 Mon] | [2012-12-31 Mon] | " 1 "#+TBLFM: $2 = if(\"$1\" == \"nan\", string(\"\"), $1); E"))) (ert-deftest test-org-table/copy-down () "Test `org-table-copy-down' specifications." ;; Error when there is nothing to copy in the current field or the ;; field above. (should-error (org-test-with-temp-text "| |\n| |" (org-table-copy-down 1))) ;; Error when there is nothing to copy in the Nth field. (should-error (org-test-with-temp-text "| |\n| foo |\n| |" (org-table-copy-down 2))) ;; In an empty field, copy field above. (should (equal "| foo |\n| foo |" (org-test-with-temp-text "| foo |\n| |" (org-table-copy-down 1) (buffer-string)))) ;; In a non-empty field, copy it below. (should (equal "| foo |\n| foo |\n" (org-test-with-temp-text "| foo |" (org-table-copy-down 1) (buffer-string)))) ;; If field is a number or a timestamp, or is prefixed or suffixed ;; with a number, increment it by one unit. (should (equal "| 1 |\n| 2 |\n" (org-test-with-temp-text "| 1 |" (let ((org-table-copy-increment t)) (org-table-copy-down 1)) (buffer-string)))) (should (string-match-p "<2012-03-30" (org-test-with-temp-text "| <2012-03-29> |" (let ((org-table-copy-increment t)) (org-table-copy-down 1)) (buffer-string)))) (should (equal "| A1 |\n| A2 |\n" (org-test-with-temp-text "| A1 |" (let ((org-table-copy-increment t)) (org-table-copy-down 1)) (buffer-string)))) (should (equal "| 1A |\n| 2A |\n" (org-test-with-temp-text "| 1A |" (let ((org-table-copy-increment t)) (org-table-copy-down 1)) (buffer-string)))) ;; When `org-table-copy-increment' is nil, or when argument is 0, do ;; not increment. (should (equal "| 1 |\n| 1 |\n" (org-test-with-temp-text "| 1 |" (let ((org-table-copy-increment nil)) (org-table-copy-down 1)) (buffer-string)))) (should (equal "| 1 |\n| 1 |\n" (org-test-with-temp-text "| 1 |" (let ((org-table-copy-increment t)) (org-table-copy-down 0)) (buffer-string)))) ;; When there is a field just above field being incremented, try to ;; use it to guess increment step. (should (equal "| 4 |\n| 3 |\n| 2 |\n" (org-test-with-temp-text "| 4 |\n| 3 |" (let ((org-table-copy-increment t)) (org-table-copy-down 1)) (buffer-string)))) (should (equal "| A0 |\n| A2 |\n| A4 |\n" (org-test-with-temp-text "| A0 |\n| A2 |" (let ((org-table-copy-increment t)) (org-table-copy-down 1)) (buffer-string)))) ;; Both fields need to have the same type. In the special case of ;; number-prefixed or suffixed fields, make sure both fields have ;; the same pattern. (should (equal "| A4 |\n| 3 |\n| 4 |\n" (org-test-with-temp-text "| A4 |\n| 3 |" (let ((org-table-copy-increment t)) (org-table-copy-down 1)) (buffer-string)))) (should (equal "| 0A |\n| A2 |\n| A3 |\n" (org-test-with-temp-text "| 0A |\n| A2 |" (let ((org-table-copy-increment t)) (org-table-copy-down 1)) (buffer-string)))) (should (equal "| A0 |\n| 2A |\n| 3A |\n" (org-test-with-temp-text "| A0 |\n| 2A |" (let ((org-table-copy-increment t)) (org-table-copy-down 1)) (buffer-string)))) ;; Do not search field above past blank fields and horizontal ;; separators. (should (equal "| 4 |\n|---|\n| 3 |\n| 4 |\n" (org-test-with-temp-text "| 4 |\n|---|\n| 3 |" (let ((org-table-copy-increment t)) (org-table-copy-down 1)) (buffer-string)))) (should (equal "| 4 |\n| |\n| 3 |\n| 4 |\n" (org-test-with-temp-text "| 4 |\n| |\n| 3 |" (let ((org-table-copy-increment t)) (org-table-copy-down 1)) (buffer-string)))) ;; When `org-table-copy-increment' is a number, use it as the ;; increment step, ignoring any previous field. (should (equal "| 1 |\n| 3 |\n| 6 |\n" (org-test-with-temp-text "| 1 |\n| 3 |" (let ((org-table-copy-increment 3)) (org-table-copy-down 1)) (buffer-string)))) ;; However, if argument is 0, do not increment whatsoever. (should (equal "| 1 |\n| 3 |\n| 3 |\n" (org-test-with-temp-text "| 1 |\n| 3 |" (let ((org-table-copy-increment t)) (org-table-copy-down 0)) (buffer-string)))) (should (equal "| 1 |\n| 3 |\n| 3 |\n" (org-test-with-temp-text "| 1 |\n| 3 |" (let ((org-table-copy-increment 3)) (org-table-copy-down 0)) (buffer-string))))) (ert-deftest test-org-table/sub-total () "Grouped rows with sub-total. Begin range with \"@II\" to handle multiline header. Convert integer to float with \"+.0\" for sub-total of items c1 and c2. Sum empty fields as value zero but without ignoring them for \"vlen\" with format specifier \"EN\". Format possibly empty results with the Calc formatter \"f-1\" instead of the printf formatter \"%.1f\"." (org-test-table-target-expect " |-------+---------+---------| | Item | Item | Sub- | | name | value | total | |-------+---------+---------| | a1 | 4.1 | replace | | a2 | 8.2 | replace | | a3 | | replace | |-------+---------+---------| | b1 | 16.0 | replace | |-------+---------+---------| | c1 | 32 | replace | | c2 | 64 | replace | |-------+---------+---------| | Total | replace | replace | |-------+---------+---------| " " |-------+-------+-------| | Item | Item | Sub- | | name | value | total | |-------+-------+-------| | a1 | 4.1 | | | a2 | 8.2 | | | a3 | | 12.3 | |-------+-------+-------| | b1 | 16.0 | 16.0 | |-------+-------+-------| | c1 | 32 | | | c2 | 64 | 96.0 | |-------+-------+-------| | Total | 124.3 | | |-------+-------+-------| " 1 (concat "#+TBLFM: @>$2 = vsum(@II..@>>) ::" "$3 = if(vlen(@0..@+I) == 1, " "vsum(@-I$2..@+I$2) +.0, string(\"\")); EN f-1 :: " "@>$3 = string(\"\")"))) (ert-deftest test-org-table/org-lookup-all () "Use `org-lookup-all' for several GROUP BY as in SQL and for ranking. See also URL `https://orgmode.org/worg/org-tutorials/org-lookups.html'." (let ((data " #+NAME: data | Purchase | Product | Shop | Rating | |----------+---------+------+--------| | a | p1 | s1 | 1 | | b | p1 | s2 | 4 | | c | p2 | s1 | 2 | | d | p3 | s2 | 8 | ")) ;; Product rating and ranking by average purchase from "#+NAME: data" (org-test-table-target-expect (concat data " | Product | Rating | Ranking | |---------+---------+---------| | p1 | replace | replace | | p2 | replace | replace | | p3 | replace | replace | ") (concat data " | Product | Rating | Ranking | |---------+--------+---------| | p1 | 2.5 | 2 | | p2 | 2.0 | 3 | | p3 | 8.0 | 1 | ") 2 (concat "#+TBLFM: $2 = '(let ((all (org-lookup-all '$1 " "'(remote(data, @I$2..@>$2)) '(remote(data, @I$4..@>$4))))) " "(/ (apply '+ all) (length all) 1.0)); L :: " "$3 = '(+ 1 (length (org-lookup-all $2 '(@I$2..@>$2) nil '<))); N")) ;; Shop rating and ranking by average purchase from "#+NAME: data" (org-test-table-target-expect (concat data " | Shop | Rating | Ranking | |------+---------+---------| | s1 | replace | replace | | s2 | replace | replace | ") (concat data " | Shop | Rating | Ranking | |------+--------+---------| | s1 | 1.5 | 2 | | s2 | 6.0 | 1 | ") 2 (concat "#+TBLFM: $2 = '(let ((all (org-lookup-all '$1 " "'(remote(data, @I$3..@>$3)) '(remote(data, @I$4..@>$4))))) " "(/ (apply '+ all) (length all) 1.0)); L :: " "$3 = '(+ 1 (length (org-lookup-all $2 '(@I$2..@>$2) nil '<))); N")))) (ert-deftest test-org-table/org-table-make-reference/mode-string-EL () ;; For Lisp formula only (should (equal "0" (org-table-make-reference "0" t nil 'literal))) (should (equal "z" (org-table-make-reference "z" t nil 'literal))) (should (equal "" (org-table-make-reference "" t nil 'literal))) (should (equal "0 1" (org-table-make-reference '("0" "1") t nil 'literal))) (should (equal "z 1" (org-table-make-reference '("z" "1") t nil 'literal))) (should (equal " 1" (org-table-make-reference '("" "1") t nil 'literal))) (should (equal " " (org-table-make-reference '("" "") t nil 'literal)))) (ert-deftest test-org-table/org-table-make-reference/mode-string-E () ;; For Lisp formula (should (equal "\"0\"" (org-table-make-reference "0" t nil t))) (should (equal "\"z\"" (org-table-make-reference "z" t nil t))) (should (equal"\"\"" (org-table-make-reference "" t nil t))) (should (equal "\"0\" \"1\"" (org-table-make-reference '("0""1") t nil t))) (should (equal "\"z\" \"1\"" (org-table-make-reference '("z""1") t nil t))) (should (equal"\"\" \"1\"" (org-table-make-reference '("""1") t nil t))) (should (equal"\"\" \"\""(org-table-make-reference '("""" ) t nil t))) ;; For Calc formula (should (equal "(0)" (org-table-make-reference "0" t nil nil))) (should (equal "(z)" (org-table-make-reference "z" t nil nil))) (should (equal "nan" (org-table-make-reference "" t nil nil))) (should (equal "[0,1]" (org-table-make-reference '("0" "1") t nil nil))) (should (equal "[z,1]" (org-table-make-reference '("z" "1") t nil nil))) (should (equal "[nan,1]" (org-table-make-reference '("" "1") t nil nil))) (should (equal "[nan,nan]" (org-table-make-reference '("" "") t nil nil))) ;; For Calc formula, special numbers (should (equal "(nan)" (org-table-make-reference "nan" t nil nil))) (should (equal "(uinf)" (org-table-make-reference "uinf" t nil nil))) (should (equal "(-inf)" (org-table-make-reference "-inf" t nil nil))) (should (equal "(inf)" (org-table-make-reference "inf" t nil nil))) (should (equal "[nan,1]" (org-table-make-reference '("nan" "1") t nil nil))) (should (equal "[uinf,1]" (org-table-make-reference '("uinf" "1") t nil nil))) (should (equal "[-inf,1]" (org-table-make-reference '("-inf" "1") t nil nil))) (should (equal "[inf,1]" (org-table-make-reference '("inf" "1") t nil nil)))) (ert-deftest test-org-table/org-table-make-reference/mode-string-EN () ;; For Lisp formula (should (equal "0" (org-table-make-reference "0" t t t))) (should (equal "0" (org-table-make-reference "z" t t t))) (should (equal "0" (org-table-make-reference "" t t t))) (should (equal "0 1" (org-table-make-reference '("0" "1") t t t))) (should (equal "0 1" (org-table-make-reference '("z" "1") t t t))) (should (equal "0 1" (org-table-make-reference '("" "1") t t t))) (should (equal "0 0" (org-table-make-reference '("" "" ) t t t))) ;; For Calc formula (should (equal "(0)" (org-table-make-reference "0" t t nil))) (should (equal "(0)" (org-table-make-reference "z" t t nil))) (should (equal "(0)" (org-table-make-reference "" t t nil))) (should (equal "[0,1]" (org-table-make-reference '("0" "1") t t nil))) (should (equal "[0,1]" (org-table-make-reference '("z" "1") t t nil))) (should (equal "[0,1]" (org-table-make-reference '("" "1") t t nil))) (should (equal "[0,0]" (org-table-make-reference '("" "" ) t t nil))) ;; For Calc formula, special numbers (should (equal "(0)" (org-table-make-reference "nan" t t nil))) (should (equal "(0)" (org-table-make-reference "uinf" t t nil))) (should (equal "(0)" (org-table-make-reference "-inf" t t nil))) (should (equal "(0)" (org-table-make-reference "inf" t t nil))) (should (equal "[0,1]" (org-table-make-reference '( "nan" "1") t t nil))) (should (equal "[0,1]" (org-table-make-reference '("uinf" "1") t t nil))) (should (equal "[0,1]" (org-table-make-reference '("-inf" "1") t t nil))) (should (equal "[0,1]" (org-table-make-reference '( "inf" "1") t t nil)))) (ert-deftest test-org-table/org-table-make-reference/mode-string-L () ;; For Lisp formula only (should (equal "0" (org-table-make-reference "0" nil nil 'literal))) (should (equal "z" (org-table-make-reference "z" nil nil 'literal))) (should (equal "" (org-table-make-reference "" nil nil 'literal))) (should (equal "0 1" (org-table-make-reference '("0" "1") nil nil 'literal))) (should (equal "z 1" (org-table-make-reference '("z" "1") nil nil 'literal))) (should (equal "1" (org-table-make-reference '("" "1") nil nil 'literal))) (should (equal "" (org-table-make-reference '("" "" ) nil nil 'literal)))) (ert-deftest test-org-table/org-table-make-reference/mode-string-none () ;; For Lisp formula (should (equal "\"0\"" (org-table-make-reference "0" nil nil t))) (should (equal "\"z\"" (org-table-make-reference "z" nil nil t))) (should (equal "\"\"" (org-table-make-reference "" nil nil t))) (should (equal "\"0\" \"1\"" (org-table-make-reference '("0" "1") nil nil t))) (should (equal "\"z\" \"1\"" (org-table-make-reference '("z" "1") nil nil t))) (should (equal "\"1\"" (org-table-make-reference '("" "1") nil nil t))) (should (equal "" (org-table-make-reference '("" "" ) nil nil t))) ;; For Calc formula (should (equal "(0)" (org-table-make-reference "0" nil nil nil))) (should (equal "(z)" (org-table-make-reference "z" nil nil nil))) (should (equal "(0)" (org-table-make-reference "" nil nil nil))) (should (equal "[0,1]" (org-table-make-reference '("0" "1") nil nil nil))) (should (equal "[z,1]" (org-table-make-reference '("z" "1") nil nil nil))) (should (equal "[1]" (org-table-make-reference '("" "1") nil nil nil))) (should (equal "[]" (org-table-make-reference '("" "" ) nil nil nil))) ;; For Calc formula, special numbers (should (equal "(nan)" (org-table-make-reference "nan" nil nil nil))) (should (equal "(uinf)" (org-table-make-reference "uinf" nil nil nil))) (should (equal "(-inf)" (org-table-make-reference "-inf" nil nil nil))) (should (equal "(inf)" (org-table-make-reference "inf" nil nil nil))) (should (equal "[nan,1]" (org-table-make-reference '( "nan" "1") nil nil nil))) (should (equal "[uinf,1]" (org-table-make-reference '("uinf" "1") nil nil nil))) (should (equal "[-inf,1]" (org-table-make-reference '("-inf" "1") nil nil nil))) (should (equal "[inf,1]" (org-table-make-reference '( "inf" "1") nil nil nil)))) (ert-deftest test-org-table/org-table-make-reference/mode-string-N () ;; For Lisp formula (should (equal "0" (org-table-make-reference "0" nil t t))) (should (equal "0" (org-table-make-reference "z" nil t t))) (should (equal "0" (org-table-make-reference "" nil t t))) (should (equal "0 1" (org-table-make-reference '("0" "1") nil t t))) (should (equal "0 1" (org-table-make-reference '("z" "1") nil t t))) (should (equal "1" (org-table-make-reference '("" "1") nil t t))) (should (equal "" (org-table-make-reference '("" "" ) nil t t))) ;; For Calc formula (should (equal "(0)" (org-table-make-reference "0" nil t nil))) (should (equal "(0)" (org-table-make-reference "z" nil t nil))) (should (equal "(0)" (org-table-make-reference "" nil t nil))) (should (equal "[0,1]" (org-table-make-reference '("0" "1") nil t nil))) (should (equal "[0,1]" (org-table-make-reference '("z" "1") nil t nil))) (should (equal "[1]" (org-table-make-reference '("" "1") nil t nil))) (should (equal "[]" (org-table-make-reference '("" "" ) nil t nil))) ;; For Calc formula, special numbers (should (equal "(0)" (org-table-make-reference "nan" nil t nil))) (should (equal "(0)" (org-table-make-reference "uinf" nil t nil))) (should (equal "(0)" (org-table-make-reference "-inf" nil t nil))) (should (equal "(0)" (org-table-make-reference "inf" nil t nil))) (should (equal "[0,1]" (org-table-make-reference '( "nan" "1") nil t nil))) (should (equal "[0,1]" (org-table-make-reference '("uinf" "1") nil t nil))) (should (equal "[0,1]" (org-table-make-reference '("-inf" "1") nil t nil))) (should (equal "[0,1]" (org-table-make-reference '( "inf" "1") nil t nil)))) (ert-deftest test-org-table/org-table-convert-refs-to-an/1 () "Simple reference @2$1." (should (string= "A2" (org-table-convert-refs-to-an "@2$1")))) (ert-deftest test-org-table/org-table-convert-refs-to-an/2 () "Self reference @1$1." (should (string= "A1 = $0" (org-table-convert-refs-to-an "@1$1 = $0")))) (ert-deftest test-org-table/org-table-convert-refs-to-an/3 () "Remote reference." (should (string= "C& = remote(FOO, @@#B&)" (org-table-convert-refs-to-an "$3 = remote(FOO, @@#$2)")))) (ert-deftest test-org-table/org-table-convert-refs-to-rc/1 () "Simple reference @2$1." (should (string= "@2$1" (org-table-convert-refs-to-rc "A2")))) (ert-deftest test-org-table/org-table-convert-refs-to-rc/2 () "Self reference $0." (should (string= "@1$1 = $0" (org-table-convert-refs-to-rc "A1 = $0")))) (ert-deftest test-org-table/org-table-convert-refs-to-rc/3 () "Remote reference." (should (string= "$3 = remote(FOO, @@#$2)" (org-table-convert-refs-to-rc "C& = remote(FOO, @@#B&)")))) (ert-deftest test-org-table/remote-reference-access () "Access to remote reference. See also `test-org-table/copy-field'." (org-test-table-target-expect " #+NAME: table | | x 42 | | | replace | replace | " " #+NAME: table | | x 42 | | | x 42 | 84 x | " 1 (concat "#+TBLFM: " ;; Copy text without calculation: Use Lisp formula "$1 = '(identity remote(table, @1$2)) :: " ;; Do a calculation: Use Calc (or Lisp ) formula "$2 = 2 * remote(table, @1$2)"))) (ert-deftest test-org-table/remote-reference-indirect () "Access to remote reference with indirection of name or ID." (let ((source-tables " #+NAME: 2012 | amount | |--------| | 1 | | 2 | |--------| | 3 | #+TBLFM: @>$1 = vsum(@I..@II) #+NAME: 2013 | amount | |--------| | 4 | | 8 | |--------| | 12 | #+TBLFM: @>$1 = vsum(@I..@II) ")) ;; Read several remote references from same column (org-test-table-target-expect (concat source-tables " #+NAME: summary | year | amount | |-------+---------| | 2012 | replace | | 2013 | replace | |-------+---------| | total | replace | ") (concat source-tables " #+NAME: summary | year | amount | |-------+--------| | 2012 | 3 | | 2013 | 12 | |-------+--------| | total | 15 | ") 1 ;; Calc formula "#+TBLFM: @<<$2..@>>$2 = remote($<, @>$1) :: @>$2 = vsum(@I..@II)" ;; Lisp formula (concat "#+TBLFM: @<<$2..@>>$2 = '(identity remote($<, @>$1)); N :: " "@>$2 = '(+ @I..@II); N")) ;; Read several remote references from same row (org-test-table-target-expect (concat source-tables " #+NAME: summary | year | 2012 | 2013 | total | |--------+---------+---------+---------| | amount | replace | replace | replace | ") (concat source-tables " #+NAME: summary | year | 2012 | 2013 | total | |--------+------+------+-------| | amount | 3 | 12 | 15 | ") 1 ;; Calc formula "#+TBLFM: @2$<<..@2$>> = remote(@<, @>$1) :: @2$> = vsum($<<..$>>)" ;; Lisp formula (concat "#+TBLFM: @2$<<..@2$>> = '(identity remote(@<, @>$1)); N :: " "@2$> = '(+ $<<..$>>); N")))) (ert-deftest test-org-table/org-at-TBLFM-p () (org-test-with-temp-text-in-file " | 1 | | 2 | #+TBLFM: $2=$1*2 " (goto-char (point-min)) (forward-line 2) (should (equal (org-at-TBLFM-p) nil)) (goto-char (point-min)) (forward-line 3) (should (equal (org-at-TBLFM-p) t)) (goto-char (point-min)) (forward-line 4) (should (equal (org-at-TBLFM-p) nil)))) (ert-deftest test-org-table/org-table-TBLFM-begin () (org-test-with-temp-text-in-file " | 1 | | 2 | #+TBLFM: $2=$1*2 " (goto-char (point-min)) (should (equal (org-table-TBLFM-begin) nil)) (goto-char (point-min)) (forward-line 1) (should (equal (org-table-TBLFM-begin) nil)) (goto-char (point-min)) (forward-line 3) (should (= (org-table-TBLFM-begin) 14)) (goto-char (point-min)) (forward-line 4) (should (= (org-table-TBLFM-begin) 14)) )) (ert-deftest test-org-table/org-table-TBLFM-begin-for-multiple-TBLFM-lines () "For multiple #+TBLFM lines." (org-test-with-temp-text-in-file " | 1 | | 2 | #+TBLFM: $2=$1*1 #+TBLFM: $2=$1*2 " (goto-char (point-min)) (should (equal (org-table-TBLFM-begin) nil)) (goto-char (point-min)) (forward-line 1) (should (equal (org-table-TBLFM-begin) nil)) (goto-char (point-min)) (forward-line 3) (should (= (org-table-TBLFM-begin) 14)) (goto-char (point-min)) (forward-line 4) (should (= (org-table-TBLFM-begin) 14)) (goto-char (point-min)) (forward-line 5) (should (= (org-table-TBLFM-begin) 14)) )) (ert-deftest test-org-table/org-table-TBLFM-begin-for-pultiple-TBLFM-lines-blocks () (org-test-with-temp-text-in-file " | 1 | | 2 | #+TBLFM: $2=$1*1 #+TBLFM: $2=$1*2 | 6 | | 7 | #+TBLFM: $2=$1*1 #+TBLFM: $2=$1*2 " (goto-char (point-min)) (should (equal (org-table-TBLFM-begin) nil)) (goto-char (point-min)) (forward-line 1) (should (equal (org-table-TBLFM-begin) nil)) (goto-char (point-min)) (forward-line 3) (should (= (org-table-TBLFM-begin) 14)) (goto-char (point-min)) (forward-line 4) (should (= (org-table-TBLFM-begin) 14)) (goto-char (point-min)) (forward-line 5) (should (= (org-table-TBLFM-begin) 14)) (goto-char (point-min)) (forward-line 6) (should (= (org-table-TBLFM-begin) 14)) (goto-char (point-min)) (forward-line 8) (should (= (org-table-TBLFM-begin) 61)) (goto-char (point-min)) (forward-line 9) (should (= (org-table-TBLFM-begin) 61)) (goto-char (point-min)) (forward-line 10) (should (= (org-table-TBLFM-begin) 61)))) (ert-deftest test-org-table/org-table-calc-current-TBLFM () (org-test-with-temp-text-in-file " | 1 | | | 2 | | #+TBLFM: $2=$1*1 #+TBLFM: $2=$1*2 #+TBLFM: $2=$1*3 " (let ((got (progn (goto-char (point-min)) (forward-line 3) (org-table-calc-current-TBLFM) (buffer-string))) (expect " | 1 | 1 | | 2 | 2 | #+TBLFM: $2=$1*1 #+TBLFM: $2=$1*2 #+TBLFM: $2=$1*3 ")) (should (string= got expect))) (let ((got (progn (goto-char (point-min)) (forward-line 4) (org-table-calc-current-TBLFM) (buffer-string))) (expect " | 1 | 2 | | 2 | 4 | #+TBLFM: $2=$1*1 #+TBLFM: $2=$1*2 #+TBLFM: $2=$1*3 ")) (should (string= got expect))))) (ert-deftest test-org-table/org-table-calc-current-TBLFM-when-stop-because-of-error () "org-table-calc-current-TBLFM should preserve the input as it was." (org-test-with-temp-text-in-file " | 1 | 1 | | 2 | 2 | #+TBLFM: $2=$1*1 #+TBLFM: $2=$1*2::$2=$1*2 #+TBLFM: $2=$1*3 " (let ((expect " | 1 | 1 | | 2 | 2 | #+TBLFM: $2=$1*1 #+TBLFM: $2=$1*2::$2=$1*2 #+TBLFM: $2=$1*3 ")) (goto-char (point-min)) (forward-line 4) (should-error (org-table-calc-current-TBLFM)) (setq got (buffer-string)) (message "%s" got) (should (string= got expect))))) ;;; Tables as Lisp (ert-deftest test-org-table/to-lisp () "Test `orgtbl-to-lisp' specifications." ;; 2x2 no header (should (equal '(("a" "b") ("c" "d")) (org-table-to-lisp "|a|b|\n|c|d|"))) ;; 2x2 with 1-line header (should (equal '(("a" "b") hline ("c" "d")) (org-table-to-lisp "|a|b|\n|-\n|c|d|"))) ;; 2x4 with 2-line header (should (equal '(("a" "b") ("A" "B") hline ("c" "d") ("aa" "bb")) (org-table-to-lisp "|a|b|\n|A|B|\n|-\n|c|d|\n|aa|bb|"))) ;; leading hlines do not get stripped (should (equal '(hline ("a" "b") hline ("c" "d")) (org-table-to-lisp "|-\n|a|b|\n|-\n|c|d|"))) (should (equal '(hline ("a" "b") ("c" "d")) (org-table-to-lisp "|-\n|a|b|\n|c|d|"))) (should (equal '(hline hline hline hline ("a" "b") ("c" "d")) (org-table-to-lisp "|-\n|-\n|-\n|-\n|a|b|\n|c|d|")))) (ert-deftest test-org-table/collapse-header () "Test `orgtbl-to-lisp' specifications." ;; 2x2 no header - no collapsing (should (equal '(("a" "b") ("c" "d")) (org-table-collapse-header (org-table-to-lisp "|a|b|\n|c|d|")))) ;; 2x2 with 1-line header - no collapsing (should (equal '(("a" "b") hline ("c" "d")) (org-table-collapse-header (org-table-to-lisp "|a|b|\n|-\n|c|d|")))) ;; 2x4 with 2-line header - collapsed (should (equal '(("a A" "b B") hline ("c" "d") ("aa" "bb")) (org-table-collapse-header (org-table-to-lisp "|a|b|\n|A|B|\n|-\n|c|d|\n|aa|bb|")))) ;; 2x4 with 2-line header, custom glue - collapsed (should (equal '(("a.A" "b.B") hline ("c" "d") ("aa" "bb")) (org-table-collapse-header (org-table-to-lisp "|a|b|\n|A|B|\n|-\n|c|d|\n|aa|bb|") "."))) ;; 2x4 with 2-line header, threshold 1 - not collapsed (should (equal '(("a" "b") ("A" "B") hline ("c" "d") ("aa" "bb")) (org-table-collapse-header (org-table-to-lisp "|a|b|\n|A|B|\n|-\n|c|d|\n|aa|bb|") nil 1))) ;; 2x4 with 2-line header, threshold 2 - collapsed (should (equal '(("a A" "b B") hline ("c" "d") ("aa" "bb")) (org-table-collapse-header (org-table-to-lisp "|a|b|\n|A|B|\n|-\n|c|d|\n|aa|bb|") nil 2))) ;; 2x8 with 6-line header, default threshold 5 - not collapsed (should (equal '(("a" "b") ("A" "B") ("a" "b") ("A" "B") ("a" "b") ("A" "B") hline ("c" "d") ("aa" "bb")) (org-table-collapse-header (org-table-to-lisp "|a|b|\n|A|B|\n|a|b|\n|A|B|\n|a|b|\n|A|B|\n|-\n|c|d|\n|aa|bb|"))))) ;;; Radio Tables (ert-deftest test-org-table/to-generic () "Test `orgtbl-to-generic' specifications." ;; Test :hline parameter. (should (equal "a\nb" (orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |") '(:hline nil)))) (should (equal "a\n~\nb" (orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |") '(:hline "~")))) ;; Test :sep parameter. (should (equal "a!b\nc!d" (orgtbl-to-generic (org-table-to-lisp "| a | b |\n|---+---|\n| c | d |") '(:sep "!")))) ;; Test :hsep parameter. (should (equal "a!b\nc?d" (orgtbl-to-generic (org-table-to-lisp "| a | b |\n|---+---|\n| c | d |") '(:sep "?" :hsep "!")))) ;; Test :tstart parameter. (should (equal "\na" (orgtbl-to-generic (org-table-to-lisp "| a |") '(:tstart "")))) (should (equal "\na" (orgtbl-to-generic (org-table-to-lisp "| a |") '(:tstart (lambda () ""))))) (should (equal "a" (orgtbl-to-generic (org-table-to-lisp "| a |") '(:tstart "" :splice t)))) ;; Test :tend parameter. (should (equal "a\n" (orgtbl-to-generic (org-table-to-lisp "| a |") '(:tend "")))) (should (equal "a\n" (orgtbl-to-generic (org-table-to-lisp "| a |") '(:tend (lambda () ""))))) (should (equal "a" (orgtbl-to-generic (org-table-to-lisp "| a |") '(:tend "" :splice t)))) ;; Test :lstart parameter. (should (equal "> a" (orgtbl-to-generic (org-table-to-lisp "| a |") '(:lstart "> ")))) (should (equal "> a" (orgtbl-to-generic (org-table-to-lisp "| a |") '(:lstart (lambda () "> "))))) ;; Test :llstart parameter. (should (equal "> a\n>> b" (orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |") '(:lstart "> " :llstart ">> ")))) ;; Test :hlstart parameter. (should (equal "!> a\n> b" (orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |") '(:lstart "> " :hlstart "!> ")))) ;; Test :hllstart parameter. (should (equal "!> a\n!!> b\n> c" (orgtbl-to-generic (org-table-to-lisp "| a |\n| b |\n|---|\n| c |") '(:lstart "> " :hlstart "!> " :hllstart "!!> ")))) ;; Test :lend parameter. (should (equal "a <" (orgtbl-to-generic (org-table-to-lisp "| a |") '(:lend " <")))) ;; Test :llend parameter. (should (equal "a <\nb <<" (orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |") '(:lend " <" :llend " <<")))) ;; Test :hlend parameter. (should (equal "a " :lend "<" :sep " ")))) ;; Test :llfmt parameter. (should (equal "a!b" (orgtbl-to-generic (org-table-to-lisp "| a | b |") '(:llfmt "%s!%s")))) (should (equal "a!b\nc+d" (orgtbl-to-generic (org-table-to-lisp "| a | b |\n| c | d |") '(:lfmt "%s!%s" :llfmt (lambda (c) (concat (car c) "+" (cadr c))))))) (should (equal "a!b" (orgtbl-to-generic (org-table-to-lisp "| a | b |") '(:llfmt "%s!%s" :lstart ">" :lend "<" :sep " ")))) ;; Test :hlfmt parameter. (should (equal "a!b\ncd" (orgtbl-to-generic (org-table-to-lisp "| a | b |\n|---+---|\n| c | d |") '(:hlfmt "%s!%s")))) (should (equal "a+b\ncd" (orgtbl-to-generic (org-table-to-lisp "| a | b |\n|---+---|\n| c | d |") '(:hlfmt (lambda (c) (concat (car c) "+" (cadr c))))))) (should (equal "a!b\n>c d<" (orgtbl-to-generic (org-table-to-lisp "| a | b |\n|---+---|\n| c | d |") '(:hlfmt "%s!%s" :lstart ">" :lend "<" :sep " ")))) ;; Test :hllfmt parameter. (should (equal "a!b\ncd" (orgtbl-to-generic (org-table-to-lisp "| a | b |\n|---+---|\n| c | d |") '(:hllfmt "%s!%s")))) (should (equal "a+b\ncd" (orgtbl-to-generic (org-table-to-lisp "| a | b |\n|---+---|\n| c | d |") '(:hllfmt (lambda (c) (concat (car c) "+" (cadr c))))))) (should (equal "a!b\n>c d<" (orgtbl-to-generic (org-table-to-lisp "| a | b |\n|---+---|\n| c | d |") '(:hllfmt "%s!%s" :lstart ">" :lend "<" :sep " ")))) ;; Test :fmt parameter. (should (equal ">a<\n>b<" (orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |") '(:fmt ">%s<")))) (should (equal ">a%s<" 2 (lambda (c) c)))))) (should (equal "a b" (orgtbl-to-generic (org-table-to-lisp "| a | b |") '(:fmt (2 " %s"))))) (should (equal ">a<" (orgtbl-to-generic (org-table-to-lisp "| a |") '(:fmt (lambda (c) (format ">%s<" c)))))) ;; Test :hfmt parameter. (should (equal ">a<\nb" (orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |") '(:hfmt ">%s<")))) (should (equal ">a%s<" 2 identity))))) (should (equal "a b\ncd" (orgtbl-to-generic (org-table-to-lisp "| a | b |\n|---+---|\n| c | d |") '(:hfmt (2 " %s"))))) (should (equal ">a<\nb" (orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |") '(:hfmt (lambda (c) (format ">%s<" c)))))) ;; Test :efmt parameter. (should (equal "2x10^3" (orgtbl-to-generic (org-table-to-lisp "| 2e3 |") '(:efmt "%sx10^%s")))) (should (equal "2x10^3" (orgtbl-to-generic (org-table-to-lisp "| 2e3 |") '(:efmt (lambda (m e) (concat m "x10^" e)))))) (should (equal "2x10^3" (orgtbl-to-generic (org-table-to-lisp "| 2e3 |") '(:efmt (1 "%sx10^%s"))))) (should (equal "2x10^3" (orgtbl-to-generic (org-table-to-lisp "| 2e3 |") '(:efmt (1 (lambda (m e) (format "%sx10^%s" m e))))))) (should (equal "2e3" (orgtbl-to-generic (org-table-to-lisp "| 2e3 |") '(:efmt nil)))) ;; Test :skip parameter. (should (equal "cd" (orgtbl-to-generic (org-table-to-lisp "| \ | |\n| a | b |\n|---+---|\n| c | d |") '(:skip 2)))) ;; Test :skipcols parameter. (should (equal "a\nc" (orgtbl-to-generic (org-table-to-lisp "| a | b |\n| c | d |") '(:skipcols (2))))) (should (equal "a\nc" (orgtbl-to-generic (org-table-to-lisp "| / | | |\n| # | a | b |\n|---+---+---|\n| | c | d |") '(:skipcols (2))))) ;; Test :raw parameter. (when (featurep 'ox-latex) (should (string-match-p "/a/" (orgtbl-to-generic (org-table-to-lisp "| /a/ | b |") '(:backend latex :raw t))))) ;; Hooks are ignored. (should (equal "a\nb" (let* ((fun-list (list (lambda (_backend) (search-forward "a") (insert "hook")))) (org-export-before-parsing-hook fun-list) (org-export-before-processing-hook fun-list)) (orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |") '(:hline nil))))) ;; User-defined export filters are ignored. (should (equal "a\nb" (let ((org-export-filter-table-cell-functions (list (lambda (_c _b _i) "filter")))) (orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |") '(:hline nil))))) ;; Macros, even if unknown, are returned as-is. (should (equal "{{{macro}}}" (orgtbl-to-generic (org-table-to-lisp "| {{{macro}}} |") nil)))) (ert-deftest test-org-table/to-latex () "Test `orgtbl-to-latex' specifications." (should (equal "\\begin{tabular}{l}\na\\\\\n\\end{tabular}" (orgtbl-to-latex (org-table-to-lisp "| a |") nil))) ;; Test :environment parameter. (should (equal "\\begin{tabularx}{l}\na\\\\\n\\end{tabularx}" (orgtbl-to-latex (org-table-to-lisp "| a |") '(:environment "tabularx")))) ;; Test :booktabs parameter. (should (string-match-p "\\toprule" (orgtbl-to-latex (org-table-to-lisp "| a |") '(:booktabs t)))) ;; Handle LaTeX snippets. (should (equal "\\begin{tabular}{l}\n\\(x\\)\\\\\n\\end{tabular}" (orgtbl-to-latex (org-table-to-lisp "| $x$ |") nil))) ;; Test pseudo objects and :raw parameter. (should (string-match-p "\\$x\\$" (orgtbl-to-latex (org-table-to-lisp "| $x$ |") '(:raw t))))) (ert-deftest test-org-table/to-html () "Test `orgtbl-to-html' specifications." (should (equal (orgtbl-to-html (org-table-to-lisp "| a |") nil) "
a
")) ;; Test :attributes parameter. (should (string-match-p "" (orgtbl-to-html (org-table-to-lisp "| a |") '(:attributes nil)))) (should (string-match-p "
" (orgtbl-to-html (org-table-to-lisp "| a |") '(:attributes (:border "2")))))) (ert-deftest test-org-table/to-texinfo () "Test `orgtbl-to-texinfo' specifications." (should (equal "@multitable {a}\n@item a\n@end multitable" (orgtbl-to-texinfo (org-table-to-lisp "| a |") nil))) ;; Test :columns parameter. (should (equal "@multitable @columnfractions .4 .6\n@item a\n@tab b\n@end multitable" (orgtbl-to-texinfo (org-table-to-lisp "| a | b |") '(:columns ".4 .6")))) (should (equal "@multitable @columnfractions .4 .6\n@item a\n@tab b\n@end multitable" (orgtbl-to-texinfo (org-table-to-lisp "| a | b |") '(:columns "@columnfractions .4 .6")))) (should (equal "@multitable {xxx} {xx}\n@item a\n@tab b\n@end multitable" (orgtbl-to-texinfo (org-table-to-lisp "| a | b |") '(:columns "{xxx} {xx}"))))) (ert-deftest test-org-table/to-orgtbl () "Test `orgtbl-to-orgtbl' specifications." (should (equal "| a | b |\n|---+---|\n| c | d |" (orgtbl-to-orgtbl (org-table-to-lisp "| a | b |\n|---+---|\n| c | d |") nil)))) (ert-deftest test-org-table/to-unicode () "Test `orgtbl-to-unicode' specifications." (should (equal "━━━\n a \n━━━" (orgtbl-to-unicode (org-table-to-lisp "| a |") nil))) ;; Test :narrow parameter. (should (equal "━━━━\n => \n━━━━" (orgtbl-to-unicode (org-table-to-lisp "| <2> |\n| xxx |") '(:narrow t))))) (ert-deftest test-org-table/send-region () "Test `orgtbl-send-table' specifications." ;; Error when not at a table. (should-error (org-test-with-temp-text "Paragraph" (orgtbl-send-table))) ;; Error when destination is missing. (should-error (org-test-with-temp-text "#+ORGTBL: SEND\n| a |" (orgtbl-send-table))) ;; Error when transformation function is not specified. (should-error (org-test-with-temp-text " # BEGIN RECEIVE ORGTBL table # END RECEIVE ORGTBL table #+ORGTBL: SEND table | a |" (orgtbl-send-table))) ;; Standard test. (should (equal "| a |\n|---|\n| b |\n" (org-test-with-temp-text " # BEGIN RECEIVE ORGTBL table # END RECEIVE ORGTBL table #+ORGTBL: SEND table orgtbl-to-orgtbl :hlines nil | a |\n|---|\n| b |" (orgtbl-send-table) (goto-char (point-min)) (buffer-substring-no-properties (search-forward "# BEGIN RECEIVE ORGTBL table\n") (progn (search-forward "# END RECEIVE ORGTBL table") (match-beginning 0)))))) ;; Allow multiple receiver locations. (should (org-test-with-temp-text " # BEGIN RECEIVE ORGTBL table # END RECEIVE ORGTBL table #+ORGTBL: SEND table orgtbl-to-orgtbl :hlines nil | a | # BEGIN RECEIVE ORGTBL table # END RECEIVE ORGTBL table" (orgtbl-send-table) (goto-char (point-min)) (search-forward "| a |" nil t 3)))) ;;; Align (ert-deftest test-org-table/align () "Test `org-table-align' specifications." ;; Regular test. (should (equal "| a |\n" (org-test-with-temp-text "| a |" (org-table-align) (buffer-string)))) ;; Preserve alignment. (should (equal " | a |\n" (org-test-with-temp-text " | a |" (org-table-align) (buffer-string)))) ;; Handle horizontal lines. (should (equal "| 123 |\n|-----|\n" (org-test-with-temp-text "| 123 |\n|-|" (org-table-align) (buffer-string)))) (should (equal "| a | b |\n|---+---|\n" (org-test-with-temp-text "| a | b |\n|-+-|" (org-table-align) (buffer-string)))) ;; Handle empty fields. (should (equal "| a | bc |\n| bcd | |\n" (org-test-with-temp-text "| a | bc |\n| bcd | |" (org-table-align) (buffer-string)))) (should (equal "| abc | bc |\n| | bcd |\n" (org-test-with-temp-text "| abc | bc |\n| | bcd |" (org-table-align) (buffer-string)))) ;; Handle missing fields. (should (equal "| a | b |\n| c | |\n" (org-test-with-temp-text "| a | b |\n| c |" (org-table-align) (buffer-string)))) (should (equal "| a | b |\n|---+---|\n" (org-test-with-temp-text "| a | b |\n|---|" (org-table-align) (buffer-string)))) ;; Alignment is done to the right when the ratio of numbers in the ;; column is superior to `org-table-number-fraction'. (should (equal "| 1 |\n| 12 |\n| abc |" (org-test-with-temp-text "| 1 |\n| 12 |\n| abc |" (let ((org-table-number-fraction 0.5)) (org-table-align)) (buffer-string)))) (should (equal "| 1 |\n| ab |\n| abc |" (org-test-with-temp-text "| 1 |\n| ab |\n| abc |" (let ((org-table-number-fraction 0.5)) (org-table-align)) (buffer-string)))) ;; Obey to alignment cookies. (should (equal "| |\n| ab |\n| abc |" (org-test-with-temp-text "| |\n| ab |\n| abc |" (let ((org-table-number-fraction 0.5)) (org-table-align)) (buffer-string)))) (should (equal "| |\n| 12 |\n| 123 |" (org-test-with-temp-text "| |\n| 12 |\n| 123 |" (let ((org-table-number-fraction 0.5)) (org-table-align)) (buffer-string)))) (should (equal "| |\n| 1 |\n| 123 |" (org-test-with-temp-text "| |\n| 1 |\n| 123 |" (let ((org-table-number-fraction 0.5)) (org-table-align)) (buffer-string)))) ;; Handle gracefully tables with only horizontal rules. (should (org-test-with-temp-text "|---|" (org-table-align) t)) (should (org-test-with-temp-text "|---|---------|\n|---|---|-----|" (org-table-align) t)) ;; Adjust table width. (should (equal (let ((org-link-descriptive t)) (org-test-with-temp-text " | a | b | |----------+---| | [[c][c]] | d |" (org-table-align) (buffer-string))) " | a | b | |---+---| | [[c][c]] | d |"))) (ert-deftest test-org-table/align-buffer-tables () "Align all tables when updating buffer." (let ((before " | a b | | c d | ") (after " | a b | | c d | ")) (should (equal (org-test-with-temp-text before (org-table-recalculate-buffer-tables) (buffer-string)) after)) (should (equal (org-test-with-temp-text before (org-table-iterate-buffer-tables) (buffer-string)) after)))) ;;; Sorting (ert-deftest test-org-table/sort-lines () "Test `org-table-sort-lines' specifications." ;; Sort numerically. (should (equal "| 1 | 2 |\n| 2 | 4 |\n| 5 | 3 |\n" (org-test-with-temp-text "| 1 | 2 |\n| 5 | 3 |\n| 2 | 4 |\n" (org-table-sort-lines nil ?n) (buffer-string)))) (should (equal "| 5 | 3 |\n| 2 | 4 |\n| 1 | 2 |\n" (org-test-with-temp-text "| 1 | 2 |\n| 5 | 3 |\n| 2 | 4 |\n" (org-table-sort-lines nil ?N) (buffer-string)))) ;; Sort alphabetically. Enforce the C locale for consistent results. (let ((original-string-collate-lessp (symbol-function 'string-collate-lessp))) (cl-letf (((symbol-function 'string-collate-lessp) (lambda (s1 s2 &optional _locale ignore-case) (funcall original-string-collate-lessp s1 s2 "C" ignore-case)))) ;; Sort alphabetically ignore case. (should (equal (if (org-test-string-collate-lessp-ignore-case-supported-p) "| a | x |\n| B | 4 |\n| c | 3 |\n" "| B | 4 |\n| a | x |\n| c | 3 |\n") (org-test-with-temp-text "| a | x |\n| c | 3 |\n| B | 4 |\n" (org-table-sort-lines nil ?a) (buffer-string)))) (should (equal (if (org-test-string-collate-lessp-ignore-case-supported-p) "| c | 3 |\n| B | 4 |\n| a | x |\n" "| c | 3 |\n| a | x |\n| B | 4 |\n") (org-test-with-temp-text "| a | x |\n| c | 3 |\n| B | 4 |\n" (org-table-sort-lines nil ?A) (buffer-string)))) ;; Sort alphabetically with case. (should (equal "| C |\n| a |\n| b |\n" (org-test-with-temp-text "| a |\n| C |\n| b |\n" (org-table-sort-lines t ?a) (buffer-string)))) (should (equal "| b |\n| a |\n| C |\n" (org-test-with-temp-text "| a |\n| C |\n| b |\n" (org-table-sort-lines t ?A) (buffer-string)))))) ;; Sort by time (timestamps) (should (equal "| <2008-08-08 sat.> |\n| <2012-03-29 thu.> |\n| <2014-03-04 tue.> |\n" (org-test-with-temp-text "| <2014-03-04 tue.> |\n| <2008-08-08 sat.> |\n| <2012-03-29 thu.> |\n" (org-table-sort-lines nil ?t) (buffer-string)))) (should (equal "| <2014-03-04 tue.> |\n| <2012-03-29 thu.> |\n| <2008-08-08 sat.> |\n" (org-test-with-temp-text "| <2014-03-04 tue.> |\n| <2008-08-08 sat.> |\n| <2012-03-29 thu.> |\n" (org-table-sort-lines nil ?T) (buffer-string)))) ;; Sort by time (HH:MM values) (should (equal "| 1:00 |\n| 17:00 |\n| 114:00 |\n" (org-test-with-temp-text "| 114:00 |\n| 17:00 |\n| 1:00 |\n" (org-table-sort-lines nil ?t) (buffer-string)))) (should (equal "| 114:00 |\n| 17:00 |\n| 1:00 |\n" (org-test-with-temp-text "| 114:00 |\n| 17:00 |\n| 1:00 |\n" (org-table-sort-lines nil ?T) (buffer-string)))) ;; Sort by time (durations) (should (equal "| 1d 3:00 |\n| 28:00 |\n" (org-test-with-temp-text "| 28:00 |\n| 1d 3:00 |\n" (org-table-sort-lines nil ?t) (buffer-string)))) ;; Sort with custom functions. (should (equal "| 22 |\n| 15 |\n| 18 |\n" (org-test-with-temp-text "| 15 |\n| 22 |\n| 18 |\n" (org-table-sort-lines nil ?f (lambda (s) (% (string-to-number s) 10)) #'<) (buffer-string)))) (should (equal "| 18 |\n| 15 |\n| 22 |\n" (org-test-with-temp-text "| 15 |\n| 22 |\n| 18 |\n" (org-table-sort-lines nil ?F (lambda (s) (% (string-to-number s) 10)) #'<) (buffer-string)))) ;; Sort according to current column. (should (equal "| 1 | 2 |\n| 7 | 3 |\n| 5 | 4 |\n" (org-test-with-temp-text "| 1 | 2 |\n| 5 | 4 |\n| 7 | 3 |\n" (org-table-sort-lines nil ?n) (buffer-string)))) ;; Sort between horizontal separators if possible. (should (equal "| 9 | 8 |\n|---+---|\n| 5 | 3 |\n| 7 | 4 |\n|---+---|\n| 1 | 2 |\n" (org-test-with-temp-text "| 9 | 8 |\n|---+---|\n| 7 | 4 |\n| 5 | 3 |\n|---+---|\n| 1 | 2 |\n" (org-table-sort-lines nil ?n) (buffer-string))))) ;;; Formulas (ert-deftest test-org-table/eval-formula () "Test `org-table-eval-formula' specifications." ;; Error when not on a table field. (should-error (org-test-with-temp-text "Text" (org-table-eval-formula))) (should-error (org-test-with-temp-text "| a |\n|---|" (org-table-eval-formula))) (should-error (org-test-with-temp-text "| a |\n#+TBLFM:" (org-table-eval-formula))) ;; Handle @<, @>, $< and $>. (should (equal "| 1 |\n| 1 |" (org-test-with-temp-text "| |\n| 1 |" (org-table-eval-formula nil "@>" nil nil t) (buffer-string)))) (should (equal "| 1 |\n| 1 |" (org-test-with-temp-text "| 1 |\n| |" (org-table-eval-formula nil "@<" nil nil t) (buffer-string)))) (should (equal "| 1 | 1 |" (org-test-with-temp-text "| | 1 |" (org-table-eval-formula nil "$>" nil nil t) (buffer-string)))) (should (equal "| 1 | 1 |" (org-test-with-temp-text "| 1 | |" (org-table-eval-formula nil "$<" nil nil t) (buffer-string))))) (ert-deftest test-org-table/field-formula-outside-table () "Test `org-table-formula-create-columns' variable." ;; Refuse to create column if variable is nil. (should-error (org-test-with-temp-text " | 2 | | 4 | | 8 | #+TBLFM: @1$2=5" (let ((org-table-formula-create-columns nil)) (org-table-calc-current-TBLFM)) (buffer-string)) :type (list 'error 'user-error)) ;; If the variable is non-nil, field formulas and columns formulas ;; can create tables. (should (equal " | 2 | 5 | | 4 | | | 8 | | #+TBLFM: @1$2=5" (org-test-with-temp-text " | 2 | | 4 | | 8 | #+TBLFM: @1$2=5" (let ((org-table-formula-create-columns t)) (org-table-calc-current-TBLFM)) (buffer-string)))) (should (equal " | 2 | | 15 | | 4 | | 15 | | 8 | | 15 | #+TBLFM: $3=15" (org-test-with-temp-text " | 2 | | 4 | | 8 | #+TBLFM: $3=15" (let ((org-table-formula-create-columns t)) (org-table-calc-current-TBLFM)) (buffer-string))))) (ert-deftest test-org-table/duration () "Test durations in table formulas." ;; Durations in cells. (should (string-match "| 2:12 | 1:47 | 03:59:00 |" (org-test-with-temp-text " | 2:12 | 1:47 | | #+TBLFM: @1$3=$1+$2;T" (org-table-calc-current-TBLFM) (buffer-string)))) (should (string-match "| 2:12 | 1:47 | 03:59 |" (org-test-with-temp-text " | 2:12 | 1:47 | | #+TBLFM: @1$3=$1+$2;U" (org-table-calc-current-TBLFM) (buffer-string)))) (should (string-match "| 3:02:20 | -2:07:00 | 0.92 |" (org-test-with-temp-text " | 3:02:20 | -2:07:00 | | #+TBLFM: @1$3=$1+$2;t" (org-table-calc-current-TBLFM) (buffer-string)))) ;; Durations set through properties. (should (string-match "| 16:00:00 |" (org-test-with-temp-text "* H :PROPERTIES: :time_constant: 08:00:00 :END: | | #+TBLFM: $1=2*$PROP_time_constant;T" (org-table-calc-current-TBLFM) (buffer-string)))) (should (string-match "| 16.00 |" (org-test-with-temp-text "* H :PROPERTIES: :time_constant: 08:00:00 :END: | | #+TBLFM: $1=2*$PROP_time_constant;t" (org-table-calc-current-TBLFM) (buffer-string))))) (ert-deftest test-org-table/end-on-hline () "Test with a table ending on a hline." (should (equal (org-test-with-temp-text " | 1 | 2 | 3 | | 4 | 5 | 6 | | | | | |---+---+---| #+TBLFM: @3$2..@3$>=vsum(@1..@2)" (org-table-calc-current-TBLFM) (buffer-string)) " | 1 | 2 | 3 | | 4 | 5 | 6 | | | 7 | 9 | |---+---+---| #+TBLFM: @3$2..@3$>=vsum(@1..@2)"))) (ert-deftest test-org-table/named-field () "Test formula with a named field." (should (string-match-p "| +| +1 +|" (org-test-with-temp-text " | | | | ^ | name | #+TBLFM: $name=1" (org-table-calc-current-TBLFM) (buffer-string)))) (should (string-match-p "| +| +1 +|" (org-test-with-temp-text " | _ | name | | | | #+TBLFM: $name=1" (org-table-calc-current-TBLFM) (buffer-string))))) (ert-deftest test-org-table/named-column () "Test formula with a named field." (should (string-match-p "| +| +1 +| +1 +|" (org-test-with-temp-text " | ! | name | | | | 1 | | #+TBLFM: @2$3=$name" (org-table-calc-current-TBLFM) (buffer-string))))) (ert-deftest test-org-table/formula-priority () "Test field formula priority over column formula." ;; Field formulas bind stronger than column formulas. (should (equal "| 1 | 3 |\n| 2 | 99 |\n" (org-test-with-temp-text "| 1 | |\n| 2 | |\n#+tblfm: $2=3*$1::@2$2=99" (org-table-calc-current-TBLFM) (buffer-substring-no-properties (point-min) (point))))) ;; When field formula is removed, table formulas is applied again. (should (equal "| 1 | 3 |\n| 2 | 6 |\n" (org-test-with-temp-text "| 1 | |\n| 2 | |\n#+tblfm: $2=3*$1::@2$2=99" (org-table-calc-current-TBLFM) (delete-region (point) (line-end-position)) (org-table-calc-current-TBLFM) (buffer-substring-no-properties (point-min) (line-beginning-position)))))) (ert-deftest test-org-table/tab-indent () "Test named fields with tab indentation." (should (string-match-p "| # | 111 |" (org-test-with-temp-text " | ! | sum | | a | b | c | |---+------+------+---+----+-----| | # | 1011 | 1000 | 1 | 10 | 100 | #+TBLFM: $2=$a+$b+$c " (org-table-calc-current-TBLFM) (buffer-string))))) (ert-deftest test-org-table/first-rc () "Test \"$<\" and \"@<\" constructs in formulas." (should (string-match-p "| 1 | 2 |" (org-test-with-temp-text "| | 2 | #+TBLFM: $<=1" (org-table-calc-current-TBLFM) (buffer-string)))) (should (string-match-p "| 2 |\n| 2 |" (org-test-with-temp-text "| 2 |\n| | #+TBLFM: @2$1=@<" (org-table-calc-current-TBLFM) (buffer-string))))) (ert-deftest test-org-table/last-rc () "Test \"$>\" and \"@>\" constructs in formulas." (should (string-match-p "| 2 | 1 |" (org-test-with-temp-text "| 2 | |\n#+TBLFM: $>=1" (org-table-calc-current-TBLFM) (buffer-string)))) (should (string-match-p "| 2 |\n| 2 |" (org-test-with-temp-text "| 2 |\n| |\n#+TBLFM: @>$1=@<" (org-table-calc-current-TBLFM) (buffer-string))))) (ert-deftest test-org-table/timestamps () "Test timestamps handling." ;; Standard test. (should (string-match-p "| 1 |" (org-test-with-temp-text "| <2016-07-07 Sun> | <2016-07-08 Fri> | |\n#+TBLFM: $3=$2-$1" (org-table-calc-current-TBLFM) (buffer-string)))) ;; Handle locale specific timestamps. (should (string-match-p "| 1 |" (org-test-with-temp-text "| <2016-07-07 Do> | <2016-07-08 Fr> | |\n#+TBLFM: $3=$2-$1" (org-table-calc-current-TBLFM) (buffer-string))))) (ert-deftest test-org-table/orgtbl-ascii-draw () "Test `orgtbl-ascii-draw'." ;; First value: Make sure that an integer input value is converted to a ;; float before division. Further values: Show some float input value ;; ranges corresponding to the same bar width. (should (equal (org-test-with-temp-text " | Value | | |----------+---------| | 19 | replace | |----------+---------| | -0.50001 | replace | | -0.49999 | replace | | 0.49999 | replace | | 0.50001 | replace | | 1.49999 | replace | | 22.50001 | replace | | 23.49999 | replace | | 23.50001 | replace | | 24.49999 | replace | | 24.50001 | replace | #+TBLFM: $2 = '(orgtbl-ascii-draw $1 0 24 3 \" 12345678\")" (org-table-calc-current-TBLFM) (buffer-string)) " | Value | | |----------+-----------| | 19 | 883 | |----------+-----------| | -0.50001 | too small | | -0.49999 | | | 0.49999 | | | 0.50001 | 1 | | 1.49999 | 1 | | 22.50001 | 887 | | 23.49999 | 887 | | 23.50001 | 888 | | 24.49999 | 888 | | 24.50001 | too large | #+TBLFM: $2 = '(orgtbl-ascii-draw $1 0 24 3 \" 12345678\")")) ;; Draw bars with a bullet. The bullet does not count in the parameter ;; WIDTH of `orgtbl-ascii-draw'. (should (equal (org-test-with-temp-text " | -1 | replace | | 0 | replace | | 1 | replace | | 2 | replace | | 3 | replace | | 4 | replace | #+TBLFM: $2 = '(orgtbl-ascii-draw $1 0 3 3 \"$-\")" (org-table-calc-current-TBLFM) (buffer-string)) " | -1 | too small | | 0 | $ | | 1 | -$ | | 2 | --$ | | 3 | ---$ | | 4 | too large | #+TBLFM: $2 = '(orgtbl-ascii-draw $1 0 3 3 \"$-\")"))) (ert-deftest test-org-table/single-rowgroup () "Test column formula in a table with a single rowgroup." (should (equal " |---+---| | 1 | 0 | |---+---| #+TBLFM: $2=$1-1" (org-test-with-temp-text " |---+---| | 1 | | |---+---| #+TBLFM: $2=$1-1" (org-table-calc-current-TBLFM) (buffer-string)))) (should (equal " | 1 | 0 | #+TBLFM: $2=$1-1" (org-test-with-temp-text " | 1 | | #+TBLFM: $2=$1-1" (org-table-calc-current-TBLFM) (buffer-string))))) ;;; Navigation (ert-deftest test-org-table/next-field () "Test `org-table-next-field' specifications." ;; Regular test. (should (equal "b" (org-test-with-temp-text "| a | b |" (org-table-next-field) (org-trim (org-table-get-field))))) ;; Create new rows as needed. (should (equal "| a |\n| |\n" (org-test-with-temp-text "| a |" (org-table-next-field) (buffer-string)))) ;; Jump over hlines, if `org-table-tab-jumps-over-hlines' is ;; non-nil. (should (equal "b" (org-test-with-temp-text "| a |\n|---|\n| b |" (let ((org-table-tab-jumps-over-hlines t)) (org-table-next-field)) (org-trim (org-table-get-field))))) ;; If `org-table-tab-jumps-over-hlines' is nil, however, create ;; a new row before the rule. (should (equal "| a |\n| |\n|---|\n| b |" (org-test-with-temp-text "| a |\n|---|\n| b |" (let ((org-table-tab-jumps-over-hlines nil)) (org-table-next-field)) (buffer-string))))) (ert-deftest test-org-table/previous-field () "Test `org-table-previous-field' specifications." ;; Regular tests. (should (eq ?a (org-test-with-temp-text "| a | b |" (org-table-previous-field) (char-after)))) (should (eq ?a (org-test-with-temp-text "| a |\n| b |" (org-table-previous-field) (char-after)))) ;; Find previous field across horizontal rules. (should (eq ?a (org-test-with-temp-text "| a |\n|---|\n| b |" (org-table-previous-field) (char-after)))) ;; When called on a horizontal rule, find previous data field. (should (eq ?b (org-test-with-temp-text "| a | b |\n|---+---|" (org-table-previous-field) (char-after)))) ;; Error when at first field. Make sure to preserve original ;; position. (should-error (org-test-with-temp-text "| a|" (org-table-previous-field))) (should-error (org-test-with-temp-text "|---|\n| a |" (org-table-previous-field))) (should (eq ?a (org-test-with-temp-text "|---|\n| a |" (ignore-errors (org-table-previous-field)) (char-after))))) ;;; Deleting columns (ert-deftest test-org-table/delete-column () "Test `org-table-delete-column'." ;; Error when outside a table. (should-error (org-test-with-temp-text "Paragraph" (org-table-delete-column))) ;; Delete first column. (should (equal "| a |\n" (org-test-with-temp-text "| | a |\n" (org-table-delete-column) (buffer-string)))) ;; Delete column and check location of point. (should (= 2 (org-test-with-temp-text "| a | b | c |" (org-table-delete-column) (org-table-current-column)))) ;; Delete column when at end of line and after a "|". (should (equal "| a |\n" (org-test-with-temp-text "| a | b |\n" (org-table-delete-column) (buffer-string)))) (should (equal "| a |\n" (org-test-with-temp-text "| a | b | \n" (org-table-delete-column) (buffer-string)))) ;; Delete two columns starting with the last column. (should (equal "| a |\n" (org-test-with-temp-text "| a | b | c |" (org-table-delete-column) (org-table-delete-column) (buffer-string))))) ;;; Inserting rows, inserting columns (ert-deftest test-org-table/insert-column () "Test `org-table-insert-column' specifications." ;; Error when outside a table. (should-error (org-test-with-temp-text "Paragraph" (org-table-insert-column))) ;; Insert new column after current one. (should (equal "| | a |\n" (org-test-with-temp-text "| a |" (org-table-insert-column) (buffer-string)))) (should (equal "| | a | b |\n" (org-test-with-temp-text "| a | b |" (org-table-insert-column) (buffer-string)))) ;; Move point into the newly created column. (should (equal " | a |" (org-test-with-temp-text "| a |" (org-table-insert-column) (buffer-substring-no-properties (point) (line-end-position))))) (should (equal " | a | b |" (org-test-with-temp-text "| a | b |" (org-table-insert-column) (buffer-substring-no-properties (point) (line-end-position))))) ;; Handle missing vertical bar in the last column. (should (equal "| | a |\n" (org-test-with-temp-text "| a" (org-table-insert-column) (buffer-string)))) (should (equal " | a |" (org-test-with-temp-text "| a" (org-table-insert-column) (buffer-substring-no-properties (point) (line-end-position))))) ;; Handle column insertion when point is before first column. (should (equal " | | a |\n" (org-test-with-temp-text " | a |" (org-table-insert-column) (buffer-string)))) (should (equal " | | a | b |\n" (org-test-with-temp-text " | a | b |" (org-table-insert-column) (buffer-string))))) (ert-deftest test-org-table/insert-column-with-formula () "Test `org-table-insert-column' with a formula in place." (should (equal "| | 1 | 1 | 2 | #+TBLFM: $4=$2+$3" (org-test-with-temp-text "| 1 | 1 | 2 | #+TBLFM: $3=$1+$2" (org-table-insert-column) (buffer-substring-no-properties (point-min) (point-max)))))) ;;; Moving single cells (ert-deftest test-org-table/move-cell-down () "Test `org-table-move-cell-down' specifications." ;; Error out when cell cannot be moved due to not in table, in the ;; last row of the table, or is on a hline. (should-error (org-test-with-temp-text "not in\na table\n" (org-table-move-cell-down))) (should-error (org-test-with-temp-text "| a |" (org-table-move-cell-down))) (should-error (org-test-with-temp-text "| a |\n" (org-table-move-cell-down))) (should-error (org-test-with-temp-text "| a | b |\n" (org-table-move-cell-down))) (should-error (org-test-with-temp-text "| a | b |\n| c | d |\n" (org-table-move-cell-down))) (should-error (org-test-with-temp-text "| a | b |\n| c | d |\n" (org-table-move-cell-down))) (should-error (org-test-with-temp-text "| a |\n|---|\n" (org-table-move-cell-down))) (should-error (org-test-with-temp-text "|---|\n| a |\n" (org-table-move-cell-down))) ;; Check for correct cell movement (should (equal (concat "| c | b |\n" "| a | d |\n" "| e | f |\n") (org-test-with-temp-text (concat "| a | b |\n" "| c | d |\n" "| e | f |\n") (org-table-move-cell-down) (buffer-string)))) (should (equal (concat "| a | d |\n" "| c | b |\n" "| e | f |\n") (org-test-with-temp-text (concat "| a | b |\n" "| c | d |\n" "| e | f |\n") (org-table-move-cell-down) (buffer-string)))) (should (equal (concat "| a | b |\n" "| e | d |\n" "| c | f |\n") (org-test-with-temp-text (concat "| a | b |\n" "| c | d |\n" "| e | f |\n") (org-table-move-cell-down) (buffer-string)))) (should (equal (concat "| a | d |\n" "| c | f |\n" "| e | b |\n") (org-test-with-temp-text (concat "| a | b |\n" "| c | d |\n" "| e | f |\n") (org-table-move-cell-down) (org-table-move-cell-down) (buffer-string)))) ;; Check for correct handling of hlines which should not change ;; position on single cell moves. (should (equal (concat "| c | b |\n" "|---+---|\n" "| a | d |\n" "| e | f |\n") (org-test-with-temp-text (concat "| a | b |\n" "|---+---|\n" "| c | d |\n" "| e | f |\n") (org-table-move-cell-down) (buffer-string)))) (should (equal (concat "| a | d |\n" "|---+---|\n" "| c | f |\n" "| e | b |\n") (org-test-with-temp-text (concat "| a | b |\n" "|---+---|\n" "| c | d |\n" "| e | f |\n") (org-table-move-cell-down) (org-table-move-cell-down) (buffer-string)))) (should (equal (concat "| a | b |\n" "|---+---|\n" "| c | f |\n" "| e | d |\n") (org-test-with-temp-text (concat "| a | b |\n" "|---+---|\n" "| c | d |\n" "| e | f |\n") (org-table-move-cell-down) (buffer-string)))) ;; Move single cell even without a final newline. (should (equal (concat "| a | d |\n" "|---+---|\n" "| c | f |\n" "| e | b |\n") (org-test-with-temp-text (concat "| a | b |\n" "|---+---|\n" "| c | d |\n" "| e | f |") (org-table-move-cell-down) (org-table-move-cell-down) (buffer-string))))) (ert-deftest test-org-table/move-cell-up () "Test `org-table-move-cell-up' specifications." ;; Error out when cell cannot be moved due to not in table, in the ;; last row of the table, or is on a hline. (should-error (org-test-with-temp-text "not in\na table\n" (org-table-move-cell-up))) (should-error (org-test-with-temp-text "| a |" (org-table-move-cell-up))) (should-error (org-test-with-temp-text "| a |\n" (org-table-move-cell-up))) (should-error (org-test-with-temp-text "| a | b |\n" (org-table-move-cell-up))) (should-error (org-test-with-temp-text "| a | b |\n| c | d |\n" (org-table-move-cell-up))) (should-error (org-test-with-temp-text "| a |\n|---|\n" (org-table-move-cell-up))) (should-error (org-test-with-temp-text "|---|\n| a |\n" (org-table-move-cell-up))) ;; Check for correct cell movement. (should (equal (concat "| c | b |\n" "| a | d |\n" "| e | f |\n") (org-test-with-temp-text (concat "| a | b |\n" "| c | d |\n" "| e | f |\n") (org-table-move-cell-up) (buffer-string)))) (should (equal (concat "| a | d |\n" "| c | b |\n" "| e | f |\n") (org-test-with-temp-text (concat "| a | b |\n" "| c | d |\n" "| e | f |\n") (org-table-move-cell-up) (buffer-string)))) (should (equal (concat "| a | b |\n" "| e | d |\n" "| c | f |\n") (org-test-with-temp-text (concat "| a | b |\n" "| c | d |\n" "| e | f |\n") (org-table-move-cell-up) (buffer-string)))) (should (equal (concat "| a | f |\n" "| c | b |\n" "| e | d |\n") (org-test-with-temp-text (concat "| a | b |\n" "| c | d |\n" "| e | f |\n") (org-table-move-cell-up) (org-table-move-cell-up) (buffer-string)))) ;; Check for correct handling of hlines which should not change ;; position on single cell moves. (should (equal (concat "| c | b |\n" "|---+---|\n" "| a | d |\n" "| e | f |\n") (org-test-with-temp-text (concat "| a | b |\n" "|---+---|\n" "| c | d |\n" "| e | f |\n") (org-table-move-cell-up) (buffer-string)))) (should (equal (concat "| a | f |\n" "|---+---|\n" "| c | b |\n" "| e | d |\n") (org-test-with-temp-text (concat "| a | b |\n" "|---+---|\n" "| c | d |\n" "| e | f |\n") (org-table-move-cell-up) (org-table-move-cell-up) (buffer-string)))) (should (equal (concat "| a | b |\n" "|---+---|\n" "| c | f |\n" "| e | d |\n") (org-test-with-temp-text (concat "| a | b |\n" "|---+---|\n" "| c | d |\n" "| e | f |\n") (org-table-move-cell-up) (buffer-string)))) ;; Move single cell even without a final newline. (should (equal (concat "| a | f |\n" "|---+---|\n" "| c | b |\n" "| e | d |\n") (org-test-with-temp-text (concat "| a | b |\n" "|---+---|\n" "| c | d |\n" "| e | f |") (org-table-move-cell-up) (org-table-move-cell-up) (buffer-string))))) (ert-deftest test-org-table/move-cell-right () "Test `org-table-move-cell-right' specifications." ;; Error out when cell cannot be moved due to not in table, in the ;; last col of the table, or is on a hline. (should-error (org-test-with-temp-text "not in\na table\n" (org-table-move-cell-right))) (should-error (org-test-with-temp-text "| a |" (org-table-move-cell-right))) (should-error (org-test-with-temp-text "| a |\n" (org-table-move-cell-right))) (should-error (org-test-with-temp-text "| a |\n| b |\n" (org-table-move-cell-right))) (should-error (org-test-with-temp-text "| a | b |\n| c | d |\n" (org-table-move-cell-right))) (should-error (org-test-with-temp-text "| a |\n|---|\n" (org-table-move-cell-right))) (should-error (org-test-with-temp-text "|---|\n| a |\n" (org-table-move-cell-right))) ;; Check for correct cell movement. (should (equal (concat "| b | a | c |\n" "| d | e | f |\n") (org-test-with-temp-text (concat "| a | b | c |\n" "| d | e | f |\n") (org-table-move-cell-right) (buffer-string)))) (should (equal (concat "| b | c | a |\n" "| d | e | f |\n") (org-test-with-temp-text (concat "| a | b | c |\n" "| d | e | f |\n") (org-table-move-cell-right) (org-table-move-cell-right) (buffer-string)))) (should (equal (concat "| a | b | c |\n" "| e | f | d |\n") (org-test-with-temp-text (concat "| a | b | c |\n" "| d | e | f |\n") (org-table-move-cell-right) (org-table-move-cell-right) (buffer-string)))) (should (equal (concat "| a | b | c |\n" "| d | f | e |\n") (org-test-with-temp-text (concat "| a | b | c |\n" "| d | e | f |\n") (org-table-move-cell-right) (buffer-string)))) (should (equal (concat "| a | b | c |\n" "|---+---+---|\n" "| e | f | d |\n") (org-test-with-temp-text (concat "| a | b | c |\n" "|---+---+---|\n" "| d | e | f |\n") (org-table-move-cell-right) (org-table-move-cell-right) (buffer-string)))) ;; Move single cell even without a final newline. (should (equal (concat "| a | b | c |\n" "|---+---+---|\n" "| e | d | f |\n") (org-test-with-temp-text (concat "| a | b | c |\n" "|---+---+---|\n" "| d | e | f |") (org-table-move-cell-right) (buffer-string))))) (ert-deftest test-org-table/move-cell-left () "Test `org-table-move-cell-left' specifications." ;; Error out when cell cannot be moved due to not in table, in the ;; last col of the table, or is on a hline. (should-error (org-test-with-temp-text "not in\na table\n" (org-table-move-cell-left))) (should-error (org-test-with-temp-text "| a |" (org-table-move-cell-left))) (should-error (org-test-with-temp-text "| a |\n" (org-table-move-cell-left))) (should-error (org-test-with-temp-text "| a |\n| b |\n" (org-table-move-cell-left))) (should-error (org-test-with-temp-text "| a | b |\n| c | d |\n" (org-table-move-cell-left))) (should-error (org-test-with-temp-text "| a |\n|---|\n" (org-table-move-cell-left))) (should-error (org-test-with-temp-text "|---|\n| a |\n" (org-table-move-cell-left))) ;; Check for correct cell movement. (should (equal (concat "| b | a | c |\n" "| d | e | f |\n") (org-test-with-temp-text (concat "| a | b | c |\n" "| d | e | f |\n") (org-table-move-cell-left) (buffer-string)))) (should (equal (concat "| c | a | b |\n" "| d | e | f |\n") (org-test-with-temp-text (concat "| a | b | c |\n" "| d | e | f |\n") (org-table-move-cell-left) (org-table-move-cell-left) (buffer-string)))) (should (equal (concat "| a | b | c |\n" "| f | d | e |\n") (org-test-with-temp-text (concat "| a | b | c |\n" "| d | e | f |\n") (org-table-move-cell-left) (org-table-move-cell-left) (buffer-string)))) (should (equal (concat "| a | b | c |\n" "| d | f | e |\n") (org-test-with-temp-text (concat "| a | b | c |\n" "| d | e | f |\n") (org-table-move-cell-left) (buffer-string)))) (should (equal (concat "| a | b | c |\n" "|---+---+---|\n" "| f | d | e |\n") (org-test-with-temp-text (concat "| a | b | c |\n" "|---+---+---|\n" "| d | e | f |\n") (org-table-move-cell-left) (org-table-move-cell-left) (buffer-string)))) ;; Move single cell even without a final newline. (should (equal (concat "| a | b | c |\n" "|---+---+---|\n" "| e | d | f |\n") (org-test-with-temp-text (concat "| a | b | c |\n" "|---+---+---|\n" "| d | e | f |") (org-table-move-cell-left) (buffer-string))))) ;;; Moving rows, moving columns (ert-deftest test-org-table/move-row-down () "Test `org-table-move-row-down' specifications." ;; Error out when row cannot be moved, e.g., it is the last row in ;; the table. (should-error (org-test-with-temp-text "| a |" (org-table-move-row-down))) (should-error (org-test-with-temp-text "| a |\n" (org-table-move-row-down))) (should-error (org-test-with-temp-text "| a |\n| b |" (org-table-move-row-down))) ;; Move data lines. (should (equal "| b |\n| a |\n" (org-test-with-temp-text "| a |\n| b |\n" (org-table-move-row-down) (buffer-string)))) (should (equal "|---|\n| a |\n" (org-test-with-temp-text "| a |\n|---|\n" (org-table-move-row-down) (buffer-string)))) ;; Move hlines. (should (equal "| b |\n|---|\n" (org-test-with-temp-text "|---|\n| b |\n" (org-table-move-row-down) (buffer-string)))) (should (equal "|---|\n|---|\n" (org-test-with-temp-text "|---|\n|---|\n" (org-table-move-row-down) (buffer-string)))) ;; Move rows even without a final newline. (should (equal "| b |\n| a |\n" (org-test-with-temp-text "| a |\n| b |" (org-table-move-row-down) (buffer-string))))) (ert-deftest test-org-table/move-row-up () "Test `org-table-move-row-up' specifications." ;; Error out when row cannot be moved, e.g., it is the first row in ;; the table. (should-error (org-test-with-temp-text "| a |" (org-table-move-row-up))) (should-error (org-test-with-temp-text "| a |\n" (org-table-move-row-up))) ;; Move data lines. (should (equal "| b |\n| a |\n" (org-test-with-temp-text "| a |\n| b |\n" (org-table-move-row-up) (buffer-string)))) (should (equal "| b |\n|---|\n" (org-test-with-temp-text "|---|\n| b |\n" (org-table-move-row-up) (buffer-string)))) ;; Move hlines. (should (equal "|---|\n| a |\n" (org-test-with-temp-text "| a |\n|---|\n" (org-table-move-row-up) (buffer-string)))) (should (equal "|---|\n|---|\n" (org-test-with-temp-text "|---|\n|---|\n" (org-table-move-row-up) (buffer-string)))) ;; Move rows even without a final newline. (should (equal "| b |\n| a |\n" (org-test-with-temp-text "| a |\n| b |" (org-table-move-row-up) (buffer-string))))) ;;; Shrunk columns (ert-deftest test-org-table/toggle-column-width () "Test `org-table-toggle-columns-width' specifications." ;; Error when not at a column. (should-error (org-test-with-temp-text "a" (org-table-toggle-column-width))) ;; A shrunk column is overlaid with ;; `org-table-shrunk-column-indicator'. (should (equal org-table-shrunk-column-indicator (org-test-with-temp-text "| a |" (org-table-toggle-column-width) (overlay-get (car (overlays-at (point))) 'display)))) (should (equal org-table-shrunk-column-indicator (org-test-with-temp-text "| a |\n|---|" (org-table-toggle-column-width) (overlay-get (car (overlays-at (point))) 'display)))) ;; Shrink every field in the same column. (should (equal org-table-shrunk-column-indicator (org-test-with-temp-text "| a |\n|---|" (org-table-toggle-column-width) (overlay-get (car (overlays-at (1+ (line-beginning-position 0)))) 'display)))) ;; When column is already shrunk, expand it, i.e., remove overlays. (should-not (org-test-with-temp-text "| a |" (org-table-toggle-column-width) (org-table-toggle-column-width) (overlays-in (point-min) (point-max)))) (should-not (org-test-with-temp-text "| a |\n| b |" (org-table-toggle-column-width) (org-table-toggle-column-width) (overlays-in (point-min) (point-max)))) ;; With a column width cookie, limit overlay to the specified number ;; of characters. (should (equal "| abc" (org-test-with-temp-text "| <3> |\n| abcd |" (org-table-toggle-column-width) (buffer-substring (line-beginning-position) (overlay-start (nth 1 (sort (overlays-in (line-beginning-position) (line-end-position)) (lambda (ov1 ov2) (< (overlay-start ov1) (overlay-start ov2)))))))))) (should (equal "| a " (org-test-with-temp-text "| <3> |\n| a |" (org-table-toggle-column-width) (buffer-substring (line-beginning-position) (overlay-start (car (sort (overlays-in (line-beginning-position) (line-end-position)) (lambda (ov1 ov2) (< (overlay-start ov1) (overlay-start ov2)))))))))) (should (equal (concat "----" org-table-shrunk-column-indicator) (org-test-with-temp-text "| <3> |\n|------|" (org-table-toggle-column-width) (overlay-get (car (sort (overlays-in (line-beginning-position) (line-end-position)) (lambda (ov1 ov2) (< (overlay-start ov1) (overlay-start ov2))))) 'display)))) ;; Width only takes into account visible characters. (should (equal "| [[http" (org-test-with-temp-text "| <4> |\n| [[http://orgmode.org]] |" (org-table-toggle-column-width) (buffer-substring (line-beginning-position) (overlay-start (nth 1 (sort (overlays-in (line-beginning-position) (line-end-position)) (lambda (ov1 ov2) (< (overlay-start ov1) (overlay-start ov2)))))))))) ;; Before the first column or after the last one, ask for columns ;; ranges. (should (catch :exit (org-test-with-temp-text "| a |" (cl-letf (((symbol-function 'read-string) (lambda (&rest_) (throw :exit t)))) (org-table-toggle-column-width) nil)))) (should (catch :exit (org-test-with-temp-text "| a |" (cl-letf (((symbol-function 'read-string) (lambda (&rest_) (throw :exit t)))) (org-table-toggle-column-width) nil)))) ;; When optional argument ARG is a string, toggle specified columns. (should (equal org-table-shrunk-column-indicator (org-test-with-temp-text "| a | b |" (org-table-toggle-column-width "2") (overlay-get (car (overlays-at (- (point-max) 2))) 'display)))) (should (equal '("b" "c") (org-test-with-temp-text "| a | b | c | d |" (org-table-toggle-column-width "2-3") (sort (mapcar (lambda (o) (overlay-get o 'help-echo)) (overlays-in (point-min) (point-max))) #'string-lessp)))) (should (equal '("b" "c" "d") (org-test-with-temp-text "| a | b | c | d |" (org-table-toggle-column-width "2-") (sort (mapcar (lambda (o) (overlay-get o 'help-echo)) (overlays-in (point-min) (point-max))) #'string-lessp)))) (should (equal '("a" "b") (org-test-with-temp-text "| a | b | c | d |" (org-table-toggle-column-width "-2") (sort (mapcar (lambda (o) (overlay-get o 'help-echo)) (overlays-in (point-min) (point-max))) #'string-lessp)))) (should (equal '("a" "b" "c" "d") (org-test-with-temp-text "| a | b | c | d |" (org-table-toggle-column-width "-") (sort (mapcar (lambda (o) (overlay-get o 'help-echo)) (overlays-in (point-min) (point-max))) #'string-lessp)))) (should (equal '("a" "d") (org-test-with-temp-text "| a | b | c | d |" (org-table-toggle-column-width "1-3") (org-table-toggle-column-width "2-4") (sort (mapcar (lambda (o) (overlay-get o 'help-echo)) (overlays-in (point-min) (point-max))) #'string-lessp)))) ;; When ARG is (16), remove any column overlay. (should-not (org-test-with-temp-text "| a |" (org-table-toggle-column-width) (org-table-toggle-column-width '(16)) (overlays-in (point-min) (point-max)))) (should-not (org-test-with-temp-text "| a | b | c | d |" (org-table-toggle-column-width "-") (org-table-toggle-column-width '(16)) (overlays-in (point-min) (point-max))))) (ert-deftest test-org-table/shrunk-columns () "Test behaviour of shrunk column." ;; Edition automatically expands a shrunk column. (should-not (org-test-with-temp-text "| a |" (org-table-toggle-column-width) (insert "a") (overlays-in (point-min) (point-max)))) ;; Other columns are not changed. (should (org-test-with-temp-text "| a | b |" (org-table-toggle-column-width "-") (insert "a") (overlays-in (point-min) (point-max)))) ;; Moving a shrunk column doesn't alter its state. (should (equal "a" (org-test-with-temp-text "| a | b |" (org-table-toggle-column-width) (org-table-move-column-right) (overlay-get (car (overlays-at (point))) 'help-echo)))) (should (equal "a" (org-test-with-temp-text "| a |\n| b |" (org-table-toggle-column-width) (org-table-move-row-down) (overlay-get (car (overlays-at (point))) 'help-echo)))) ;; State is preserved upon inserting a column. (should (equal '("a") (org-test-with-temp-text "| a |" (org-table-toggle-column-width) (org-table-insert-column) (sort (mapcar (lambda (o) (overlay-get o 'help-echo)) (overlays-in (point-min) (point-max))) #'string-lessp)))) ;; State is preserved upon deleting a column. (should (equal '("a" "c") (org-test-with-temp-text "| a | b | c |" (org-table-toggle-column-width "-") (org-table-delete-column) (sort (mapcar (lambda (o) (overlay-get o 'help-echo)) (overlays-in (point-min) (point-max))) #'string-lessp)))) ;; State is preserved upon deleting a row. (should (equal '("b1" "b2") (org-test-with-temp-text "| a1 | a2 |\n| b1 | b2 |" (org-table-toggle-column-width "-") (org-table-kill-row) (sort (mapcar (lambda (o) (overlay-get o 'help-echo)) (overlays-in (point-min) (point-max))) #'string-lessp)))) (should (equal '("a1" "a2") (org-test-with-temp-text "| a1 | a2 |\n| b1 | b2 |" (org-table-toggle-column-width "-") (org-table-kill-row) (sort (mapcar (lambda (o) (overlay-get o 'help-echo)) (overlays-in (point-min) (point-max))) #'string-lessp)))) ;; State is preserved upon inserting a row or hline. (should (equal '("" "a1" "b1") (org-test-with-temp-text "| a1 | a2 |\n| b1 | b2 |" (org-table-toggle-column-width) (org-table-insert-row) (sort (mapcar (lambda (o) (overlay-get o 'help-echo)) (overlays-in (point-min) (point-max))) #'string-lessp)))) (should (equal '("a1" "b1") (org-test-with-temp-text "| a1 | a2 |\n| b1 | b2 |" (org-table-toggle-column-width) (org-table-insert-hline) (sort (mapcar (lambda (o) (overlay-get o 'help-echo)) (overlays-in (point-min) (point-max))) #'string-lessp)))) ;; State is preserved upon sorting a column for all the columns but ;; the one being sorted. (should (equal '("a2" "b2") (org-test-with-temp-text "| a1 | a2 |\n| b1 | b2 |" (org-table-toggle-column-width "-") (org-table-sort-lines nil ?A) (sort (mapcar (lambda (o) (overlay-get o 'help-echo)) (overlays-in (point-min) (point-max))) #'string-lessp)))) ;; State is preserved upon replacing a field non-interactively. (should (equal '("a") (org-test-with-temp-text "| a |" (org-table-toggle-column-width) (org-table-get-field nil "b") (mapcar (lambda (o) (overlay-get o 'help-echo)) (overlays-in (point-min) (point-max)))))) ;; Moving to next field doesn't change shrunk state. (should (equal "a" (org-test-with-temp-text "| a | b |" (org-table-toggle-column-width) (org-table-next-field) (overlay-get (car (overlays-at (1+ (line-beginning-position)))) 'help-echo)))) (should (equal "b" (org-test-with-temp-text "| a | b |" (org-table-toggle-column-width) (goto-char 2) (org-table-next-field) (overlay-get (car (overlays-at (point))) 'help-echo)))) ;; Aligning table doesn't alter shrunk state. (should (equal "a" (org-test-with-temp-text "| a | b |" (org-table-toggle-column-width) (org-table-align) (overlay-get (car (overlays-at (1+ (line-beginning-position)))) 'help-echo)))) (should (equal "b" (org-test-with-temp-text "|---+-----|\n| a | b |" (org-table-toggle-column-width) (org-table-align) (overlay-get (car (overlays-at (point))) 'help-echo)))) (should (equal '("b") (org-test-with-temp-text "|---+-----|\n| a | b |" (org-table-toggle-column-width) (org-table-align) (mapcar (lambda (o) (overlay-get o 'help-echo)) (overlays-in (line-beginning-position) (line-end-position)))))) ;; Recalculating formulas doesn't change shrunk state. (should (equal "2" (org-test-with-temp-text "| 1 | 0 |\n#+TBLFM: $2=$1+1\n" (org-table-toggle-column-width) (org-table-recalculate) (overlay-get (car (overlays-at (point))) 'help-echo))))) ;;; Miscellaneous (ert-deftest test-org-table/current-column () "Test `org-table-current-column' specifications." (should (= 1 (org-test-with-temp-text "| a |" (org-table-current-column)))) (should (= 1 (org-test-with-temp-text "|---|" (org-table-current-column)))) (should (= 2 (org-test-with-temp-text "| 1 | 2 |" (org-table-current-column)))) (should (= 2 (org-test-with-temp-text "|---+---|" (org-table-current-column))))) (ert-deftest test-org-table/get-field () "Test `org-table-get-field' specifications." ;; Regular test. (should (equal " a " (org-test-with-temp-text "| a |" (org-table-get-field)))) ;; Get field in open last column. (should (equal " a " (org-test-with-temp-text "| a " (org-table-get-field)))) ;; Get empty field. (should (equal "" (org-test-with-temp-text "||" (org-table-get-field)))) (should (equal " " (org-test-with-temp-text "| |" (org-table-get-field)))) ;; Outside the table, return the empty string. (should (equal "" (org-test-with-temp-text "| a |" (org-table-get-field)))) (should (equal "" (org-test-with-temp-text "| a |" (org-table-get-field)))) ;; With optional N argument, select a particular column in current ;; row. (should (equal " 3 " (org-test-with-temp-text "| 1 | 2 | 3 |" (org-table-get-field 3)))) (should (equal " 4 " (org-test-with-temp-text "| 1 | 2 |\n| 3 | 4 |" (org-table-get-field 2)))) ;; REPLACE optional argument is used to replace selected field. (should (equal "| foo |" (org-test-with-temp-text "| 1 |" (org-table-get-field nil " foo ") (buffer-string)))) (should (equal "| 1 | 2 | foo |" (org-test-with-temp-text "| 1 | 2 | 3 |" (org-table-get-field 3 " foo ") (buffer-string)))) (should (equal " foo " (org-test-with-temp-text "| 1 | 2 |\n| 3 | 4 |" (org-table-get-field 2 " foo ") (org-table-get-field 2)))) ;; An empty REPLACE string clears the field. (should (equal "| |" (org-test-with-temp-text "| 1 |" (org-table-get-field nil "") (buffer-string)))) ;; When using REPLACE still return old value. (should (equal " 1 " (org-test-with-temp-text "| 1 |" (org-table-get-field nil " foo "))))) (provide 'test-org-table) ;;; test-org-table.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-org-tempo.el000066400000000000000000000072601500430433700221120ustar00rootroot00000000000000;;; test-org-tempo.el --- Tests for org-tempo.el -*- lexical-binding: t; -*- ;; Copyright (C) 2017, 2019 Rasmus Pank Roulund ;; Author: Rasmus Pank Roulund ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (require 'org-tempo) (unless (featurep 'org-tempo) (signal 'missing-test-dependency "org-tempo")) (ert-deftest test-org-tempo/completion () "Test that blocks and keywords are expanded correctly by org-tempo." ;; Tempo completion should recognize snippet keywords and expand with tab (should (equal (org-test-with-temp-text "" (org-tempo-setup) (tempo-complete-tag) (buffer-string)) "#+latex: ")) ;; Tempo completion should recognize snippet Blocks (should (equal (org-test-with-temp-text "" (org-tempo-setup) (call-interactively 'org-cycle) (buffer-string)) "#+begin_export latex\n\n#+end_export")) ;; Tab should work for expansion. (should (equal (org-test-with-temp-text "" (org-tempo-setup) (tempo-complete-tag) (buffer-string)) (org-test-with-temp-text "" (org-tempo-setup) (org-cycle) (buffer-string)))) ;; Tempo should not expand unknown snippets (equal (org-test-with-temp-text "" (org-tempo-setup) (tempo-complete-tag) (goto-char (point-min)) (end-of-line) (skip-chars-backward " ")))) ;; src blocks, export blocks and keywords should have one space at ;; the end of the first line. (should (cl-every (apply-partially 'eq 1) (mapcar (lambda (s) (org-test-with-temp-text (format "<%s" s) (org-tempo-setup) (tempo-complete-tag) (goto-char (point-min)) (end-of-line) (abs (skip-chars-backward " ")))) '("s" "E" "L"))))) (ert-deftest test-org-tempo/cursor-placement () "Test the placement of the cursor after tempo expand" ;; Normal blocks place point "inside" block. (should (eq (org-test-with-temp-text "" (org-tempo-setup) (tempo-complete-tag) (point)) (length "#\\+begin_export latex\n"))) ;; Special block stop at end of #+begin line. (should (eq (org-test-with-temp-text "" (org-tempo-setup) (tempo-complete-tag) (point)) (length "#\\+begin_src ")))) (ert-deftest test-org-tempo/add-new-templates () "Test that new structures and keywords are added correctly." ;; New blocks should be added. (should (let ((org-structure-template-alist '(("n" . "new_block")))) (org-tempo-add-templates) (assoc " ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (eval-and-compile (require 'cl-lib)) (require 'org-timer) (defmacro test-org-timer/with-temp-text (text &rest body) "Like `org-test-with-temp-text', but set timer-specific variables. Also, mute output from `message'." (declare (indent 1)) `(cl-letf (((symbol-function 'message) (lambda (&rest _args) nil))) (org-test-with-temp-text ,text (let (org-timer-start-time org-timer-pause-time org-timer-countdown-timer org-timer-display) (unwind-protect (progn ,@body) (when (timerp org-timer-countdown-timer) (cancel-timer org-timer-countdown-timer))))))) (defmacro test-org-timer/with-current-time (time &rest body) "Run BODY, setting `current-time' output to TIME." (declare (indent 1)) `(org-test-at-time ,time ,@body)) ;;; Time conversion and formatting (ert-deftest test-org-timer/secs-to-hms () "Test conversion between HMS format and seconds." ;; Seconds to HMS, and back again (should (equal "0:00:30" (org-timer-secs-to-hms 30))) (should (equal 30 (org-timer-hms-to-secs (org-timer-secs-to-hms 30)))) ;; Minutes to HMS, and back again (should (equal "0:02:10" (org-timer-secs-to-hms 130))) (should (equal 130 (org-timer-hms-to-secs (org-timer-secs-to-hms 130)))) ;; Hours to HMS, and back again (should (equal "1:01:30" (org-timer-secs-to-hms 3690))) (should (equal 3690 (org-timer-hms-to-secs (org-timer-secs-to-hms 3690)))) ;; Negative seconds to HMS, and back again (should (equal "-1:01:30" (org-timer-secs-to-hms -3690))) (should (equal -3690 (org-timer-hms-to-secs (org-timer-secs-to-hms -3690))))) (ert-deftest test-org-timer/fix-incomplete () "Test conversion to complete HMS format." ;; No fix is needed. (should (equal "1:02:03" (org-timer-fix-incomplete "1:02:03"))) ;; Hour is missing. (should (equal "0:02:03" (org-timer-fix-incomplete "02:03"))) ;; Minute is missing. (should (equal "0:00:03" (org-timer-fix-incomplete "03")))) (ert-deftest test-org-timer/change-times () "Test changing HMS format by offset." ;; Add time. (should (equal " 1:31:15 4:00:55" (org-test-with-temp-text " 0:00:25 2:30:05" (org-timer-change-times-in-region (point-min) (point-max) "1:30:50") (buffer-string)))) ;; Subtract time. (should (equal " -1:30:25 0:59:15" (org-test-with-temp-text " 0:00:25 2:30:05" (org-timer-change-times-in-region (point-min) (point-max) "-1:30:50") (buffer-string))))) ;;; Timers ;; Dummy times for overriding `current-time' (defvar test-org-timer/time0 '(21635 62793 797149 675000)) ;; Add 3 minutes and 26 seconds. (defvar test-org-timer/time1 (time-add test-org-timer/time0 (seconds-to-time 206))) ;; Add 2 minutes and 41 seconds (6 minutes and 7 seconds total). (defvar test-org-timer/time2 (time-add test-org-timer/time1 (seconds-to-time 161))) ;; Add 4 minutes and 55 seconds (11 minutes and 2 seconds total). (defvar test-org-timer/time3 (time-add test-org-timer/time2 (seconds-to-time 295))) (ert-deftest test-org-timer/start-relative () "Test starting relative timer." ;; Insert plain timer string, starting with `org-timer-start'. (should (equal "0:03:26" (test-org-timer/with-temp-text "" (test-org-timer/with-current-time test-org-timer/time0 (org-timer-start)) (test-org-timer/with-current-time test-org-timer/time1 (org-timer)) (org-trim (buffer-string))))) ;; Insert item timer string. (should (equal "- 0:03:26 ::" (test-org-timer/with-temp-text "" (test-org-timer/with-current-time test-org-timer/time0 (org-timer-start)) (test-org-timer/with-current-time test-org-timer/time1 (org-timer-item)) (org-trim (buffer-string))))) ;; Start with `org-timer'. (should (equal "0:00:00 0:03:26" (test-org-timer/with-temp-text "" (test-org-timer/with-current-time test-org-timer/time0 (org-timer)) (test-org-timer/with-current-time test-org-timer/time1 (org-timer)) (org-trim (buffer-string))))) ;; Restart with `org-timer'. (should (equal "0:00:00" (test-org-timer/with-temp-text "" (test-org-timer/with-current-time test-org-timer/time0 (org-timer-start)) (test-org-timer/with-current-time test-org-timer/time1 (org-timer '(4))) (org-trim (buffer-string)))))) (ert-deftest test-org-timer/set-timer () "Test setting countdown timer." (should (equal "0:06:34" (test-org-timer/with-temp-text "" (test-org-timer/with-current-time test-org-timer/time0 (org-timer-set-timer 10)) (test-org-timer/with-current-time test-org-timer/time1 (org-timer)) (org-trim (buffer-string))))) (should (equal "0:00:04" (test-org-timer/with-temp-text "" (test-org-timer/with-current-time test-org-timer/time0 (org-timer-set-timer "3:30")) (test-org-timer/with-current-time test-org-timer/time1 (org-timer)) (org-trim (buffer-string)))))) (ert-deftest test-org-timer/pause-timer () "Test pausing relative and countdown timers." ;; Pause relative timer. (should (equal "0:03:26" (test-org-timer/with-temp-text "" (test-org-timer/with-current-time test-org-timer/time0 (org-timer-start)) (test-org-timer/with-current-time test-org-timer/time1 (org-timer-pause-or-continue)) (org-timer) (org-trim (buffer-string))))) ;; Pause then continue relative timer. (should (equal "0:08:21" (test-org-timer/with-temp-text "" (test-org-timer/with-current-time test-org-timer/time0 (org-timer-start)) (test-org-timer/with-current-time test-org-timer/time1 (org-timer-pause-or-continue)) (test-org-timer/with-current-time test-org-timer/time2 (org-timer-pause-or-continue)) (test-org-timer/with-current-time test-org-timer/time3 (org-timer)) (org-trim (buffer-string))))) ;; Pause then continue countdown timer. (should (equal "0:01:39" (test-org-timer/with-temp-text "" (test-org-timer/with-current-time test-org-timer/time0 (org-timer-set-timer 10)) (test-org-timer/with-current-time test-org-timer/time1 (org-timer-pause-or-continue)) (test-org-timer/with-current-time test-org-timer/time2 (org-timer-pause-or-continue)) (test-org-timer/with-current-time test-org-timer/time3 (org-timer)) (org-trim (buffer-string)))))) (ert-deftest test-org-timer/stop () "Test stopping relative and countdown timers." ;; Stop running relative timer. (test-org-timer/with-temp-text "" (test-org-timer/with-current-time test-org-timer/time0 (org-timer-start)) (test-org-timer/with-current-time test-org-timer/time1 (org-timer-stop)) (should-not org-timer-start-time)) ;; Stop paused relative timer. (test-org-timer/with-temp-text "" (test-org-timer/with-current-time test-org-timer/time0 (org-timer-start)) (test-org-timer/with-current-time test-org-timer/time1 (org-timer-pause-or-continue) (org-timer-stop)) (should-not org-timer-start-time) (should-not org-timer-pause-time)) ;; Stop running countdown timer. (test-org-timer/with-temp-text "" (test-org-timer/with-current-time test-org-timer/time0 (org-timer-set-timer 10)) (test-org-timer/with-current-time test-org-timer/time1 (org-timer-stop)) (should-not org-timer-start-time) (should-not org-timer-countdown-timer)) ;; Stop paused countdown timer. (test-org-timer/with-temp-text "" (test-org-timer/with-current-time test-org-timer/time0 (org-timer-set-timer 10)) (test-org-timer/with-current-time test-org-timer/time1 (org-timer-pause-or-continue) (org-timer-stop)) (should-not org-timer-start-time) (should-not org-timer-pause-time) (should-not org-timer-countdown-timer))) (ert-deftest test-org-timer/other-timer-error () "Test for error when other timer running." ;; Relative timer is running. (should-error (test-org-timer/with-temp-text "" (org-timer-start) (org-timer-set-timer 10)) :type (list 'error 'user-error)) ;; Countdown timer is running. (should-error (test-org-timer/with-temp-text "" (org-timer-set-timer 10) (org-timer-start)) :type (list 'error 'user-error))) (ert-deftest test-org-timer/set-timer-from-effort-prop () "Test timer setting from effort property." (should (< (* 60 9) ; 9m (test-org-timer/with-temp-text "* foo :PROPERTIES: :Effort: 10 :END:" (org-mode) (org-timer-set-timer) (org-timer-hms-to-secs (org-timer nil t))) (1+ (* 60 10)) ; 10m 1s ))) (provide 'test-org-timer) ;;; test-org-timer.el end here org-mode-9.7.29+dfsg/testing/lisp/test-org.el000066400000000000000000011740601500430433700207740ustar00rootroot00000000000000;;; test-org.el --- tests for org.el -*- lexical-binding: t -*- ;; Copyright (c) David Maus ;; Authors: David Maus ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;; Template test file for Org tests ;;; Code: (eval-and-compile (require 'cl-lib)) (eval-when-compile (require 'org-macs)) ;For `org-with-gensyms'. (require 'org) (require 'org-inlinetask) (require 'org-refile) (require 'org-agenda) ;;; Helpers (defmacro org-test-with-timezone (tz &rest body) "Evaluate BODY with TZ environment temporary set to the passed value." (declare (indent 1)) (org-with-gensyms (tz-saved) `(let ((,tz-saved (getenv "TZ"))) (unwind-protect (progn (setenv "TZ" ,tz) ,@body) (setenv "TZ" ,tz-saved))))) (defmacro org-test-with-result (result &rest body) "Evaluate BODY, and return buffer content based on RESULT. RESULT is an sexp, and is processed according to the following rules. If RESULT is the quoted symbol `buffer', return buffer string. If RESULT is the quoted symbol `buffer-no-properties', return buffer string with no properties. If RESULT is a positive integer, return the RESULT-th line of the buffer. Otherwise, evaluate RESULT as an sexp and return its result." (declare (indent 1)) `(progn ,@body ,(pcase result (''buffer-no-properties '(buffer-substring-no-properties (point-min) (point-max))) (''buffer '(buffer-string)) ((and (pred numberp) (pred cl-plusp)) `(buffer-substring (line-beginning-position ,result) (line-end-position ,result))) (_ result)))) (defmacro org-test-without-dow (&rest body) "Eval BODY skipping day-of-week in timestamps." (declare (debug (body))) `(let ((org-time-stamp-formats '("%Y-%m-%d" . "%Y-%m-%d %H:%M"))) ,@body)) ;;; Comments (ert-deftest test-org/toggle-comment () "Test `org-toggle-comment' specifications." ;; Simple headline. (should (equal "* Test" (org-test-with-temp-text "* COMMENT Test" (org-toggle-comment) (buffer-string)))) (should (equal "* COMMENT Test" (org-test-with-temp-text "* Test" (org-toggle-comment) (buffer-string)))) ;; Headline with a regular keyword. (should (equal "* TODO Test" (org-test-with-temp-text "* TODO COMMENT Test" (org-toggle-comment) (buffer-string)))) (should (equal "* TODO COMMENT Test" (org-test-with-temp-text "* TODO Test" (org-toggle-comment) (buffer-string)))) ;; Empty headline. (should (equal "* " (org-test-with-temp-text "* COMMENT" (org-toggle-comment) (buffer-string)))) (should (equal "* COMMENT" (org-test-with-temp-text "* " (org-toggle-comment) (buffer-string)))) ;; Headline with a single keyword. (should (equal "* TODO " (org-test-with-temp-text "* TODO COMMENT" (org-toggle-comment) (buffer-string)))) (should (equal "* TODO COMMENT" (org-test-with-temp-text "* TODO" (org-toggle-comment) (buffer-string)))) ;; Headline with a keyword, a priority cookie and contents. (should (equal "* TODO [#A] Headline" (org-test-with-temp-text "* TODO [#A] COMMENT Headline" (org-toggle-comment) (buffer-string)))) (should (equal "* TODO [#A] COMMENT Headline" (org-test-with-temp-text "* TODO [#A] Headline" (org-toggle-comment) (buffer-string))))) (ert-deftest test-org/comment-dwim () "Test `comment-dwim' behaviour in an Org buffer." ;; No region selected, no comment on current line and line not ;; empty: insert comment on line above. (should (equal "# \nComment" (org-test-with-temp-text "Comment" (call-interactively #'org-comment-dwim) (buffer-string)))) ;; No region selected, no comment on current line and line empty: ;; insert comment on this line. (should (equal "# \nParagraph" (org-test-with-temp-text "\nParagraph" (call-interactively #'org-comment-dwim) (buffer-string)))) ;; No region selected, and a comment on this line: indent it. (should (equal "* Headline\n # Comment" (org-test-with-temp-text "* Headline\n# Comment" (let ((org-adapt-indentation t)) (call-interactively #'org-comment-dwim)) (buffer-string)))) ;; Also recognize single # at column 0 as comments. (should (equal "# Comment" (org-test-with-temp-text "# Comment" (call-interactively #'org-comment-dwim) (buffer-string)))) ;; Region selected and only comments and blank lines within it: ;; un-comment all commented lines. (should (equal "Comment 1\n\nComment 2" (org-test-with-temp-text "# Comment 1\n\n# Comment 2" (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (call-interactively #'org-comment-dwim) (buffer-string)))) ;; Region selected without comments: comment all lines if ;; `comment-empty-lines' is non-nil, only non-blank lines otherwise. (should (equal "# Comment 1\n\n# Comment 2" (org-test-with-temp-text "Comment 1\n\nComment 2" (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (let ((comment-empty-lines nil)) (call-interactively #'org-comment-dwim)) (buffer-string)))) (should (equal "# Comment 1\n# \n# Comment 2" (org-test-with-temp-text "Comment 1\n\nComment 2" (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (let ((comment-empty-lines t)) (call-interactively #'org-comment-dwim)) (buffer-string)))) ;; In front of a keyword without region, insert a new comment. (should (equal "# \n#+KEYWORD: value" (org-test-with-temp-text "#+KEYWORD: value" (call-interactively #'org-comment-dwim) (buffer-string)))) ;; Comment a heading (should (equal "* COMMENT Test" (org-test-with-temp-text "* Test" (call-interactively #'org-comment-dwim) (buffer-string)))) ;; Uncomment a heading (should (equal "* Test" (org-test-with-temp-text "* COMMENT Test" (call-interactively #'org-comment-dwim) (buffer-string)))) ;; Comment an inlinetask (should (equal "*** COMMENT Test" (let ((org-inlinetask-min-level 3)) (org-test-with-temp-text "*** Test" (call-interactively #'org-comment-dwim) (buffer-string))))) ;; Uncomment an inlinetask (should (equal "*** Test" (let ((org-inlinetask-min-level 3)) (org-test-with-temp-text "*** COMMENT Test" (call-interactively #'org-comment-dwim) (buffer-string))))) ;; In a source block, use appropriate syntax. (should (equal " ;; " (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n\n#+END_SRC" (let ((org-edit-src-content-indentation 2)) (call-interactively #'org-comment-dwim)) (buffer-substring-no-properties (line-beginning-position) (point))))) (should (equal "#+BEGIN_SRC emacs-lisp\n ;; a\n ;; b\n#+END_SRC" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\na\nb\n#+END_SRC" (transient-mark-mode 1) (push-mark (point) t t) (forward-line 2) (let ((org-edit-src-content-indentation 2)) (call-interactively #'org-comment-dwim)) (buffer-string))))) ;;; Date and time analysis (ert-deftest test-org/org-encode-time () "Test various ways to call `org-encode-time'" (org-test-with-timezone "UTC" ;; list as the sole argument (should (string-equal "2022-03-24 23:30:01" (format-time-string "%F %T" (org-encode-time '(1 30 23 24 3 2022 nil -1 nil))))) ;; SECOND...YEAR (should (string-equal "2022-03-24 23:30:02" (format-time-string "%F %T" (org-encode-time 2 30 23 24 3 2022)))) ;; SECOND...YEAR IGNORED DST ZONE (should (string-equal "2022-03-24 23:30:03" (format-time-string "%F %T" (org-encode-time 3 30 23 24 3 2022 nil -1 nil)))) ;; function call (should (string-equal "2022-03-24 23:30:04" (format-time-string "%F %T" (org-encode-time (apply #'list 4 30 23 '(24 3 2022 nil -1 nil)))))) ;; wrong number of arguments (if (not (version< emacs-version "27.1")) (should-error (string-equal "2022-03-24 23:30:05" (format-time-string "%F %T" (org-encode-time 5 30 23 24 3 2022 nil)))))) ;; daylight saving time (if (not (version< emacs-version "27.1")) ;; DST value is not ignored for multiple arguments unlike for `encode-time' (should (string-equal "2022-04-01 00:30:06 +0200 CEST" (format-time-string "%F %T %z %Z" (org-encode-time 6 30 23 31 3 2022 nil nil "Europe/Madrid") "Europe/Madrid"))) (should (string-equal "2022-03-31 23:30:07 +0200 CEST" (format-time-string "%F %T %z %Z" (org-encode-time 7 30 23 31 3 2022 nil t "Europe/Madrid") "Europe/Madrid")))) (org-test-with-timezone "Europe/Madrid" ;; Standard time is not forced when DST is not specified (should (string-equal "2022-03-31 23:30:08" (format-time-string "%F %T" (org-encode-time 8 30 23 31 3 2022)))))) (ert-deftest test-org/org-time-string-to-time () "Test `org-time-string-to-time' around DST transition." (org-test-with-timezone "UTC" (should (string-equal "2022-03-31 23:31:00" (format-time-string "%F %T" (org-time-string-to-time "2022-03-31 23:31"))))) (org-test-with-timezone "Europe/Madrid" (should (string-equal "2022-03-24 23:32:00 +0100 CET" (format-time-string "%F %T %z %Z" (org-time-string-to-time "2022-03-24 23:32")))) (should (string-equal "2022-03-31 23:33:00 +0200 CEST" (format-time-string "%F %T %z %Z" (org-time-string-to-time "2022-03-31 23:33")))))) (ert-deftest test-org/org-read-date () "Test `org-read-date' specifications." (defvar org-time-was-given) ; dynamically scoped parameter ;; Parse ISO date with abbreviated year and month. (should (equal "2012-03-29 16:40" (let ((org-time-was-given t)) (org-read-date t nil "12-3-29 16:40")))) ;; Parse Europeans dates. (should (equal "2012-03-29 16:40" (let ((org-time-was-given t)) (org-read-date t nil "29.03.2012 16:40")))) ;; Parse Europeans dates without year. (should (string-match "2[0-9]\\{3\\}-03-29 16:40" (let ((org-time-was-given t)) (org-read-date t nil "29.03. 16:40")))) ;; Relative date applied to current time if there is single ;; plus/minus, or to default date when there are two of them. (should (equal "2015-03-04" (org-test-at-time "2014-03-04" (org-read-date t nil "+1y" nil (org-time-string-to-time "2012-03-29"))))) (should (equal "2013-03-29" (org-test-at-time "2014-03-04" (org-read-date t nil "++1y" nil (org-time-string-to-time "2012-03-29"))))) ;; When `org-read-date-prefer-future' is non-nil, prefer future ;; dates (relatively to now) when incomplete. Otherwise, use ;; default date. (should (equal "2014-04-01" (org-test-at-time "2014-03-04" (let ((org-read-date-prefer-future t)) (org-read-date t nil "1"))))) (should (equal "2013-03-04" (org-test-at-time "2012-03-29" (let ((org-read-date-prefer-future t)) (org-read-date t nil "3-4"))))) (should (equal "2012-03-04" (org-test-at-time "2012-03-29" (let ((org-read-date-prefer-future nil)) (org-read-date t nil "3-4"))))) ;; When set to `org-read-date-prefer-future' is set to `time', read ;; day is moved to tomorrow if specified hour is before current ;; time. However, it only happens in no other part of the date is ;; specified. (should (equal "2012-03-30" (org-test-at-time "2012-03-29 16:40" (let ((org-read-date-prefer-future 'time)) (org-read-date t nil "00:40" nil))))) (should-not (equal "2012-03-30" (org-test-at-time "2012-03-29 16:40" (let ((org-read-date-prefer-future 'time)) (org-read-date t nil "29 00:40" nil))))) ;; Caveat: `org-read-date-prefer-future' always refers to current ;; time, not default time, when they differ. (should (equal "2014-04-01" (org-test-at-time "2014-03-04" (let ((org-read-date-prefer-future t)) (org-read-date t nil "1" nil (org-time-string-to-time "2012-03-29")))))) (should (equal "2014-03-25" (org-test-at-time "2014-03-04" (let ((org-read-date-prefer-future t)) (org-read-date t nil "25" nil (org-time-string-to-time "2012-03-29"))))))) (ert-deftest test-org/org-parse-time-string () "Test `org-parse-time-string'." (should (equal (org-parse-time-string "2012-03-29 16:40") '(0 40 16 29 3 2012 nil -1 nil))) (should (equal (org-parse-time-string "[2012-03-29 16:40]") '(0 40 16 29 3 2012 nil -1 nil))) (should (equal (org-parse-time-string "<2012-03-29 16:40>") '(0 40 16 29 3 2012 nil -1 nil))) (should (equal (org-parse-time-string "<2012-03-29>") '(0 0 0 29 3 2012 nil -1 nil))) (should (equal (org-parse-time-string "<2012-03-29>" t) '(0 nil nil 29 3 2012 nil -1 nil)))) (ert-deftest test-org/closest-date () "Test `org-closest-date' specifications." (require 'calendar) ;; Time stamps without a repeater are returned unchanged. (should (equal '(3 29 2012) (calendar-gregorian-from-absolute (org-closest-date "<2012-03-29>" "<2014-03-04>" nil)))) ;; Time stamps with a null repeater are returned unchanged. (should (equal '(3 29 2012) (calendar-gregorian-from-absolute (org-closest-date "<2012-03-29 +0d>" "<2014-03-04>" nil)))) ;; if PREFER is set to `past' always return a date before, or equal ;; to CURRENT. (should (equal '(3 1 2014) (calendar-gregorian-from-absolute (org-closest-date "<2012-03-29 +1m>" "<2014-03-04>" 'past)))) (should (equal '(3 4 2014) (calendar-gregorian-from-absolute (org-closest-date "<2012-03-04 +1m>" "<2014-03-04>" 'past)))) ;; if PREFER is set to `future' always return a date before, or equal ;; to CURRENT. (should (equal '(3 29 2014) (calendar-gregorian-from-absolute (org-closest-date "<2012-03-29 +1m>" "<2014-03-04>" 'future)))) (should (equal '(3 4 2014) (calendar-gregorian-from-absolute (org-closest-date "<2012-03-04 +1m>" "<2014-03-04>" 'future)))) ;; If PREFER is neither `past' nor `future', select closest date. (should (equal '(3 1 2014) (calendar-gregorian-from-absolute (org-closest-date "<2012-03-29 +1m>" "<2014-03-04>" nil)))) (should (equal '(5 4 2014) (calendar-gregorian-from-absolute (org-closest-date "<2012-03-04 +1m>" "<2014-04-28>" nil)))) ;; Test "day" repeater. (should (equal '(3 8 2014) (calendar-gregorian-from-absolute (org-closest-date "<2014-03-04 +2d>" "<2014-03-09>" 'past)))) (should (equal '(3 10 2014) (calendar-gregorian-from-absolute (org-closest-date "<2014-03-04 +2d>" "<2014-03-09>" 'future)))) ;; Test "month" repeater. (should (equal '(1 5 2015) (calendar-gregorian-from-absolute (org-closest-date "<2014-03-05 +2m>" "<2015-02-04>" 'past)))) (should (equal '(3 29 2014) (calendar-gregorian-from-absolute (org-closest-date "<2012-03-29 +2m>" "<2014-03-04>" 'future)))) ;; Test "year" repeater. (should (equal '(3 5 2014) (calendar-gregorian-from-absolute (org-closest-date "<2014-03-05 +2y>" "<2015-02-04>" 'past)))) (should (equal '(3 29 2014) (calendar-gregorian-from-absolute (org-closest-date "<2012-03-29 +2y>" "<2014-03-04>" 'future))))) (ert-deftest test-org/deadline-close-p () "Test `org-deadline-close-p' specifications." (org-test-at-time "2016-06-03 Fri 01:43" ;; Timestamps are close if they are within `ndays' of lead time. (org-test-with-temp-text "* Heading" (should (org-deadline-close-p "2016-06-03 Fri" 0)) (should (org-deadline-close-p "2016-06-02 Thu" 0)) (should-not (org-deadline-close-p "2016-06-04 Sat" 0)) (should (org-deadline-close-p "2016-06-04 Sat" 1)) (should (org-deadline-close-p "2016-06-03 Fri 12:00" 0))) ;; Read `ndays' from timestamp if argument not given. (org-test-with-temp-text "* H" (should (org-deadline-close-p "2016-06-04 Sat -1d")) (should-not (org-deadline-close-p "2016-06-04 Sat -0d")) (should (org-deadline-close-p "2016-06-10 Fri -1w")) (should-not (org-deadline-close-p "2016-06-11 Sat -1w"))) ;; Prefer `ndays' argument over lead time in timestamp. (org-test-with-temp-text "* H" (should (org-deadline-close-p "2016-06-04 Sat -0d" 1)) (should-not (org-deadline-close-p "2016-06-04 Sat -0d" 0))) ;; Completed tasks are never close. (let ((org-todo-keywords '(("TODO" "|" "DONE")))) (org-test-with-temp-text "* TODO Heading" (should (org-deadline-close-p "2016-06-03"))) (org-test-with-temp-text "* DONE Heading" (should-not (org-deadline-close-p "2016-06-03")))))) ;;; Drawers (ert-deftest test-org/at-property-p () "Test `org-at-property-p' specifications." (should (equal 't (org-test-with-temp-text "* H\n:PROPERTIES:\n:PROP: t\n:END:\n" (org-at-property-p)))) (should (equal 't (org-test-with-temp-text ":PROPERTIES:\n:PROP: t\n:END:\n" (org-at-property-p))))) (ert-deftest test-org/at-property-drawer-p () "Test `org-at-property-drawer-p' specifications." (should (org-test-with-temp-text "* H\n:PROPERTIES:\n:PROP: t\n:END:\n" (org-at-property-drawer-p))) (should (org-test-with-temp-text ":PROPERTIES:\n:PROP: t\n:END:\n" (org-at-property-drawer-p))) ;; The function only returns t if point is at the first line of ;; a property block. (should-not (org-test-with-temp-text ":PROPERTIES:\n:PROP: t\n:END:\n" (org-at-property-drawer-p))) ;; The function ignores incomplete drawers. (should-not (org-test-with-temp-text ":PROPERTIES:\n:PROP: t\n" (org-at-property-drawer-p))) ;; tab separating the value. (should (org-test-with-temp-text ":PROPERTIES:\n:PROP: t\n:END:\n" (org-at-property-drawer-p)))) (ert-deftest test-org/get-property-block () "Test `org-get-property-block' specifications." (should (equal '(14 . 14) (org-test-with-temp-text ":PROPERTIES:\n:END:\n* H\n" (org-get-property-block)))) (should (equal '(14 . 14) (org-test-with-temp-text ":PROPERTIES:\n:END:\n" (org-get-property-block)))) ;; Comments above a document property block is ok. (should (equal '(18 . 18) (org-test-with-temp-text "# C\n:PROPERTIES:\n:END:\n" (org-get-property-block)))) ;; Keywords above a document property block is ok. (should (equal '(22 . 22) (org-test-with-temp-text "# C\n# C\n:PROPERTIES:\n:END:\n" (org-get-property-block)))) ;; Comments and keywords are allowed before a document property block. (should (equal '(18 . 27) (org-test-with-temp-text "# C\n:PROPERTIES:\n:KEY: V:\n:END:\n" (org-get-property-block)))) ;; A document property block will not be valid if there are lines ;; with whitespace above it (should-not (org-test-with-temp-text "\n:PROPERTIES:\n:END:\n" (org-get-property-block))) (should (equal '(18 . 18) (org-test-with-temp-text "* H\n:PROPERTIES:\n:END:\n" (org-get-property-block)))) (should (equal "* H\n:PROPERTIES:\n:END:\n" (org-test-with-temp-text "* H" (let ((org-adapt-indentation nil)) (org-get-property-block nil 'force)) (buffer-string)))) (should (equal ":PROPERTIES:\n:END:\n" (org-test-with-temp-text "" (org-get-property-block nil 'force) (buffer-string)))) (should (equal "* H1\n :PROPERTIES:\n :END:\n* H2" (org-test-with-temp-text "* H1\n* H2" (let ((org-adapt-indentation t)) (org-get-property-block nil 'force)) (buffer-string))))) (ert-deftest test-org/insert-property-drawer () "Test `org-insert-property-drawer' specifications." ;; Insert drawer in empty buffer (should (equal ":PROPERTIES:\n:END:\n" (org-test-with-temp-text "" (let ((org-adapt-indentation nil)) (org-insert-property-drawer)) (buffer-string)))) ;; Insert drawer in document header with existing comment and ;; keyword. (should (equal "# C\n:PROPERTIES:\n:END:\n#+TITLE: T" (org-test-with-temp-text "# C\n#+TITLE: T" (let ((org-adapt-indentation nil)) (org-insert-property-drawer)) (buffer-string)))) ;; Insert drawer in document header with existing keyword. (should (equal ":PROPERTIES:\n:END:\n#+TITLE: T" (org-test-with-temp-text "#+TITLE: T" (let ((org-adapt-indentation nil)) (org-insert-property-drawer)) (buffer-string)))) (should (equal ":PROPERTIES:\n:END:" (org-test-with-temp-text ":PROPERTIES:\n:END:" (let ((org-adapt-indentation nil)) (org-insert-property-drawer)) (buffer-string)))) ;; Insert drawer in document header with one existing heading in buffer. (should (equal ":PROPERTIES:\n:END:\n\n* T\n" (org-test-with-temp-text "\n* T\n" (let ((org-adapt-indentation nil)) (org-insert-property-drawer)) (buffer-string)))) ;; Insert drawer right after headline if there is no planning line, ;; or after it otherwise. (should (equal "* H\n:PROPERTIES:\n:END:\nParagraph" (org-test-with-temp-text "* H\nParagraph" (let ((org-adapt-indentation nil)) (org-insert-property-drawer)) (buffer-string)))) (should (equal "* H\nDEADLINE: <2014-03-04 tue.>\n:PROPERTIES:\n:END:\nParagraph" (org-test-with-temp-text "* H\nDEADLINE: <2014-03-04 tue.>\nParagraph" (let ((org-adapt-indentation nil)) (org-insert-property-drawer)) (buffer-string)))) ;; Indent inserted drawer. (should (equal "* H\n :PROPERTIES:\n :END:\nParagraph" (org-test-with-temp-text "* H\nParagraph" (let ((org-adapt-indentation t)) (org-insert-property-drawer)) (buffer-string)))) ;; Handle insertion at eob. (should (equal "* H\n:PROPERTIES:\n:END:\n" (org-test-with-temp-text "* H" (let ((org-adapt-indentation nil)) (org-insert-property-drawer)) (buffer-string)))) ;; Skip inlinetasks before point. (when (featurep 'org-inlinetask) (should (equal "* H\n:PROPERTIES:\n:END:\n*************** I\n*************** END\nP" (org-test-with-temp-text "* H\n*************** I\n*************** END\nP" (let ((org-adapt-indentation nil) (org-inlinetask-min-level 15)) (org-insert-property-drawer)) (buffer-string))))) ;; Correctly set drawer in an inlinetask. (when (featurep 'org-inlinetask) (should (equal "* H\n*************** I\n:PROPERTIES:\n:END:\nP\n*************** END" (org-test-with-temp-text "* H\n*************** I\nP\n*************** END" (let ((org-adapt-indentation nil) (org-inlinetask-min-level 15)) (org-insert-property-drawer)) (buffer-string)))))) ;;; Filling (ert-deftest test-org/fill-element () "Test `org-fill-element' specifications." ;; At an Org table, align it. (should (equal "| a |\n" (org-test-with-temp-text "|a|" (org-fill-element) (buffer-string)))) (should (equal "#+name: table\n| a |\n" (org-test-with-temp-text "#+name: table\n| a |\n" (org-fill-element) (buffer-string)))) ;; At a paragraph, preserve line breaks. (org-test-with-temp-text "some \\\\\nlong\ntext" (let ((fill-column 20)) (org-fill-element) (should (equal (buffer-string) "some \\\\\nlong text")))) ;; Correctly fill a paragraph when point is at its very end. (should (equal "A B" (org-test-with-temp-text "A\nB" (let ((fill-column 20)) (goto-char (point-max)) (org-fill-element) (buffer-string))))) ;; Correctly fill the last paragraph of a greater element. (should (equal "#+BEGIN_CENTER\n- 012345\n 789\n#+END_CENTER" (org-test-with-temp-text "#+BEGIN_CENTER\n- 012345 789\n#+END_CENTER" (let ((fill-column 8)) (forward-line) (end-of-line) (org-fill-element) (buffer-string))))) ;; Correctly fill an element in a narrowed buffer. (should (equal "01234\n6" (org-test-with-temp-text "01234 6789" (let ((fill-column 5)) (narrow-to-region 1 8) (org-fill-element) (buffer-string))))) ;; Handle `adaptive-fill-regexp' in paragraphs. (should (equal "> a b" (org-test-with-temp-text "> a\n> b" (let ((fill-column 5) (adaptive-fill-regexp "[ \t]*>+[ \t]*")) (org-fill-element) (buffer-string))))) ;; Special case: Fill first paragraph when point is at an item or ;; a plain-list or a footnote reference. (should (equal "- A B" (org-test-with-temp-text "- A\n B" (let ((fill-column 20)) (org-fill-element) (buffer-string))))) (should (equal "[fn:1] A B" (org-test-with-temp-text "[fn:1] A\nB" (let ((fill-column 20)) (org-fill-element) (buffer-string))))) (org-test-with-temp-text "#+BEGIN_VERSE\nSome \\\\\nlong\ntext\n#+END_VERSE" (let ((fill-column 20)) (org-fill-element) (should (equal (buffer-string) "#+BEGIN_VERSE\nSome \\\\\nlong\ntext\n#+END_VERSE")))) ;; Fill contents of `comment-block' elements. (should (equal (org-test-with-temp-text "#+BEGIN_COMMENT\nSome\ntext\n#+END_COMMENT" (let ((fill-column 20)) (forward-line) (org-fill-element) (buffer-string))) "#+BEGIN_COMMENT\nSome text\n#+END_COMMENT")) ;; Fill `comment' elements. (should (equal " # A B" (org-test-with-temp-text " # A\n # B" (let ((fill-column 20)) (org-fill-element) (buffer-string))))) ;; Do not mix consecutive comments when filling one of them. (should (equal "# A B\n\n# C" (org-test-with-temp-text "# A\n# B\n\n# C" (let ((fill-column 20)) (org-fill-element) (buffer-string))))) ;; Use commented empty lines as separators when filling comments. (should (equal "# A B\n#\n# C" (org-test-with-temp-text "# A\n# B\n#\n# C" (let ((fill-column 20)) (org-fill-element) (buffer-string))))) ;; Handle `adaptive-fill-regexp' in comments. (should (equal "# > a b" (org-test-with-temp-text "# > a\n# > b" (let ((fill-column 20) (adaptive-fill-regexp "[ \t]*>+[ \t]*")) (org-fill-element) (buffer-string))))) ;; Do nothing at affiliated keywords. (should (equal "#+NAME: para\nSome\ntext." (org-test-with-temp-text "#+NAME: para\nSome\ntext." (let ((fill-column 20)) (org-fill-element) (buffer-string))))) ;; Do not move point after table when filling a table. (should-not (org-test-with-temp-text "| a | b |\n| c | d |\n" (forward-char) (org-fill-element) (eobp))) ;; Do not fill "n" macro, with or without arguments, followed by ;; a dot or a closing parenthesis since it could be confused with ;; a numbered bullet. (should-not (equal "123456789\n{{{n}}}." (org-test-with-temp-text "123456789 {{{n}}}." (let ((fill-column 10)) (org-fill-element) (buffer-string))))) (should-not (equal "123456789\n{{{n}}}\)" (org-test-with-temp-text "123456789 {{{n}}}\)" (let ((fill-column 10)) (org-fill-element) (buffer-string))))) (should-not (equal "123456789\n{{{n()}}}." (org-test-with-temp-text "123456789 {{{n()}}}." (let ((fill-column 10)) (org-fill-element) (buffer-string))))) (should-not (equal "123456789\n{{{n(counter)}}}." (org-test-with-temp-text "123456789 {{{n(counter)}}}." (let ((fill-column 10)) (org-fill-element) (buffer-string)))))) (ert-deftest test-org/fill-paragraph () "Test `org-fill-paragraph' specifications." ;; Regular test. (should (equal "012345678\n9" (org-test-with-temp-text "012345678 9" (let ((fill-column 10)) (org-fill-paragraph) (buffer-string))))) ;; Fill paragraph even at end of buffer. (should (equal "012345678\n9\n" (org-test-with-temp-text "012345678 9\n" (let ((fill-column 10)) (org-fill-paragraph) (buffer-string))))) ;; Between two paragraphs, fill the next one. (should (equal "012345678 9\n\n012345678\n9" (org-test-with-temp-text "012345678 9\n\n012345678 9" (let ((fill-column 10)) (org-fill-paragraph) (buffer-string))))) (should (equal "012345678\n9\n\n012345678 9" (org-test-with-temp-text "012345678 9\n\n012345678 9" (let ((fill-column 10)) (org-fill-paragraph) (buffer-string))))) ;; Fill paragraph in a comment block. (should (equal "#+begin_comment\n012345678\n9\n#+end_comment" (org-test-with-temp-text "#+begin_comment\n012345678 9\n#+end_comment" (let ((fill-column 10)) (org-fill-paragraph) (buffer-string))))) ;; When a region is selected, fill every paragraph in the region. (should (equal "012345678\n9\n\n012345678\n9" (org-test-with-temp-text "012345678 9\n\n012345678 9" (let ((fill-column 10)) (transient-mark-mode 1) (push-mark (point-min) t t) (goto-char (point-max)) (call-interactively #'org-fill-paragraph) (buffer-string))))) (should (equal "012345678\n9\n\n012345678 9" (org-test-with-temp-text "012345678 9\n\n012345678 9" (let ((fill-column 10)) (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-min)) (call-interactively #'org-fill-paragraph) (buffer-string))))) (should (equal "012345678 9\n\n012345678\n9" (org-test-with-temp-text "012345678 9\n\n012345678 9" (let ((fill-column 10)) (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (call-interactively #'org-fill-paragraph) (buffer-string))))) ;; Fill every list item in a region (should (equal "\n- 2345678\n 9\n- 2345678\n 9" (org-test-with-temp-text "\n- 2345678 9\n- 2345678 9" (let ((fill-column 10)) (transient-mark-mode 1) (push-mark (point-min) t t) (goto-char (point-max)) (call-interactively #'org-fill-paragraph) (buffer-string))))) (should (equal "\n- 2345678\n 9\n- 2345678" (org-test-with-temp-text "\n- 2345678 9\n- 2345678" (let ((fill-column 10)) (transient-mark-mode 1) (push-mark (point-min) t t) (goto-char (point-max)) (call-interactively #'org-fill-paragraph) (buffer-string)))))) (ert-deftest test-org/fill-region () "Test `fill-region' behaviour." ;; fill-region should fill every item of a list (should (equal "\n- 2345678\n 9\n- 2345678\n 9" (org-test-with-temp-text "\n- 2345678 9\n- 2345678 9" (let ((fill-column 10)) (transient-mark-mode 1) (push-mark (point-min) t t) (goto-char (point-max)) (call-interactively #'fill-region) (buffer-string))))) (should (equal "\n- 1 2\n- 1 2" (org-test-with-temp-text "\n- 1\n 2\n- 1\n 2" (let ((fill-column 10)) (transient-mark-mode 1) (push-mark (point-min) t t) (goto-char (point-max)) (call-interactively #'fill-region) (buffer-string))))) ) (ert-deftest test-org/auto-fill-function () "Test auto-filling features." ;; Auto fill paragraph. (should (equal "12345\n7890" (org-test-with-temp-text "12345 7890" (let ((fill-column 5)) (end-of-line) (org-auto-fill-function) (buffer-string))))) ;; Auto fill first paragraph in an item. (should (equal "- 12345\n 7890" (org-test-with-temp-text "- 12345 7890" (let ((fill-column 7)) (end-of-line) (org-auto-fill-function) (buffer-string))))) ;; Auto fill paragraph when `adaptive-fill-regexp' matches. (should (equal "> 12345\n 7890" (org-test-with-temp-text "> 12345 7890" (let ((fill-column 10) (adaptive-fill-regexp "[ \t]*>+[ \t]*") (adaptive-fill-first-line-regexp "\\`[ ]*\\'")) (end-of-line) (org-auto-fill-function) (buffer-string))))) (should (equal "> 12345\n> 12345\n> 7890" (org-test-with-temp-text "> 12345\n> 12345 7890" (let ((fill-column 10) (adaptive-fill-regexp "[ \t]*>+[ \t]*")) (goto-char (point-max)) (org-auto-fill-function) (buffer-string))))) (should-not (equal " 12345\n *12345\n *12345" (org-test-with-temp-text " 12345\n *12345 12345" (let ((fill-column 10) (adaptive-fill-regexp "[ \t]*>+[ \t]*")) (goto-char (point-max)) (org-auto-fill-function) (buffer-string))))) ;; Auto fill comments. (should (equal " # 12345\n # 7890" (org-test-with-temp-text " # 12345 7890" (let ((fill-column 10)) (end-of-line) (org-auto-fill-function) (buffer-string))))) ;; A hash within a line isn't a comment. (should-not (equal "12345 # 7890\n# 1" (org-test-with-temp-text "12345 # 7890 1" (let ((fill-column 12)) (end-of-line) (org-auto-fill-function) (buffer-string))))) ;; Correctly interpret empty prefix. (should-not (equal "# a\n# b\nRegular\n# paragraph" (org-test-with-temp-text "# a\n# b\nRegular paragraph" (let ((fill-column 12)) (end-of-line 3) (org-auto-fill-function) (buffer-string))))) ;; Do not fill VERSE blocks. (should-not (equal "#+BEGIN_VERSE\n12345\n7890\n#+END_VERSE" (org-test-with-temp-text "#+BEGIN_VERSE\n12345 7890\n#+END_VERSE" (let ((fill-column 5)) (forward-line) (end-of-line) (org-auto-fill-function) (buffer-string))))) ;; Comment block: auto fill contents. (should (equal "#+BEGIN_COMMENT\n12345\n7890\n#+END_COMMENT" (org-test-with-temp-text "#+BEGIN_COMMENT\n12345 7890\n#+END_COMMENT" (let ((fill-column 5)) (forward-line) (end-of-line) (org-auto-fill-function) (buffer-string))))) ;; Do not fill if a new item could be created. (should-not (equal "12345\n- 90" (org-test-with-temp-text "12345 - 90" (let ((fill-column 5)) (end-of-line) (org-auto-fill-function) (buffer-string))))) ;; Do not fill if a line break could be introduced. (should-not (equal "123\\\\\n7890" (org-test-with-temp-text "123\\\\ 7890" (let ((fill-column 6)) (end-of-line) (org-auto-fill-function) (buffer-string))))) ;; Do not fill affiliated keywords. (should-not (equal "#+ATTR_LATEX: ABC\nDEFGHIJKL" (org-test-with-temp-text "#+ATTR_LATEX: ABC DEFGHIJKL" (let ((fill-column 20)) (end-of-line) (org-auto-fill-function) (buffer-string)))))) ;;; Indentation (ert-deftest test-org/indent-line () "Test `org-indent-line' specifications." ;; Do not indent diary sexps, footnote definitions or headlines. (should (zerop (org-test-with-temp-text "%%(org-calendar-holiday)" (org-indent-line) (org-get-indentation)))) (should (zerop (org-test-with-temp-text "[fn:1] fn" (let ((org-adapt-indentation t)) (org-indent-line)) (org-get-indentation)))) (should (zerop (org-test-with-temp-text "* H" (org-indent-line) (org-get-indentation)))) ;; Do not indent before first headline. (should (zerop (org-test-with-temp-text "" (org-indent-line) (org-get-indentation)))) ;; Indent according to headline level otherwise, unless ;; `org-adapt-indentation' is nil. (should (= 2 (org-test-with-temp-text "* H\nA" (let ((org-adapt-indentation t)) (org-indent-line)) (org-get-indentation)))) (should (= 2 (org-test-with-temp-text "* H\n\nA" (let ((org-adapt-indentation t)) (org-indent-line)) (org-get-indentation)))) (should (zerop (org-test-with-temp-text "* H\nA" (let ((org-adapt-indentation nil)) (org-indent-line)) (org-get-indentation)))) ;; Indenting preserves point position. (should (org-test-with-temp-text "* H\nAB" (let ((org-adapt-indentation t)) (org-indent-line)) (looking-at "B"))) ;; Do not change indentation at an item or a LaTeX environment. (should (= 1 (org-test-with-temp-text "* H\n - A" (let ((org-adapt-indentation t)) (org-indent-line)) (org-get-indentation)))) (should (= 1 (org-test-with-temp-text "\\begin{equation}\n 1+1=2\n\\end{equation}" (org-indent-line) (org-get-indentation)))) ;; On blank lines at the end of a list, indent like last element ;; within it if the line is still in the list. If the last element ;; is an item, indent like its contents. Otherwise, indent like the ;; whole list. (should (= 4 (org-test-with-temp-text "* H\n- A\n - AA\n" (let ((org-adapt-indentation t)) (org-indent-line)) (org-get-indentation)))) (should (= 4 (org-test-with-temp-text "* H\n- A\n -\n\n" (let ((org-adapt-indentation t)) (org-indent-line)) (org-get-indentation)))) (should (zerop (org-test-with-temp-text "* H\n- A\n - AA\n\n\n\n" (let ((org-adapt-indentation t)) (org-indent-line)) (org-get-indentation)))) (should (= 4 (org-test-with-temp-text "* H\n- A\n - \n" (let ((org-adapt-indentation t)) (org-indent-line)) (org-get-indentation)))) (should (= 4 (org-test-with-temp-text "* H\n - \n #+BEGIN_SRC emacs-lisp\n t\n #+END_SRC\n" (let ((org-adapt-indentation t)) (org-indent-line)) (org-get-indentation)))) (should (= 2 (org-test-with-temp-text "- A\n B\n\n" (let ((org-adapt-indentation nil)) (org-indent-line)) (org-get-indentation)))) (should (= 2 (org-test-with-temp-text "- A\n \begin{cases} 1 + 1\n \end{cases}\n\n" (let ((org-adapt-indentation nil)) (org-indent-line)) (org-get-indentation)))) ;; Likewise, on a blank line at the end of a footnote definition, ;; indent at column 0 if line belongs to the definition. Otherwise, ;; indent like the definition itself. (should (zerop (org-test-with-temp-text "* H\n[fn:1] Definition\n" (let ((org-adapt-indentation t)) (org-indent-line)) (org-get-indentation)))) (should (zerop (org-test-with-temp-text "* H\n[fn:1] Definition\n\n\n\n" (let ((org-adapt-indentation t)) (org-indent-line)) (org-get-indentation)))) ;; After the end of the contents of a greater element, indent like ;; the beginning of the element. (should (= 1 (org-test-with-temp-text " #+BEGIN_CENTER\n Contents\n#+END_CENTER" (org-indent-line) (org-get-indentation)))) ;; On blank lines after a paragraph, indent like its last non-empty ;; line. (should (= 1 (org-test-with-temp-text " Paragraph\n\n" (org-indent-line) (org-get-indentation)))) ;; At the first line of an element, indent like previous element's ;; first line, ignoring footnotes definitions and inline tasks, or ;; according to parent. (let ((org-adapt-indentation t)) (should (= 2 (org-test-with-temp-text "A\n\n B\n\nC" (org-indent-line) (org-get-indentation)))) (should (= 1 (org-test-with-temp-text " A\n\n[fn:1] B\n\n\nC" (org-indent-line) (org-get-indentation)))) (should (= 1 (org-test-with-temp-text " #+BEGIN_CENTER\n Contents\n#+END_CENTER" (org-indent-line) (org-get-indentation))))) ;; Within code part of a source block, use language major mode if ;; `org-src-tab-acts-natively' is non-nil. Otherwise, indent ;; according to line above. (should (= 6 (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n (and A\nB)\n#+END_SRC" (let ((org-src-tab-acts-natively t) (org-edit-src-content-indentation 0)) (org-indent-line)) (org-get-indentation)))) (should (= 1 (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n (and A\nB)\n#+END_SRC" (let ((org-src-tab-acts-natively nil) (org-edit-src-content-indentation 0)) (org-indent-line)) (org-get-indentation)))) ;; Otherwise, indent like the first non-blank line above. (should (zerop (org-test-with-temp-text "#+BEGIN_CENTER\nline1\n\n line2\n#+END_CENTER" (org-indent-line) (org-get-indentation)))) ;; Align node properties according to `org-property-format'. Handle ;; nicely empty values. (should (equal "* H\n:PROPERTIES:\n:key: value\n:END:" (org-test-with-temp-text "* H\n:PROPERTIES:\n:key: value\n:END:" (let ((org-property-format "%-10s %s")) (org-indent-line)) (buffer-string)))) (should (equal "* H\n:PROPERTIES:\n:key:\n:END:" (org-test-with-temp-text "* H\n:PROPERTIES:\n:key:\n:END:" (let ((org-property-format "%-10s %s")) (org-indent-line)) (buffer-string))))) (ert-deftest test-org/indent-region () "Test `org-indent-region' specifications." ;; Indent paragraph. (let ((org-adapt-indentation t)) (should (equal "A\nB\nC" (org-test-with-temp-text " A\nB\n C" (org-indent-region (point-min) (point-max)) (buffer-string))))) ;; Indent greater elements along with their contents. (should (equal "#+BEGIN_CENTER\nA\nB\n#+END_CENTER" (org-test-with-temp-text "#+BEGIN_CENTER\n A\n B\n#+END_CENTER" (org-indent-region (point-min) (point-max)) (buffer-string)))) ;; Ignore contents of verse blocks. Only indent block delimiters. (should (equal "#+BEGIN_VERSE\n A\n B\n#+END_VERSE" (org-test-with-temp-text "#+BEGIN_VERSE\n A\n B\n#+END_VERSE" (org-indent-region (point-min) (point-max)) (buffer-string)))) (let ((org-adapt-indentation t)) (should (equal "#+BEGIN_VERSE\n A\n B\n#+END_VERSE" (org-test-with-temp-text " #+BEGIN_VERSE\n A\n B\n #+END_VERSE" (org-indent-region (point-min) (point-max)) (buffer-string))))) ;; Indent example blocks as a single block, unless indentation ;; should be preserved. In this case only indent the block markers. (should (equal "#+BEGIN_EXAMPLE\n A\n B\n#+END_EXAMPLE" (org-test-with-temp-text "#+BEGIN_EXAMPLE\n A\n B\n#+END_EXAMPLE" (org-indent-region (point-min) (point-max)) (buffer-string)))) (let ((org-adapt-indentation t)) (should (equal "#+BEGIN_EXAMPLE\n A\n B\n#+END_EXAMPLE" (org-test-with-temp-text " #+BEGIN_EXAMPLE\n A\n B\n #+END_EXAMPLE" (org-indent-region (point-min) (point-max)) (buffer-string)))) (should (equal "#+BEGIN_EXAMPLE -i\n A\n B\n#+END_EXAMPLE" (org-test-with-temp-text " #+BEGIN_EXAMPLE -i\n A\n B\n #+END_EXAMPLE" (org-indent-region (point-min) (point-max)) (buffer-string)))) (should (equal "#+BEGIN_EXAMPLE\n A\n B\n#+END_EXAMPLE" (org-test-with-temp-text " #+BEGIN_EXAMPLE\n A\n B\n #+END_EXAMPLE" (let ((org-src-preserve-indentation t)) (org-indent-region (point-min) (point-max))) (buffer-string))))) ;; Treat export blocks as a whole. (should (equal "#+BEGIN_EXPORT latex\n A\n B\n#+END_EXPORT" (org-test-with-temp-text "#+BEGIN_EXPORT latex\n A\n B\n#+END_EXPORT" (org-indent-region (point-min) (point-max)) (buffer-string)))) (let ((org-adapt-indentation t)) (should (equal "#+BEGIN_EXPORT latex\n A\n B\n#+END_EXPORT" (org-test-with-temp-text " #+BEGIN_EXPORT latex\n A\n B\n #+END_EXPORT" (org-indent-region (point-min) (point-max)) (buffer-string))))) ;; Indent according to mode if `org-src-tab-acts-natively' is ;; non-nil. Otherwise, do not indent code at all. (should (equal "#+BEGIN_SRC emacs-lisp\n(and A\n B)\n#+END_SRC" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n (and A\nB)\n#+END_SRC" (let ((org-src-tab-acts-natively t) (org-edit-src-content-indentation 0)) (org-indent-region (point-min) (point-max))) (buffer-string)))) (should (equal "#+BEGIN_SRC emacs-lisp\n (and A\nB)\n#+END_SRC" (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n (and A\nB)\n#+END_SRC" (let ((org-src-tab-acts-natively nil) (org-edit-src-content-indentation 0)) (org-indent-region (point-min) (point-max))) (buffer-string)))) ;; Align node properties according to `org-property-format'. Handle ;; nicely empty values. (should (equal "* H\n:PROPERTIES:\n:key: value\n:END:" (org-test-with-temp-text "* H\n:PROPERTIES:\n:key: value\n:END:" (let ((org-property-format "%-10s %s") (org-adapt-indentation nil)) (org-indent-region (point) (point-max))) (buffer-string)))) (should (equal "* H\n:PROPERTIES:\n:key:\n:END:" (org-test-with-temp-text "* H\n:PROPERTIES:\n:key:\n:END:" (let ((org-property-format "%-10s %s") (org-adapt-indentation nil)) (org-indent-region (point) (point-max))) (buffer-string)))) ;; Indent property drawers according to `org-adapt-indentation'. (let ((org-adapt-indentation 'headline-data)) (should (equal "* H\n :PROPERTIES:\n :key:\n :END:" (org-test-with-temp-text "* H\n:PROPERTIES:\n:key:\n:END:" (org-indent-region (point-min) (point-max)) (buffer-string))))) ;; Indent planning according to `org-adapt-indentation'. (let ((org-adapt-indentation 'headline-data)) (should (equal "* H\n SCHEDULED: <2022-11-03>" (org-test-with-temp-text "* H\nSCHEDULED: <2022-11-03>" (org-indent-region (point-min) (point-max)) (buffer-string))))) ;; Indent LOGBOOK according to `org-adapt-indentation'. (let ((org-adapt-indentation 'headline-data)) (should (equal "* H\n :LOGBOOK: CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46 :END:" (org-test-with-temp-text "* H\n:LOGBOOK: CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46 :END:" (org-indent-region (point-min) (point-max)) (buffer-string))))) ;; Indent clock lines according to `org-adapt-indentation'. (let ((org-adapt-indentation 'headline-data)) (should (equal "* H CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46" (org-test-with-temp-text "* H CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46" (org-indent-region (point-min) (point-max)) (buffer-string))))) ;; Do not indent beyond headline data. (let ((org-adapt-indentation 'headline-data)) (should (equal "* H\n SCHEDULED: <2022-11-03>\nParagraph" (org-test-with-temp-text "* H\nSCHEDULED: <2022-11-03>\nParagraph" (org-indent-region (point-min) (point-max)) (buffer-string))))) ;; Do not indent empty lines after heading, when no headline data. (let ((org-adapt-indentation 'headline-data)) (should (equal "* H1\n\n* H2\n" (org-test-with-temp-text "* H1\n\n* H2\n" (org-indent-line) (buffer-string))))) (let ((org-adapt-indentation 'headline-data) (org-log-into-drawer t)) (should (equal "* TODO A task :PROPERTIES: :CAPTURED: [2022-09-11 dim. 21:25] :END: :LOGBOOK: CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46 :END: Paragraph" (org-test-with-temp-text "* TODO A task :PROPERTIES: :CAPTURED: [2022-09-11 dim. 21:25] :END: :LOGBOOK: CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46 :END: Paragraph" (org-indent-region (point-min) (point-max)) (buffer-string))))) (let ((org-adapt-indentation 'headline-data)) (should (equal "* TODO A task CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46 Paragraph CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46" (org-test-with-temp-text "* TODO A task CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46 Paragraph CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46" (org-indent-region (point-min) (point-max)) (buffer-string))))) ;; Indent plain lists. (let ((org-adapt-indentation t)) (should (equal "- A\n B\n - C\n\n D" (org-test-with-temp-text "- A\n B\n - C\n\n D" (org-indent-region (point-min) (point-max)) (buffer-string)))) (should (equal "- A\n\n- B" (org-test-with-temp-text " - A\n\n - B" (org-indent-region (point-min) (point-max)) (buffer-string))))) ;; Indent footnote definitions. (should (equal "[fn:1] Definition\n\nDefinition" (org-test-with-temp-text "[fn:1] Definition\n\n Definition" (org-indent-region (point-min) (point-max)) (buffer-string)))) ;; Special case: Start indenting on a blank line. (should (equal "\nParagraph" (org-test-with-temp-text "\n Paragraph" (org-indent-region (point-min) (point-max)) (buffer-string))))) (ert-deftest test-org/default-indent-new-line () "Test behavior of default binding `M-j'." ;; Calling `M-j' when point is not in an Org comment: (should (equal "* Some heading\n" (org-test-with-temp-text "* Some heading" (call-interactively #'default-indent-new-line) (buffer-string)))) ;; Calling `M-j' when point is in an Org comment: (should (equal "# Some Org comment\n# " (org-test-with-temp-text "# Some Org comment" (call-interactively #'default-indent-new-line) (buffer-string)))) (should (equal "# Some Org\n# comment" (org-test-with-temp-text "# Some Org comment" (call-interactively #'default-indent-new-line) (buffer-string))))) ;;; Editing (ert-deftest test-org/delete-indentation () "Test `org-delete-indentation' specifications." ;; Regular test. (should (equal "foo bar" (org-test-with-temp-text "foo \n bar" (org-delete-indentation) (buffer-string)))) ;; With optional argument. (should (equal "foo bar" (org-test-with-temp-text "foo \n bar" (org-delete-indentation t) (buffer-string)))) ;; At headline text should be appended to the headline text. (should (equal"* foo bar :tag:" (let (org-auto-align-tags) (org-test-with-temp-text "* foo :tag:\n bar" (org-delete-indentation) (buffer-string))))) (should (equal "* foo bar :tag:" (let (org-auto-align-tags) (org-test-with-temp-text "* foo :tag:\n bar" (org-delete-indentation t) (buffer-string)))))) (ert-deftest test-org/return () "Test `org-return' specifications." ;; Regular test. (should (equal "Para\ngraph" (org-test-with-temp-text "Paragraph" (org-return) (buffer-string)))) ;; With optional argument, indent line. (should (equal " Para\n graph" (org-test-with-temp-text " Paragraph" (org-return t) (buffer-string)))) ;; On a table, call `org-table-next-row'. (should (org-test-with-temp-text "| a |\n| b |" (org-return) (looking-at-p "b"))) ;; Open link or timestamp under point when `org-return-follows-link' ;; is non-nil. (should (org-test-with-temp-text "Link [[target]] <>" (let ((org-return-follows-link t) (org-link-search-must-match-exact-headline nil)) (org-return)) (looking-at-p "<>"))) (should-not (org-test-with-temp-text "Link [[target]] <>" (let ((org-return-follows-link nil)) (org-return)) (looking-at-p "<>"))) (should (org-test-with-temp-text "* [[b][a]]\n* b" (let ((org-return-follows-link t)) (org-return)) (looking-at-p "* b"))) (should (org-test-with-temp-text "Link [[target][/desciption/]] <>" (let ((org-return-follows-link t) (org-link-search-must-match-exact-headline nil)) (org-return)) (looking-at-p "<>"))) (should-not (org-test-with-temp-text "Link [[target]] <>" (let ((org-return-follows-link t) (org-link-search-must-match-exact-headline nil)) (org-return)) (looking-at-p "<>"))) ;; When `org-return-follows-link' is non-nil, tolerate links and ;; timestamps in comments, node properties, etc. (should (org-test-with-temp-text "# Comment [[target]]\n <>" (let ((org-return-follows-link t) (org-link-search-must-match-exact-headline nil)) (org-return)) (looking-at-p "<>"))) (should-not (org-test-with-temp-text "# Comment [[target]]\n <>" (let ((org-return-follows-link nil)) (org-return)) (looking-at-p "<>"))) (should-not (org-test-with-temp-text "# Comment [[target]]\n <>" (let ((org-return-follows-link t) (org-link-search-must-match-exact-headline nil)) (org-return)) (looking-at-p "<>"))) ;; Non-nil `org-return-follows-link' ignores read-only state of ;; a buffer. (should (org-test-with-temp-text "Link [[target]] <>" (let ((org-return-follows-link t) (org-link-search-must-match-exact-headline nil)) (setq buffer-read-only t) (call-interactively #'org-return)) (looking-at-p "<>"))) ;; `org-return-follows-link' handle multi-line lines. (should (org-test-with-temp-text "[[target][This is a very\n long description]]\n <>" (let ((org-return-follows-link t) (org-link-search-must-match-exact-headline nil)) (org-return)) (looking-at-p "<>"))) (should-not (org-test-with-temp-text "[[target][This is a very\n long description]]\n <>" (let ((org-return-follows-link t) (org-link-search-must-match-exact-headline nil)) (org-return)) (looking-at-p "<>"))) ;; However, do not open link when point is in a table. (should (org-test-with-temp-text "| [[target]] |\n| between |\n| <> |" (let ((org-return-follows-link t)) (org-return)) (looking-at-p "between"))) ;; Special case: in a list, when indenting, do not break structure. (should (equal "- A\n B" (org-test-with-temp-text "- A B" (org-return t) (buffer-string)))) (should (equal "- A\n\n- B" (org-test-with-temp-text "- A\n- B" (org-return t) (buffer-string)))) ;; On tags part of a headline, add a newline below it instead of ;; breaking it. (should (equal "* H :tag:\n" (org-test-with-temp-text "* H :tag:" (org-return) (buffer-string)))) ;; Before headline text, add a newline below it instead of breaking ;; it. (should (equal "* TODO H :tag:\n" (org-test-with-temp-text "* TODO H :tag:" (org-return) (buffer-string)))) (should (equal "* TODO [#B] H :tag:\n" (org-test-with-temp-text "* TODO [#B] H :tag:" (org-return) (buffer-string)))) (should ;TODO are case-sensitive (equal "* \nTodo" (org-test-with-temp-text "* Todo" (org-return) (buffer-string)))) ;; At headline text, break headline text but preserve tags. (should (equal "* TODO [#B] foo :tag:\nbar" (let (org-auto-align-tags) (org-test-with-temp-text "* TODO [#B] foobar :tag:" (org-return) (buffer-string))))) ;; At bol of headline insert newline. (should (equal "\n* h" (org-test-with-temp-text "* h" (org-return) (buffer-string)))) ;; Refuse to leave invalid headline in buffer. (should (equal "* h\n" (org-test-with-temp-text "* h" (org-return) (buffer-string)))) ;; Before first column or after last one in a table, split the ;; table. (should (equal "| a |\n\n| b |" (org-test-with-temp-text "| a |\n| b |" (org-return) (buffer-string)))) (should (equal "| a |\n\n| b |" (org-test-with-temp-text "| a |\n| b |" (org-return) (buffer-string)))) ;; Do not auto-fill on hitting inside a property drawer. (should (equal "* Heading\n:PROPERTIES:\n:SOME_PROP: This is a very long property value that goes beyond the fill-column. But this is inside a property drawer, so the auto-filling should be disabled.\n\n:END:" (org-test-with-temp-text "* Heading\n:PROPERTIES:\n:SOME_PROP: This is a very long property value that goes beyond the fill-column. But this is inside a property drawer, so the auto-filling should be disabled.\n:END:" (setq-local fill-column 10) (auto-fill-mode 1) (org-return) (buffer-string)))) ;; When `org-return-follows-link' is non-nil, run `org-open-at-point' ;; on citation. (should (org-test-with-temp-text "[cite: @key1; @key2]" (catch :called (cl-letf (((symbol-function 'org-open-at-point) (lambda (&rest _) (interactive) (throw :called t)))) (let ((org-return-follows-link t)) (org-return)) nil)))) (should-not (org-test-with-temp-text "[cite: @key1; @key2]" (catch :called (cl-letf (((symbol-function 'org-open-at-point) (lambda (&rest _) (interactive) (throw :called t)))) (let ((org-return-follows-link nil)) (org-return)) nil)))) (should (org-test-with-temp-text "[cite: @key1; @key2]" (catch :called (cl-letf (((symbol-function 'org-open-at-point) (lambda (&rest _) (interactive) (throw :called t)))) (let ((org-return-follows-link t)) (org-return)) nil)))) (should-not (org-test-with-temp-text "[cite: @key1; @key2]" (catch :called (cl-letf (((symbol-function 'org-open-at-point) (lambda (&rest _) (interactive) (throw :called t)))) (let ((org-return-follows-link nil)) (org-return)) nil))))) (ert-deftest test-org/with-electric-indent () "Test RET and C-j specifications with `electric-indent-mode' on." ;; Call commands interactively, since this is how `newline' knows it ;; must run `post-self-insert-hook'. ;; ;; RET, like `newline', should indent. (should (equal " Para\n graph" (org-test-with-temp-text " Paragraph" (electric-indent-local-mode 1) (call-interactively 'org-return) (buffer-string)))) (should (equal "- item1\n item2" (org-test-with-temp-text "- item1item2" (electric-indent-local-mode 1) (call-interactively 'org-return) (buffer-string)))) ;; TODO: test more values of `org-adapt-indentation'. (let ((org-adapt-indentation t)) (should (equal "* heading\n body" (org-test-with-temp-text "* headingbody" (electric-indent-local-mode 1) (call-interactively 'org-return) (buffer-string))))) ;; Make sure that we do not mess things up when indenting remotely ;; in src block buffer. (let ((org-edit-src-content-indentation 2)) (should ;; Add `org-edit-src-content-indentation' and no more. ;; https://orgmode.org/list/5O9VMGb6WRaqeHR5_NXTb832Z2Lek_5L40YPDA52-S3kPwGYJspI8kLWaGtuq3DXyhtHpj1J7jTIXb39RX9BtCa2ecrWHjijZqI8QAD742U=@proton.me (equal "#+begin_src fundamental\n \n#+end_src" ; 2 spaces (org-test-with-temp-text "#+begin_src fundamental\n#+end_src" (electric-indent-local-mode 1) (call-interactively 'org-return) (should (looking-at-p "\n\\#")) (buffer-string))))) ;; C-j, like `electric-newline-and-maybe-indent', should not indent. (should (equal " Para\ngraph" (org-test-with-temp-text " Paragraph" (electric-indent-local-mode 1) (call-interactively 'org-return-and-maybe-indent) (buffer-string)))) (should (equal "- item1\nitem2" (org-test-with-temp-text "- item1item2" (electric-indent-local-mode 1) (call-interactively 'org-return-and-maybe-indent) (buffer-string)))) ;; TODO: test more values of `org-adapt-indentation'. (let ((org-adapt-indentation t)) (should (equal "* heading\nbody" (org-test-with-temp-text "* headingbody" (electric-indent-local-mode 1) (call-interactively 'org-return-and-maybe-indent) (buffer-string)))))) (ert-deftest test-org/without-electric-indent () "Test RET and C-j specifications with `electric-indent-mode' off." ;; Call commands interactively, since this is how `newline' knows it ;; must run `post-self-insert-hook'. ;; ;; RET, like `newline', should not indent. (should (equal " Para\ngraph" (org-test-with-temp-text " Paragraph" (electric-indent-local-mode 0) (call-interactively 'org-return) (buffer-string)))) (should (equal "- item1\nitem2" (org-test-with-temp-text "- item1item2" (electric-indent-local-mode 0) (call-interactively 'org-return) (buffer-string)))) (should (equal "* heading\nbody" (org-test-with-temp-text "* headingbody" (electric-indent-local-mode 0) (call-interactively 'org-return) (buffer-string)))) ;; C-j, like `electric-newline-and-maybe-indent', should indent. (should (equal " Para\n graph" (org-test-with-temp-text " Paragraph" (electric-indent-local-mode 0) (call-interactively 'org-return-and-maybe-indent) (buffer-string)))) (should (equal "- item1\n item2" (org-test-with-temp-text "- item1item2" (electric-indent-local-mode 0) (call-interactively 'org-return-and-maybe-indent) (buffer-string)))) ;; TODO: test more values of `org-adapt-indentation'. (let ((org-adapt-indentation t)) (should (equal "* heading\n body" (org-test-with-temp-text "* headingbody" (electric-indent-local-mode 0) (call-interactively 'org-return-and-maybe-indent) (buffer-string)))))) (ert-deftest test-org/meta-return () "Test M-RET (`org-meta-return') specifications." ;; In a table field insert a row above. (should (org-test-with-temp-text "| a |" (forward-char) (org-meta-return) (forward-line -1) (looking-at "| |$"))) ;; In a paragraph change current line into a header. (should (org-test-with-temp-text "a" (org-meta-return) (beginning-of-line) (looking-at "\* a$"))) ;; In an item insert an item, in this case above. (should (org-test-with-temp-text "- a" (org-meta-return) (beginning-of-line) (looking-at "- $"))) ;; In a drawer and item insert an item, in this case above. (should (org-test-with-temp-text ":MYDRAWER:\n- a\n:END:" (forward-line) (org-fold-reveal) (org-meta-return) (beginning-of-line) (looking-at "- $")))) (ert-deftest test-org/insert-heading () "Test `org-insert-heading' specifications." ;; In an empty buffer, insert a new headline. (should (equal "* " (org-test-with-temp-text "" (org-insert-heading) (buffer-string)))) ;; At the beginning of a line, turn it into a headline. (should (equal "* P" (org-test-with-temp-text "P" (org-insert-heading) (buffer-string)))) ;; In the middle of a line, split the line if allowed, otherwise, ;; insert the headline at its end. (should (equal "Para\n* graph" (org-test-with-temp-text "Paragraph" (let ((org-M-RET-may-split-line '((default . t)))) (org-insert-heading)) (buffer-string)))) (should (equal "Paragraph\n* " (org-test-with-temp-text "Paragraph" (let ((org-M-RET-may-split-line '((default . nil)))) (org-insert-heading)) (buffer-string)))) ;; At the beginning of a headline, create one above. (should (equal "* \n* H" (org-test-with-temp-text "* H" (org-insert-heading) (buffer-string)))) ;; In the middle of a headline, split it if allowed. (should (equal "* H\n* 1" (org-test-with-temp-text "* H1" (let ((org-M-RET-may-split-line '((headline . t)))) (org-insert-heading)) (buffer-string)))) (should (equal "* H1\n* " (org-test-with-temp-text "* H1" (let ((org-M-RET-may-split-line '((headline . nil)))) (org-insert-heading)) (buffer-string)))) ;; However, splitting cannot happen on TODO keywords, priorities or ;; tags. (should (equal "* TODO H1\n* " (org-test-with-temp-text "* TODO H1" (let ((org-M-RET-may-split-line '((headline . t)))) (org-insert-heading)) (buffer-string)))) (should (equal "* [#A] H1\n* " (org-test-with-temp-text "* [#A] H1" (let ((org-M-RET-may-split-line '((headline . t)))) (org-insert-heading)) (buffer-string)))) (should (equal "* H1 :tag:\n* " (org-test-with-temp-text "* H1 :tag:" (let ((org-M-RET-may-split-line '((headline . t)))) (org-insert-heading)) (buffer-string)))) ;; New headline level depends on the level of the headline above. (should (equal "** H\n** P" (org-test-with-temp-text "** H\nP" (org-insert-heading) (buffer-string)))) (should (equal "** H\nPara\n** graph" (org-test-with-temp-text "** H\nParagraph" (let ((org-M-RET-may-split-line '((default . t)))) (org-insert-heading)) (buffer-string)))) (should (equal "** \n** H" (org-test-with-temp-text "** H" (org-insert-heading) (buffer-string)))) ;; When called with one universal argument, insert a new headline at ;; the end of the current subtree, independently on the position of ;; point. (should (equal "* \n" (org-test-with-temp-text "" (let ((org-insert-heading-respect-content nil)) (org-insert-heading '(4))) (buffer-string)))) (should (equal "* 1 ** 1.1 ** 1.2 * * 2" (org-test-with-temp-text "* 1 ** 1.1 ** 1.2 * 2" (let ((org-insert-heading-respect-content nil)) (org-insert-heading '(4))) (buffer-string)))) (should (equal "entry * \n" (org-test-with-temp-text "entry" (let ((org-insert-heading-respect-content nil)) (org-insert-heading '(4))) (buffer-string)))) (should (equal "* H1\n** H2\n* \n" (org-test-with-temp-text "* H1\n** H2" (let ((org-insert-heading-respect-content nil)) (org-insert-heading '(4))) (buffer-string)))) (should (equal "* H1\n** H2\n* \n" (org-test-with-temp-text "* H1\n** H2" (let ((org-insert-heading-respect-content nil)) (org-insert-heading '(4))) (buffer-string)))) ;; When called with two universal arguments, insert a new headline ;; at the end of the grandparent subtree. (should (equal "* H1\n** H3\n- item\n** H2\n** \n" (org-test-with-temp-text "* H1\n** H3\n- item\n** H2" (let ((org-insert-heading-respect-content nil)) (org-insert-heading '(16))) (buffer-string)))) ;; When optional LEVEL argument is a number, insert a heading at ;; that level. (should (equal "* H1\n** H2\n* " (org-test-with-temp-text "* H1\n** H2" (org-insert-heading nil nil 1) (buffer-string)))) (should (equal "* H1\n** H2\n** " (org-test-with-temp-text "* H1\n** H2" (org-insert-heading nil nil 2) (buffer-string)))) (should (equal "* H1\n** H2\n*** " (org-test-with-temp-text "* H1\n** H2" (org-insert-heading nil nil 3) (buffer-string)))) (should (equal "* H1\n- item\n* " (org-test-with-temp-text "* H1\n- item" (org-insert-heading nil nil 1) (buffer-string)))) ;; When optional LEVEL argument is non-nil, always insert a level 1 ;; heading. (should (equal "* H1\n** H2\n* " (org-test-with-temp-text "* H1\n** H2" (org-insert-heading nil nil t) (buffer-string)))) (should (equal "* H1\n- item\n* " (org-test-with-temp-text "* H1\n- item" (org-insert-heading nil nil t) (buffer-string)))) ;; Obey `org-blank-before-new-entry'. (should (equal "* H1\n\n* " (org-test-with-temp-text "* H1" (let ((org-blank-before-new-entry '((heading . t)))) (org-insert-heading)) (buffer-string)))) (should (equal "* H1\n* " (org-test-with-temp-text "* H1" (let ((org-blank-before-new-entry '((heading . nil)))) (org-insert-heading)) (buffer-string)))) (should (equal "* H1\n* H2\n* " (org-test-with-temp-text "* H1\n* H2" (let ((org-blank-before-new-entry '((heading . auto)))) (org-insert-heading)) (buffer-string)))) (should (equal "* H1\n\n* H2\n\n* " (org-test-with-temp-text "* H1\n\n* H2" (let ((org-blank-before-new-entry '((heading . auto)))) (org-insert-heading)) (buffer-string)))) ;; Corner case: correctly insert a headline after an empty one. (should (equal "* \n* " (org-test-with-temp-text "* " (org-insert-heading) (buffer-string)))) (should (org-test-with-temp-text "* \n" (org-insert-heading) (looking-at-p "\n\\'"))) ;; Do not insert spurious headlines when inserting a new headline. (should (equal "* H1\n* H2\n* \n" (org-test-with-temp-text "* H1\n* H2\n" (org-insert-heading) (buffer-string)))) ;; Preserve visibility at beginning of line. In particular, when ;; removing spurious blank lines, do not visually merge heading with ;; the line visible above. (should-not (org-test-with-temp-text "* H1\nContents\n\n* H2\n" (org-overview) (let ((org-blank-before-new-entry '((heading . nil)))) (org-insert-heading '(4))) (invisible-p (line-end-position 0)))) ;; Properly handle empty lines when forcing a headline below current ;; one. (should (equal "* H1\n\n* H\n\n* \n" (org-test-with-temp-text "* H1\n\n* H" (let ((org-blank-before-new-entry '((heading . t)))) (org-insert-heading '(4)) (buffer-string))))) ;; Do not include potentially folded empty lines. (org-test-with-temp-text " * Sec1 ** SubSec1 text ** SubSec2 text " (org-content) (org-insert-heading '(4)) (should-not (org-fold-folded-p)))) (ert-deftest test-org/insert-todo-heading-respect-content () "Test `org-insert-todo-heading-respect-content' specifications." ;; Create a TODO heading. (should (org-test-with-temp-text "* H1\n Body" (org-insert-todo-heading-respect-content) (nth 2 (org-heading-components)))) ;; Add headline at the end of the first subtree (should (equal "* TODO \n" (org-test-with-temp-text "* H1\nH1Body\n** H2\nH2Body" (org-insert-todo-heading-respect-content) (buffer-substring-no-properties (line-beginning-position) (point-max))))) ;; In a list, do not create a new item. (should (equal "* TODO \n" (org-test-with-temp-text "* H\n- an item\n- another one" (search-forward "an ") (org-insert-todo-heading-respect-content) (buffer-substring-no-properties (line-beginning-position) (point-max))))) ;; Use the same TODO keyword as current heading. (should (equal "* TODO \n" (org-test-with-temp-text "* TODO\n** WAITING\n" (org-insert-todo-heading-respect-content) (buffer-substring-no-properties (line-beginning-position) (point-max))))) (should (equal "* TODO \n" (let ((org-todo-keywords '((sequence "FIRST" "TODO" "|" "DONE")))) (org-test-with-temp-text "* TODO\n** WAITING\n" (org-insert-todo-heading-respect-content) (buffer-substring-no-properties (line-beginning-position) (point-max)))))) ;; Pass prefix argument. (should (equal "* FIRST \n" (let ((org-todo-keywords '((sequence "FIRST" "TODO" "|" "DONE")))) (org-test-with-temp-text "* TODO\n** WAITING\n" (org-insert-todo-heading-respect-content '(4)) (buffer-substring-no-properties (line-beginning-position) (point-max)))))) ) (ert-deftest test-org/clone-with-time-shift () "Test `org-clone-subtree-with-time-shift'." ;; Raise an error before first heading. (should-error (org-test-with-temp-text "" (org-clone-subtree-with-time-shift 1))) ;; Raise an error on invalid number of clones. (should-error (org-test-with-temp-text "* Clone me" (org-clone-subtree-with-time-shift -1))) ;; Clone non-repeating once. (should (equal "\ * H1\n<2015-06-21> * H1\n<2015-06-23> " (org-test-with-temp-text "* H1\n<2015-06-21>" (org-test-without-dow (org-test-with-result 'buffer (org-clone-subtree-with-time-shift 1 "+2d")))))) ;; Clone repeating once. (should (equal "\ * H1\n<2015-06-21> * H1\n<2015-06-23> * H1\n<2015-06-25 +1w> " (org-test-with-temp-text "* H1\n<2015-06-21 +1w>" (org-test-without-dow (org-test-with-result 'buffer (org-clone-subtree-with-time-shift 1 "+2d")))))) ;; Clone repeating once in backward. (should (equal "\ * H1\n<2015-06-21> * H1\n<2015-06-19> * H1\n<2015-06-17 +1w> " (org-test-with-temp-text "* H1\n<2015-06-21 +1w>" (org-test-without-dow (org-test-with-result 'buffer (org-clone-subtree-with-time-shift 1 "-2d")))))) ;; Clone non-repeating zero times. (should (equal "\ * H1\n<2015-06-21> " (org-test-with-temp-text "* H1\n<2015-06-21>" (org-test-without-dow (org-test-with-result 'buffer (org-clone-subtree-with-time-shift 0 "+2d")))))) ;; Clone repeating "zero" times. (should (equal "\ * H1\n<2015-06-21> * H1\n<2015-06-23 +1w> " (org-test-with-temp-text "* H1\n<2015-06-21 +1w>" (org-test-without-dow (org-test-with-result 'buffer (org-clone-subtree-with-time-shift 0 "+2d")))))) ;; Clone with blank SHIFT argument. (should (string-prefix-p "* H <2012-03-29" (org-test-with-temp-text "* H <2012-03-29 Thu>" (org-test-without-dow (org-test-with-result 2 (org-clone-subtree-with-time-shift 1 "")))))) ;; Find time stamps before point. If SHIFT is not specified, ask ;; for a time shift. (should (string-prefix-p "* H <2012-03-30" (org-test-with-temp-text "* H <2012-03-29 Thu>" (org-test-without-dow (org-test-with-result 2 (org-clone-subtree-with-time-shift 1 "+1d")))))) (should (string-prefix-p "* H <2014-03-05" (org-test-with-temp-text "* H <2014-03-04 Tue>" (cl-letf (((symbol-function 'read-from-minibuffer) (lambda (&rest _args) "+1d"))) (org-test-without-dow (org-test-with-result 2 (org-clone-subtree-with-time-shift 1))))))) ;; Hour shift. (should (equal "\ * H1\n<2015-06-21 20:00> * H1\n<2015-06-21 23:00> * H1\n<2015-06-22 02:00> " (org-test-with-temp-text "* H1\n<2015-06-21 20:00>" (org-test-without-dow (org-test-with-result 'buffer (org-clone-subtree-with-time-shift 2 "+3h")))))) (should (equal "\ * H1\n<2015-06-21 20:00> * H1\n<2015-06-21 18:00> " (org-test-with-temp-text "* H1\n<2015-06-21 20:00>" (org-test-without-dow (org-test-with-result 'buffer (org-clone-subtree-with-time-shift 1 "-2h"))))))) ;;; Fixed-Width Areas (ert-deftest test-org/toggle-fixed-width () "Test `org-toggle-fixed-width' specifications." ;; No region: Toggle on fixed-width marker in paragraphs. (should (equal ": A" (org-test-with-temp-text "A" (org-toggle-fixed-width) (buffer-string)))) ;; No region: Toggle off fixed-width markers in fixed-width areas. (should (equal "A" (org-test-with-temp-text ": A" (org-toggle-fixed-width) (buffer-string)))) ;; No region: Toggle on marker in blank lines after elements or just ;; after a headline. (should (equal "* H\n: " (org-test-with-temp-text "* H\n" (forward-line) (org-toggle-fixed-width) (buffer-string)))) (should (equal "#+BEGIN_EXAMPLE\nContents\n#+END_EXAMPLE\n: " (org-test-with-temp-text "#+BEGIN_EXAMPLE\nContents\n#+END_EXAMPLE\n" (goto-char (point-max)) (org-toggle-fixed-width) (buffer-string)))) ;; No region: Toggle on marker in front of one line elements (e.g., ;; headlines, clocks) (should (equal ": * Headline" (org-test-with-temp-text "* Headline" (org-toggle-fixed-width) (buffer-string)))) (should (equal ": #+KEYWORD: value" (org-test-with-temp-text "#+KEYWORD: value" (org-toggle-fixed-width) (buffer-string)))) ;; No region: error in other situations. (should-error (org-test-with-temp-text "#+BEGIN_EXAMPLE\n: A\n#+END_EXAMPLE" (forward-line) (org-toggle-fixed-width) (buffer-string))) ;; No region: Indentation is preserved. (should (equal "- A\n : B" (org-test-with-temp-text "- A\n B" (forward-line) (org-toggle-fixed-width) (buffer-string)))) ;; Region: If it contains only fixed-width elements and blank lines, ;; toggle off fixed-width markup. (should (equal "A\n\nB" (org-test-with-temp-text ": A\n\n: B" (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (org-toggle-fixed-width) (buffer-string)))) ;; Region: If it contains anything else, toggle on fixed-width but ;; not on fixed-width areas. (should (equal ": A\n: \n: B\n: \n: C" (org-test-with-temp-text "A\n\n: B\n\nC" (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (org-toggle-fixed-width) (buffer-string)))) ;; Region: Ignore blank lines at its end, unless it contains only ;; such lines. (should (equal ": A\n\n" (org-test-with-temp-text "A\n\n" (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (org-toggle-fixed-width) (buffer-string)))) (should (equal ": \n: \n" (org-test-with-temp-text "\n\n" (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (org-toggle-fixed-width) (buffer-string))))) (ert-deftest test-org/kill-line () "Test `org-kill-line' specifications." ;; At the beginning of a line, kill whole line. (should (equal "" (org-test-with-temp-text "abc" (org-kill-line) (buffer-string)))) ;; In the middle of a line, kill line until its end. (should (equal "a" (org-test-with-temp-text "abc" (org-kill-line) (buffer-string)))) ;; Do not kill newline character. (should (equal "\n123" (org-test-with-temp-text "abc\n123" (org-kill-line) (buffer-string)))) (should (equal "a\n123" (org-test-with-temp-text "abc\n123" (org-kill-line) (buffer-string)))) ;; When `org-special-ctrl-k' is non-nil and point is at a headline, ;; kill until tags. (should (equal "* A :tag:" (org-test-with-temp-text "* AB :tag:" (let ((org-special-ctrl-k t) (org-tags-column 0)) (org-kill-line)) (buffer-string)))) ;; If point is on tags, only kill part left until the end of line. (should (equal "* A :tag:" (org-test-with-temp-text "* A :tag:tag2:" (let ((org-special-ctrl-k t) (org-tags-column 0)) (org-kill-line)) (buffer-string)))) ;; However, if point is at the beginning of the line, kill whole ;; headline. (should (equal "" (org-test-with-temp-text "* AB :tag:" (let ((org-special-ctrl-k t) (org-tags-column 0)) (org-kill-line)) (buffer-string)))) ;; When `org-ctrl-k-protect-subtree' is non-nil, and point is in ;; invisible text, ask before removing it. When set to `error', ;; throw an error. (should-error (org-test-with-temp-text "* H\n** H2\nContents\n* H3" (org-overview) (let ((org-special-ctrl-k nil) (org-ctrl-k-protect-subtree t)) (cl-letf (((symbol-function 'y-or-n-p) 'ignore)) (org-kill-line))))) (should-error (org-test-with-temp-text "* H\n** H2\nContents\n* H3" (org-overview) (let ((org-special-ctrl-k nil) (org-ctrl-k-protect-subtree 'error)) (org-kill-line))))) ;;; Headline (ert-deftest test-org/org-back-to-heading () "Test `org-back-to-heading' specifications." ;; On heading already (org-test-with-temp-text "* Heading" (org-back-to-heading) (should (bobp))) ;; Below heading (org-test-with-temp-text "* Heading Text" (org-back-to-heading) (should (bobp))) ;; At inlinetask (let ((org-inlinetask-min-level 3)) (org-test-with-temp-text "* Heading *** Inlinetask " (org-back-to-heading) (should (= 11 (point))))) ;; Below inlinetask (let ((org-inlinetask-min-level 3)) (org-test-with-temp-text "* Heading *** Inlinetask Test " (org-back-to-heading) ;; Not at or inside inlinetask. Move to parent heading. (should (bobp)))) ;; Inside inlinetask (let ((org-inlinetask-min-level 3)) (org-test-with-temp-text "* Heading *** Inlinetask Test *** END" (org-back-to-heading) (should (= 11 (point))))) ;; At END (let ((org-inlinetask-min-level 3)) (org-test-with-temp-text "* Heading *** Inlinetask Test *** END" (org-back-to-heading) (should (= 11 (point)))))) (ert-deftest test-org/up-heading-safe () "Test `org-up-heading-safe' specifications." ;; Jump to parent. Simple case. (org-test-with-temp-text " * H1 ** H2" (should (= 1 (org-up-heading-safe))) (should (looking-at-p "^\\* H1"))) ;; Return true level. Ignore `org-odd-levels-only'. (let ((org-odd-levels-only t)) (org-test-with-temp-text " *** H1 ***** H2" (should (= 3 (org-up-heading-safe))) (should (looking-at-p "^\\*\\{3\\} H1")))) ;; Do not jump beyond the level 1 heading. (org-test-with-temp-text " Text. * Heading " (let ((pos (point))) (should-not (org-up-heading-safe)) (should (looking-at-p "^\\* Heading")))) ;; Jump from inside a heading. (org-test-with-temp-text " * H1 ** H2 Text " (should (= 1 (org-up-heading-safe))) (should (looking-at-p "^\\* H1"))) ;; Test inlinetask. (let ((org-inlinetask-min-level 3)) (org-test-with-temp-text " ** Heading Text. *** Inlinetask Text *** END" (should (= 2 (org-up-heading-safe))) (should (looking-at-p "^\\*\\{2\\} Heading")))) (let ((org-inlinetask-min-level 3)) (org-test-with-temp-text " ** Heading Text. *** Inlinetask" (should (= 2 (org-up-heading-safe))) (should (looking-at-p "^\\*\\{2\\} Heading")))) ;; Respect narrowing. (org-test-with-temp-text " * H1 ** text ** H2" (save-excursion (search-backward "** text") (narrow-to-region (point) (point-max))) (should-not (org-up-heading-safe)) (should (looking-at-p "^\\*\\* H2")))) (ert-deftest test-org/goto-sibling () "Test `org-goto-sibling' specifications." (org-test-with-temp-text "* Parent ** Heading 1 ** Heading 2 ** Heading 3" (should (org-goto-sibling)) (should (looking-at-p "^\\*\\* Heading 3")) (should-not (org-goto-sibling)) (should (org-goto-sibling 'previous)) (should (looking-at-p "^\\*\\* Heading 2")) (should (org-goto-sibling 'previous)) (should (looking-at-p "^\\*\\* Heading 1")) (should-not (org-goto-sibling 'previous))) ;; Inside heading. (org-test-with-temp-text "* Parent ** Heading 1 ** Heading 2 Some text. ** Heading 3" (should (org-goto-sibling)) (should (looking-at-p "^\\*\\* Heading 3"))) (org-test-with-temp-text "* Parent ** Heading 1 ** Heading 2 Some text. ** Heading 3" (should (org-goto-sibling 'previous)) (should (looking-at-p "^\\*\\* Heading 1"))) (org-test-with-temp-text "* Parent ** Heading 2 Some text. " (should-not (org-goto-sibling)) (should-not (org-goto-sibling 'previous))) ;; Ignore inlinetasks. (let ((org-inlinetask-min-level 3)) (org-test-with-temp-text "* Parent ** Heading 1 ** Heading 2 *** Inlinetask 1 test *** END *** Inlinetask 2 ** Heading 3" (should (org-goto-sibling)) (should (looking-at-p "^\\*\\* Heading 3")) (should-not (org-goto-sibling)) (should (org-goto-sibling 'previous)) (should (looking-at-p "^\\*\\* Heading 2")) (should (org-goto-sibling 'previous)) (should (looking-at-p "^\\*\\* Heading 1")) (should-not (org-goto-sibling 'previous))))) (ert-deftest test-org/get-heading () "Test `org-get-heading' specifications." ;; Return current heading, even if point is not on it. (should (equal "H" (org-test-with-temp-text "* H" (org-get-heading)))) (should (equal "H" (org-test-with-temp-text "* H\nText" (org-get-heading)))) ;; Without any optional argument, return TODO keyword, priority ;; cookie, COMMENT keyword and tags. (should (equal "TODO H" (org-test-with-temp-text "#+TODO: TODO | DONE\n* TODO H" (org-get-heading)))) (should (equal "[#A] H" (org-test-with-temp-text "* [#A] H" (org-get-heading)))) (should (equal "COMMENT H" (org-test-with-temp-text "* COMMENT H" (org-get-heading)))) (should (equal "H :tag:" (org-test-with-temp-text "* H :tag:" (org-get-heading)))) ;; With NO-TAGS argument, ignore tags. (should (equal "TODO H" (org-test-with-temp-text "#+TODO: TODO | DONE\n* TODO H" (org-get-heading t)))) (should (equal "H" (org-test-with-temp-text "* H :tag:" (org-get-heading t)))) ;; With NO-TODO, ignore TODO keyword. (should (equal "H" (org-test-with-temp-text "#+TODO: TODO | DONE\n* TODO H" (org-get-heading nil t)))) (should (equal "H :tag:" (org-test-with-temp-text "* H :tag:" (org-get-heading nil t)))) ;; TODO keywords are case-sensitive. (should (equal "Todo H" (org-test-with-temp-text "#+TODO: TODO | DONE\n* Todo H" (org-get-heading nil t)))) ;; With NO-PRIORITY, ignore priority. (should (equal "H" (org-test-with-temp-text "* [#A] H" (org-get-heading nil nil t)))) (should (equal "H" (org-test-with-temp-text "* H" (org-get-heading nil nil t)))) (should (equal "TODO H" (org-test-with-temp-text "* TODO [#A] H" (org-get-heading nil nil t)))) ;; With NO-COMMENT, ignore COMMENT keyword. (should (equal "H" (org-test-with-temp-text "* COMMENT H" (org-get-heading nil nil nil t)))) (should (equal "H" (org-test-with-temp-text "* H" (org-get-heading nil nil nil t)))) (should (equal "TODO [#A] H" (org-test-with-temp-text "* TODO [#A] COMMENT H" (org-get-heading nil nil nil t)))) ;; On an empty headline, return value is consistent. (should (equal "" (org-test-with-temp-text "* " (org-get-heading)))) (should (equal "" (org-test-with-temp-text "* " (org-get-heading t)))) (should (equal "" (org-test-with-temp-text "* " (org-get-heading nil t)))) (should (equal "" (org-test-with-temp-text "* " (org-get-heading nil nil t)))) (should (equal "" (org-test-with-temp-text "* " (org-get-heading nil nil nil t))))) (ert-deftest test-org/in-commented-heading-p () "Test `org-in-commented-heading-p' specifications." ;; Commented headline. (should (org-test-with-temp-text "* COMMENT Headline\nBody" (goto-char (point-max)) (org-in-commented-heading-p))) ;; Commented ancestor. (should (org-test-with-temp-text "* COMMENT Headline\n** Level 2\nBody" (goto-char (point-max)) (org-in-commented-heading-p))) ;; Comment keyword is case-sensitive. (should-not (org-test-with-temp-text "* Comment Headline\nBody" (goto-char (point-max)) (org-in-commented-heading-p))) ;; Keyword is standalone. (should-not (org-test-with-temp-text "* COMMENTHeadline\nBody" (goto-char (point-max)) (org-in-commented-heading-p))) ;; Optional argument. (should-not (org-test-with-temp-text "* COMMENT Headline\n** Level 2\nBody" (goto-char (point-max)) (org-in-commented-heading-p t)))) (ert-deftest test-org/in-archived-heading-p () "Test `org-in-archived-heading-p' specifications." ;; Archived headline. (should (org-test-with-temp-text "* Headline :ARCHIVE:\nBody" (goto-char (point-max)) (org-in-archived-heading-p))) ;; Archived ancestor. (should (org-test-with-temp-text "* Headline :ARCHIVE:\n** Level 2\nBody" (goto-char (point-max)) (org-in-archived-heading-p))) ;; Optional argument. (should-not (org-test-with-temp-text "* Headline :ARCHIVE:\n** Level 2\nBody" (goto-char (point-max)) (org-in-archived-heading-p t))) ;; Archive tag containing ARCHIVE as substring (should-not (org-test-with-temp-text "* Headline :NOARCHIVE:\n** Level 2\nBody" (goto-char (point-max)) (org-in-archived-heading-p)))) (ert-deftest test-org/entry-blocked-p () ;; Check other dependencies. (should (org-test-with-temp-text "* TODO Blocked\n** DONE one\n** TODO two" (let ((org-enforce-todo-dependencies t) (org-blocker-hook '(org-block-todo-from-children-or-siblings-or-parent))) (org-entry-blocked-p)))) (should-not (org-test-with-temp-text "* TODO Blocked\n** DONE one\n** DONE two" (let ((org-enforce-todo-dependencies t) (org-blocker-hook '(org-block-todo-from-children-or-siblings-or-parent))) (org-entry-blocked-p)))) ;; Entry without a TODO keyword or with a DONE keyword cannot be ;; blocked. (should-not (org-test-with-temp-text "* Blocked\n** TODO one" (let ((org-enforce-todo-dependencies t) (org-blocker-hook '(org-block-todo-from-children-or-siblings-or-parent))) (org-entry-blocked-p)))) (should-not (org-test-with-temp-text "* DONE Blocked\n** TODO one" (let ((org-enforce-todo-dependencies t) (org-blocker-hook '(org-block-todo-from-children-or-siblings-or-parent))) (org-entry-blocked-p)))) ;; Follow :ORDERED: specifications. (should (org-test-with-temp-text "* H\n:PROPERTIES:\n:ORDERED: t\n:END:\n** TODO one\n** TODO two" (let ((org-enforce-todo-dependencies t) (org-blocker-hook '(org-block-todo-from-children-or-siblings-or-parent))) (org-entry-blocked-p)))) (should-not (org-test-with-temp-text "* H\n:PROPERTIES:\n:ORDERED: t\n:END:\n** TODO one\n** DONE two" (let ((org-enforce-todo-dependencies t) (org-blocker-hook '(org-block-todo-from-children-or-siblings-or-parent))) (org-entry-blocked-p))))) (ert-deftest test-org/get-outline-path () "Test `org-get-outline-path' specifications." ;; Top-level headlines have no outline path. (should-not (org-test-with-temp-text "* H" (org-get-outline-path))) ;; Otherwise, outline path is the path leading to the headline. (should (equal '("H") (org-test-with-temp-text "* H\n** S" (org-get-outline-path)))) ;; Find path even when point is not on a headline. (should (equal '("H") (org-test-with-temp-text "* H\n** S\nText" (org-get-outline-path)))) ;; TODO keywords, tags and statistics cookies are ignored. (should (equal '("H") (org-test-with-temp-text "* TODO H [0/1] :tag:\n** S" (org-get-outline-path)))) ;; Links are replaced with their description or their path. (should (equal '("Org") (org-test-with-temp-text "* [[https://orgmode.org][Org]]\n** S" (org-get-outline-path)))) (should (equal '("https://orgmode.org") (org-test-with-temp-text "* [[https://orgmode.org]]\n** S" (org-get-outline-path)))) ;; When WITH-SELF is non-nil, include current heading. (should (equal '("H") (org-test-with-temp-text "* H" (org-get-outline-path t)))) (should (equal '("H" "S") (org-test-with-temp-text "* H\n** S\nText" (org-get-outline-path t)))) ;; Using cache is transparent to the user. (should (equal '("H") (org-test-with-temp-text "* H\n** S" (setq org-outline-path-cache nil) (org-get-outline-path nil t)))) ;; Do not corrupt cache when finding outline path in distant part of ;; the buffer. (should (equal '("H2") (org-test-with-temp-text "* H\n** S\n* H2\n** S2" (setq org-outline-path-cache nil) (org-get-outline-path nil t) (search-forward "S2") (org-get-outline-path nil t)))) ;; Do not choke on empty headlines. (should (org-test-with-temp-text "* H\n** " (org-get-outline-path))) (should (org-test-with-temp-text "* \n** H" (org-get-outline-path))) ;; Remove COMMENTED keywords. (should (equal '("This" "is") (org-test-with-temp-text "* COMMENT This ** COMMENT is *** test " (org-get-outline-path))))) (ert-deftest test-org/format-outline-path () "Test `org-format-outline-path' specifications." (should (string= (org-format-outline-path (list "one" "two" "three")) "one/two/three")) ;; Empty path. (should (string= (org-format-outline-path '()) "")) (should (string= (org-format-outline-path '(nil)) "")) ;; Empty path and prefix. (should (string= (org-format-outline-path '() nil ">>") ">>")) ;; Trailing whitespace in headings. (should (string= (org-format-outline-path (list "one\t" "tw o " "three ")) "one/tw o/three")) ;; Non-default prefix and separators. (should (string= (org-format-outline-path (list "one" "two" "three") nil ">>" "|") ">>|one|two|three")) ;; Truncate. (should (string= (org-format-outline-path (list "one" "two" "three" "four") 10) "one/two/..")) ;; Give a very narrow width. (should (string= (org-format-outline-path (list "one" "two" "three" "four") 2) "on")) ;; Give a prefix that extends beyond the width. (should (string= (org-format-outline-path (list "one" "two" "three" "four") 10 ">>>>>>>>>>") ">>>>>>>>.."))) (ert-deftest test-org/org-find-olp () "Test `org-find-olp' specifications." (org-test-with-temp-text " * Headline ** COMMENT headline2 ** TODO headline3 *** [#A] headline4 :tags: ** [#A]headline5 ** [0%] headline6 ** headline7 [100%] ** headline8 [1/5] :some:more:tags: * Test " (should (org-find-olp '("Headline") t)) (should-error (org-find-olp '("Headline" "Test") t)) (should-error (org-find-olp '("Headlinealksjd") t)) (should (org-find-olp '("Headline" "headline2") t)) (should (org-find-olp '("Headline" "headline3") t)) (should (org-find-olp '("Headline" "headline3" "headline4") t)) (should-error (org-find-olp '("Headline" "headline5") t)) (should (org-find-olp '("Headline" "headline6") t)) (should (org-find-olp '("Headline" "headline7") t)) (should (org-find-olp '("Headline" "headline8") t)))) (ert-deftest test-org/map-entries () "Test `org-map-entries' and `org-element-cache-map' specifications." (dolist (org-element-use-cache '(t nil)) ;; Full match. (should (equal '(1 11) (org-test-with-temp-text "* Level 1\n** Level 2" (org-map-entries #'point)))) ;; Level match. (should (equal '(1) (org-test-with-temp-text "* Level 1\n** Level 2" (let (org-odd-levels-only) (org-map-entries #'point "LEVEL=1"))))) (should (equal '(11) (org-test-with-temp-text "* Level 1\n** Level 2" (let (org-odd-levels-only) (org-map-entries #'point "LEVEL>1"))))) ;; Category match. (should (equal '(59) (org-test-with-temp-text " #+CATEGORY: foo * H1 :PROPERTIES: :CATEGORY: bar :END: * H2" (org-map-entries #'point "CATEGORY=\"foo\"")))) ;; Todo match. (should (equal '(6) (org-test-with-temp-text "* H1\n* TODO H2\n* DONE H3" (org-map-entries #'point "TODO=\"TODO\"")))) ;; Tag match. (should (equal '(11) (org-test-with-temp-text "* H1 :no:\n* H2 :yes:" (org-map-entries #'point "yes")))) (should (equal '(14) (org-test-with-temp-text "* H1 :yes:a:\n* H2 :yes:b:" (org-map-entries #'point "+yes-a")))) (should (equal '(11 23) (org-test-with-temp-text "* H1 :no:\n* H2 :yes1:\n* H3 :yes2:" (org-map-entries #'point "{yes.?}")))) ;; Priority match. (should (equal '(1) (org-test-with-temp-text "* [#A] H1\n* [#B] H2" (org-map-entries #'point "PRIORITY=\"A\"")))) ;; Negative priority match. (should (equal '(11) (org-test-with-temp-text "* [#A] H1\n* [#B] H2" (org-map-entries #'point "PRIORITY/=\"A\"")))) ;; Date match. (should (equal '(36) (org-test-with-temp-text " * H1 SCHEDULED: <2012-03-29 thu.> * H2 SCHEDULED: <2014-03-04 tue.>" (org-map-entries #'point "SCHEDULED=\"<2014-03-04 tue.>\"")))) (should (equal '(2) (org-test-with-temp-text " * H1 SCHEDULED: <2012-03-29 thu.> * H2 SCHEDULED: <2014-03-04 tue.>" (org-map-entries #'point "SCHEDULED<\"<2013-01-01>\"")))) ;; Regular property match. (should (equal '(2) (org-test-with-temp-text " * H1 :PROPERTIES: :TEST: 1 :END: * H2 :PROPERTIES: :TEST: 2 :END:" (org-map-entries #'point "TEST=1")))) ;; Regular negative property match. (should (equal '(35 68) (org-test-with-temp-text " * H1 :PROPERTIES: :TEST: 1 :END: * H2 :PROPERTIES: :TEST: 2 :END: * H3" (org-map-entries #'point "TEST!=1")))) ;; Starred negative property match. (should (equal '(35) (org-test-with-temp-text " * H1 :PROPERTIES: :TEST: 1 :END: * H2 :PROPERTIES: :TEST: 2 :END: * H3" (org-map-entries #'point "TEST!=*1")))) ;; Property matches on names containing quoted characters. (org-test-with-temp-text " * H1 :BAR: :PROPERTIES: :TEST-FOO: 1 :END: * H2 :FOO: :PROPERTIES: :TEST-FOO: 2 :END: * H3 :BAR: :PROPERTIES: :-FOO: 1 :END: * H4 :FOO: :PROPERTIES: :-FOO: 2 :END: * H5 :TEST:" (should (equal '(2) (org-map-entries #'point "TEST\\-FOO!=*0-FOO"))) (should (equal '(2) (org-map-entries #'point "-FOO+TEST\\-FOO!=*0"))) (should (equal '(88) (org-map-entries #'point "\\-FOO!=*0-FOO"))) (should (equal '(88) (org-map-entries #'point "-FOO+\\-FOO!=*0"))) (should (equal '(88) (org-map-entries #'point "-TEST-FOO-TEST\\-FOO=1")))) ;; Multiple criteria. (should (equal '(23) (org-test-with-temp-text "* H1 :no:\n** H2 :yes:\n* H3 :yes:" (let (org-odd-levels-only (org-use-tag-inheritance nil)) (org-map-entries #'point "yes+LEVEL=1"))))) ;; "or" criteria. (should (equal '(12 24) (org-test-with-temp-text "* H1 :yes:\n** H2 :yes:\n** H3 :no:" (let (org-odd-levels-only) (org-map-entries #'point "LEVEL=2|no"))))) (should (equal '(1 12) (org-test-with-temp-text "* H1 :yes:\n* H2 :no:\n* H3 :maybe:" (let (org-odd-levels-only) (org-map-entries #'point "yes|no"))))) ;; "and" criteria. (should (equal '(22) (org-test-with-temp-text "* H1 :yes:\n* H2 :no:\n* H3 :yes:no:" (let (org-odd-levels-only) (org-map-entries #'point "yes&no"))))) ;; Setting `org-map-continue-from' (should (string= "" (org-test-with-temp-text "* H1\n* H2\n* H3n* H4" (org-map-entries (lambda () (org-cut-subtree) (setq org-map-continue-from (point)))) (buffer-string)))) (should (string= "* H1\n* H2\n* H3\n" (org-test-with-temp-text "* H1\n* H2\n* H3\n* H4" (org-map-entries (lambda () (when (string= "H4" (org-element-property :raw-value (org-element-at-point))) (org-cut-subtree) (setq org-map-continue-from (org-element-property :begin (org-element-at-point)))))) (buffer-string)))) ;; Move point. (should (= 1 (org-test-with-temp-text "* H1\n** H1.1\n** H1.2\n" (let (acc) (org-map-entries (lambda () (push (org-element-property :title (org-element-at-point)) acc) (setq org-map-continue-from (org-element-property :end (org-element-at-point))))) (length acc))))) (should (= 2 (org-test-with-temp-text "* H1\n** H1.1\n** H1.2\n" (let (acc) (org-map-entries (lambda () (push (org-element-property :title (org-element-at-point)) acc) (setq org-map-continue-from (line-end-position 2)))) (length acc))))) ;; Modifications inside indirect buffer. (should (= 3 (org-test-with-temp-text "* H1\n** H1.1\n** H1.2\n" (with-current-buffer (org-get-indirect-buffer) (let ((acc 0)) (org-map-entries (lambda () (cl-incf acc) (beginning-of-line 2) (insert "test\n") (beginning-of-line -1))) acc))))) ;; Removing heading being processed. (should (equal "Some text Some text Some more text Let’s stop here " (org-test-with-temp-text "* Heading 1 Some text ** Heading 1.1 Some text * Heading 2 Some more text ** Heading 2.1 Let’s stop here " (org-map-entries (lambda () (delete-region (point) (line-beginning-position 2)) (setq org-map-continue-from (point)))) (buffer-string)))) ;; :next-re in `org-element-cache-map' (org-test-with-temp-text "* one * TODO two * three * four " (should (equal '("two") (org-element-cache-map (lambda (el) (org-element-property :title el)) :next-re "TODO")))))) (ert-deftest test-org/edit-headline () "Test `org-edit-headline' specifications." (should (equal "* B" (org-test-with-temp-text "* A" (org-edit-headline "B") (buffer-string)))) ;; Handle empty headings. (should (equal "* " (org-test-with-temp-text "* A" (org-edit-headline "") (buffer-string)))) (should (equal "* A" (org-test-with-temp-text "* " (org-edit-headline "A") (buffer-string)))) ;; Handle TODO keywords and priority cookies. (should (equal "* TODO B" (org-test-with-temp-text "* TODO A" (org-edit-headline "B") (buffer-string)))) (should (equal "* [#A] B" (org-test-with-temp-text "* [#A] A" (org-edit-headline "B") (buffer-string)))) (should (equal "* TODO [#A] B" (org-test-with-temp-text "* TODO [#A] A" (org-edit-headline "B") (buffer-string)))) ;; Handle tags. (equal "* B :tag:" (org-test-with-temp-text "* A :tag:" (let ((org-tags-column 4)) (org-edit-headline "B")) (buffer-string)))) ;;; Keywords (ert-deftest test-org/set-regexps-and-options () "Test `org-set-regexps-and-options' specifications." ;; TAGS keyword. (should (equal '(("A")) (let ((org-tag-alist '(("A"))) (org-tag-persistent-alist nil)) (org-test-with-temp-text "" (org-mode-restart) org-current-tag-alist)))) (should (equal '(("B")) (let ((org-tag-alist '(("A"))) (org-tag-persistent-alist nil)) (org-test-with-temp-text "#+TAGS: B" (org-mode-restart) org-current-tag-alist)))) (should (equal '(("C") ("B")) (let ((org-tag-alist '(("A"))) (org-tag-persistent-alist '(("C")))) (org-test-with-temp-text "#+TAGS: B" (org-mode-restart) org-current-tag-alist)))) (should (equal '(("B")) (let ((org-tag-alist '(("A"))) (org-tag-persistent-alist '(("C")))) (org-test-with-temp-text "#+STARTUP: noptag\n#+TAGS: B" (org-mode-restart) org-current-tag-alist)))) (should (equal '(("A" . ?a) ("B") ("C")) (let ((org-tag-persistent-alist nil)) (org-test-with-temp-text "#+TAGS: A(a) B C" (org-mode-restart) org-current-tag-alist)))) (should (equal '(("A") (:newline) ("B")) (let ((org-tag-persistent-alist nil)) (org-test-with-temp-text "#+TAGS: A\n#+TAGS: B" (org-mode-restart) org-current-tag-alist)))) (should (equal '((:startgroup) ("A") ("B") (:endgroup) ("C")) (let ((org-tag-persistent-alist nil)) (org-test-with-temp-text "#+TAGS: { A B } C" (org-mode-restart) org-current-tag-alist)))) (should (equal '((:startgroup) ("A") (:grouptags) ("B") ("C") (:endgroup)) (let ((org-tag-persistent-alist nil)) (org-test-with-temp-text "#+TAGS: { A : B C }" (org-mode-restart) org-current-tag-alist)))) (should (equal '(("A" "B" "C")) (let ((org-tag-persistent-alist nil)) (org-test-with-temp-text "#+TAGS: { A : B C }" (org-mode-restart) org-tag-groups-alist)))) (should (equal '((:startgrouptag) ("A") (:grouptags) ("B") ("C") (:endgrouptag)) (let ((org-tag-persistent-alist nil)) (org-test-with-temp-text "#+TAGS: [ A : B C ]" (org-mode-restart) org-current-tag-alist)))) (should (equal '(("A" "B" "C")) (let ((org-tag-persistent-alist nil)) (org-test-with-temp-text "#+TAGS: [ A : B C ]" (org-mode-restart) org-tag-groups-alist)))) (should-not (let ((org-tag-alist '(("A")))) (org-test-with-temp-text "#+TAGS:" (org-mode-restart) org-current-tag-alist))) ;; FILETAGS keyword. (should (equal '("A" "B" "C") (org-test-with-temp-text "#+FILETAGS: :A:B:C:" (org-mode-restart) org-file-tags))) ;; PROPERTY keyword. Property names are case-insensitive. (should (equal "foo=1" (org-test-with-temp-text "#+PROPERTY: var foo=1" (org-mode-restart) (cdr (assoc "var" org-keyword-properties))))) (should (equal "foo=1 bar=2" (org-test-with-temp-text "#+PROPERTY: var foo=1\n#+PROPERTY: var+ bar=2" (org-mode-restart) (cdr (assoc "var" org-keyword-properties))))) (should (equal "foo=1 bar=2" (org-test-with-temp-text "#+PROPERTY: var foo=1\n#+PROPERTY: VAR+ bar=2" (org-mode-restart) (cdr (assoc "var" org-keyword-properties))))) ;; ARCHIVE keyword. (should (equal "%s_done::" (org-test-with-temp-text "#+ARCHIVE: %s_done::" (org-mode-restart) org-archive-location))) ;; CATEGORY keyword. (should (eq 'test (org-test-with-temp-text "#+CATEGORY: test" (org-mode-restart) org-category))) (should (equal "test" (org-test-with-temp-text "#+CATEGORY: test" (org-mode-restart) (cdr (assoc "CATEGORY" org-keyword-properties))))) ;; COLUMNS keyword. (should (equal "%25ITEM %TAGS %PRIORITY %TODO" (org-test-with-temp-text "#+COLUMNS: %25ITEM %TAGS %PRIORITY %TODO" (org-mode-restart) org-columns-default-format))) ;; CONSTANTS keyword. Constants names are case sensitive. (should (equal '("299792458." "3.14") (org-test-with-temp-text "#+CONSTANTS: c=299792458. pi=3.14" (org-mode-restart) (mapcar (lambda (n) (cdr (assoc n org-table-formula-constants-local))) '("c" "pi"))))) (should (equal "3.14" (org-test-with-temp-text "#+CONSTANTS: pi=22/7 pi=3.14" (org-mode-restart) (cdr (assoc "pi" org-table-formula-constants-local))))) (should (equal "22/7" (org-test-with-temp-text "#+CONSTANTS: PI=22/7 pi=3.14" (org-mode-restart) (cdr (assoc "PI" org-table-formula-constants-local))))) ;; LINK keyword. (should (equal '("url1" "url2") (org-test-with-temp-text "#+LINK: a url1\n#+LINK: b url2" (org-mode-restart) (mapcar (lambda (abbrev) (cdr (assoc abbrev org-link-abbrev-alist-local))) '("a" "b"))))) ;; PRIORITIES keyword. Incomplete priorities sets are ignored. (should (equal '(?X ?Z ?Y) (org-test-with-temp-text "#+PRIORITIES: X Z Y" (org-mode-restart) (list org-priority-highest org-priority-lowest org-priority-default)))) (should (equal '(?A ?C ?B) (org-test-with-temp-text "#+PRIORITIES: X Z" (org-mode-restart) (list org-priority-highest org-priority-lowest org-priority-default)))) ;; STARTUP keyword. (should (equal '(fold t) (org-test-with-temp-text "#+STARTUP: fold odd" (org-mode-restart) (list org-startup-folded org-odd-levels-only)))) ;; TODO keywords. (should (equal '(("A" "B") ("C")) (org-test-with-temp-text "#+TODO: A B | C" (org-mode-restart) (list org-not-done-keywords org-done-keywords)))) (should (equal '(("A" "C") ("B" "D")) (org-test-with-temp-text "#+TODO: A | B\n#+TODO: C | D" (org-mode-restart) (list org-not-done-keywords org-done-keywords)))) (should (equal '(("A" "B") ("C")) (org-test-with-temp-text "#+TYP_TODO: A B | C" (org-mode-restart) (list org-not-done-keywords org-done-keywords)))) (should (equal '((:startgroup) ("A" . ?a) (:endgroup)) (org-test-with-temp-text "#+TODO: A(a)" (org-mode-restart) org-todo-key-alist))) (should (equal '(("D" note nil) ("C" time nil) ("B" note time)) (org-test-with-temp-text "#+TODO: A(a) B(b@/!) | C(c!) D(d@)" (org-mode-restart) org-todo-log-states))) ;; Enter SETUPFILE keyword. (should (equal "1" (org-test-with-temp-text (format "#+SETUPFILE: \"%s/examples/setupfile.org\"" org-test-dir) (org-mode-restart) (cdr (assoc "a" org-keyword-properties)))))) (ert-deftest test-org/collect-keywords () "Test `org-collect-keywords'." (should-not (org-test-with-temp-text "#+begin_example\n#+foo: bar\n#+end_example" (org-collect-keywords '("FOO"))))) ;;; Links ;;;; Coderefs (ert-deftest test-org/coderef () "Test coderef links specifications." (should (org-test-with-temp-text " #+BEGIN_SRC emacs-lisp \(+ 1 1) (ref:sc) #+END_SRC \[[(sc)]]" (org-open-at-point) (looking-at "(ref:sc)"))) ;; Find coderef even with alternate label format. (should (org-test-with-temp-text " #+BEGIN_SRC emacs-lisp -l \"{ref:%s}\" \(+ 1 1) {ref:sc} #+END_SRC \[[(sc)]]" (org-open-at-point) (looking-at "{ref:sc}")))) ;;;; Custom ID (ert-deftest test-org/custom-id () "Test custom ID links specifications." (should (org-test-with-temp-text "* H1\n:PROPERTIES:\n:CUSTOM_ID: custom\n:END:\n* H2\n[[#custom]]" (org-open-at-point) (looking-at-p "\\* H1"))) ;; Handle escape characters. (should (org-test-with-temp-text "* H1\n:PROPERTIES:\n:CUSTOM_ID: [%]\n:END:\n* H2\n[[#\\[%\\]]]" (org-open-at-point) (looking-at-p "\\* H1"))) ;; Throw an error on false positives. (should-error (org-test-with-temp-text "* H1\n:DRAWER:\n:CUSTOM_ID: custom\n:END:\n* H2\n[[#custom]]" (org-open-at-point) (looking-at-p "\\* H1")))) ;;;; Fuzzy Links ;; Fuzzy links [[text]] encompass links to a target (<>), to ;; a named element (#+name: text) and to headlines (* Text). (ert-deftest test-org/fuzzy-links () "Test fuzzy links specifications." ;; Fuzzy link goes in priority to a matching target. (should (org-test-with-temp-text "#+NAME: Test\n|a|b|\n<>\n* Test\n[[Test]]" (let ((org-link-search-must-match-exact-headline nil)) (org-open-at-point)) (looking-at "<>"))) ;; Then fuzzy link points to an element with a given name. (should (org-test-with-temp-text "Test\n#+NAME: Test\n|a|b|\n* Test\n[[Test]]" (let ((org-link-search-must-match-exact-headline nil)) (org-open-at-point)) (looking-at "#\\+NAME: Test"))) ;; A target still lead to a matching headline otherwise. (should (org-test-with-temp-text "* Head1\n* Head2\n*Head3\n[[Head2]]" (let ((org-link-search-must-match-exact-headline nil)) (org-open-at-point)) (looking-at "\\* Head2"))) ;; With a leading star in link, enforce heading match. (should (org-test-with-temp-text "* Test\n<>\n[[*Test]]" (let ((org-link-search-must-match-exact-headline nil)) (org-open-at-point)) (looking-at "\\* Test"))) ;; With a leading star in link, enforce exact heading match, even ;; with `org-link-search-must-match-exact-headline' set to nil. (should-error (org-test-with-temp-text "* Test 1\nFoo Bar\n[[*Test]]" (let ((org-link-search-must-match-exact-headline nil)) (org-open-at-point)))) ;; Handle non-nil `org-link-search-must-match-exact-headline'. (should (org-test-with-temp-text "* Test\nFoo Bar\n[[Test]]" (let ((org-link-search-must-match-exact-headline t)) (org-open-at-point)) (looking-at "\\* Test"))) (should (org-test-with-temp-text "* Test\nFoo Bar\n[[*Test]]" (let ((org-link-search-must-match-exact-headline t)) (org-open-at-point)) (looking-at "\\* Test"))) ;; Heading match should not care about spaces, cookies, TODO ;; keywords, priorities, and tags. However, TODO keywords are ;; case-sensitive. (should (let ((first-line "** TODO [#A] [/] Test [1/2] [33%] 1 \t 2 [%] :work:urgent: ")) (org-test-with-temp-text (concat first-line "\nFoo Bar\n[[*Test 1 2]]") (let ((org-link-search-must-match-exact-headline nil) (org-todo-regexp "TODO")) (org-open-at-point)) (looking-at (regexp-quote first-line))))) (should-error (org-test-with-temp-text "** todo Test 1 2\nFoo Bar\n[[*Test 1 2]]" (let ((org-link-search-must-match-exact-headline nil) (org-todo-regexp "TODO")) (org-open-at-point)))) ;; Heading match should still be exact. (should-error (org-test-with-temp-text " ** TODO [#A] [/] Test [1/2] [33%] 1 \t 2 [%] :work:urgent: Foo Bar [[*Test 1]]" (let ((org-link-search-must-match-exact-headline nil) (org-todo-regexp "TODO")) (org-open-at-point)))) (should (org-test-with-temp-text "* Test 1 2 3\n** Test 1 2\n[[*Test 1 2]]" (let ((org-link-search-must-match-exact-headline nil) (org-todo-regexp "TODO")) (org-open-at-point)) (looking-at-p (regexp-quote "** Test 1 2")))) ;; Heading match ignores COMMENT keyword. (should (org-test-with-temp-text "[[*Test]]\n* COMMENT Test" (org-open-at-point) (looking-at "\\* COMMENT Test"))) (should (org-test-with-temp-text "[[*Test]]\n* TODO COMMENT Test" (org-open-at-point) (looking-at "\\* TODO COMMENT Test"))) ;; Correctly un-escape fuzzy links. (should (org-test-with-temp-text "* [foo]\n[[*\\[foo\\]][With escaped characters]]" (org-open-at-point) (bobp))) ;; Match search strings containing newline characters, including ;; blank lines. (should (org-test-with-temp-text-in-file "Paragraph\n\nline1\nline2\n\n" (let ((file (buffer-file-name))) (goto-char (point-max)) (insert (format "[[file:%s::line1 line2]]" file)) (beginning-of-line) (let ((org-link-search-must-match-exact-headline nil)) (org-open-at-point 0)) (looking-at-p "line1")))) (should (org-test-with-temp-text-in-file "Paragraph\n\nline1\n\nline2\n\n" (let ((file (buffer-file-name))) (goto-char (point-max)) (insert (format "[[file:%s::line1 line2]]" file)) (beginning-of-line) (let ((org-link-search-must-match-exact-headline nil)) (org-open-at-point 0)) (looking-at-p "line1"))))) ;;;; Open at point (ert-deftest test-org/open-at-point/keyword () "Does `org-open-at-point' open link in a keyword line?" (should (org-test-with-temp-text "<>\n#+KEYWORD: [[top]]" (org-open-at-point) t)) (should (org-test-with-temp-text "* H\n<>\n#+KEYWORD: [[top]]" (org-open-at-point) t))) (ert-deftest test-org/open-at-point/property () "Does `org-open-at-point' open link in property drawer?" (should (org-test-with-temp-text "* Headline :PROPERTIES: :URL: [[*Headline]] :END:" (org-open-at-point) t))) (ert-deftest test-org/open-at-point/comment () "Does `org-open-at-point' open link in a commented line?" (should (org-test-with-temp-text "<>\n# [[top]]" (org-open-at-point) t)) (should (org-test-with-temp-text "* H\n<>\n# [[top]]" (org-open-at-point) t))) (ert-deftest test-org/open-at-point/inline-image () "Test `org-open-at-point' on nested links." (should (org-test-with-temp-text "<>\n[[top][file:unicorn.jpg]]" (org-open-at-point) (bobp)))) (ert-deftest test-org/open-at-point/radio-target () "Test `org-open-at-point' on radio targets." (should (org-test-with-temp-text "<<>> target" (org-update-radio-target-regexp) (org-open-at-point) (org-element-type-p (org-element-context) 'radio-target)))) (ert-deftest test-org/open-at-point/radio-target-shadowed () "Test `org-open-at-point' on shadowed radio targets." (should (org-test-with-temp-text "<<>> <<>> target shadowed" (org-update-radio-target-regexp) (org-open-at-point) (string= (org-element-property :value (org-element-radio-target-parser)) "target shadowed")))) (ert-deftest test-org/open-at-point/tag () "Test `org-open-at-point' on tags." (should (org-test-with-temp-text "* H :tag:" (catch :result (cl-letf (((symbol-function 'org-tags-view) (lambda (&rest _args) (throw :result t)))) (org-open-at-point) nil)))) (should-not (org-test-with-temp-text-in-file "* H :tag:" (catch :result (cl-letf (((symbol-function 'org-tags-view) (lambda (&rest _args) (throw :result t)))) ;; When point isn't on a tag it's going to try other things, ;; possibly trying to open attachments which will return an ;; error if there isn't an attachment. Suppress that error. (ignore-errors (org-open-at-point)) nil))))) ;;; Thing at point (ert-deftest test-org/thing-at-point/url () "Test that `thing-at-point' returns the URL at point." (org-test-with-temp-text "[[https://www.gnu.org/software/emacs/][GNU Emacs]]" (when (boundp 'thing-at-point-provider-alist) (should (string= (thing-at-point 'url) "https://www.gnu.org/software/emacs/"))) (when (boundp 'bounds-of-thing-at-point-provider-alist) (should (equal (bounds-of-thing-at-point 'url) '(1 . 51)))))) ;;; Node Properties (ert-deftest test-org/accumulated-properties-in-drawers () "Ensure properties accumulate in subtree drawers." (org-test-at-id "75282ba2-f77a-4309-a970-e87c149fe125" (org-babel-next-src-block) (should (equal '(2 1) (org-babel-execute-src-block))))) (ert-deftest test-org/custom-properties () "Test custom properties specifications." ;; Standard test. (should (let ((org-custom-properties '("FOO"))) (org-test-with-temp-text "* H\n:PROPERTIES:\n:FOO: val\n:END:\n" (org-toggle-custom-properties-visibility) (org-invisible-p2)))) ;; Properties are case-insensitive. (should (let ((org-custom-properties '("FOO"))) (org-test-with-temp-text "* H\n:PROPERTIES:\n:foo: val\n:END:\n" (org-toggle-custom-properties-visibility) (org-invisible-p2)))) (should (let ((org-custom-properties '("foo"))) (org-test-with-temp-text "* H\n:PROPERTIES:\n:FOO: val\n:END:\n" (org-toggle-custom-properties-visibility) (org-invisible-p2)))) ;; Multiple custom properties in the same drawer. (should (let ((org-custom-properties '("FOO" "BAR"))) (org-test-with-temp-text "* H\n:PROPERTIES:\n:FOO: val\n:P: 1\n:BAR: baz\n:END:\n" (org-fold-reveal) (org-toggle-custom-properties-visibility) (and (org-invisible-p2) (not (progn (forward-line) (org-invisible-p2))) (progn (forward-line) (org-invisible-p2)))))) ;; Hide custom properties with an empty value. (should (let ((org-custom-properties '("FOO"))) (org-test-with-temp-text "* H\n:PROPERTIES:\n:FOO:\n:END:\n" (org-toggle-custom-properties-visibility) (org-invisible-p2)))) ;; Do not hide fake properties. (should-not (let ((org-custom-properties '("FOO"))) (org-test-with-temp-text ":FOO: val\n" (org-toggle-custom-properties-visibility) (org-invisible-p2)))) (should-not (let ((org-custom-properties '("A"))) (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:\n\n:PROPERTIES:\n:A: 2\n:END:" (org-fold-reveal) (org-toggle-custom-properties-visibility) (org-invisible-p2))))) ;;; Mark Region (ert-deftest test-org/mark-element () "Test `org-mark-element' specifications." ;; Mark beginning and end of element. (should (equal '(t t) (org-test-with-temp-text "Paragraph" (org-mark-element) (list (bobp) (= (mark) (point-max)))))) (should (equal '(t t) (org-test-with-temp-text "P1\n\nParagraph\n\nP2" (org-mark-element) (list (looking-at "Paragraph") (org-with-point-at (mark) (looking-at "P2")))))) ;; Do not set mark past (point-max). (should (org-test-with-temp-text "Paragraph" (narrow-to-region 2 6) (org-mark-element) (= 6 (mark))))) (ert-deftest test-org/mark-subtree () "Test `org-mark-subtree' specifications." ;; Error when point is before first headline. (should-error (org-test-with-temp-text "Paragraph\n* Headline\nBody" (progn (transient-mark-mode 1) (org-mark-subtree)))) ;; Without argument, mark current subtree. (should (equal '(12 32) (org-test-with-temp-text "* Headline\n** Sub-headline\nBody" (progn (transient-mark-mode 1) (forward-line 2) (org-mark-subtree) (list (region-beginning) (region-end)))))) ;; With an argument, move ARG up. (should (equal '(1 32) (org-test-with-temp-text "* Headline\n** Sub-headline\nBody" (progn (transient-mark-mode 1) (forward-line 2) (org-mark-subtree 1) (list (region-beginning) (region-end)))))) ;; Do not get fooled by inlinetasks. (when (featurep 'org-inlinetask) (should (= 1 (org-test-with-temp-text "* Headline\n*************** Task\nContents" (progn (transient-mark-mode 1) (forward-line 1) (let ((org-inlinetask-min-level 15)) (org-mark-subtree)) (region-beginning))))))) ;;; Miscellaneous (ert-deftest test-org/sort-entries () "Test `org-sort-entries'." ;; Sort alphabetically. (should (equal "\n* abc\n* def\n* xyz\n" (org-test-with-temp-text "\n* def\n* xyz\n* abc\n" (org-sort-entries nil ?a) (buffer-string)))) (should (equal "\n* xyz\n* def\n* abc\n" (org-test-with-temp-text "\n* def\n* xyz\n* abc\n" (org-sort-entries nil ?A) (buffer-string)))) (should (equal "\n* \n* klm\n* xyz\n" (org-test-with-temp-text "\n* xyz\n* \n* klm\n" (org-sort-entries nil ?a) (buffer-string)))) ;; Sort numerically. (should (equal "\n* 1\n* 2\n* 10\n" (org-test-with-temp-text "\n* 10\n* 1\n* 2\n" (org-sort-entries nil ?n) (buffer-string)))) (should (equal "\n* 10\n* 2\n* 1\n" (org-test-with-temp-text "\n* 10\n* 1\n* 2\n" (org-sort-entries nil ?N) (buffer-string)))) (should (equal "\n* \n* 1\n* 2\n" (org-test-with-temp-text "\n* 1\n* \n* 2\n" (org-sort-entries nil ?n) (buffer-string)))) ;; Sort by custom function. (should (equal "\n* b\n* aa\n* ccc\n" (org-test-with-temp-text "\n* ccc\n* b\n* aa\n" (org-sort-entries nil ?f (lambda () (length (buffer-substring (point-at-bol) (point-at-eol)))) #'<) (buffer-string)))) (should (equal "\n* ccc\n* aa\n* b\n" (org-test-with-temp-text "\n* ccc\n* b\n* aa\n" (org-sort-entries nil ?F (lambda () (length (buffer-substring (point-at-bol) (point-at-eol)))) #'<) (buffer-string)))) ;; Sort by TODO keyword. (should (equal "\n* TODO h1\n* TODO h3\n* DONE h2\n" (org-test-with-temp-text "\n* TODO h1\n* DONE h2\n* TODO h3\n" (org-sort-entries nil ?o) (buffer-string)))) (should (equal "\n* DONE h2\n* TODO h1\n* TODO h3\n" (org-test-with-temp-text "\n* TODO h1\n* DONE h2\n* TODO h3\n" (org-sort-entries nil ?O) (buffer-string)))) ;; Sort by priority. (should (equal "\n* [#A] h2\n* [#B] h3\n* [#C] h1\n" (org-test-with-temp-text "\n* [#C] h1\n* [#A] h2\n* [#B] h3\n" (org-sort-entries nil ?p) (buffer-string)))) (should (equal "\n* [#C] h1\n* [#B] h3\n* [#A] h2\n" (org-test-with-temp-text "\n* [#C] h1\n* [#A] h2\n* [#B] h3\n" (org-sort-entries nil ?P) (buffer-string)))) ;; Sort by creation time. (should (equal " * h3 [2017-05-08 Mon] * h2 [2017-05-09 Tue] * h1 [2018-05-09 Wed] " (org-test-with-temp-text " * h1 [2018-05-09 Wed] * h2 [2017-05-09 Tue] * h3 [2017-05-08 Mon] " (org-sort-entries nil ?c) (buffer-string)))) ;; Sort by scheduled date. (should (equal " * TODO h4 SCHEDULED: <2017-05-06 Sat> * TODO h3 SCHEDULED: <2017-05-08 Mon> * TODO h2 DEADLINE: <2017-05-09 Tue> * TODO h1 DEADLINE: <2017-05-07 Sun> " (org-test-with-temp-text " * TODO h2 DEADLINE: <2017-05-09 Tue> * TODO h1 DEADLINE: <2017-05-07 Sun> * TODO h3 SCHEDULED: <2017-05-08 Mon> * TODO h4 SCHEDULED: <2017-05-06 Sat> " (org-sort-entries nil ?s) (buffer-string)))) ;; Sort by deadline date. (should (equal " * TODO h1 DEADLINE: <2017-05-07 Sun> * TODO h2 DEADLINE: <2017-05-09 Tue> * TODO h3 SCHEDULED: <2017-05-08 Mon> * TODO h4 SCHEDULED: <2017-05-06 Sat> " (org-test-with-temp-text " * TODO h2 DEADLINE: <2017-05-09 Tue> * TODO h1 DEADLINE: <2017-05-07 Sun> * TODO h3 SCHEDULED: <2017-05-08 Mon> * TODO h4 SCHEDULED: <2017-05-06 Sat> " (org-sort-entries nil ?d) (buffer-string)))) ;; Sort by any date/time (should (equal " * TODO h4 SCHEDULED: <2017-05-06 Sat> * TODO h1 DEADLINE: <2017-05-07 Sun> * TODO h3 SCHEDULED: <2017-05-08 Mon> * TODO h2 DEADLINE: <2017-05-09 Tue> " (org-test-with-temp-text " * TODO h2 DEADLINE: <2017-05-09 Tue> * TODO h1 DEADLINE: <2017-05-07 Sun> * TODO h3 SCHEDULED: <2017-05-08 Mon> * TODO h4 SCHEDULED: <2017-05-06 Sat> " (org-sort-entries nil ?t) (buffer-string)))) ;; Sort by clocking time. (should (equal " * clocked h2 :LOGBOOK: CLOCK: [2017-05-09 Tue 00:15]--[2017-05-09 Tue 00:22] => 0:07 CLOCK: [2017-05-09 Tue 00:00]--[2017-05-09 Tue 00:10] => 0:10 :END: * clocked h1 :LOGBOOK: CLOCK: [2017-05-09 Tue 00:15]--[2017-05-09 Tue 00:22] => 0:07 CLOCK: [2017-05-09 Tue 00:00]--[2017-05-09 Tue 00:12] => 0:12 :END: " (org-test-with-temp-text " * clocked h1 :LOGBOOK: CLOCK: [2017-05-09 Tue 00:15]--[2017-05-09 Tue 00:22] => 0:07 CLOCK: [2017-05-09 Tue 00:00]--[2017-05-09 Tue 00:12] => 0:12 :END: * clocked h2 :LOGBOOK: CLOCK: [2017-05-09 Tue 00:15]--[2017-05-09 Tue 00:22] => 0:07 CLOCK: [2017-05-09 Tue 00:00]--[2017-05-09 Tue 00:10] => 0:10 :END: " (org-sort-entries nil ?k) (buffer-string)))) ;; Preserve file local variables when sorting. (should (equal "\n* A\n* B\n# Local Variables:\n# foo: t\n# End:\n" (org-test-with-temp-text "\n* B\n* A\n# Local Variables:\n# foo: t\n# End:" (org-sort-entries nil ?a) (buffer-string)))) ;; Sort region (should (equal " * [#A] h2 * [#B] h3 * [#C] h1 " (org-test-with-temp-text " * [#C] h1 * [#A] h2 * [#B] h3" (transient-mark-mode 1) (push-mark (point) t t) (search-forward "h3") (org-sort-entries nil ?p) (buffer-string)))) ) (ert-deftest test-org/string-collate-greaterp () "Test `org-string-collate-greaterp' specifications." (should (org-string-collate-greaterp "def" "abc")) (should-not (org-string-collate-greaterp "abc" "def"))) (ert-deftest test-org/file-contents () "Test `org-file-contents' specifications." ;; Open files. (should (string= "#+BIND: variable value #+DESCRIPTION: l2 #+LANGUAGE: en #+SELECT_TAGS: b #+TITLE: b #+PROPERTY: a 1 " (org-file-contents (expand-file-name "setupfile3.org" (concat org-test-dir "examples/"))))) ;; Throw error when trying to access an invalid file. (should-error (org-file-contents "this-file-must-not-exist")) ;; Try to access an invalid file, but do not throw an error. (should (progn (org-file-contents "this-file-must-not-exist" :noerror) t)) ;; Open URL. (should (let ((org-resource-download-policy t)) (string= "foo" (let ((buffer (generate-new-buffer "url-retrieve-output"))) (unwind-protect ;; Simulate successful retrieval of a URL. (cl-letf (((symbol-function 'url-retrieve-synchronously) (lambda (&rest_) (with-current-buffer buffer (insert "HTTP/1.1 200 OK\n\nfoo")) buffer))) (org-file-contents "http://some-valid-url")) (kill-buffer buffer)))))) ;; Throw error when trying to access an invalid URL. (should-not (let ((buffer (generate-new-buffer "url-retrieve-output")) (org-resource-download-policy t)) (unwind-protect ;; Simulate unsuccessful retrieval of a URL. (cl-letf (((symbol-function 'url-retrieve-synchronously) (lambda (&rest_) (with-current-buffer buffer (insert "HTTP/1.1 404 Not found\n\ndoes not matter")) buffer))) (org-file-contents "http://this-url-must-not-exist" 'noerror)) (kill-buffer buffer)))) ;; Try to access an invalid URL, but do not throw an error. (should-error (let ((buffer (generate-new-buffer "url-retrieve-output")) (org-resource-download-policy t)) (unwind-protect ;; Simulate unsuccessful retrieval of a URL. (cl-letf (((symbol-function 'url-retrieve-synchronously) (lambda (&rest_) (with-current-buffer buffer (insert "HTTP/1.1 404 Not found\n\ndoes not matter")) buffer))) (org-file-contents "http://this-url-must-not-exist")) (kill-buffer buffer)))) (should (let ((buffer (generate-new-buffer "url-retrieve-output")) (org-resource-download-policy t)) (unwind-protect ;; Simulate unsuccessful retrieval of a URL. (cl-letf (((symbol-function 'url-retrieve-synchronously) (lambda (&rest_) (with-current-buffer buffer (insert "HTTP/1.1 404 Not found\n\ndoes not matter")) buffer))) (org-file-contents "http://this-url-must-not-exist" :noerror)) (kill-buffer buffer)) t))) (ert-deftest test-org/org-ctrl-c-ctrl-c () "Test `org-ctrl-c-ctrl-c' specifications." ;; FIXME: Improve coverage. ;; Preserve visibility after refreshing Org setup. (org-test-with-temp-text "#+TITLE: Test * Heading text" (org-overview) (should (org-fold-folded-p (point) 'outline)) (save-excursion (goto-char (point-min)) (org-ctrl-c-ctrl-c)) (should (org-fold-folded-p (point) 'outline))) ;; Quit column view. (org-test-with-temp-text "* Heading text" (org-columns) (should org-columns-overlays) (save-excursion (goto-char (point-min)) (org-ctrl-c-ctrl-c)) (should-not org-columns-overlays))) ;;; Navigation (ert-deftest test-org/next-visible-heading () "Test `org-next-visible-heading' specifications." ;; Move to the beginning of the next headline, taking into ;; consideration ARG. (should (org-test-with-temp-text "* H1\n* H2" (org-next-visible-heading 1) (looking-at "\\* H2"))) (should (org-test-with-temp-text "* H1\n* H2\n* H3" (org-next-visible-heading 2) (looking-at "\\* H3"))) ;; Ignore invisible headlines. (should (org-test-with-temp-text "* H1\n** H2\n* H3" (org-cycle) (org-next-visible-heading 1) (looking-at "\\* H3"))) ;; Move point between headlines, not on blank lines between. (should (org-test-with-temp-text "* H1\n** H2\n\n\n\n* H3" (let ((org-cycle-separator-lines 1)) (org-cycle) (org-next-visible-heading 1)) (looking-at "\\* H3"))) ;; Move at end of buffer when there is no more headline. (should (org-test-with-temp-text "* H1" (org-next-visible-heading 1) (eobp))) (should (org-test-with-temp-text "* H1\n* H2" (org-next-visible-heading 2) (eobp))) ;; With a negative argument, move backwards. (should (org-test-with-temp-text "* H1\n* H2\n* H3" (org-next-visible-heading -1) (looking-at "\\* H2"))) (should (org-test-with-temp-text "* H1\n* H2\n* H3" (org-next-visible-heading -2) (looking-at "\\* H1"))) ;; Edge case: visible links. (should (let ((org-link-descriptive nil)) (org-test-with-temp-text "* H1\n* [[https://orgmode.org][Org mode]]\n* H3" (org-next-visible-heading 1) (looking-at "\\* \\[\\[https:"))))) (ert-deftest test-org/previous-visible-heading () "Test `org-previous-visible-heading' specifications." ;; Move to the beginning of the next headline, taking into ;; consideration ARG. (should (org-test-with-temp-text "* H1\n* H2" (org-previous-visible-heading 1) (looking-at "\\* H1"))) (should (org-test-with-temp-text "* H1\n* H2\n* H3" (org-previous-visible-heading 2) (looking-at "\\* H1"))) ;; Ignore invisible headlines. (should (org-test-with-temp-text "* H1\n** H2\n* H3" (org-overview) (org-previous-visible-heading 1) (looking-at "\\* H1"))) ;; Move point between headlines, not on blank lines between. (should (org-test-with-temp-text "* H1\n\n\n\n** H2\n* H3" (let ((org-cycle-separator-lines 1)) (org-overview) (org-previous-visible-heading 1)) (looking-at "\\* H1"))) ;; Move at end of buffer when there is no more headline. (should (org-test-with-temp-text "* H1" (org-previous-visible-heading 1) (bobp))) (should (org-test-with-temp-text "* H1\n* H2" (org-previous-visible-heading 2) (bobp))) ;; Invisible parts may not start at a headline, i.e., when revealing ;; parts of the buffer. Handle this. (should (org-test-with-temp-text "* Main\n** H1\nFoo\n** H2\nBar\n** H3\nBaz" (org-overview) (search-forward "H1") (org-show-context 'minimal) (org-cycle) (search-forward "H3") (org-show-context 'minimal) ;; At this point, buffer displays, with point at "|", ;; ;; * Main ;; ** H1 ;; Foo ;; ** H3| (org-previous-visible-heading 1) (looking-at "\\*+ H1")))) (ert-deftest test-org/forward-heading-same-level () "Test `org-forward-heading-same-level' specifications." ;; Test navigation at top level, forward and backward. (should (equal "* H2" (org-test-with-temp-text "* H1\n* H2" (org-forward-heading-same-level 1) (buffer-substring-no-properties (point) (line-end-position))))) (should (equal "* H1" (org-test-with-temp-text "* H1\n* H2" (org-forward-heading-same-level -1) (buffer-substring-no-properties (point) (line-end-position))))) ;; Test navigation in a sub-tree, forward and backward. (should (equal "* H2" (org-test-with-temp-text "* H1\n** H11\n** H12\n* H2" (org-forward-heading-same-level 1) (buffer-substring-no-properties (point) (line-end-position))))) (should (equal "* H1" (org-test-with-temp-text "* H1\n** H11\n** H12\n* H2" (org-forward-heading-same-level -1) (buffer-substring-no-properties (point) (line-end-position))))) ;; Stop at first or last sub-heading. (should-not (equal "* H2" (org-test-with-temp-text "* H1\n** H11\n** H12\n* H2" (org-forward-heading-same-level 1) (buffer-substring-no-properties (point) (line-end-position))))) (should-not (equal "* H2" (org-test-with-temp-text "* H1\n** H11\n** H12\n* H2" (org-forward-heading-same-level -1) (buffer-substring-no-properties (point) (line-end-position))))) ;; Allow multiple moves. (should (equal "* H3" (org-test-with-temp-text "* H1\n* H2\n* H3" (org-forward-heading-same-level 2) (buffer-substring-no-properties (point) (line-end-position))))) (should (equal "* H1" (org-test-with-temp-text "* H1\n* H2\n* H3" (org-forward-heading-same-level -2) (buffer-substring-no-properties (point) (line-end-position))))) ;; Ignore spurious moves when first (or last) sibling is reached. (should (equal "** H3" (org-test-with-temp-text "* First\n** H1\n** H2\n** H3\n* Last" (org-forward-heading-same-level 100) (buffer-substring-no-properties (point) (line-end-position))))) (should (equal "** H1" (org-test-with-temp-text "* First\n** H1\n** H2\n** H3\n* Last" (org-forward-heading-same-level -100) (buffer-substring-no-properties (point) (line-end-position)))))) (ert-deftest test-org/end-of-meta-data () "Test `org-end-of-meta-data' specifications." ;; Skip planning line. (should (org-test-with-temp-text "* Headline\nSCHEDULED: <2014-03-04 tue.>" (org-end-of-meta-data) (eobp))) ;; Skip properties drawer. (should (org-test-with-temp-text "* Headline\nSCHEDULED: <2014-03-04 tue.>\n:PROPERTIES:\n:A: 1\n:END:" (org-end-of-meta-data) (eobp))) ;; Skip both. (should (org-test-with-temp-text "* Headline\n:PROPERTIES:\n:A: 1\n:END:" (org-end-of-meta-data) (eobp))) ;; Nothing to skip, go to first line. (should (org-test-with-temp-text "* Headline\nContents" (org-end-of-meta-data) (looking-at "Contents"))) ;; With option argument, skip empty lines, regular drawers and ;; clocking lines. (should (org-test-with-temp-text "* Headline\n\nContents" (org-end-of-meta-data t) (looking-at "Contents"))) (should (org-test-with-temp-text "* Headline\nCLOCK:\nContents" (org-end-of-meta-data t) (looking-at "Contents"))) (should (org-test-with-temp-text "* Headline\n:LOGBOOK:\nlogging\n:END:\nContents" (org-end-of-meta-data t) (looking-at "Contents"))) ;; Special case: do not skip incomplete drawers. (should (org-test-with-temp-text "* Headline\n:LOGBOOK:\nlogging\nContents" (org-end-of-meta-data t) (looking-at ":LOGBOOK:"))) ;; Special case: Be careful about consecutive headlines. (should-not (org-test-with-temp-text "* H1\n*H2\nContents" (org-end-of-meta-data t) (looking-at "Contents")))) (ert-deftest test-org/end-of-subtree () "Test `org-end-of-subtree' specifictions." ;; Simple call with no arguments. (org-test-with-temp-text " * Heading ** Sub1 ** Sub 2 asd * Heading 2" (org-end-of-subtree) ;; Move one point before next level 1 heading. (forward-char) (should (looking-at-p "^\\* Heading 2")) (forward-line -1) (should (looking-at-p "^asd"))) ;; TO-HEADING (org-test-with-temp-text " * Heading ** Sub1 ** Sub 2 asd * Heading 2" (org-end-of-subtree nil t) (should (looking-at-p "^\\* Heading 2"))) ;; Ignore trailing blank lines. (org-test-with-temp-text " * Heading ** Sub1 ** Sub 2 asd * Heading 2" (org-end-of-subtree) (forward-line 0) (should (looking-at-p "^asd"))) ;; Ignore inlinetasks (let ((org-inlinetask-min-level 3)) (org-test-with-temp-text " * Heading some text *** Inlinetask t *** END ** Sub1 ** Sub 2 asd * Heading 2" (org-end-of-subtree) (forward-line 0) (should (looking-at-p "^asd")))) (let ((org-inlinetask-min-level 3)) (org-test-with-temp-text " * Heading some text *** Inlinetask t *** END *** Inlinetask2 ** Sub1 ** Sub 2 asd * Heading 2" (org-end-of-subtree nil nil (org-element-at-point)) (forward-line 0) (should (looking-at-p "^asd")))) ;; Before first heading. (org-test-with-temp-text " Some text. * Heading ** Sub1 ** Sub 2 asd * Heading 2 text" (org-end-of-subtree) (should (eobp))) (org-test-with-temp-text " Some text. * Heading ** Sub1 ** Sub 2 asd * Heading 2" (org-end-of-subtree) (should (eobp))) (org-test-with-temp-text " Some text. * Heading ** Sub1 ** Sub 2 asd * Heading 2 " (org-end-of-subtree) ;; This is tricky. Users may or may not want moving to a heading ;; May need to be re-considered. (should (equal (point) (1- (point-max))))) (org-test-with-temp-text " Some text. * Heading ** Sub1 ** Sub 2 asd * Heading 2 " (org-end-of-subtree) (forward-line 0) (should (looking-at-p "^\\* Heading 2"))) ;; TO-HEADING before first heading. (org-test-with-temp-text " Some text. * Heading ** Sub1 ** Sub 2 asd * Heading 2 " (org-end-of-subtree nil t) (should (eobp))) (org-test-with-temp-text " Some text. * Heading ** Sub1 ** Sub 2 asd * Heading 2" (org-end-of-subtree nil t) (should (eobp))) ;; Honor visibility. (org-test-with-temp-text " * Heading ** Sub1 *** Subsub text ** Sub 2 asd * Heading 2" (org-overview) (org-end-of-subtree nil t) ;; Current subtree is fully hidden. Jump to the end of the first ;; visible subtree. (should (looking-at-p "^\\* Heading 2")))) (ert-deftest test-org/shiftright-heading () "Test `org-shiftright' on headings." (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (should (equal "* TODO a1\n** a2\n* DONE b1\n" (org-test-with-temp-text "* a1\n** a2\n* DONE b1\n" (org-shiftright) (buffer-string)))) (should (equal "* TODO a1\n** TODO a2\n* b1\n" (org-test-with-temp-text "* a1\n** a2\n* DONE b1\n" (let ((org-loop-over-headlines-in-active-region t)) (transient-mark-mode 1) (push-mark (point) t t) (search-forward "* DONE b1") (org-shiftright)) (buffer-string)))) (should (equal "* TODO a1\n** a2\n* b1\n" (org-test-with-temp-text "* a1\n** a2\n* DONE b1\n" (let ((org-loop-over-headlines-in-active-region 'start-level)) (transient-mark-mode 1) (push-mark (point) t t) (search-forward "* DONE b1") (org-shiftright)) (buffer-string)))))) (ert-deftest test-org/beginning-of-line () "Test `org-beginning-of-line' specifications." ;; Move to beginning of line. If current line in invisible, move to ;; beginning of visible line instead. (should (org-test-with-temp-text "Some text\nSome other text" (org-beginning-of-line) (bolp))) (should (org-test-with-temp-text "* H1\n** H2" (org-overview) (org-beginning-of-line) (= (line-beginning-position) 1))) ;; With `visual-line-mode' active, move to beginning of visual line. (should-not (org-test-with-temp-text "A long line of text\nSome other text" (visual-line-mode) (dotimes (_ 1000) (insert "very ")) (org-beginning-of-line) (bolp))) ;; In a wide headline, with `visual-line-mode', prefer going to the ;; beginning of a visual line than to the logical beginning of line, ;; even if special movement is active. (should-not (org-test-with-temp-text "* A long headline" (visual-line-mode) (dotimes (_ 1000) (insert "very ")) (goto-char (point-max)) (org-beginning-of-line) (bobp))) (should-not (org-test-with-temp-text "* A long headline" (visual-line-mode) (dotimes (_ 1000) (insert "very ")) (goto-char (point-max)) (let ((org-special-ctrl-a/e t)) (org-beginning-of-line)) (bobp))) ;; At an headline with special movement, first move at beginning of ;; title, then at the beginning of line, rinse, repeat. (should (org-test-with-temp-text "* TODO Headline" (let ((org-special-ctrl-a/e t)) (and (progn (org-beginning-of-line) (looking-at-p "Headline")) (progn (org-beginning-of-line) (bolp)) (progn (org-beginning-of-line) (looking-at-p "Headline")))))) (should (org-test-with-temp-text "* TODO [#A] Headline" (let ((org-special-ctrl-a/e t)) (org-beginning-of-line) (looking-at "Headline")))) (should (org-test-with-temp-text "* TODO [#A] Headline" (let ((org-special-ctrl-a/e '(t . nil))) (org-beginning-of-line) (looking-at "Headline")))) (should-not (org-test-with-temp-text "* TODO [#A] Headline" (let ((org-special-ctrl-a/e '(nil . nil))) (org-beginning-of-line) (looking-at "Headline")))) (should (org-test-with-temp-text "* TODO [#A] Headline\n" (let ((org-special-ctrl-a/e t)) (org-beginning-of-line 0) (looking-at-p "Headline")))) (should (org-test-with-temp-text "\n* TODO [#A] Headline" (let ((org-special-ctrl-a/e t)) (org-beginning-of-line 2) (looking-at-p "Headline")))) ;; At an headline with reversed movement, first move to beginning of ;; line, then to the beginning of title. (should (org-test-with-temp-text "* TODO Headline" (let ((org-special-ctrl-a/e 'reversed) (this-command last-command)) (and (progn (org-beginning-of-line) (bolp)) (progn (org-beginning-of-line) (looking-at-p "Headline")))))) (should (org-test-with-temp-text "* TODO Headline" (let ((org-special-ctrl-a/e '(reversed . nil)) (this-command last-command)) (and (progn (org-beginning-of-line) (bolp)) (progn (org-beginning-of-line) (looking-at-p "Headline")))))) (should-not (org-test-with-temp-text "* TODO Headline" (let ((org-special-ctrl-a/e '(t . nil)) (this-command last-command)) (and (progn (org-beginning-of-line) (bolp)) (progn (org-beginning-of-line) (looking-at-p "Headline")))))) (should (org-test-with-temp-text "* TODO Headline\n" (let ((org-special-ctrl-a/e 'reversed) (this-command last-command)) (and (progn (org-beginning-of-line 0) (bolp)) (progn (org-beginning-of-line) (looking-at-p "Headline")))))) (should (org-test-with-temp-text "\n* TODO Headline" (let ((org-special-ctrl-a/e 'reversed) (this-command last-command)) (and (progn (org-beginning-of-line 2) (bolp)) (progn (org-beginning-of-line) (looking-at-p "Headline")))))) ;; At an item with special movement, first move after to beginning ;; of title, then to the beginning of line, rinse, repeat. (should (org-test-with-temp-text "- [ ] Item" (let ((org-special-ctrl-a/e t)) (and (progn (org-beginning-of-line) (looking-at-p "Item")) (progn (org-beginning-of-line) (bolp)) (progn (org-beginning-of-line) (looking-at-p "Item")))))) (should (org-test-with-temp-text "- [ ] Item\n" (let ((org-special-ctrl-a/e t)) (org-beginning-of-line 0) (looking-at-p "Item")))) (should (org-test-with-temp-text "\n- [ ] Item" (let ((org-special-ctrl-a/e t)) (org-beginning-of-line 2) (looking-at-p "Item")))) ;; At an item with reversed movement, first move to beginning of ;; line, then to the beginning of title. (should (org-test-with-temp-text "- [X] Item" (let ((org-special-ctrl-a/e 'reversed) (this-command last-command)) (and (progn (org-beginning-of-line) (bolp)) (progn (org-beginning-of-line) (looking-at-p "Item")))))) (should (org-test-with-temp-text "- [X] Item\n" (let ((org-special-ctrl-a/e 'reversed) (this-command last-command)) (and (progn (org-beginning-of-line 0) (bolp)) (progn (org-beginning-of-line) (looking-at-p "Item")))))) (should (org-test-with-temp-text "\n- [X] Item" (let ((org-special-ctrl-a/e 'reversed) (this-command last-command)) (and (progn (org-beginning-of-line 2) (bolp)) (progn (org-beginning-of-line) (looking-at-p "Item")))))) ;; Leave point before invisible characters at column 0. (should (org-test-with-temp-text "[[https://orgmode.org]]" (let ((org-special-ctrl-a/e nil)) (org-beginning-of-line) (bolp)))) (should (org-test-with-temp-text "[[https://orgmode.org]]" (let ((org-special-ctrl-a/e t)) (org-beginning-of-line) (bolp)))) (should (org-test-with-temp-text "[[http://orgmode.org]]" (visual-line-mode) (org-beginning-of-line) (bolp))) ;; Special case: Do not error when the buffer contains only a single ;; asterisk. (should (org-test-with-temp-text "*" (let ((org-special-ctrl-a/e t)) (org-beginning-of-line) t))) (should (org-test-with-temp-text "*" (let ((org-special-ctrl-a/e nil)) (org-beginning-of-line) t)))) (ert-deftest test-org/end-of-line () "Test `org-end-of-line' specifications." ;; Standard test. (should (org-test-with-temp-text "Some text\nSome other text" (org-end-of-line) (eolp))) ;; With `visual-line-mode' active, move to end of visible line. ;; However, never go past ellipsis. (should-not (org-test-with-temp-text "A long line of text\nSome other text" (visual-line-mode) (dotimes (_ 1000) (insert "very ")) (goto-char (point-min)) (org-end-of-line) (eolp))) (should-not (org-test-with-temp-text "* A short headline\nSome contents" (visual-line-mode) (org-overview) (org-end-of-line) (eobp))) ;; In a wide headline, with `visual-line-mode', prefer going to end ;; of visible line if tags, or end of line, are farther. (should-not (org-test-with-temp-text "* A long headline" (visual-line-mode) (dotimes (_ 1000) (insert "very ")) (goto-char (point-min)) (org-end-of-line) (eolp))) (should-not (org-test-with-temp-text "* A long headline :tag:" (visual-line-mode) (dotimes (_ 1000) (insert "very ")) (goto-char (point-min)) (org-end-of-line) (eolp))) ;; At an headline without special movement, go to end of line. ;; However, never go past ellipsis. (should (org-test-with-temp-text "* Headline2b :tag:\n" (let ((org-special-ctrl-a/e nil)) (and (progn (org-end-of-line) (eolp)) (progn (org-end-of-line) (eolp)))))) (should (org-test-with-temp-text "* Headline2b :tag:\n" (let ((org-special-ctrl-a/e '(t . nil))) (and (progn (org-end-of-line) (eolp)) (progn (org-end-of-line) (eolp)))))) (should (org-test-with-temp-text "* Headline2a :tag:\n** Sub" (org-overview) (let ((org-special-ctrl-a/e nil)) (org-end-of-line) (= 1 (line-beginning-position))))) ;; At an headline with special movement, first move before tags, ;; then at the end of line, rinse, repeat. However, never go past ;; ellipsis. (should (org-test-with-temp-text "* Headline1 :tag:\n" (let ((org-special-ctrl-a/e t)) (and (progn (org-end-of-line) (looking-at-p " :tag:")) (progn (org-end-of-line) (eolp)) (progn (org-end-of-line) (looking-at-p " :tag:")))))) (should (org-test-with-temp-text "* Headline1 :tag:\n" (let ((org-special-ctrl-a/e '(nil . t))) (and (progn (org-end-of-line) (looking-at-p " :tag:")) (progn (org-end-of-line) (eolp)) (progn (org-end-of-line) (looking-at-p " :tag:")))))) (should-not (org-test-with-temp-text "* Headline1 :tag:\n" (let ((org-special-ctrl-a/e '(nil . nil))) (and (progn (org-end-of-line) (looking-at-p " :tag:")) (progn (org-end-of-line) (eolp)) (progn (org-end-of-line) (looking-at-p " :tag:")))))) (should (org-test-with-temp-text "* Headline1 :tag:\n" (let ((org-special-ctrl-a/e t)) (org-end-of-line 0) (looking-at-p " :tag:")))) (should (org-test-with-temp-text "\n* Headline1 :tag:\n" (let ((org-special-ctrl-a/e t)) (org-end-of-line 2) (looking-at-p " :tag:")))) (should (org-test-with-temp-text "* Headline2a :tag:\n** Sub" (org-overview) (let ((org-special-ctrl-a/e t)) (org-end-of-line) (org-end-of-line) (= 1 (line-beginning-position))))) ;; At an headline, with reversed movement, first go to end of line, ;; then before tags. However, never go past ellipsis. (should (org-test-with-temp-text "* Headline3 :tag:\n" (let ((org-special-ctrl-a/e 'reversed) (this-command last-command)) (and (progn (org-end-of-line) (eolp)) (progn (org-end-of-line) (looking-at-p " :tag:")))))) (should (org-test-with-temp-text "* Headline3 :tag:\n" (let ((org-special-ctrl-a/e '(nil . reversed)) (this-command last-command)) (and (progn (org-end-of-line) (eolp)) (progn (org-end-of-line) (looking-at-p " :tag:")))))) (should-not (org-test-with-temp-text "* Headline3 :tag:\n" (let ((org-special-ctrl-a/e '(nil . t)) (this-command last-command)) (and (progn (org-end-of-line) (eolp)) (progn (org-end-of-line) (looking-at-p " :tag:")))))) (should (org-test-with-temp-text "* Headline3 :tag:\n" (let ((org-special-ctrl-a/e 'reversed) (this-command last-command)) (and (progn (org-end-of-line 0) (eolp)) (progn (org-end-of-line) (looking-at-p " :tag:")))))) (should (org-test-with-temp-text "\n* Headline3 :tag:\n" (let ((org-special-ctrl-a/e 'reversed) (this-command last-command)) (and (progn (org-end-of-line 2) (eolp)) (progn (org-end-of-line) (looking-at-p " :tag:")))))) (should (org-test-with-temp-text "* Headline2a :tag:\n** Sub" (org-overview) (let ((org-special-ctrl-a/e 'reversed)) (org-end-of-line) (= 1 (line-beginning-position))))) ;; At a block without hidden contents. (should (org-test-with-temp-text "#+BEGIN_CENTER\nContents\n#+END_CENTER" (progn (org-end-of-line) (eolp)))) ;; At a block with hidden contents. (should-not (org-test-with-temp-text "#+BEGIN_CENTER\nContents\n#+END_CENTER" (let ((org-special-ctrl-a/e t)) (org-fold-hide-block-toggle) (org-end-of-line) (eobp)))) ;; Get past invisible characters at the end of line. (should (org-test-with-temp-text "[[https://orgmode.org]]" (org-end-of-line) (eolp)))) (ert-deftest test-org/open-line () "Test `org-open-line' specifications." ;; Call `open-line' outside of tables. (should (equal "\nText" (org-test-with-temp-text "Text" (org-open-line 1) (buffer-string)))) ;; At a table, create a row above. (should (equal "\n| |\n| a |" (org-test-with-temp-text "\n| a |" (org-open-line 1) (buffer-string)))) ;; At the very first character of the buffer, also call `open-line'. (should (equal "\n| a |" (org-test-with-temp-text "| a |" (org-open-line 1) (buffer-string)))) ;; Narrowing does not count. (should (equal "Text\n| |\n| a |" (org-test-with-temp-text "Text\n| a |" (narrow-to-region (point) (point-max)) (org-open-line 1) (widen) (buffer-string))))) (ert-deftest test-org/forward-sentence () "Test `org-forward-sentence' specifications." ;; At the end of a table cell, move to the end of the next one. (should (org-test-with-temp-text "| a | b |" (org-forward-sentence) (looking-at " |$"))) ;; Elsewhere in a cell, move to its end. (should (org-test-with-temp-text "| ac | b |" (org-forward-sentence) (looking-at " | b |$"))) ;; Otherwise, simply call `forward-sentence'. (should (org-test-with-temp-text "Sentence 1. Sentence 2." (org-forward-sentence) (looking-at " Sentence 2."))) (should (org-test-with-temp-text "Sentence 1. Sentence 2." (org-forward-sentence) (org-forward-sentence) (eobp))) ;; At the end of an element, jump to the next one, without stopping ;; on blank lines in-between. (should (org-test-with-temp-text "Paragraph 1.\n\nParagraph 2." (org-forward-sentence) (eobp))) ;; Headlines are considered to be sentences by themselves, even if ;; they do not end with a full stop. (should (equal "* Headline" (org-test-with-temp-text "* Headline\nSentence." (org-forward-sentence) (buffer-substring-no-properties (line-beginning-position) (point))))) (should (org-test-with-temp-text "* Headline\nSentence." (org-forward-sentence) (eobp))) (should (org-test-with-temp-text "Sentence.\n\n* Headline\n\nSentence 2." (org-forward-sentence) (and (org-at-heading-p) (eolp))))) (ert-deftest test-org/backward-sentence () "Test `org-backward-sentence' specifications." ;; At the beginning of a table cell, move to the beginning of the ;; previous one. (should (org-test-with-temp-text "| a | b |" (org-backward-sentence) (looking-at "a | b |$"))) ;; Elsewhere in a cell, move to its beginning. (should (org-test-with-temp-text "| a | bc |" (org-backward-sentence) (looking-at "bc |$"))) ;; Otherwise, simply call `backward-sentence'. (should (org-test-with-temp-text "Sentence 1. Sentence 2." (org-backward-sentence) (looking-at "Sentence 2."))) (should (org-test-with-temp-text "Sentence 1. Sentence 2." (org-backward-sentence) (org-backward-sentence) (bobp))) ;; Make sure to hit the beginning of a sentence on the same line as ;; an item. (should (org-test-with-temp-text "- Line 1\n line 2." (org-backward-sentence) (looking-at "Line 1")))) (ert-deftest test-org/forward-paragraph () "Test `org-forward-paragraph' specifications." ;; At end of buffer, do not return an error. (should (org-test-with-temp-text "Paragraph" (goto-char (point-max)) (org-forward-paragraph) t)) ;; Standard test. (should (= 2 (org-test-with-temp-text "P1\n\nP2" (org-forward-paragraph) (org-current-line)))) (should (= 2 (org-test-with-temp-text "P1\n\nP2\n\nP3" (org-forward-paragraph) (org-current-line)))) ;; Enter greater elements. (should (= 2 (org-test-with-temp-text "#+begin_center\nP1\n#+end_center\nP2" (org-forward-paragraph) (org-current-line)))) ;; Do not enter elements with invisible contents. (should (= 4 (org-test-with-temp-text "* H1\n P1\n\n* H2" (org-cycle) (org-forward-paragraph) (org-current-line)))) (should (= 6 (org-test-with-temp-text "#+begin_center\nP1\n\nP2\n#+end_center\nP3" (org-fold-hide-block-toggle) (org-forward-paragraph) (org-current-line)))) ;; On an item or a footnote definition, move past the first element ;; inside, if any. (should (= 2 (org-test-with-temp-text "- Item1\n\n Paragraph\n- Item2" (org-forward-paragraph) (org-current-line)))) (should (= 2 (org-test-with-temp-text "[fn:1] Def1\n\nParagraph\n\n[fn:2] Def2" (org-forward-paragraph) (org-current-line)))) ;; On a table (resp. a property drawer) do not move through table ;; rows (resp. node properties). (should (org-test-with-temp-text "| a | b |\n| c | d |\nParagraph" (org-forward-paragraph) (looking-at "Paragraph"))) (should (org-test-with-temp-text "* H\n:PROPERTIES:\n:prop: value\n:END:\nParagraph" (org-forward-paragraph) (looking-at "Paragraph"))) ;; Skip consecutive keywords, clocks and diary S-exps. (should (org-test-with-temp-text "#+key: val\n #+key2: val\n#+key3: val\n" (org-forward-paragraph) (eobp))) (should (org-test-with-temp-text "CLOCK: val\n CLOCK: val\nCLOCK: val\n" (org-forward-paragraph) (eobp))) (should (org-test-with-temp-text "%%(foo)\n%%(bar)\n%%(baz)\n" (org-forward-paragraph) (eobp))) (should-not (org-test-with-temp-text "#+key: val\n #+key2: val\n\n#+key3: val\n" (org-forward-paragraph) (eobp))) (should-not (org-test-with-temp-text "#+key: val\nCLOCK: ...\n" (org-forward-paragraph) (eobp))) ;; In a plain list with one item every line, skip the whole list, ;; even with point in the middle of the list. (should (org-test-with-temp-text "- A\n - B\n- C\n" (org-forward-paragraph) (eobp))) (should (org-test-with-temp-text "- A\n - B\n- C\n" (org-forward-paragraph) (eobp))) ;; On a comment, verse or source block, stop at "contents" ;; boundaries and blank lines. (should (= 2 (org-test-with-temp-text "#+begin_src emacs-lisp\nL1\n\nL2\n#+end_src" (org-forward-paragraph) (org-current-line)))) (should (= 3 (org-test-with-temp-text "#+begin_verse\nL1\n\nL2\n#+end_verse" (org-forward-paragraph) (org-current-line)))) (should (= 5 (org-test-with-temp-text "#+begin_comment\nL1\n\nL2\n#+end_comment" (org-forward-paragraph) (org-current-line)))) ;; Being on an affiliated keyword shouldn't make any difference. (should (org-test-with-temp-text "#+name: para\n#+caption: caption\nPara" (org-forward-paragraph) (eobp)))) (ert-deftest test-org/backward-paragraph () "Test `org-backward-paragraph' specifications." ;; Do not error at beginning of buffer. (should (org-test-with-temp-text "Paragraph" (org-backward-paragraph) t)) ;; At blank lines at the very beginning of a buffer, move to ;; point-min. (should (org-test-with-temp-text "\n\n\n\nParagraph" (org-backward-paragraph) (bobp))) ;; Regular test. (should (= 2 (org-test-with-temp-text "P1\n\nP2" (org-backward-paragraph) (org-current-line)))) (should (= 4 (org-test-with-temp-text "P1\n\nP2\n\nP3" (org-backward-paragraph) (org-current-line)))) ;; Try to move on the line above current element. (should (= 2 (org-test-with-temp-text "\n\nParagraph" (org-backward-paragraph) (org-current-line)))) ;; Do not leave point in an invisible area. (should (org-test-with-temp-text "* H1\n P1\n\n* H2" (org-cycle) (goto-char (point-max)) (beginning-of-line) (org-backward-paragraph) (bobp))) (should (org-test-with-temp-text "#+begin_center\nP1\n\nP2\n#+end_center\n" (org-fold-hide-block-toggle) (goto-char (point-max)) (org-backward-paragraph) (bobp))) ;; On the first element in an item or a footnote definition, jump ;; before the footnote or the item. (should (org-test-with-temp-text "- line1" (org-backward-paragraph) (bobp))) (should (org-test-with-temp-text "[fn:1] line1n" (org-backward-paragraph) (bobp))) ;; On the second element in an item or a footnote definition, jump ;; to item or the definition. (should (= 2 (org-test-with-temp-text "- line1\n\n line2" (org-backward-paragraph) (org-current-line)))) (should (= 2 (org-test-with-temp-text "[fn:1] line1\n\n line2" (org-backward-paragraph) (org-current-line)))) ;; On a table (resp. a property drawer), ignore table rows ;; (resp. node properties). (should (org-test-with-temp-text "| a | b |\n| c | d |\nP1" (org-backward-paragraph) (bobp))) (should (= 2 (org-test-with-temp-text "* H\n:PROPERTIES:\n:prop: value\n:END:\nP1" (org-backward-paragraph) (org-current-line)))) ;; In a plain list with one item every line, skip the whole list, ;; even with point in the middle of the list. (should (org-test-with-temp-text "- A\n - B\n- C\n" (org-backward-paragraph) (bobp))) (should (org-test-with-temp-text "- A\n - B\n- C\n" (org-backward-paragraph) (bobp))) ;; Skip consecutive keywords, clocks and diary S-exps. (should (org-test-with-temp-text "#+key: val\n #+key2: val\n#+key3: val\n" (org-backward-paragraph) (bobp))) (should (org-test-with-temp-text "CLOCK: val\n CLOCK: val\nCLOCK: val\n" (org-backward-paragraph) (bobp))) (should (org-test-with-temp-text "%%(foo)\n%%(bar)\n%%(baz)\n" (org-backward-paragraph) (bobp))) (should-not (org-test-with-temp-text "#+key: val\n #+key2: val\n\n#+key3: val\n" (org-backward-paragraph) (bobp))) (should-not (org-test-with-temp-text "#+key: val\nCLOCK: ...\n" (org-backward-paragraph) (bobp))) ;; On a comment, example, source and verse blocks, stop at blank ;; lines. (should (= 1 (org-test-with-temp-text "#+begin_comment\nL1\n\nL2\n\nL3\n#+end_comment" (org-backward-paragraph) (org-current-line)))) (should (= 2 (org-test-with-temp-text "#+begin_verse\nL1\n\nL2\n\nL3\n#+end_verse" (org-backward-paragraph) (org-current-line)))) (should (= 3 (org-test-with-temp-text "#+begin_src emacs-lisp\nL1\n\nL2\n\nL3\n#+end_src" (org-backward-paragraph) (org-current-line)))) ;; When called from the opening line itself, however, move to ;; beginning of block. (should (org-test-with-temp-text "#+begin_example\nL1\n#+end_example" (org-backward-paragraph) (bobp))) ;; On an empty heading, move above it. (should (org-test-with-temp-text "\n* " (org-backward-paragraph) (bobp))) (should (org-test-with-temp-text "\n* \n" (org-backward-paragraph) (bobp)))) (ert-deftest test-org/forward-element () "Test `org-forward-element' specifications." ;; 1. At EOB: should error. (org-test-with-temp-text "Some text\n" (goto-char (point-max)) (should-error (org-forward-element))) ;; 2. Standard move: expected to ignore blank lines. (org-test-with-temp-text "First paragraph.\n\n\nSecond paragraph." (org-forward-element) (should (looking-at (regexp-quote "Second paragraph.")))) ;; 3. Headline tests. (org-test-with-temp-text " * Head 1 ** Head 1.1 *** Head 1.1.1 ** Head 1.2" ;; 3.1. At an headline beginning: move to next headline at the ;; same level. (goto-line 3) (org-forward-element) (should (looking-at (regexp-quote "** Head 1.2"))) ;; 3.2. At an headline beginning: move to parent headline if no ;; headline at the same level. (goto-line 3) (org-forward-element) (should (looking-at (regexp-quote "** Head 1.2")))) ;; 4. Greater element tests. (org-test-with-temp-text "#+BEGIN_CENTER\nInside.\n#+END_CENTER\n\nOutside." ;; 4.1. At a greater element: expected to skip contents. (org-forward-element) (should (looking-at (regexp-quote "Outside."))) ;; 4.2. At the end of greater element contents: expected to skip ;; to the end of the greater element. (goto-line 2) (org-forward-element) (should (looking-at (regexp-quote "Outside.")))) ;; 5. List tests. (org-test-with-temp-text " - item1 - sub1 - sub2 - sub3 Inner paragraph. - item2 Outside." ;; 5.1. At list top point: expected to move to the element after ;; the list. (goto-line 2) (org-forward-element) (should (looking-at (regexp-quote "Outside."))) ;; 5.2. Special case: at the first line of a sub-list, but not at ;; beginning of line, move to next item. (goto-line 2) (forward-char) (org-forward-element) (should (looking-at "- item2")) (goto-line 4) (forward-char) (org-forward-element) (should (looking-at " - sub2")) ;; 5.3 At sub-list beginning: expected to move after the sub-list. (goto-line 4) (org-forward-element) (should (looking-at (regexp-quote " Inner paragraph."))) ;; 5.4. At sub-list end: expected to move outside the sub-list. (goto-line 8) (org-forward-element) (should (looking-at (regexp-quote " Inner paragraph."))) ;; 5.5. At an item: expected to move to next item, if any. (goto-line 6) (org-forward-element) (should (looking-at " - sub3")))) (ert-deftest test-org/backward-element () "Test `org-backward-element' specifications." ;; 1. Should error at BOB. (org-test-with-temp-text " \nParagraph." (should-error (org-backward-element))) ;; 2. Should move at BOB when called on the first element in buffer. (should (org-test-with-temp-text "\n#+TITLE: test" (progn (forward-line) (org-backward-element) (bobp)))) ;; 3. Not at the beginning of an element: move at its beginning. (org-test-with-temp-text "Paragraph1.\n\nParagraph2." (goto-line 3) (end-of-line) (org-backward-element) (should (looking-at (regexp-quote "Paragraph2.")))) ;; 4. Headline tests. (org-test-with-temp-text " * Head 1 ** Head 1.1 *** Head 1.1.1 ** Head 1.2" ;; 4.1. At an headline beginning: move to previous headline at the ;; same level. (goto-line 5) (org-backward-element) (should (looking-at (regexp-quote "** Head 1.1"))) ;; 4.2. At an headline beginning: move to parent headline if no ;; headline at the same level. (goto-line 3) (org-backward-element) (should (looking-at (regexp-quote "* Head 1"))) ;; 4.3. At the first top-level headline: should error. (goto-line 2) (should-error (org-backward-element))) ;; 5. At beginning of first element inside a greater element: ;; expected to move to greater element's beginning. (org-test-with-temp-text "Before.\n#+BEGIN_CENTER\nInside.\n#+END_CENTER" (goto-line 3) (org-backward-element) (should (looking-at "#\\+BEGIN_CENTER"))) ;; 6. At the beginning of the first element in a section: should ;; move back to headline, if any. (should (org-test-with-temp-text "#+TITLE: test\n* Headline\n\nParagraph" (progn (goto-char (point-max)) (beginning-of-line) (org-backward-element) (org-at-heading-p)))) ;; 7. List tests. (org-test-with-temp-text " - item1 - sub1 - sub2 - sub3 Inner paragraph. - item2 Outside." ;; 7.1. At beginning of sub-list: expected to move to the ;; paragraph before it. (goto-line 4) (org-backward-element) (should (looking-at "item1")) ;; 7.2. At an item in a list: expected to move at previous item. (goto-line 8) (org-backward-element) (should (looking-at " - sub2")) (goto-line 12) (org-backward-element) (should (looking-at "- item1")) ;; 7.3. At end of list/sub-list: expected to move to list/sub-list ;; beginning. (goto-line 10) (org-backward-element) (should (looking-at " - sub1")) (goto-line 15) (org-backward-element) (should (looking-at "- item1")) ;; 7.4. At blank-lines before list end: expected to move to top ;; item. (goto-line 14) (org-backward-element) (should (looking-at "- item1")))) (ert-deftest test-org/up-element () "Test `org-up-element' specifications." ;; 1. At BOB or with no surrounding element: should error. (org-test-with-temp-text "Paragraph." (should-error (org-up-element))) (org-test-with-temp-text "* Head1\n* Head2" (goto-line 2) (should-error (org-up-element))) (org-test-with-temp-text "Paragraph1.\n\nParagraph2." (goto-line 3) (should-error (org-up-element))) ;; 2. At an headline: move to parent headline. (org-test-with-temp-text "* Head1\n** Sub-Head1\n** Sub-Head2" (goto-line 3) (org-up-element) (should (looking-at "\\* Head1"))) ;; 3. Inside a greater element: move to greater element beginning. (org-test-with-temp-text "Before.\n#+BEGIN_CENTER\nParagraph1\nParagraph2\n#+END_CENTER\n" (goto-line 3) (org-up-element) (should (looking-at "#\\+BEGIN_CENTER"))) ;; 4. List tests. (org-test-with-temp-text "* Top - item1 - sub1 - sub2 Paragraph within sub2. - item2" ;; 4.1. Within an item: move to the item beginning. (goto-line 8) (org-up-element) (should (looking-at " - sub2")) ;; 4.2. At an item in a sub-list: move to parent item. (goto-line 4) (org-up-element) (should (looking-at "- item1")) ;; 4.3. At an item in top list: move to beginning of whole list. (goto-line 10) (org-up-element) (should (looking-at "- item1")) ;; 4.4. Special case. At very top point: should move to parent of ;; list. (goto-line 2) (org-up-element) (should (looking-at "\\* Top")))) (ert-deftest test-org/down-element () "Test `org-down-element' specifications." ;; Error when the element hasn't got a recursive type. (org-test-with-temp-text "Paragraph." (should-error (org-down-element))) ;; Error when the element has no contents (org-test-with-temp-text "* Headline" (should-error (org-down-element))) ;; When at a plain-list, move to first item. (org-test-with-temp-text "- Item 1\n - Item 1.1\n - Item 2.2" (goto-line 2) (org-down-element) (should (looking-at " - Item 1.1"))) (org-test-with-temp-text "#+NAME: list\n- Item 1" (org-down-element) (should (looking-at " Item 1"))) ;; When at a table, move to first row (org-test-with-temp-text "#+NAME: table\n| a | b |" (org-down-element) (should (looking-at " a | b |"))) ;; Otherwise, move inside the greater element. (org-test-with-temp-text "#+BEGIN_CENTER\nParagraph.\n#+END_CENTER" (org-down-element) (should (looking-at "Paragraph")))) (ert-deftest test-org/drag-element-backward () "Test `org-drag-element-backward' specifications." ;; Standard test. (should (equal "#+key2: val2\n#+key1: val1\n#+key3: val3" (org-test-with-temp-text "#+key1: val1\n#+key2: val2\n#+key3: val3" (org-drag-element-backward) (buffer-string)))) (should (equal "#+BEGIN_CENTER\n#+B: 2\n#+A: 1\n#+END_CENTER" (org-test-with-temp-text "#+BEGIN_CENTER\n#+A: 1\n#+B: 2\n#+END_CENTER" (org-drag-element-backward) (buffer-string)))) ;; Preserve blank lines. (should (equal "Paragraph 2\n\n\nPara1\n\nPara3" (org-test-with-temp-text "Para1\n\n\nParagraph 2\n\nPara3" (org-drag-element-backward) (buffer-string)))) ;; Preserve column. (should (org-test-with-temp-text "#+key1: v\n#+key2: v\n#+key3: v" (org-drag-element-backward) (looking-at-p "2"))) ;; Error when trying to move first element of buffer. (should-error (org-test-with-temp-text "Paragraph 1.\n\nParagraph 2." (org-drag-element-backward)) :type 'user-error) ;; Error when trying to swap nested elements. (should-error (org-test-with-temp-text "#+BEGIN_CENTER\nTest.\n#+END_CENTER" (org-drag-element-backward)) :type 'user-error) ;; Error when trying to swap an headline element and a non-headline ;; element. (should-error (org-test-with-temp-text "Test.\n* Head 1" (org-drag-element-backward)) :type 'error) ;; Error when called before first element. (should-error (org-test-with-temp-text "\n" (org-drag-element-backward)) :type 'user-error) ;; Preserve visibility of elements and their contents. (should (equal '((63 . 82) (26 . 48)) (let ((org-fold-core-style 'text-properties)) (org-test-with-temp-text " #+BEGIN_CENTER Text. #+END_CENTER - item 1 #+BEGIN_QUOTE Text. #+END_QUOTE" (while (search-forward "BEGIN_" nil t) (org-cycle)) (search-backward "- item 1") (org-drag-element-backward) (let (regions) (goto-char (point-min)) (while (< (point) (point-max)) (let ((region (org-fold-get-region-at-point))) (if (not region) (goto-char (org-fold-next-folding-state-change)) (goto-char (cdr region)) (push region regions)))) regions))))) (should (equal '((26 . 48) (63 . 82)) (let ((org-fold-core-style 'overlays)) (org-test-with-temp-text " #+BEGIN_CENTER Text. #+END_CENTER - item 1 #+BEGIN_QUOTE Text. #+END_QUOTE" (while (search-forward "BEGIN_" nil t) (org-cycle)) (search-backward "- item 1") (org-drag-element-backward) (mapcar (lambda (ov) (cons (overlay-start ov) (overlay-end ov))) (overlays-in (point-min) (point-max))))))) ;; Pathological case: handle call with point in blank lines right ;; after a headline. (should (equal "* H2\n\n* H1\nText\n" (org-test-with-temp-text "* H1\nText\n* H2\n\n" (org-drag-element-backward) (buffer-string))))) (ert-deftest test-org/drag-element-forward () "Test `org-drag-element-forward' specifications." ;; 1. Error when trying to move first element of buffer. (org-test-with-temp-text "Paragraph 1.\n\nParagraph 2." (goto-line 3) (should-error (org-drag-element-forward))) ;; 2. Error when trying to swap nested elements. (org-test-with-temp-text "#+BEGIN_CENTER\nTest.\n#+END_CENTER" (forward-line) (should-error (org-drag-element-forward))) ;; 3. Error when trying to swap a non-headline element and an ;; headline. (org-test-with-temp-text "Test.\n* Head 1" (should-error (org-drag-element-forward))) ;; 4. Error when called before first element. (should-error (org-test-with-temp-text "\n" (forward-line) (org-drag-element-backward)) :type 'user-error) ;; 5. Otherwise, swap elements, preserving column and blank lines ;; between elements. (org-test-with-temp-text "Paragraph 1\n\n\nPara2\n\nPara3" (search-forward "graph") (org-drag-element-forward) (should (equal (buffer-string) "Para2\n\n\nParagraph 1\n\nPara3")) (should (looking-at " 1"))) ;; 5. Preserve visibility of elements and their contents. (let ((org-fold-core-style 'text-properties)) (org-test-with-temp-text " #+BEGIN_CENTER Text. #+END_CENTER - item 1 #+BEGIN_QUOTE Text. #+END_QUOTE" (while (search-forward "BEGIN_" nil t) (org-cycle)) (search-backward "#+BEGIN_CENTER") (org-drag-element-forward) (should (equal '((63 . 82) (26 . 48)) (let (regions) (goto-char (point-min)) (while (< (point) (point-max)) (let ((region (org-fold-get-region-at-point))) (if (not region) (goto-char (org-fold-next-folding-state-change)) (goto-char (cdr region)) (push region regions)))) regions))))) (let ((org-fold-core-style 'overlays)) (org-test-with-temp-text " #+BEGIN_CENTER Text. #+END_CENTER - item 1 #+BEGIN_QUOTE Text. #+END_QUOTE" (while (search-forward "BEGIN_" nil t) (org-cycle)) (search-backward "#+BEGIN_CENTER") (org-drag-element-forward) (should (equal '((26 . 48) (63 . 82)) (mapcar (lambda (ov) (cons (overlay-start ov) (overlay-end ov))) (overlays-in (point-min) (point-max)))))))) (ert-deftest test-org/next-block () "Test `org-next-block' specifications." ;; Regular test. (should (org-test-with-temp-text "Paragraph\n#+BEGIN_CENTER\ncontents\n#+END_CENTER" (org-next-block 1) (looking-at "#\\+BEGIN_CENTER"))) ;; Ignore case. (should (org-test-with-temp-text "Paragraph\n#+begin_center\ncontents\n#+end_center" (let ((case-fold-search nil)) (org-next-block 1) (looking-at "#\\+begin_center")))) ;; Ignore current line. (should (org-test-with-temp-text "#+BEGIN_QUOTE\n#+END_QUOTE\n#+BEGIN_CENTER\n#+END_CENTER" (org-next-block 1) (looking-at "#\\+BEGIN_CENTER"))) ;; Throw an error when no block is found. (should-error (org-test-with-temp-text "Paragraph" (org-next-block 1))) ;; With an argument, skip many blocks at once. (should (org-test-with-temp-text "Start\n#+BEGIN_CENTER\nA\n#+END_CENTER\n#+BEGIN_QUOTE\nB\n#+END_QUOTE" (org-next-block 2) (looking-at "#\\+BEGIN_QUOTE"))) ;; With optional argument BLOCK-REGEXP, filter matched blocks. (should (org-test-with-temp-text "Start\n#+BEGIN_CENTER\nA\n#+END_CENTER\n#+BEGIN_QUOTE\nB\n#+END_QUOTE" (org-next-block 1 nil "^[ \t]*#\\+BEGIN_QUOTE") (looking-at "#\\+BEGIN_QUOTE"))) ;; Optional argument is also case-insensitive. (should (org-test-with-temp-text "Start\n#+BEGIN_CENTER\nA\n#+END_CENTER\n#+begin_quote\nB\n#+end_quote" (let ((case-fold-search nil)) (org-next-block 1 nil "^[ \t]*#\\+BEGIN_QUOTE") (looking-at "#\\+begin_quote"))))) (ert-deftest test-org/insert-structure-template () "Test `org-insert-structure-template'." ;; Test in empty buffer. (should (string= "#+begin_foo\n#+end_foo\n" (org-test-with-temp-text "" (org-insert-structure-template "foo") (buffer-string)))) ;; Test with multiple lines in buffer. (should (string= "#+begin_foo\nI'm a paragraph\n#+end_foo\n\nI'm a second paragraph" (org-test-with-temp-text "I'm a paragraph\n\nI'm a second paragraph" (transient-mark-mode 1) (org-mark-element) (org-insert-structure-template "foo") (buffer-string)))) ;; Mark only the current line. (should (string= "#+begin_foo\nI'm a paragraph\n#+end_foo\n\nI'm a second paragraph" (org-test-with-temp-text "I'm a paragraph\n\nI'm a second paragraph" (transient-mark-mode 1) (set-mark (point-min)) (end-of-line) (org-insert-structure-template "foo") (buffer-string)))) ;; Middle of paragraph. (should (string= "p1\n#+begin_foo\np2\n#+end_foo\np3" (org-test-with-temp-text "p1\np2\np3" (set-mark (line-beginning-position)) (end-of-line) (activate-mark) (org-insert-structure-template "foo") (buffer-string)))) ;; Test with text in buffer, no region, no final newline. (should (string= "#+begin_foo\nI'm a paragraph.\n#+end_foo\n" (org-test-with-temp-text "I'm a paragraph." (org-mark-element) (org-insert-structure-template "foo") (buffer-string)))) ;; Test with text in buffer and region set. (should (string= "#+begin_foo\nI'm a paragraph\n\nI'm a second paragrah\n#+end_foo\n" (org-test-with-temp-text "I'm a paragraph\n\nI'm a second paragrah" (set-mark (point)) (goto-char (point-max)) (org-insert-structure-template "foo") (buffer-string)))) ;; Test with example escaping. (should (string= "#+begin_example\n,* Heading\n#+end_example\n" (org-test-with-temp-text "* Heading" (org-mark-element) (org-insert-structure-template "example") (buffer-string)))) ;; Test with indentation. (should (string= " #+begin_foo\n This is a paragraph\n #+end_foo\n" (org-test-with-temp-text " This is a paragraph" (org-mark-element) (org-insert-structure-template "foo") (buffer-string)))) (should (string= " #+begin_foo\n Line 1\n Line2\n #+end_foo\n" (org-test-with-temp-text " Line 1\n Line2" (org-mark-element) (org-insert-structure-template "foo") (buffer-string)))) ;; Test point location. (should (string= "#+begin_foo\n" (org-test-with-temp-text "" (org-insert-structure-template "foo") (buffer-substring (point-min) (point))))) (should (string= "#+begin_src " (org-test-with-temp-text "" (org-insert-structure-template "src") (buffer-substring (point-min) (point)))))) (ert-deftest test-org/previous-block () "Test `org-previous-block' specifications." ;; Regular test. (should (org-test-with-temp-text "#+BEGIN_CENTER\ncontents\n#+END_CENTER\n" (org-previous-block 1) (looking-at "#\\+BEGIN_CENTER"))) ;; Ignore case. (should (org-test-with-temp-text "#+begin_center\ncontents\n#+end_center\n" (let ((case-fold-search nil)) (org-previous-block 1) (looking-at "#\\+begin_center")))) ;; Ignore current line. (should (org-test-with-temp-text "#+BEGIN_QUOTE\n#+END_QUOTE\n#+BEGIN_CENTER\n#+END_CENTER" (org-previous-block 1) (looking-at "#\\+BEGIN_QUOTE"))) ;; Throw an error when no block is found. (should-error (org-test-with-temp-text "Paragraph" (org-previous-block 1))) ;; With an argument, skip many blocks at once. (should (org-test-with-temp-text "#+BEGIN_CENTER\nA\n#+END_CENTER\n#+BEGIN_QUOTE\nB\n#+END_QUOTE\n" (org-previous-block 2) (looking-at "#\\+BEGIN_CENTER"))) ;; With optional argument BLOCK-REGEXP, filter matched blocks. (should (org-test-with-temp-text "#+BEGIN_CENTER\nA\n#+END_CENTER\n#+BEGIN_QUOTE\nB\n#+END_QUOTE\n" (org-previous-block 1 "^[ \t]*#\\+BEGIN_QUOTE") (looking-at "#\\+BEGIN_QUOTE"))) ;; Optional argument is also case-insensitive. (should (org-test-with-temp-text "#+BEGIN_CENTER\nA\n#+END_CENTER\n#+begin_quote\nB\n#+end_quote\n" (let ((case-fold-search nil)) (org-next-block 1 "^[ \t]*#\\+BEGIN_QUOTE") (looking-at "#\\+begin_quote"))))) ;;; Outline structure (ert-deftest test-org/move-subtree () "Test `org-metaup' and `org-metadown' on headings." (should (equal "* H2\n* H1\n" (org-test-with-temp-text "* H1\n* H2\n" (org-metadown) (buffer-string)))) (should (equal "* H2\n* H1\n" (org-test-with-temp-text "* H1\n* H2\n" (org-metaup) (buffer-string)))) (should-error (org-test-with-temp-text "* H1\n* H2\n" (org-metadown) (buffer-string))) (should-error (org-test-with-temp-text "* H1\n* H2\n" (org-metaup) (buffer-string))) (should-error (org-test-with-temp-text "* H1\n** H1.2\n* H2" (org-metadown) (buffer-string))) (should-error (org-test-with-temp-text "* H1\n** H1.2\n" (org-metaup) (buffer-string))) ;; With selection (should (equal "* T\n** H3\n** H1\n** H2\n" (org-test-with-temp-text "* T\n** H1\n** H2\n** H3\n" (set-mark (point)) (search-forward "H2") (org-metadown) (buffer-string)))) (should (equal "* T\n** H1\n** H2\n** H0\n** H3\n" (org-test-with-temp-text "* T\n** H0\n** H1\n** H2\n** H3\n" (set-mark (point)) (search-forward "H2") (org-metaup) (buffer-string)))) (should-error (org-test-with-temp-text "* T\n** H1\n** H2\n* T2\n" (set-mark (point)) (search-forward "H2") (org-metadown))) (should-error (org-test-with-temp-text "* T\n** H1\n** H2\n* T2\n" (set-mark (point)) (search-forward "H2") (org-metaup)))) (ert-deftest test-org/demote () "Test `org-demote' specifications." ;; Add correct number of stars according to `org-odd-levels-only'. (should (= 2 (org-test-with-temp-text "* H" (let ((org-odd-levels-only nil)) (org-demote)) (org-current-level)))) (should (= 3 (org-test-with-temp-text "* H" (let ((org-odd-levels-only t)) (org-demote)) (org-current-level)))) ;; When `org-auto-align-tags' is non-nil, move tags accordingly. (should (org-test-with-temp-text "* H :tag:" (let ((org-tags-column 10) (org-auto-align-tags t) (org-odd-levels-only nil)) (org-demote)) (org-move-to-column 10) (looking-at-p ":tag:$"))) (should-not (org-test-with-temp-text "* H :tag:" (let ((org-tags-column 10) (org-auto-align-tags nil) (org-odd-levels-only nil)) (org-demote)) (org-move-to-column 10) (looking-at-p ":tag:$"))) ;; When `org-adapt-indentation' is non-nil, always indent planning ;; info and property drawers accordingly. (should (= 3 (org-test-with-temp-text "* H\n SCHEDULED: <2014-03-04 tue.>" (let ((org-odd-levels-only nil) (org-adapt-indentation t)) (org-demote)) (forward-line) (org-get-indentation)))) (should (= 3 (org-test-with-temp-text "* H\n :PROPERTIES:\n :FOO: Bar\n :END:" (let ((org-odd-levels-only nil) (org-adapt-indentation t)) (org-demote)) (forward-line) (org-get-indentation)))) (should-not (= 3 (org-test-with-temp-text "* H\n SCHEDULED: <2014-03-04 tue.>" (let ((org-odd-levels-only nil) (org-adapt-indentation nil)) (org-demote)) (forward-line) (org-get-indentation)))) ;; When `org-adapt-indentation' is non-nil, shift all lines in ;; section accordingly. Ignore, however, footnote definitions and ;; inlinetasks boundaries. (should (= 3 (org-test-with-temp-text "* H\n Paragraph" (let ((org-odd-levels-only nil) (org-adapt-indentation t)) (org-demote)) (forward-line) (org-get-indentation)))) (should (= 2 (org-test-with-temp-text "* H\n Paragraph" (let ((org-odd-levels-only nil) (org-adapt-indentation nil)) (org-demote)) (forward-line) (org-get-indentation)))) (should (zerop (org-test-with-temp-text "* H\n[fn:1] def line 1\ndef line 2" (let ((org-odd-levels-only nil) (org-adapt-indentation t)) (org-demote)) (goto-char (point-max)) (org-get-indentation)))) (should (= 3 (org-test-with-temp-text "* H\n[fn:1] Def.\n\n\n After def." (let ((org-odd-levels-only nil) (org-adapt-indentation t)) (org-demote)) (goto-char (point-max)) (org-get-indentation)))) (when (featurep 'org-inlinetask) (should (zerop (let ((org-inlinetask-min-level 5) (org-adapt-indentation t)) (org-test-with-temp-text "* H\n***** I\n***** END" (org-demote) (forward-line) (org-get-indentation)))))) (when (featurep 'org-inlinetask) (should (= 3 (let ((org-inlinetask-min-level 5) (org-adapt-indentation t)) (org-test-with-temp-text "* H\n***** I\n Contents\n***** END" (org-demote) (forward-line 2) (org-get-indentation)))))) ;; When `org-adapt-indentation' is non-nil, log drawers are ;; adjusted. (should (equal "** H\n :LOGBOOK:\n - a\n :END:\n b" (org-test-with-temp-text "* H\n :LOGBOOK:\n - a\n :END:\n b" (let ((org-odd-levels-only nil) (org-adapt-indentation t)) (org-demote)) (buffer-string)))) (should (equal "** H\n :LOGBOOK:\n - a\n :END:\n b" (org-test-with-temp-text "* H\n :LOGBOOK:\n - a\n :END:\n b" (let ((org-odd-levels-only nil) (org-adapt-indentation 'headline-data)) (org-demote)) (buffer-string)))) (should (equal "** H\n :LOGBOOK:\n - a\n :END:" (org-test-with-temp-text "* H\n:LOGBOOK:\n- a\n:END:" (let ((org-odd-levels-only nil) (org-adapt-indentation t)) (org-demote)) (buffer-string)))) ;; Ignore contents of source blocks or example blocks when ;; indentation should be preserved (through ;; `org-src-preserve-indentation' or "-i" flag). (should-not (zerop (org-test-with-temp-text "* H\n#+BEGIN_SRC emacs-lisp\n(+ 1 1)\n#+END_SRC" (let ((org-adapt-indentation t) (org-src-preserve-indentation nil)) (org-demote)) (forward-line 2) (org-get-indentation)))) (should (zerop (org-test-with-temp-text "* H\n#+BEGIN_EXAMPLE\n(+ 1 1)\n#+END_EXAMPLE" (let ((org-adapt-indentation t) (org-src-preserve-indentation t)) (org-demote)) (forward-line 2) (org-get-indentation)))) (should (zerop (org-test-with-temp-text "* H\n#+BEGIN_SRC emacs-lisp\n(+ 1 1)\n#+END_SRC" (let ((org-adapt-indentation t) (org-src-preserve-indentation t)) (org-demote)) (forward-line 2) (org-get-indentation)))) (should (zerop (org-test-with-temp-text "* H\n#+BEGIN_SRC emacs-lisp -i\n(+ 1 1)\n#+END_SRC" (let ((org-adapt-indentation t) (org-src-preserve-indentation nil)) (org-demote)) (forward-line 2) (org-get-indentation))))) (ert-deftest test-org/promote () "Test `org-promote' specifications." ;; Return an error if headline is to be promoted to level 0, unless ;; `org-allow-promoting-top-level-subtree' is non-nil, in which case ;; headline becomes a comment. (should-error (org-test-with-temp-text "* H" (let ((org-allow-promoting-top-level-subtree nil)) (org-promote)))) (should (equal "# H" (org-test-with-temp-text "* H" (let ((org-allow-promoting-top-level-subtree t)) (org-promote)) (buffer-string)))) ;; Remove correct number of stars according to ;; `org-odd-levels-only'. (should (= 2 (org-test-with-temp-text "*** H" (let ((org-odd-levels-only nil)) (org-promote)) (org-current-level)))) (should (= 1 (org-test-with-temp-text "*** H" (let ((org-odd-levels-only t)) (org-promote)) (org-current-level)))) ;; When `org-auto-align-tags' is non-nil, move tags accordingly. (should (org-test-with-temp-text "** H :tag:" (let ((org-tags-column 10) (org-auto-align-tags t) (org-odd-levels-only nil)) (org-promote)) (org-move-to-column 10) (looking-at-p ":tag:$"))) (should-not (org-test-with-temp-text "** H :tag:" (let ((org-tags-column 10) (org-auto-align-tags nil) (org-odd-levels-only nil)) (org-promote)) (org-move-to-column 10) (looking-at-p ":tag:$"))) ;; When `org-adapt-indentation' is non-nil, always indent planning ;; info and property drawers. (should (= 2 (org-test-with-temp-text "** H\n SCHEDULED: <2014-03-04 tue.>" (let ((org-odd-levels-only nil) (org-adapt-indentation t)) (org-promote)) (forward-line) (org-get-indentation)))) (should (= 2 (org-test-with-temp-text "** H\n :PROPERTIES:\n :FOO: Bar\n :END:" (let ((org-odd-levels-only nil) (org-adapt-indentation t)) (org-promote)) (forward-line) (org-get-indentation)))) (should-not (= 2 (org-test-with-temp-text "** H\n SCHEDULED: <2014-03-04 tue.>" (let ((org-odd-levels-only nil) (org-adapt-indentation nil)) (org-promote)) (forward-line) (org-get-indentation)))) ;; When `org-adapt-indentation' is non-nil, shift all lines in ;; section accordingly. Ignore, however, footnote definitions and ;; inlinetasks boundaries. (should (= 2 (org-test-with-temp-text "** H\n Paragraph" (let ((org-odd-levels-only nil) (org-adapt-indentation t)) (org-promote)) (forward-line) (org-get-indentation)))) (should-not (= 2 (org-test-with-temp-text "** H\n Paragraph" (let ((org-odd-levels-only nil) (org-adapt-indentation nil)) (org-promote)) (forward-line) (org-get-indentation)))) (should (= 2 (org-test-with-temp-text "** H\n Paragraph\n[fn:1] line1\nline2" (let ((org-odd-levels-only nil) (org-adapt-indentation t)) (org-promote)) (forward-line) (org-get-indentation)))) (when (featurep 'org-inlinetask) (should (zerop (let ((org-inlinetask-min-level 5) (org-adapt-indentation t)) (org-test-with-temp-text "** H\n***** I\n***** END" (org-promote) (forward-line) (org-get-indentation)))))) (when (featurep 'org-inlinetask) (should (= 2 (let ((org-inlinetask-min-level 5) (org-adapt-indentation t)) (org-test-with-temp-text "** H\n***** I\n Contents\n***** END" (org-promote) (forward-line 2) (org-get-indentation)))))) ;; Give up shifting if it would break document's structure ;; otherwise. (should (= 3 (org-test-with-temp-text "** H\n Paragraph\n [fn:1] Def." (let ((org-odd-levels-only nil) (org-adapt-indentation t)) (org-promote)) (forward-line) (org-get-indentation)))) (should (= 3 (org-test-with-temp-text "** H\n Paragraph\n * list." (let ((org-odd-levels-only nil) (org-adapt-indentation t)) (org-promote)) (forward-line) (org-get-indentation)))) ;; When `org-adapt-indentation' is non-nil, log drawers are ;; adjusted. (should (equal "* H\n :LOGBOOK:\n - a\n :END:\n b" (org-test-with-temp-text "** H\n :LOGBOOK:\n - a\n :END:\n b" (let ((org-odd-levels-only nil) (org-adapt-indentation t)) (org-promote)) (buffer-string)))) (should (equal "* H\n :LOGBOOK:\n - a\n :END:\n b" (org-test-with-temp-text "** H\n :LOGBOOK:\n - a\n :END:\n b" (let ((org-odd-levels-only nil) (org-adapt-indentation 'headline-data)) (org-promote)) (buffer-string)))) (should (equal "* H\n:LOGBOOK:\n- a\n:END:" (org-test-with-temp-text "** H\n:LOGBOOK:\n- a\n:END:" (let ((org-odd-levels-only nil) (org-adapt-indentation t)) (org-promote)) (buffer-string)))) (should (equal "# H\n:LOGBOOK:\n- a\n:END:" (org-test-with-temp-text "* H\n:LOGBOOK:\n- a\n:END:" (let ((org-odd-levels-only nil) (org-allow-promoting-top-level-subtree t) (org-adapt-indentation t)) (org-promote)) (buffer-string)))) ;; Ignore contents of source blocks or example blocks when ;; indentation should be preserved (through ;; `org-src-preserve-indentation' or "-i" flag). (should-not (zerop (org-test-with-temp-text "** H\n #+BEGIN_SRC emacs-lisp\n(+ 1 1)\n #+END_SRC" (let ((org-adapt-indentation t) (org-src-preserve-indentation nil) (org-odd-levels-only nil)) (org-promote)) (forward-line) (org-get-indentation)))) (should (zerop (org-test-with-temp-text "** H\n #+BEGIN_EXAMPLE\nContents\n #+END_EXAMPLE" (let ((org-adapt-indentation t) (org-src-preserve-indentation t) (org-odd-levels-only nil)) (org-promote)) (forward-line) (org-get-indentation)))) (should (zerop (org-test-with-temp-text "** H\n #+BEGIN_SRC emacs-lisp\n(+ 1 1)\n #+END_SRC" (let ((org-adapt-indentation t) (org-src-preserve-indentation t) (org-odd-levels-only nil)) (org-promote)) (forward-line) (org-get-indentation)))) (should (zerop (org-test-with-temp-text "** H\n #+BEGIN_SRC emacs-lisp -i\n(+ 1 1)\n #+END_SRC" (let ((org-adapt-indentation t) (org-src-preserve-indentation nil) (org-odd-levels-only nil)) (org-promote)) (forward-line) (org-get-indentation))))) (ert-deftest test-org/org-get-valid-level () "Test function `org-get-valid-level' specifications." (let ((org-odd-levels-only nil)) (should (equal 1 (org-get-valid-level 0 0))) (should (equal 1 (org-get-valid-level 0 1))) (should (equal 2 (org-get-valid-level 0 2))) (should (equal 3 (org-get-valid-level 0 3))) (should (equal 1 (org-get-valid-level 1 0))) (should (equal 2 (org-get-valid-level 1 1))) (should (equal 23 (org-get-valid-level 1 22))) (should (equal 1 (org-get-valid-level 1 -1))) (should (equal 1 (org-get-valid-level 2 -1)))) (let ((org-odd-levels-only t)) (should (equal 1 (org-get-valid-level 0 0))) (should (equal 1 (org-get-valid-level 0 1))) (should (equal 3 (org-get-valid-level 0 2))) (should (equal 5 (org-get-valid-level 0 3))) (should (equal 1 (org-get-valid-level 1 0))) (should (equal 3 (org-get-valid-level 1 1))) (should (equal 3 (org-get-valid-level 2 1))) (should (equal 5 (org-get-valid-level 3 1))) (should (equal 5 (org-get-valid-level 4 1))) (should (equal 43 (org-get-valid-level 1 21))) (should (equal 1 (org-get-valid-level 1 -1))) (should (equal 1 (org-get-valid-level 2 -1))) (should (equal 1 (org-get-valid-level 3 -1))) (should (equal 3 (org-get-valid-level 4 -1))) (should (equal 3 (org-get-valid-level 5 -1))))) ;;; Planning (ert-deftest test-org/at-planning-p () "Test `org-at-planning-p' specifications." ;; Regular test. (should (org-test-with-temp-text "* Headline\nDEADLINE: <2014-03-04 tue.>" (org-at-planning-p))) (should-not (org-test-with-temp-text "DEADLINE: <2014-03-04 tue.>" (org-at-planning-p))) ;; Correctly find planning attached to inlinetasks. (when (featurep 'org-inlinetask) (should (org-test-with-temp-text "*** Inlinetask\nDEADLINE: <2014-03-04 tue.>\n*** END" (let ((org-inlinetask-min-level 3)) (org-at-planning-p)))) (should-not (org-test-with-temp-text "*** Inlinetask\nDEADLINE: <2014-03-04 tue.>" (let ((org-inlinetask-min-level 3)) (org-at-planning-p)))) (should-not (org-test-with-temp-text "* Headline\n*** Inlinetask\nDEADLINE: <2014-03-04 tue.>" (let ((org-inlinetask-min-level 3)) (org-at-planning-p)))) (should-not (org-test-with-temp-text "* Headline\n*** Inlinetask\n*** END\nDEADLINE: <2014-03-04 tue.>" (let ((org-inlinetask-min-level 3)) (org-at-planning-p)))))) (ert-deftest test-org/add-planning-info () "Test `org-add-planning-info'." ;; Create deadline when `org-adapt-indentation' is non-nil. (should (equal "* H\n DEADLINE: <2015-06-25>\nParagraph" (org-test-with-temp-text "* H\nParagraph" (let ((org-adapt-indentation t)) (org-test-without-dow (org-test-with-result 'buffer (org-add-planning-info 'deadline "<2015-06-25 Thu>"))))))) ;; Create deadline when `org-adapt-indentation' is nil. (should (equal "* H\nDEADLINE: <2015-06-25>\nParagraph" (org-test-with-temp-text "* H\nParagraph" (let ((org-adapt-indentation nil)) (org-test-without-dow (org-test-with-result 'buffer (org-add-planning-info 'deadline "<2015-06-25 Thu>"))))))) ;; Update deadline when `org-adapt-indentation' is non-nil. (should (equal "* H\n DEADLINE: <2015-06-25>\nParagraph" (org-test-with-temp-text "\ * H DEADLINE: <2015-06-24 Wed> Paragraph" (let ((org-adapt-indentation t)) (org-test-without-dow (org-test-with-result 'buffer (org-add-planning-info 'deadline "<2015-06-25 Thu>"))))))) ;; Update deadline when `org-adapt-indentation' is nil. (should (equal "* H\nDEADLINE: <2015-06-25>\nParagraph" (org-test-with-temp-text "\ * H DEADLINE: <2015-06-24 Wed> Paragraph" (let ((org-adapt-indentation nil)) (org-test-without-dow (org-test-with-result 'buffer (org-add-planning-info 'deadline "<2015-06-25 Thu>"))))))) ;; Schedule when `org-adapt-indentation' is non-nil. (should (equal "* H\n SCHEDULED: <2015-06-25>\nParagraph" (org-test-with-temp-text "* H\nParagraph" (let ((org-adapt-indentation t)) (org-test-without-dow (org-test-with-result 'buffer (org-add-planning-info 'scheduled "<2015-06-25 Thu>"))))))) ;; Schedule when `org-adapt-indentation' is nil. (should (equal "* H\nSCHEDULED: <2015-06-25>\nParagraph" (org-test-with-temp-text "* H\nParagraph" (let ((org-adapt-indentation nil)) (org-test-without-dow (org-test-with-result 'buffer (org-add-planning-info 'scheduled "<2015-06-25 Thu>"))))))) ;; Add deadline when scheduled. (should (equal "\ * H DEADLINE: <2015-06-25> SCHEDULED: <2015-06-24> Paragraph" (org-test-with-temp-text "\ * H SCHEDULED: <2015-06-24> Paragraph" (let ((org-adapt-indentation t)) (org-test-without-dow (org-test-with-result 'buffer (org-add-planning-info 'deadline "<2015-06-25 Thu>"))))))) ;; Remove middle entry. (should (equal "\ * H CLOSED: [2015-06-24] SCHEDULED: <2015-06-24> Paragraph" (org-test-with-temp-text "\ * H CLOSED: [2015-06-24] DEADLINE: <2015-06-25 Thu> SCHEDULED: <2015-06-24> Paragraph" (let ((org-adapt-indentation t)) (org-test-without-dow (org-test-with-result 'buffer (org-add-planning-info nil nil 'deadline))))))) ;; Remove last entry and then middle entry (order should not ;; matter). (should (equal "\ * H CLOSED: [2015-06-24] Paragraph" (org-test-with-temp-text "\ * H CLOSED: [2015-06-24] DEADLINE: <2015-06-25 Thu> SCHEDULED: <2015-06-24 Wed> Paragraph" (let ((org-adapt-indentation t)) (org-test-without-dow (org-test-with-result 'buffer (org-add-planning-info nil nil 'scheduled 'deadline))))))) ;; Remove closed when `org-adapt-indentation' is non-nil. (should (equal "* H\n DEADLINE: <2015-06-25>\nParagraph" (org-test-with-temp-text "\ * H CLOSED: [2015-06-25 Thu] DEADLINE: <2015-06-25> Paragraph" (let ((org-adapt-indentation t)) (org-test-without-dow (org-test-with-result 'buffer (org-add-planning-info nil nil 'closed))))))) (should (equal "* H\n Paragraph" (org-test-with-temp-text "\ * H CLOSED: [2015-06-25 Thu] Paragraph" (let ((org-adapt-indentation t)) (org-test-without-dow (org-test-with-result 'buffer (org-add-planning-info nil nil 'closed))))))) ;; Remove closed when `org-adapt-indentation' is nil. (should (equal "* H\nDEADLINE: <2015-06-25>\nParagraph" (org-test-with-temp-text "\ * H CLOSED: [2015-06-25 Thu] DEADLINE: <2015-06-25> Paragraph" (let ((org-adapt-indentation nil)) (org-test-without-dow (org-test-with-result 'buffer (org-add-planning-info nil nil 'closed))))))) (should (equal "* H\nParagraph" (org-test-with-temp-text "\ * H CLOSED: [2015-06-25 Thu] Paragraph" (let ((org-adapt-indentation nil)) (org-test-without-dow (org-test-with-result 'buffer (org-add-planning-info nil nil 'closed))))))) ;; Remove closed entry and delete empty line. (should (equal "\ * H Paragraph" (org-test-with-temp-text "\ * H CLOSED: [2015-06-24 Wed] Paragraph" (let ((org-adapt-indentation t)) (org-test-without-dow (org-test-with-result 'buffer (org-add-planning-info nil nil 'closed))))))) ;; Remove one entry and update another. (should (equal "* H\n DEADLINE: <2015-06-25>\nParagraph" (org-test-with-temp-text "\ * H SCHEDULED: <2015-06-23 Tue> DEADLINE: <2015-06-24 Wed> Paragraph" (let ((org-adapt-indentation t)) (org-test-without-dow (org-test-with-result 'buffer (org-add-planning-info 'deadline "<2015-06-25 Thu>" 'scheduled)))))))) (ert-deftest test-org/deadline () "Test `org-deadline' specifications." ;; Insert a new value or replace existing one. (should (equal "* H\nDEADLINE: <2012-03-29>" (org-test-with-temp-text "* H" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-test-without-dow (org-test-with-result 'buffer (org-deadline nil "<2012-03-29 Tue>"))))))) (should (equal "* H\nDEADLINE: <2014-03-04>" (org-test-with-temp-text "* H\nDEADLINE: <2012-03-29>" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-test-without-dow (org-test-with-result 'buffer (org-deadline nil "<2014-03-04 Thu>"))))))) ;; Accept delta time, e.g., "+2d". (should (equal "* H\nDEADLINE: <2015-03-04>" (org-test-at-time "2014-03-04" (org-test-with-temp-text "* H" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-test-without-dow (org-test-with-result 'buffer (org-deadline nil "+1y")))))))) ;; Preserve repeater. (should (equal "* H\nDEADLINE: <2012-03-29 +2y>" (org-test-with-temp-text "* H" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-test-without-dow (org-test-with-result 'buffer (org-deadline nil "<2012-03-29 Tue +2y>"))))))) ;; Preserve warning period. (should (equal "* H\nDEADLINE: <2021-07-20 -1d>" (org-test-with-temp-text "* H" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-test-without-dow (org-test-with-result 'buffer (org-deadline nil "<2021-07-20 Tue -1d>"))))))) (should (equal "* H\nDEADLINE: <2021-07-20 +1m -3d>" (org-test-with-temp-text "* H" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-test-without-dow (org-test-with-result 'buffer (org-deadline nil "<2021-07-20 Tue +1m -3d>"))))))) ;; Remove CLOSED keyword, if any. (should (equal "* H\nDEADLINE: <2012-03-29>" (org-test-with-temp-text "* H\nCLOSED: [2017-01-25 Wed]" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-test-without-dow (org-test-with-result 'buffer (org-deadline nil "<2012-03-29 Tue>"))))))) ;; With C-u argument, remove DEADLINE keyword. (should (equal "* H\n" (org-test-with-temp-text "* H\nDEADLINE: <2012-03-29>" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-deadline '(4))) (buffer-string)))) (should (equal "* H" (org-test-with-temp-text "* H" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-deadline '(4))) (buffer-string)))) ;; With C-u C-u argument, prompt for a delay cookie. (should (equal "* H\nDEADLINE: <2012-03-29 -705d>" (cl-letf (((symbol-function 'org-read-date) (lambda (&rest _args) (org-time-string-to-time "2014-03-04")))) (org-test-with-temp-text "* H\nDEADLINE: <2012-03-29>" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-deadline '(16))) (buffer-string))))) (should-error (cl-letf (((symbol-function 'org-read-date) (lambda (&rest _args) (org-time-string-to-time "2014-03-04")))) (org-test-with-temp-text "* H" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-deadline '(16))) (buffer-string)))) ;; When a region is active and ;; `org-loop-over-headlines-in-active-region' is non-nil, insert the ;; same value in all headlines in region. (should (equal "* H1\nDEADLINE: <2012-03-29>\n* H2\nDEADLINE: <2012-03-29>" (org-test-with-temp-text "* H1\n* H2" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil) (org-loop-over-headlines-in-active-region t)) (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (org-test-without-dow (org-test-with-result 'buffer (org-deadline nil "2012-03-29"))))))) (should-not (equal "* H1\nDEADLINE: <2012-03-29>\n* H2\nDEADLINE: <2012-03-29>" (org-test-with-temp-text "* H1\n* H2" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil) (org-loop-over-headlines-in-active-region nil)) (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (org-test-without-dow (org-test-with-result 'buffer (org-deadline nil "2012-03-29")))))))) (ert-deftest test-org/schedule () "Test `org-schedule' specifications." ;; Insert a new value or replace existing one. (should (equal "* H\nSCHEDULED: <2012-03-29>" (org-test-with-temp-text "* H" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-test-without-dow (org-test-with-result 'buffer (org-schedule nil "<2012-03-29 Tue>"))))))) (should (equal "* H\nSCHEDULED: <2014-03-04>" (org-test-with-temp-text "* H\nSCHEDULED: <2012-03-29>" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-test-without-dow (org-test-with-result 'buffer (org-schedule nil "<2014-03-04 Thu>"))))))) ;; Accept delta time, e.g., "+2d". (should (equal "* H\nSCHEDULED: <2015-03-04>" (org-test-at-time "2014-03-04" (org-test-with-temp-text "* H" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-test-without-dow (org-test-with-result 'buffer (org-schedule nil "+1y")))))))) ;; Preserve repeater. (should (equal "* H\nSCHEDULED: <2012-03-29 +2y>" (org-test-with-temp-text "* H" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-test-without-dow (org-test-with-result 'buffer (org-schedule nil "<2012-03-29 Tue +2y>"))))))) ;; Remove CLOSED keyword, if any. (should (equal "* H\nSCHEDULED: <2012-03-29>" (org-test-with-temp-text "* H\nCLOSED: [2017-01-25 Wed]" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-test-without-dow (org-test-with-result 'buffer (org-schedule nil "<2012-03-29 Tue>"))))))) ;; With C-u argument, remove SCHEDULED keyword. (should (equal "* H\n" (org-test-with-temp-text "* H\nSCHEDULED: <2012-03-29>" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-schedule '(4))) (buffer-string)))) (should (equal "* H" (org-test-with-temp-text "* H" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-schedule '(4))) (buffer-string)))) ;; With C-u C-u argument, prompt for a delay cookie. (should (equal "* H\nSCHEDULED: <2012-03-29 -705d>" (cl-letf (((symbol-function 'org-read-date) (lambda (&rest _args) (org-time-string-to-time "2014-03-04")))) (org-test-with-temp-text "* H\nSCHEDULED: <2012-03-29>" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-schedule '(16))) (buffer-string))))) (should-error (cl-letf (((symbol-function 'org-read-date) (lambda (&rest _args) (org-time-string-to-time "2014-03-04")))) (org-test-with-temp-text "* H" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-schedule '(16))) (buffer-string)))) ;; When a region is active and ;; `org-loop-over-headlines-in-active-region' is non-nil, insert the ;; same value in all headlines in region. (should (equal "* H1\nSCHEDULED: <2012-03-29>\n* H2\nSCHEDULED: <2012-03-29>" (org-test-with-temp-text "* H1\n* H2" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil) (org-loop-over-headlines-in-active-region t)) (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (org-test-without-dow (org-test-with-result 'buffer (org-schedule nil "2012-03-29"))))))) (should-not (equal "* H1\nSCHEDULED: <2012-03-29>\n* H2\nSCHEDULED: <2012-03-29>" (org-test-with-temp-text "* H1\n* H2" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil) (org-loop-over-headlines-in-active-region nil)) (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (org-test-without-dow (org-test-with-result 'buffer (org-schedule nil "2012-03-29"))))))) (should ;; check if a repeater survives re-scheduling. (equal "* H\nSCHEDULED: <2017-02-01 ++7d>\n" (org-test-with-temp-text "* H\nSCHEDULED: <2017-01-19 ++7d>\n" (let ((org-adapt-indentation nil) (org-last-inserted-timestamp nil)) (org-test-without-dow (org-test-with-result 'buffer (org-schedule nil "2017-02-01")))))))) ;;; Property API (ert-deftest test-org/buffer-property-keys () "Test `org-buffer-property-keys' specifications." ;; Retrieve properties across siblings. (should (equal '("A" "B") (org-test-with-temp-text " * H1 :PROPERTIES: :A: 1 :END: * H2 :PROPERTIES: :B: 1 :END:" (org-buffer-property-keys)))) ;; Retrieve properties across children. (should (equal '("A" "B") (org-test-with-temp-text " * H1 :PROPERTIES: :A: 1 :END: ** H2 :PROPERTIES: :B: 1 :END:" (org-buffer-property-keys)))) ;; Retrieve multiple properties in the same drawer. (should (equal '("A" "B") (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:B: 2\n:END:" (org-buffer-property-keys)))) ;; Ignore extension symbol in property name. (should (equal '("A") (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:A+: 2\n:END:" (org-buffer-property-keys)))) ;; Add bare property if xxx_ALL property is there (should (equal '("A" "B" "B_ALL") (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:A+: 2\n:B_ALL: foo bar\n:END:" (org-buffer-property-keys)))) ;; Add bare property if xxx_ALL property is there - check dupes (should (equal '("A" "B" "B_ALL") (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:B: 2\n:B_ALL: foo bar\n:END:" (org-buffer-property-keys)))) ;; Retrieve properties from #+PROPERTY keyword lines (should (equal '("A" "C") (org-test-with-temp-text "#+PROPERTY: C foo\n* H\n:PROPERTIES:\n:A: 1\n:A+: 2\n:END:" (org-buffer-property-keys)))) ;; Retrieve properties from #+PROPERTY keyword lines - make sure an _ALL property also ;; adds the bare property (should (equal '("A" "C" "C_ALL") (org-test-with-temp-text "#+PROPERTY: C_ALL foo bar\n* H\n:PROPERTIES:\n:A: 1\n:A+: 2\n:END:" (org-buffer-property-keys)))) ;; With non-nil COLUMNS, extract property names from columns. (should (equal '("A" "B") (org-test-with-temp-text "#+COLUMNS: %25ITEM %A %20B" (org-buffer-property-keys nil nil t)))) (should (equal '("A" "B" "COLUMNS") (org-test-with-temp-text "* H\n:PROPERTIES:\n:COLUMNS: %25ITEM %A %20B\n:END:" (org-buffer-property-keys nil nil t)))) ;; In COLUMNS, ignore title and summary-type. (should (equal '("A") (org-test-with-temp-text "#+COLUMNS: %A(Foo)" (org-buffer-property-keys nil nil t)))) (should (equal '("A") (org-test-with-temp-text "#+COLUMNS: %A{Foo}" (org-buffer-property-keys nil nil t)))) (should (equal '("A") (org-test-with-temp-text "#+COLUMNS: %A(Foo){Bar}" (org-buffer-property-keys nil nil t))))) (ert-deftest test-org/property-values () "Test `org-property-values' specifications." ;; Regular test. (should (equal '("2" "1") (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:\n* H\n:PROPERTIES:\n:A: 2\n:END:" (org-property-values "A")))) ;; Ignore empty values. (should-not (org-test-with-temp-text "* H1\n:PROPERTIES:\n:A:\n:END:\n* H2\n:PROPERTIES:\n:A: \n:END:" (org-property-values "A"))) ;; Take into consideration extended values. (should (equal '("1 2") (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:A+: 2\n:END:" (org-property-values "A"))))) (ert-deftest test-org/set-property () "Test `org-set-property' specifications." (should (equal ":PROPERTIES:\n:TEST: t\n:END:\n" (org-test-with-temp-text "" (let ((org-property-format "%s %s")) (org-set-property "TEST" "t")) (buffer-string)))) (should (equal "* H\n:PROPERTIES:\n:TEST: t\n:END:\n" (org-test-with-temp-text "* H" (let ((org-adapt-indentation nil) (org-property-format "%s %s")) (org-set-property "TEST" "t")) (buffer-string))))) (ert-deftest test-org/delete-property () "Test `org-delete-property' specifications." (should (equal "" (org-test-with-temp-text ":PROPERTIES:\n:TEST: t\n:END:\n" (org-delete-property "TEST") (buffer-string)))) (should (equal ":PROPERTIES:\n:TEST1: t\n:END:\n" (org-test-with-temp-text ":PROPERTIES:\n:TEST1: t\n:TEST2: t\n:END:\n" (org-delete-property "TEST2") (buffer-string)))) (should (equal "* H\n" (org-test-with-temp-text "* H\n:PROPERTIES:\n:TEST: t\n:END:\n" (org-delete-property "TEST") (buffer-string)))) (should (equal "* H\n:PROPERTIES:\n:TEST1: t\n:END:\n" (org-test-with-temp-text "* H\n:PROPERTIES:\n:TEST1: t\n:TEST2: t\n:END:\n" (org-delete-property "TEST2") (buffer-string))))) (ert-deftest test-org/delete-property-globally () "Test `org-delete-property-global' specifications." (should (equal "" (org-test-with-temp-text ":PROPERTIES:\n:TEST: t\n:END:\n" (org-delete-property-globally "TEST") (buffer-string)))) (should (equal "* H\n" (org-test-with-temp-text ":PROPERTIES:\n:TEST: t\n:END:\n* H\n:PROPERTIES:\n:TEST: nil\n:END:" (org-delete-property-globally "TEST") (buffer-string))))) (ert-deftest test-org/find-property () "Test `org-find-property' specifications." ;; Regular test. (should (= 1 (org-test-with-temp-text "* H\n:PROPERTIES:\n:PROP: value\n:END:" (org-find-property "prop")))) ;; Find properties in top-level property drawer. (should (= 1 (org-test-with-temp-text ":PROPERTIES:\n:PROP: value\n:END:" (org-find-property "prop")))) ;; Ignore false positives. (should (= 27 (org-test-with-temp-text "* H1\n:DRAWER:\n:A: 1\n:END:\n* H2\n:PROPERTIES:\n:A: 1\n:END:" (org-find-property "A")))) ;; Return first entry found in buffer. (should (= 1 (org-test-with-temp-text "* H1\n:PROPERTIES:\n:A: 1\n:END:\n* H2\n:PROPERTIES:\n:A: 1\n:END:" (org-find-property "A")))) ;; Only search visible part of the buffer. (should (= 31 (org-test-with-temp-text "* H1\n:PROPERTIES:\n:A: 1\n:END:\n* H2\n:PROPERTIES:\n:A: 1\n:END:" (org-narrow-to-subtree) (org-find-property "A")))) ;; With optional argument, only find entries with a specific value. (should-not (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:" (org-find-property "A" "2"))) (should (= 31 (org-test-with-temp-text "* H1\n:PROPERTIES:\n:A: 1\n:END:\n* H2\n:PROPERTIES:\n:A: 2\n:END:" (org-find-property "A" "2")))) ;; Use "nil" for explicit nil values. (should (= 31 (org-test-with-temp-text "* H1\n:PROPERTIES:\n:A: 1\n:END:\n* H2\n:PROPERTIES:\n:A: nil\n:END:" (org-find-property "A" "nil"))))) (ert-deftest test-org/entry-delete () "Test `org-entry-delete' specifications." ;; Regular test. (should (string-match " *:PROPERTIES:\n *:B: +2\n *:END:" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:B: 2\n:END:" (org-entry-delete (point) "A") (buffer-string)))) ;; Also remove accumulated properties. (should-not (string-match ":A" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:A+: 2\n:B: 3\n:END:" (org-entry-delete (point) "A") (buffer-string)))) ;; When last property is removed, remove the property drawer. (should-not (string-match ":PROPERTIES:" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:\nParagraph" (org-entry-delete (point) "A") (buffer-string)))) ;; Return a non-nil value when some property was removed. (should (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:B: 2\n:END:" (org-entry-delete (point) "A"))) (should-not (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:B: 2\n:END:" (org-entry-delete (point) "C"))) ;; Special properties cannot be located in a drawer. Allow to ;; remove them anyway, in case of user error. (should (org-test-with-temp-text "* H\n:PROPERTIES:\n:SCHEDULED: 1\n:END:" (org-entry-delete (point) "SCHEDULED")))) (ert-deftest test-org/entry-get () "Test `org-entry-get' specifications." ;; Regular test. (should (equal "1" (org-test-with-temp-text ":PROPERTIES:\n:A: 1\n:END:" (org-entry-get (point) "A")))) (should (equal "1" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:" (org-entry-get (point) "A")))) ;; Ignore case. (should (equal "1" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:" (org-entry-get (point) "a")))) ;; Handle extended values, both before and after base value. (should (equal "1 2 3" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A+: 2\n:A: 1\n:A+: 3\n:END:" (org-entry-get (point) "A")))) ;; Empty values are returned as the empty string. (should (equal "" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A:\n:END:" (org-entry-get (point) "A")))) ;; Special nil value. If LITERAL-NIL is non-nil, return "nil", ;; otherwise, return nil. (should-not (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: nil\n:END:" (org-entry-get (point) "A"))) (should (equal "nil" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: nil\n:END:" (org-entry-get (point) "A" nil t)))) ;; Return nil when no property is found, independently on the ;; LITERAL-NIL argument. (should-not (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:" (org-entry-get (point) "B"))) (should-not (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:" (org-entry-get (point) "B" nil t))) ;; Handle inheritance, when allowed. Include extended values and ;; possibly global values. (should (equal "1" (org-test-with-temp-text ":PROPERTIES:\n:A: 1\n:END:\n* H" (org-entry-get (point-max) "A" t)))) (should (equal "1" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:\n** H2" (org-entry-get (point-max) "A" t)))) (should (equal "1" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:\n** H2" (let ((org-use-property-inheritance t)) (org-entry-get (point-max) "A" 'selective))))) (should-not (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:\n** H2" (let ((org-use-property-inheritance nil)) (org-entry-get (point-max) "A" 'selective)))) (should-not (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:\n* H2" (let ((org-use-property-inheritance t)) (org-entry-get (point-max) "A" t)))) (should (equal "1 2" (org-test-with-temp-text ":PROPERTIES:\n:A: 1\n:END:\n* H\n:PROPERTIES:\n:A+: 2\n:END:" (org-entry-get (point-max) "A" t)))) (should (equal "1 2" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:\n** H2\n:PROPERTIES:\n:A+: 2\n:END:" (org-entry-get (point-max) "A" t)))) (should (equal "1 2" (org-test-with-temp-text ":PROPERTIES:\n:A: 1\n:END:\n* H1\n* H2\n:PROPERTIES:\n:A+: 2\n:END:" (org-entry-get (point-max) "A" t)))) (should (equal "1 2" (org-test-with-temp-text "* H1\n:PROPERTIES:\n:A: 1\n:END:\n** H2.1\n** H2.2\n:PROPERTIES:\n:A+: 2\n:END:" (org-entry-get (point-max) "A" t)))) (should (equal "1" (org-test-with-temp-text "#+PROPERTY: A 0\n* H\n:PROPERTIES:\n:A: 1\n:END:" (org-mode-restart) (org-entry-get (point-max) "A" t)))) (should (equal "0 1" (org-test-with-temp-text "#+PROPERTY: A 0\n* H\n:PROPERTIES:\n:A+: 1\n:END:" (org-mode-restart) (org-entry-get (point-max) "A" t)))) ;; Explicit nil value takes precedence over parent non-nil properties. (should-not (org-test-with-temp-text "* 1 :PROPERTIES: :PROP: value :END: ** 2 :PROPERTIES: :PROP: nil :END: *** 3 " (org-entry-get (point-max) "PROP" t))) (should (equal "value" (org-test-with-temp-text "* 1 :PROPERTIES: :PROP: value :END: ** 2 :PROPERTIES: :PROP: nil :END: *** 3 " (org-entry-get nil "PROP" t)))) ;; document level property-drawer has precedance over ;; global-property by PROPERTY-keyword. (should (equal "0 2" (org-test-with-temp-text ":PROPERTIES:\n:A: 0\n:END:\n#+PROPERTY: A 1\n* H\n:PROPERTIES:\n:A+: 2\n:END:" (org-mode-restart) (org-entry-get (point-max) "A" t)))) ;; Use alternate separators (should (equal "0~2" (org-test-with-temp-text ":PROPERTIES:\n:A: 0\n:A+: 2\n:END:" (let ((org-property-separators '((("A") . "~")))) (org-entry-get (point) "A"))))) ;; Default separator is single space (should (equal "0 2" (org-test-with-temp-text ":PROPERTIES:\n:A: 0\n:B: 1\n:A+: 2\n:B+: 3\n:END:" (let ((org-property-separators '((("B") . "~")))) (org-entry-get (point) "A"))))) ;; Regular expression matching for separator (should (equal "0/2" (org-test-with-temp-text ":PROPERTIES:\n:A: 0\n:A+: 2\n:END:" (let ((org-property-separators '((("B") . "~") ("[AC]" . "/")))) (org-entry-get (point) "A"))))) ;; Separator works with inheritance (should (equal "1~2" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:\n** H2\n:PROPERTIES:\n:A+: 2\n:END:" (let ((org-property-separators '((("A") . "~")))) (org-entry-get (point-max) "A" t)))))) (ert-deftest test-org/entry-properties () "Test `org-entry-properties' specifications." ;; Get "ITEM" property. (should (equal "H" (org-test-with-temp-text "* TODO H" (cdr (assoc "ITEM" (org-entry-properties nil "ITEM")))))) (should (equal "H" (org-test-with-temp-text "* TODO H" (cdr (assoc "ITEM" (org-entry-properties)))))) ;; Get "TODO" property. TODO keywords are case sensitive. (should (equal "TODO" (org-test-with-temp-text "* TODO H" (cdr (assoc "TODO" (org-entry-properties nil "TODO")))))) (should (equal "TODO" (org-test-with-temp-text "* TODO H" (cdr (assoc "TODO" (org-entry-properties)))))) (should-not (org-test-with-temp-text "* H" (assoc "TODO" (org-entry-properties nil "TODO")))) (should-not (org-test-with-temp-text "* todo H" (assoc "TODO" (org-entry-properties nil "TODO")))) ;; Get "PRIORITY" property. (should (equal "A" (org-test-with-temp-text "* [#A] H" (cdr (assoc "PRIORITY" (org-entry-properties nil "PRIORITY")))))) (should (equal "A" (org-test-with-temp-text "* [#A] H" (cdr (assoc "PRIORITY" (org-entry-properties)))))) (should (equal (char-to-string org-priority-default) (org-test-with-temp-text "* H" (cdr (assoc "PRIORITY" (org-entry-properties nil "PRIORITY")))))) ;; Get "FILE" property. (should (org-test-with-temp-text-in-file "* H\nParagraph" (file-equal-p (cdr (assoc "FILE" (org-entry-properties nil "FILE"))) (buffer-file-name)))) (should (org-test-with-temp-text-in-file "* H\nParagraph" (file-equal-p (cdr (assoc "FILE" (org-entry-properties))) (buffer-file-name)))) (should-not (org-test-with-temp-text "* H\nParagraph" (cdr (assoc "FILE" (org-entry-properties nil "FILE"))))) ;; Get "TAGS" property. (should (equal ":tag1:tag2:" (org-test-with-temp-text "* H :tag1:tag2:" (cdr (assoc "TAGS" (org-entry-properties nil "TAGS")))))) (should (equal ":tag1:tag2:" (org-test-with-temp-text "* H :tag1:tag2:" (cdr (assoc "TAGS" (org-entry-properties)))))) (should-not (org-test-with-temp-text "* H" (cdr (assoc "TAGS" (org-entry-properties nil "TAGS"))))) ;; Get "ALLTAGS" property. (should (equal ":tag1:tag2:" (org-test-with-temp-text "* H :tag1:\n** H2 :tag2:" (cdr (assoc "ALLTAGS" (org-entry-properties nil "ALLTAGS")))))) (should (equal ":tag1:tag2:" (org-test-with-temp-text "* H :tag1:\n** H2 :tag2:" (cdr (assoc "ALLTAGS" (org-entry-properties)))))) (should-not (org-test-with-temp-text "* H" (cdr (assoc "ALLTAGS" (org-entry-properties nil "ALLTAGS"))))) ;; Get "BLOCKED" property. (should (equal "t" (org-test-with-temp-text "* TODO Blocked\n** DONE one\n** TODO two" (let ((org-enforce-todo-dependencies t) (org-blocker-hook '(org-block-todo-from-children-or-siblings-or-parent))) (cdr (assoc "BLOCKED" (org-entry-properties nil "BLOCKED"))))))) (should (equal "" (org-test-with-temp-text "* TODO Blocked\n** DONE one\n** DONE two" (let ((org-enforce-todo-dependencies t) (org-blocker-hook '(org-block-todo-from-children-or-siblings-or-parent))) (cdr (assoc "BLOCKED" (org-entry-properties nil "BLOCKED"))))))) ;; Get "CLOSED", "DEADLINE" and "SCHEDULED" properties. (should (equal "[2012-03-29 thu.]" (org-test-with-temp-text "* H\nCLOSED: [2012-03-29 thu.]" (cdr (assoc "CLOSED" (org-entry-properties nil "CLOSED")))))) (should (equal "[2012-03-29 thu.]" (org-test-with-temp-text "* H\nCLOSED: [2012-03-29 thu.]" (cdr (assoc "CLOSED" (org-entry-properties)))))) (should-not (org-test-with-temp-text "* H" (cdr (assoc "CLOSED" (org-entry-properties nil "CLOSED"))))) (should (equal "<2014-03-04 tue.>" (org-test-with-temp-text "* H\nDEADLINE: <2014-03-04 tue.>" (cdr (assoc "DEADLINE" (org-entry-properties nil "DEADLINE")))))) (should (equal "<2014-03-04 tue.>" (org-test-with-temp-text "* H\nDEADLINE: <2014-03-04 tue.>" (cdr (assoc "DEADLINE" (org-entry-properties)))))) (should-not (org-test-with-temp-text "* H" (cdr (assoc "DEADLINE" (org-entry-properties nil "DEADLINE"))))) (should (equal "<2014-03-04 tue.>" (org-test-with-temp-text "* H\nSCHEDULED: <2014-03-04 tue.>" (cdr (assoc "SCHEDULED" (org-entry-properties nil "SCHEDULED")))))) (should (equal "<2014-03-04 tue.>" (org-test-with-temp-text "* H\nSCHEDULED: <2014-03-04 tue.>" (cdr (assoc "SCHEDULED" (org-entry-properties)))))) (should-not (org-test-with-temp-text "* H" (cdr (assoc "SCHEDULED" (org-entry-properties nil "SCHEDULED"))))) ;; Get "CATEGORY" (should (equal "cat" (org-test-with-temp-text "#+CATEGORY: cat\n* H" (cdr (assoc "CATEGORY" (org-entry-properties)))))) (should (equal "cat" (org-test-with-temp-text "#+CATEGORY: cat\n* H" (cdr (assoc "CATEGORY" (org-entry-properties nil "CATEGORY")))))) (should (equal "cat" (org-test-with-temp-text "* H\n:PROPERTIES:\n:CATEGORY: cat\n:END:" (cdr (assoc "CATEGORY" (org-entry-properties nil "CATEGORY")))))) (should (equal "cat2" (org-test-with-temp-text (concat "* H\n:PROPERTIES:\n:CATEGORY: cat1\n:END:" "\n" "** H2\n:PROPERTIES:\n:CATEGORY: cat2\n:END:") (cdr (assoc "CATEGORY" (org-entry-properties nil "CATEGORY")))))) ;; Get "TIMESTAMP" and "TIMESTAMP_IA" properties. (should (equal "<2012-03-29 thu.>" (org-test-with-temp-text "* Entry\n<2012-03-29 thu.>" (cdr (assoc "TIMESTAMP" (org-entry-properties)))))) (should (equal "[2012-03-29 thu.]" (org-test-with-temp-text "* Entry\n[2012-03-29 thu.]" (cdr (assoc "TIMESTAMP_IA" (org-entry-properties)))))) (should (equal "<2012-03-29 thu.>" (org-test-with-temp-text "* Entry\n[2014-03-04 tue.]<2012-03-29 thu.>" (cdr (assoc "TIMESTAMP" (org-entry-properties nil "TIMESTAMP")))))) (should (equal "[2014-03-04 tue.]" (org-test-with-temp-text "* Entry\n<2012-03-29 thu.>[2014-03-04 tue.]" (cdr (assoc "TIMESTAMP_IA" (org-entry-properties nil "TIMESTAMP_IA")))))) (should-not (equal "<2012-03-29 thu.>" (org-test-with-temp-text "* Current\n* Next\n<2012-03-29 thu.>" (cdr (assoc "TIMESTAMP" (org-entry-properties)))))) ;; Get standard properties. (should (equal "1" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:" (cdr (assoc "A" (org-entry-properties nil 'standard)))))) ;; Handle extended properties. (should (equal "1 2 3" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A+: 2\n:A: 1\n:A+: 3\n:END:" (cdr (assoc "A" (org-entry-properties nil 'standard)))))) (should (equal "1 2 3" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A+: 2\n:A: 1\n:a+: 3\n:END:" (cdr (assoc "A" (org-entry-properties nil 'standard)))))) ;; Ignore forbidden (special) properties. (should-not (org-test-with-temp-text "* H\n:PROPERTIES:\n:TODO: foo\n:END:" (cdr (assoc "TODO" (org-entry-properties nil 'standard)))))) (ert-deftest test-org/entry-put () "Test `org-entry-put' specifications." ;; Error when not a string or nil. (should-error (org-test-with-temp-text "* H\n:PROPERTIES:\n:test: 1\n:END:" (org-entry-put 1 "test" 2))) ;; Error when property name is invalid. (should-error (org-test-with-temp-text "* H\n:PROPERTIES:\n:test: 1\n:END:" (org-entry-put 1 "no space" "value"))) (should-error (org-test-with-temp-text "* H\n:PROPERTIES:\n:test: 1\n:END:" (org-entry-put 1 "" "value"))) ;; Set "TODO" property. (should (string-match (regexp-quote " TODO H") (org-test-with-temp-text "#+TODO: TODO | DONE\n* H" (org-entry-put (point) "TODO" "TODO") (buffer-string)))) (should (string-match (regexp-quote "* H") (org-test-with-temp-text "#+TODO: TODO | DONE\n* H" (org-entry-put (point) "TODO" nil) (buffer-string)))) ;; Set "PRIORITY" property. (should (equal "* [#A] H" (org-test-with-temp-text "* [#B] H" (org-entry-put (point) "PRIORITY" "A") (buffer-string)))) (should (equal "* H" (org-test-with-temp-text "* [#B] H" (org-entry-put (point) "PRIORITY" nil) (buffer-string)))) ;; Set "SCHEDULED" property. (should (string-match "* H\n *SCHEDULED: <2014-03-04 .*?>" (org-test-with-temp-text "* H" (org-entry-put (point) "SCHEDULED" "2014-03-04") (buffer-string)))) (should (string= "* H\n" (org-test-with-temp-text "* H\nSCHEDULED: <2014-03-04 tue.>" (org-entry-put (point) "SCHEDULED" nil) (buffer-string)))) (should (string-match "* H\n *SCHEDULED: <2014-03-03 .*?>" (org-test-with-temp-text "* H\nSCHEDULED: <2014-03-04 tue.>" (org-entry-put (point) "SCHEDULED" "earlier") (buffer-string)))) (should (string-match "^ *SCHEDULED: <2014-03-05 .*?>" (org-test-with-temp-text "* H\nSCHEDULED: <2014-03-04 tue.>" (org-entry-put (point) "SCHEDULED" "later") (buffer-string)))) ;; Set "DEADLINE" property. (should (string-match "^ *DEADLINE: <2014-03-04 .*?>" (org-test-with-temp-text "* H" (org-entry-put (point) "DEADLINE" "2014-03-04") (buffer-string)))) (should (string= "* H\n" (org-test-with-temp-text "* H\nDEADLINE: <2014-03-04 tue.>" (org-entry-put (point) "DEADLINE" nil) (buffer-string)))) (should (string-match "^ *DEADLINE: <2014-03-03 .*?>" (org-test-with-temp-text "* H\nDEADLINE: <2014-03-04 tue.>" (org-entry-put (point) "DEADLINE" "earlier") (buffer-string)))) (should (string-match "^ *DEADLINE: <2014-03-05 .*?>" (org-test-with-temp-text "* H\nDEADLINE: <2014-03-04 tue.>" (org-entry-put (point) "DEADLINE" "later") (buffer-string)))) ;; Set "CATEGORY" property (should (string-match "^ *:CATEGORY: cat" (org-test-with-temp-text "* H" (org-entry-put (point) "CATEGORY" "cat") (buffer-string)))) ;; Regular properties, with or without pre-existing drawer. (should (string-match "^ *:A: +2$" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:" (org-entry-put (point) "A" "2") (buffer-string)))) (should (string-match "^ *:A: +1$" (org-test-with-temp-text "* H" (org-entry-put (point) "A" "1") (buffer-string)))) ;; Special case: two consecutive headlines. (should (string-match "\\* A\n *:PROPERTIES:" (org-test-with-temp-text "* A\n** B" (org-entry-put (point) "A" "1") (buffer-string))))) (ert-deftest test-org/refresh-properties () "Test `org-refresh-properties' specifications." (should (equal "1" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:" (org-refresh-properties "A" 'org-test) (get-text-property (point) 'org-test)))) (should-not (org-test-with-temp-text "* H\n:PROPERTIES:\n:A: 1\n:END:" (org-refresh-properties "B" 'org-test) (get-text-property (point) 'org-test))) ;; Handle properties only defined with extension syntax, i.e., ;; "PROPERTY+". (should (equal "1" (org-test-with-temp-text "* H\n:PROPERTIES:\n:A+: 1\n:END:" (org-refresh-properties "A" 'org-test) (get-text-property (point) 'org-test)))) ;; When property is inherited, add text property to the whole ;; sub-tree. (should (equal "1" (org-test-with-temp-text "* H1\n:PROPERTIES:\n:A: 1\n:END:\n** H2" (let ((org-use-property-inheritance t)) (org-refresh-properties "A" 'org-test)) (get-text-property (point) 'org-test)))) ;; When a document level property-drawer is used, those properties ;; should work exactly like headline-properties as if at a ;; headline-level 0. (should (equal "1" (org-test-with-temp-text ":PROPERTIES:\n:A: 1\n:END:\n" (org-mode-restart) (let ((org-use-property-inheritance t)) (org-refresh-properties "A" 'org-test)) (get-text-property (point) 'org-test)))) (should-not (equal "1" (org-test-with-temp-text ":PROPERTIES:\n:A: 1\n:END:\n* H1" (org-mode-restart) (let ((org-use-property-inheritance nil)) (org-refresh-properties "A" 'org-test)) (get-text-property (point) 'org-test)))) (should (equal "1" (org-test-with-temp-text ":PROPERTIES:\n:A: 1\n:END:\n* H1" (org-mode-restart) (let ((org-use-property-inheritance t)) (org-refresh-properties "A" 'org-test)) (get-text-property (point) 'org-test)))) (should (equal "2" (org-test-with-temp-text ":PROPERTIES:\n:A: 1\n:END:\n* H1\n:PROPERTIES:\n:A: 2\n:END:" (org-mode-restart) (let ((org-use-property-inheritance t)) (org-refresh-properties "A" 'org-test)) (get-text-property (point) 'org-test)))) ;; When property is inherited, use global value across the whole ;; buffer. However local values have precedence, as well as the ;; document level property-drawer. (should-not (equal "1" (org-test-with-temp-text "#+PROPERTY: A 1\n* H1" (org-mode-restart) (let ((org-use-property-inheritance nil)) (org-refresh-properties "A" 'org-test)) (get-text-property (point) 'org-test)))) (should (equal "1" (org-test-with-temp-text "#+PROPERTY: A 1\n* H1" (org-mode-restart) (let ((org-use-property-inheritance t)) (org-refresh-properties "A" 'org-test)) (get-text-property (point) 'org-test)))) (should (equal "2" (org-test-with-temp-text "#+PROPERTY: A 1\n* H\n:PROPERTIES:\n:A: 2\n:END:" (org-mode-restart) (let ((org-use-property-inheritance t)) (org-refresh-properties "A" 'org-test)) (get-text-property (point) 'org-test)))) ;; When both keyword-property and document-level property-block is ;; defined, the property-block has precedance. (should (equal "1" (org-test-with-temp-text ":PROPERTIES:\n:A: 1\n:END:\n#+PROPERTY: A 2\n* H1" (org-mode-restart) (let ((org-use-property-inheritance t)) (org-refresh-properties "A" 'org-test)) (get-text-property (point) 'org-test))))) (ert-deftest test-org/get-category () "Test `org-get-category' specifications." (should (equal "cat1" (org-test-with-temp-text ":PROPERTIES:\n:CATEGORY: cat1\n:END:" (org-get-category)))) (should (equal "cat1" (org-test-with-temp-text "* H\n:PROPERTIES:\n:CATEGORY: cat1\n:END:" (org-get-category)))) ;; Even though property-inheritance is deactivated, category ;; property should be inherited. As described in ;; `org-use-property-inheritance'. (should (equal "cat1" (let ((org-use-property-inheritance nil)) (org-test-with-temp-text ":PROPERTIES:\n:CATEGORY: cat1\n:END:\n* H" (org-get-category))))) (should (equal "cat1" (let ((org-use-property-inheritance t)) (org-test-with-temp-text ":PROPERTIES:\n:CATEGORY: cat1\n:END:\n* H" (org-get-category))))) (should (equal "cat2" (let ((org-use-property-inheritance t)) (org-test-with-temp-text ":PROPERTIES:\n:CATEGORY: cat1\n:END:\n* H\n:PROPERTIES:\n:CATEGORY: cat2\n:END:\n" (org-get-category)))))) ;;; Refile (ert-deftest test-org/refile-get-targets () "Test `org-refile-get-targets' specifications." ;; :maxlevel includes all headings above specified value. (should (equal '("H1" "H2" "H3") (org-test-with-temp-text "* H1\n** H2\n*** H3" (let ((org-refile-use-outline-path nil) (org-refile-targets `((nil :maxlevel . 3)))) (mapcar #'car (org-refile-get-targets)))))) (should (equal '("H1" "H2") (org-test-with-temp-text "* H1\n** H2\n*** H3" (let ((org-refile-use-outline-path nil) (org-refile-targets `((nil :maxlevel . 2)))) (mapcar #'car (org-refile-get-targets)))))) ;; :level limits targets to headlines with the specified level. (should (equal '("H2") (org-test-with-temp-text "* H1\n** H2\n*** H3" (let ((org-refile-use-outline-path nil) (org-refile-targets `((nil :level . 2)))) (mapcar #'car (org-refile-get-targets)))))) ;; :tag limits targets to headlines with specified tag. (should (equal '("H1") (org-test-with-temp-text "* H1 :foo:\n** H2\n*** H3 :bar:" (let ((org-refile-use-outline-path nil) (org-refile-targets `((nil :tag . "foo")))) (mapcar #'car (org-refile-get-targets)))))) ;; :todo limits targets to headlines with specified TODO keyword. (should (equal '("H2") (org-test-with-temp-text "* H1\n** TODO H2\n*** DONE H3" (let ((org-refile-use-outline-path nil) (org-refile-targets `((nil :todo . "TODO")))) (mapcar #'car (org-refile-get-targets)))))) ;; :regexp filters targets matching provided regexp. (should (equal '("F2" "F3") (org-test-with-temp-text "* H1\n** F2\n*** F3" (let ((org-refile-use-outline-path nil) (org-refile-targets `((nil :regexp . "F")))) (mapcar #'car (org-refile-get-targets)))))) ;; A nil `org-refile-targets' includes only top level headlines in ;; current buffer. (should (equal '("H1" "H2") (org-test-with-temp-text "* H1\n** S1\n* H2" (let ((org-refile-use-outline-path nil) (org-refile-targets nil)) (mapcar #'car (org-refile-get-targets)))))) ;; Return value is the union of the targets according to all the ;; defined rules. However, prevent duplicates. (should (equal '("F2" "F3" "H1") (org-test-with-temp-text "* TODO H1\n** F2\n*** F3" (let ((org-refile-use-outline-path nil) (org-refile-targets `((nil :regexp . "F") (nil :todo . "TODO")))) (mapcar #'car (org-refile-get-targets)))))) (should (equal '("F2" "F3" "H1") (org-test-with-temp-text "* TODO H1\n** TODO F2\n*** F3" (let ((org-refile-use-outline-path nil) (org-refile-targets `((nil :regexp . "F") (nil :todo . "TODO")))) (mapcar #'car (org-refile-get-targets)))))) ;; When `org-refile-use-outline-path' is non-nil, provide targets as ;; paths. (should (equal '("H1" "H1/H2" "H1/H2/H3") (org-test-with-temp-text "* H1\n** H2\n*** H3" (let ((org-refile-use-outline-path t) (org-refile-targets `((nil :maxlevel . 3)))) (mapcar #'car (org-refile-get-targets)))))) ;; When providing targets as paths, escape forward slashes in ;; headings with backslashes. (should (equal '("H1\\/foo") (org-test-with-temp-text "* H1/foo" (let ((org-refile-use-outline-path t) (org-refile-targets `((nil :maxlevel . 1)))) (mapcar #'car (org-refile-get-targets)))))) ;; When `org-refile-use-outline-path' is `file', include file name ;; without directory in targets. (should (org-test-with-temp-text-in-file "* H1" (let* ((filename (buffer-file-name)) (org-refile-use-outline-path 'file) (org-refile-targets `(((,filename) :level . 1)))) (member (file-name-nondirectory filename) (mapcar #'car (org-refile-get-targets)))))) ;; When `org-refile-use-outline-path' is `full-file-path', include ;; full file name. (should (org-test-with-temp-text-in-file "* H1" (let* ((filename (file-truename (buffer-file-name))) (org-refile-use-outline-path 'full-file-path) (org-refile-targets `(((,filename) :level . 1)))) (member filename (mapcar #'car (org-refile-get-targets)))))) ;; When `org-refile-use-outline-path' is `buffer-name', include ;; buffer name. (should (org-test-with-temp-text "* H1" (let* ((org-refile-use-outline-path 'buffer-name) (org-refile-targets `((nil :level . 1)))) (member (buffer-name) (mapcar #'car (org-refile-get-targets)))))) ;; When `org-refile-use-outline-path' is `title', return extracted ;; document title (should (equal '("T" "T/H1") (org-test-with-temp-text-in-file "#+title: T\n* H1" (let* ((org-refile-use-outline-path 'title) (org-refile-targets `((nil :level . 1)))) (mapcar #'car (org-refile-get-targets)))))) ;; When `org-refile-use-outline-path' is `title' validate that ;; deeper levels are correctly reported too (the same behaviour as ;; 'file) (should (equal '("T" "T/H1" "T/H1/H2" "T/H1/H2/H3" "T/H1") (org-test-with-temp-text-in-file "#+title: T\n* H1\n** H2\n*** H3\n* H1" (let ((org-refile-use-outline-path 'title) (org-refile-targets `((nil :maxlevel . 3)))) (mapcar #'car (org-refile-get-targets)))))) ;; When `org-refile-use-outline-path' is `title' and document do not ;; have an extracted document title, return just the file name (should (org-test-with-temp-text-in-file "* H1" (let* ((filename (buffer-file-name)) (org-refile-use-outline-path 'title) (org-refile-targets `((nil :level . 1)))) (member (file-name-nondirectory filename) (mapcar #'car (org-refile-get-targets)))))) ;; When `org-refile-use-outline-path' is `title' and document is a ;; temporary buffer without a file, it is still possible to extract ;; a title (should (equal '("T" "T/H1") (org-test-with-temp-text "#+title: T\n* H1\n** H2" (let* ((org-refile-use-outline-path 'title) (org-refile-targets `((nil :level . 1)))) (mapcar #'car (org-refile-get-targets)))))) ;; When `org-refile-use-outline-path' is `title' and there are two ;; title keywords in the file, titles are concatenated into a single ;; one. (should (equal '("T1 T2" "T1 T2/H1") (org-test-with-temp-text "#+title: T1\n#+title: T2\n* H1\n** H2" (let* ((org-refile-use-outline-path 'title) (org-refile-targets `((nil :level . 1)))) (mapcar #'car (org-refile-get-targets)))))) ;; When `org-refile-use-outline-path' is `title' and there are two ;; title keywords in the file, titles are concatenated into a single ;; one even if they are in the middle of the file. (should (equal '("T1 T2" "T1 T2/H1") (org-test-with-temp-text "#+title: T1\n* H1\n** H2\n#+title: T2\n" (let* ((org-refile-use-outline-path 'title) (org-refile-targets `((nil :level . 1)))) (mapcar #'car (org-refile-get-targets))))))) (ert-deftest test-org/refile () "Test `org-refile' specifications." ;; Test edge case when we refile heading into the same location. (should (equal "* H1 ** H2\n" (org-test-with-temp-text-in-file "* H1 * H2" (org-refile nil nil `("H1" ,(buffer-file-name) nil 1)) (buffer-string)))) ;; Throw an error when trying to refile into itself. (should-error (org-test-with-temp-text-in-file "* H1 * H2" (org-refile nil nil `("H1" ,(buffer-file-name) nil 1)) (buffer-string))) (should-error (org-test-with-temp-text-in-file "* one * two * three" (org-refile nil nil `("two" ,(buffer-file-name) nil 7)) (buffer-string)))) ;;; Sparse trees (ert-deftest test-org/match-sparse-tree () "Test `org-match-sparse-tree' specifications." ;; Match tags. (should-not (org-test-with-temp-text "* H\n** H1 :tag:" (org-match-sparse-tree nil "tag") (search-forward "H1") (org-invisible-p2))) (should (org-test-with-temp-text "* H\n** H1 :tag:\n** H2 :tag2:" (org-match-sparse-tree nil "tag") (search-forward "H2") (org-invisible-p2))) ;; "-" operator for tags. (should-not (org-test-with-temp-text "* H\n** H1 :tag1:\n** H2 :tag1:tag2:" (org-match-sparse-tree nil "tag1-tag2") (search-forward "H1") (org-invisible-p2))) (should (org-test-with-temp-text "* H\n** H1 :tag1:\n** H2 :tag1:tag2:" (org-match-sparse-tree nil "tag1-tag2") (search-forward "H2") (org-invisible-p2))) ;; "&" operator for tags. (should (org-test-with-temp-text "* H\n** H1 :tag1:\n** H2 :tag1:tag2:" (org-match-sparse-tree nil "tag1&tag2") (search-forward "H1") (org-invisible-p2))) (should-not (org-test-with-temp-text "* H\n** H1 :tag1:\n** H2 :tag1:tag2:" (org-match-sparse-tree nil "tag1&tag2") (search-forward "H2") (org-invisible-p2))) ;; "|" operator for tags. (should-not (org-test-with-temp-text "* H\n** H1 :tag1:\n** H2 :tag1:tag2:" (org-match-sparse-tree nil "tag1|tag2") (search-forward "H1") (org-invisible-p2))) (should-not (org-test-with-temp-text "* H\n** H1 :tag1:\n** H2 :tag1:tag2:" (org-match-sparse-tree nil "tag1|tag2") (search-forward "H2") (org-invisible-p2))) ;; Regexp match on tags. (should-not (org-test-with-temp-text "* H\n** H1 :tag1:\n** H2 :foo:" (org-match-sparse-tree nil "{^tag.*}") (search-forward "H1") (org-invisible-p2))) (should (org-test-with-temp-text "* H\n** H1 :tag1:\n** H2 :foo:" (org-match-sparse-tree nil "{^tag.*}") (search-forward "H2") (org-invisible-p2))) ;; Match group tags. (should-not (org-test-with-temp-text "#+TAGS: { work : lab }\n* H\n** H1 :work:\n** H2 :lab:" (org-match-sparse-tree nil "work") (search-forward "H1") (org-invisible-p2))) (should-not (org-test-with-temp-text "#+TAGS: { work : lab }\n* H\n** H1 :work:\n** H2 :lab:" (org-match-sparse-tree nil "work") (search-forward "H2") (org-invisible-p2))) ;; Match group tags with hard brackets. (should-not (org-test-with-temp-text "#+TAGS: [ work : lab ]\n* H\n** H1 :work:\n** H2 :lab:" (org-match-sparse-tree nil "work") (search-forward "H1") (org-invisible-p2))) (should-not (org-test-with-temp-text "#+TAGS: [ work : lab ]\n* H\n** H1 :work:\n** H2 :lab:" (org-match-sparse-tree nil "work") (search-forward "H2") (org-invisible-p2))) ;; Match tags in hierarchies (should-not (org-test-with-temp-text "#+TAGS: [ Lev_1 : Lev_2 ]\n #+TAGS: [ Lev_2 : Lev_3 ]\n #+TAGS: { Lev_3 : Lev_4 }\n * H\n** H1 :Lev_1:\n** H2 :Lev_2:\n** H3 :Lev_3:\n** H4 :Lev_4:" (org-match-sparse-tree nil "Lev_1") (search-forward "H4") (org-invisible-p2))) (should-not (org-test-with-temp-text "#+TAGS: [ Lev_1 : Lev_2 ]\n #+TAGS: [ Lev_2 : Lev_3 ]\n #+TAGS: { Lev_3 : Lev_4 }\n * H\n** H1 :Lev_1:\n** H2 :Lev_2:\n** H3 :Lev_3:\n** H4 :Lev_4:" (org-match-sparse-tree nil "Lev_1+Lev_3") (search-forward "H4") (org-invisible-p2))) ;; Match regular expressions in tags (should-not (org-test-with-temp-text "#+TAGS: [ Lev : {Lev_[0-9]} ]\n* H\n** H1 :Lev_1:" (org-match-sparse-tree nil "Lev") (search-forward "H1") (org-invisible-p2))) (should (org-test-with-temp-text "#+TAGS: [ Lev : {Lev_[0-9]} ]\n* H\n** H1 :Lev_n:" (org-match-sparse-tree nil "Lev") (search-forward "H1") (org-invisible-p2))) ;; Match properties. (should (org-test-with-temp-text "* H\n** H1\n:PROPERTIES:\n:A: 1\n:END:\n** H2\n:PROPERTIES:\n:A: 2\n:END:" (org-match-sparse-tree nil "A=\"1\"") (search-forward "H2") (org-invisible-p2))) (should-not (org-test-with-temp-text "* H1\n** H2\n:PROPERTIES:\n:A: 1\n:END:" (org-match-sparse-tree nil "A=\"1\"") (search-forward "H2") (org-invisible-p2))) ;; Case is not significant when matching properties. (should-not (org-test-with-temp-text "* H1\n** H2\n:PROPERTIES:\n:A: 1\n:END:" (org-match-sparse-tree nil "a=\"1\"") (search-forward "H2") (org-invisible-p2))) (should-not (org-test-with-temp-text "* H1\n** H2\n:PROPERTIES:\n:a: 1\n:END:" (org-match-sparse-tree nil "A=\"1\"") (search-forward "H2") (org-invisible-p2))) ;; Match special LEVEL property. (should-not (org-test-with-temp-text "* H\n** H1\n*** H2" (let ((org-odd-levels-only nil)) (org-match-sparse-tree nil "LEVEL=2")) (search-forward "H1") (org-invisible-p2))) (should (org-test-with-temp-text "* H\n** H1\n*** H2" (let ((org-odd-levels-only nil)) (org-match-sparse-tree nil "LEVEL=2")) (search-forward "H2") (org-invisible-p2))) ;; Comparison operators when matching properties. (should (org-test-with-temp-text "* H\n** H1\nSCHEDULED: <2014-03-04 tue.>\n** H2\nSCHEDULED: <2012-03-29 thu.>" (org-match-sparse-tree nil "SCHEDULED<=\"<2013-01-01>\"") (search-forward "H1") (org-invisible-p2))) (should-not (org-test-with-temp-text "* H\n** H1\nSCHEDULED: <2014-03-04 tue.>\n** H2\nSCHEDULED: <2012-03-29 thu.>" (org-match-sparse-tree nil "SCHEDULED<=\"<2013-01-01>\"") (search-forward "H2") (org-invisible-p2))) ;; Regexp match on properties values. (should-not (org-test-with-temp-text "* H\n** H1\n:PROPERTIES:\n:A: foo\n:END:\n** H2\n:PROPERTIES:\n:A: bar\n:END:" (org-match-sparse-tree nil "A={f.*}") (search-forward "H1") (org-invisible-p2))) (should (org-test-with-temp-text "* H\n** H1\n:PROPERTIES:\n:A: foo\n:END:\n** H2\n:PROPERTIES:\n:A: bar\n:END:" (org-match-sparse-tree nil "A={f.*}") (search-forward "H2") (org-invisible-p2))) ;; With an optional argument, limit match to TODO entries. (should-not (org-test-with-temp-text "* H\n** TODO H1 :tag:\n** H2 :tag:" (org-match-sparse-tree t "tag") (search-forward "H1") (org-invisible-p2))) (should (org-test-with-temp-text "* H\n** TODO H1 :tag:\n** H2 :tag:" (org-match-sparse-tree t "tag") (search-forward "H2") (org-invisible-p2)))) (ert-deftest test-org/occur () "Test `org-occur' specifications." ;; Count number of matches. (should (= 1 (org-test-with-temp-text "* H\nA\n* H2" (org-occur "A")))) (should (= 2 (org-test-with-temp-text "* H\nA\n* H2\nA" (org-occur "A")))) ;; Test CALLBACK optional argument. (should (= 0 (org-test-with-temp-text "* H\nA\n* H2" (org-occur "A" nil (lambda () (equal (org-get-heading) "H2")))))) (should (= 1 (org-test-with-temp-text "* H\nA\n* H2\nA" (org-occur "A" nil (lambda () (equal (org-get-heading) "H2")))))) ;; Case-fold searches according to `org-occur-case-fold-search'. (should (= 2 (org-test-with-temp-text "Aa" (let ((org-occur-case-fold-search t)) (org-occur "A"))))) (should (= 2 (org-test-with-temp-text "Aa" (let ((org-occur-case-fold-search t)) (org-occur "a"))))) (should (= 1 (org-test-with-temp-text "Aa" (let ((org-occur-case-fold-search nil)) (org-occur "A"))))) (should (= 1 (org-test-with-temp-text "Aa" (let ((org-occur-case-fold-search nil)) (org-occur "a"))))) (should (= 1 (org-test-with-temp-text "Aa" (let ((org-occur-case-fold-search 'smart)) (org-occur "A"))))) (should (= 2 (org-test-with-temp-text "Aa" (let ((org-occur-case-fold-search 'smart)) (org-occur "a")))))) ;;; Tags (ert-deftest test-org/tag-string-to-alist () "Test `org-tag-string-to-alist' specifications." ;; Tag without selection key. (should (equal (org-tag-string-to-alist "tag1") '(("tag1")))) ;; Tag with selection key. (should (equal (org-tag-string-to-alist "tag1(t)") '(("tag1" . ?t)))) ;; Tag group. (should (equal (org-tag-string-to-alist "[ group : t1 t2 ]") '((:startgrouptag) ("group") (:grouptags) ("t1") ("t2") (:endgrouptag)))) ;; Mutually exclusive tags. (should (equal (org-tag-string-to-alist "{ tag1 tag2 }") '((:startgroup) ("tag1") ("tag2") (:endgroup)))) (should (equal (org-tag-string-to-alist "{ group : tag1 tag2 }") '((:startgroup) ("group") (:grouptags) ("tag1") ("tag2") (:endgroup))))) (ert-deftest test-org/tag-alist-to-string () "Test `org-tag-alist-to-string' specifications." (should (equal (org-tag-alist-to-string '(("tag1"))) "tag1")) (should (equal (org-tag-alist-to-string '(("tag1" . ?t))) "tag1(t)")) (should (equal (org-tag-alist-to-string '((:startgrouptag) ("group") (:grouptags) ("t1") ("t2") (:endgrouptag))) "[ group : t1 t2 ]")) (should (equal (org-tag-alist-to-string '((:startgroup) ("tag1") ("tag2") (:endgroup))) "{ tag1 tag2 }")) (should (equal (org-tag-alist-to-string '((:startgroup) ("group") (:grouptags) ("tag1") ("tag2") (:endgroup))) "{ group : tag1 tag2 }"))) (ert-deftest test-org/tag-alist-to-groups () "Test `org-tag-alist-to-groups' specifications." (should (equal (org-tag-alist-to-groups '((:startgroup) ("group") (:grouptags) ("t1") ("t2") (:endgroup))) '(("group" "t1" "t2")))) (should (equal (org-tag-alist-to-groups '((:startgrouptag) ("group") (:grouptags) ("t1") ("t2") (:endgrouptag))) '(("group" "t1" "t2")))) (should-not (org-tag-alist-to-groups '((:startgroup) ("group") ("t1") ("t2") (:endgroup))))) (ert-deftest test-org/tag-align () "Test tags alignment." ;; Test aligning tags with different display width. (should ;; 12345678901234567890 (equal "* Test :abc:" (org-test-with-temp-text "* Test :abc:" (let ((org-tags-column -20) (indent-tabs-mode nil)) (org-fix-tags-on-the-fly)) (buffer-string)))) (should ;; 12345678901234567890 (equal "* Test :日本語:" (org-test-with-temp-text "* Test :日本語:" (let ((org-tags-column -20) (indent-tabs-mode nil)) (org-fix-tags-on-the-fly)) (buffer-string)))) ;; Make sure aligning tags do not skip invisible text. (should (equal "* [[linkx]] :tag:" (org-test-with-temp-text "* [[link]] :tag:" (let ((org-tags-column 0)) (org-fix-tags-on-the-fly) (insert "x") (buffer-string))))) ;; Aligning tags preserve position. (should (= 6 (org-test-with-temp-text "* 345 :tag:" (let ((org-tags-column 78) (indent-tabs-mode nil)) (org-fix-tags-on-the-fly)) (current-column)))) ;; Aligning all tags in visible buffer. (should ;; 12345678901234567890 (equal (concat "* Level 1 :abc:\n" "** Level 2 :def:") (org-test-with-temp-text (concat "* Level 1 :abc:\n" "** Level 2 :def:") (let ((org-tags-column -20) (indent-tabs-mode nil)) ;; (org-align-tags :all) must work even when the point ;; is at the end of the buffer. (goto-char (point-max)) (org-align-tags :all)) (buffer-string))))) (ert-deftest test-org/get-tags () "Test `org-get-tags' specifications." ;; Standard test. (should (equal '("foo") (org-test-with-temp-text "* Test :foo:" (org-get-tags)))) (should (equal '("foo" "bar") (org-test-with-temp-text "* Test :foo:bar:" (org-get-tags)))) ;; Tags for inlinetasks. (should (equal '("foo" "bar") (progn (require 'org-inlinetask) (org-test-with-temp-text (concat (make-string org-inlinetask-min-level ?*) " Test :foo:bar:") (org-get-tags (org-element-at-point)))))) (should (equal '("foo" "bar") (progn (require 'org-inlinetask) (org-test-with-temp-text (concat (make-string org-inlinetask-min-level ?*) " Test :foo:bar:") (org-get-tags nil))))) ;; Return nil when there is no tag. (should-not (org-test-with-temp-text "* Test" (org-get-tags))) ;; Tags are inherited from parent headlines. (should (equal '("tag") (let ((org-use-tag-inheritance t)) (org-test-with-temp-text "* H0 :foo:\n* H1 :tag:\n** H2" (org-get-tags))))) ;; Tags are inherited from `org-file-tags'. (should (equal '("tag") (org-test-with-temp-text "* H1" (let ((org-file-tags '("tag")) (org-use-tag-inheritance t)) (org-get-tags))))) ;; Only inherited tags have the `inherited' text property. (should (get-text-property 0 'inherited (org-test-with-temp-text "* H1 :foo:\n** H2 :bar:" (let ((org-use-tag-inheritance t)) (assoc-string "foo" (org-get-tags)))))) (should-not (get-text-property 0 'inherited (org-test-with-temp-text "* H1 :foo:\n** H2 :bar:" (let ((org-use-tag-inheritance t)) (assoc-string "bar" (org-get-tags)))))) ;; Obey to `org-use-tag-inheritance'. (should-not (org-test-with-temp-text "* H1 :foo:\n** H2 :bar:" (let ((org-use-tag-inheritance nil)) (assoc-string "foo" (org-get-tags))))) (should-not (org-test-with-temp-text "* H1 :foo:\n** H2 :bar:" (let ((org-use-tag-inheritance nil) (org-file-tags '("foo"))) (assoc-string "foo" (org-get-tags))))) (should-not (org-test-with-temp-text "* H1 :foo:bar:\n** H2 :baz:" (let ((org-use-tag-inheritance '("bar"))) (assoc-string "foo" (org-get-tags))))) (should (org-test-with-temp-text "* H1 :foo:bar:\n** H2 :baz:" (let ((org-use-tag-inheritance '("bar"))) (assoc-string "bar" (org-get-tags))))) (should-not (org-test-with-temp-text "* H1 :foo:bar:\n** H2 :baz:" (let ((org-use-tag-inheritance "b.*")) (assoc-string "foo" (org-get-tags))))) (should (org-test-with-temp-text "* H1 :foo:bar:\n** H2 :baz:" (let ((org-use-tag-inheritance "b.*")) (assoc-string "bar" (org-get-tags))))) ;; When optional argument LOCAL is non-nil, ignore tag inheritance. (should (equal '("baz") (org-test-with-temp-text "* H1 :foo:bar:\n** H2 :baz:" (let ((org-use-tag-inheritance t)) (org-get-tags nil t))))) ;; When optional argument POS is non-nil, get tags there instead. (should (equal '("foo") (org-test-with-temp-text "* H1 :foo:\n* H2 :bar:" (org-get-tags 1)))) ;; Make sure tags excluded from inheritance are returned if local (should (equal '("foo") (org-test-with-temp-text "* Test :foo:" (let ((org-use-tag-inheritance t) (org-tags-exclude-from-inheritance '("foo"))) (org-get-tags))))) ;; Test the collection of tags from #+filetags and parent tags. (should (equal '("a" "b" "c" "d") (org-test-with-temp-text (concat "#+filetags: a\n" "* Level 1 :b:\n" "** Level 2 :c:\n" "*** Level 3 :d:\n" "") (let ((org-use-tag-inheritance t)) (org-mode-restart) ;So that `org-file-tags' get populated from #+filetags (org-get-tags))))) ;; Pathological case: tagged headline with an empty body. (should (org-test-with-temp-text "* :tag:" (org-get-tags)))) (ert-deftest test-org/set-tags () "Test `org-set-tags' specifications." ;; Throw an error on invalid data. (should-error (org-test-with-temp-text "* H" (org-set-tags 'foo))) ;; `nil', an empty, and a blank string remove all tags. (should (equal "* H" (org-test-with-temp-text "* H :tag1:tag2:" (org-set-tags nil) (buffer-string)))) (should (equal "* H" (org-test-with-temp-text "* H :tag1:tag2:" (org-set-tags "") (buffer-string)))) (should (equal "* H" (org-test-with-temp-text "* H :tag1:tag2:" (org-set-tags " ") (buffer-string)))) ;; If there's nothing to remove, just bail out. (should (equal "* H" (org-test-with-temp-text "* H" (org-set-tags nil) (buffer-string)))) (should (equal "* " (org-test-with-temp-text "* " (org-set-tags nil) (buffer-string)))) ;; If DATA is a tag string, set current tags to it, even if it means ;; replacing old tags. (should (equal "* H :tag0:" (org-test-with-temp-text "* H :tag1:tag2:" (let ((org-tags-column 1)) (org-set-tags ":tag0:")) (buffer-string)))) (should (equal "* H :tag0:" (org-test-with-temp-text "* H" (let ((org-tags-column 1)) (org-set-tags ":tag0:")) (buffer-string)))) ;; If DATA is a list, set tags to this list, even if it means ;; replacing old tags. (should (equal "* H :tag0:" (org-test-with-temp-text "* H :tag1:tag2:" (let ((org-tags-column 1)) (org-set-tags '("tag0"))) (buffer-string)))) (should (equal "* H :tag0:" (org-test-with-temp-text "* H" (let ((org-tags-column 1)) (org-set-tags '("tag0"))) (buffer-string)))) ;; When set, apply `org-tags-sort-function'. (should (equal "* H :a:b:" (org-test-with-temp-text "* H" (let ((org-tags-column 1) (org-tags-sort-function #'string<)) (org-set-tags '("b" "a")) (buffer-string))))) ;; When new tags are identical to the previous ones, still align. (should (equal "* H :foo:" (org-test-with-temp-text "* H :foo:" (let ((org-tags-column 1)) (org-set-tags '("foo")) (buffer-string))))) ;; When tags have been changed, run `org-after-tags-change-hook'. (should (catch :return (org-test-with-temp-text "* H :foo:" (let ((org-after-tags-change-hook (lambda () (throw :return t)))) (org-set-tags '("bar")) nil)))) (should-not (catch :return (org-test-with-temp-text "* H :foo:" (let ((org-after-tags-change-hook (lambda () (throw :return t)))) (org-set-tags '("foo")) nil)))) ;; Special case: handle empty headlines. (should (equal "* :tag0:" (org-test-with-temp-text "* " (let ((org-tags-column 1)) (org-set-tags '("tag0"))) (buffer-string)))) ;; Modify buffer only when a tag change happens or alignment is ;; done. (should-not (org-test-with-temp-text "* H :foo:" (set-buffer-modified-p nil) (let ((org-tags-column 1)) (org-set-tags '("foo"))) (buffer-modified-p))) (should (org-test-with-temp-text "* H :foo:" (set-buffer-modified-p nil) (let ((org-tags-column 10)) (org-set-tags '("foo"))) (buffer-modified-p))) (should (org-test-with-temp-text "* H :foo:" (set-buffer-modified-p nil) (let ((org-tags-column 10)) (org-set-tags '("bar"))) (buffer-modified-p))) ;; Pathological case: when setting tags of a folded headline, do not ;; let new tags being sucked into invisibility. (should-not (org-test-with-temp-text "* H1\nContent\n* H2\n\n Other Content" ;; Show only headlines (org-content) ;; Set NEXT tag on current entry (org-set-tags ":NEXT:") ;; Move point to that NEXT tag (search-forward "NEXT") (backward-word) ;; And it should be visible (i.e. no overlays) (overlays-at (point))))) (ert-deftest test-org/set-tags-command () "Test `org-set-tags-command' specifications" ;; Set tags at current headline. (should (equal "* H1 :foo:" (org-test-with-temp-text "* H1" (cl-letf (((symbol-function 'completing-read-multiple) (lambda (&rest _args) '("foo")))) (let ((org-use-fast-tag-selection nil) (org-tags-column 1)) (org-set-tags-command))) (buffer-string)))) ;; Preserve position when called from the section below. (should (equal "* H1 :foo:\nContents" (org-test-with-temp-text "* H1\nContents" (cl-letf (((symbol-function 'completing-read-multiple) (lambda (&rest _args) '("foo")))) (let ((org-use-fast-tag-selection nil) (org-tags-column 1)) (org-set-tags-command))) (buffer-string)))) (should-not (equal "* H1 :foo:\nContents2" (org-test-with-temp-text "* H1\nContents2" (cl-letf (((symbol-function 'completing-read-multiple) (lambda (&rest _args) '("foo")))) (let ((org-use-fast-tag-selection nil) (org-tags-column 1)) (org-set-tags-command))) (org-at-heading-p)))) ;; When a region is active and ;; `org-loop-over-headlines-in-active-region' is non-nil, insert the ;; same value in all headlines in region. (should (equal "* H1 :foo:\nContents\n* H2 :foo:" (org-test-with-temp-text "* H1\nContents\n* H2" (cl-letf (((symbol-function 'completing-read-multiple) (lambda (&rest _args) '("foo")))) (let ((org-use-fast-tag-selection nil) (org-loop-over-headlines-in-active-region t) (org-tags-column 1)) (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (org-set-tags-command))) (buffer-string)))) (should (equal "* H1\nContents\n* H2 :foo:" (org-test-with-temp-text "* H1\nContents\n* H2" (cl-letf (((symbol-function 'completing-read-multiple) (lambda (&rest _args) '("foo")))) (let ((org-use-fast-tag-selection nil) (org-loop-over-headlines-in-active-region nil) (org-tags-column 1)) (transient-mark-mode 1) (push-mark (point) t t) (goto-char (point-max)) (org-set-tags-command))) (buffer-string)))) ;; With a C-u prefix argument, align all tags in the buffer. (should (equal "* H1 :foo:\n* H2 :bar:" (org-test-with-temp-text "* H1 :foo:\n* H2 :bar:" (let ((org-tags-column 1)) (org-set-tags-command '(4))) (buffer-string)))) ;; Point does not move with empty headline. (should (equal ":foo:" (org-test-with-temp-text "* " (cl-letf (((symbol-function 'completing-read-multiple) (lambda (&rest _args) '("foo")))) (let ((org-use-fast-tag-selection nil) (org-tags-column 1)) (org-set-tags-command))) (buffer-substring (point) (line-end-position))))) ;; Point does not move at start of line. (should (equal "* H1 :foo:" (org-test-with-temp-text "* H1" (cl-letf (((symbol-function 'completing-read-multiple) (lambda (&rest _args) '("foo")))) (let ((org-use-fast-tag-selection nil) (org-tags-column 1)) (org-set-tags-command))) (buffer-substring (point) (line-end-position))))) ;; Point does not move when within *'s. (should (equal "* H1 :foo:" (org-test-with-temp-text "** H1" (cl-letf (((symbol-function 'completing-read-multiple) (lambda (&rest _args) '("foo")))) (let ((org-use-fast-tag-selection nil) (org-tags-column 1)) (org-set-tags-command))) (buffer-substring (point) (line-end-position))))) ;; Point workaround does not get fooled when looking at a space. (should (equal " b :foo:" (org-test-with-temp-text "* a b" (cl-letf (((symbol-function 'completing-read-multiple) (lambda (&rest _args) '("foo")))) (let ((org-use-fast-tag-selection nil) (org-tags-column 1)) (org-set-tags-command))) (buffer-substring (point) (line-end-position))))) ;; Handle tags both set locally and inherited. (should (equal "b :foo:" (org-test-with-temp-text "* a :foo:\n** b :foo:" (cl-letf (((symbol-function 'completing-read-multiple) (lambda (_prompt _coll &optional _pred _req initial &rest _) (list initial)))) (let ((org-use-fast-tag-selection nil) (org-tags-column 1)) (org-set-tags-command))) (buffer-substring (point) (line-end-position)))))) (ert-deftest test-org/toggle-tag () "Test `org-toggle-tag' specifications." ;; Insert missing tag. (should (equal "* H :tag:" (org-test-with-temp-text "* H" (let ((org-tags-column 1)) (org-toggle-tag "tag")) (buffer-string)))) (should (equal "* H :tag1:tag2:" (org-test-with-temp-text "* H :tag1:" (let ((org-tags-column 1)) (org-toggle-tag "tag2")) (buffer-string)))) ;; Remove existing tag. (should (equal "* H" (org-test-with-temp-text "* H :tag:" (org-toggle-tag "tag") (buffer-string)))) (should (equal "* H :tag1:" (org-test-with-temp-text "* H :tag1:tag2:" (let ((org-tags-column 1)) (org-toggle-tag "tag2")) (buffer-string)))) (should (equal "* H :tag2:" (org-test-with-temp-text "* H :tag1:tag2:" (let ((org-tags-column 1)) (org-toggle-tag "tag1")) (buffer-string)))) ;; With optional argument ONOFF set to `on', try to insert the tag, ;; even if its already there. (should (equal "* H :tag:" (org-test-with-temp-text "* H" (let ((org-tags-column 1)) (org-toggle-tag "tag" 'on)) (buffer-string)))) (should (equal "* H :tag:" (org-test-with-temp-text "* H :tag:" (let ((org-tags-column 1)) (org-toggle-tag "tag" 'on)) (buffer-string)))) ;; With optional argument ONOFF set to `off', try to remove the tag, ;; even if its not there. (should (equal "* H" (org-test-with-temp-text "* H :tag:" (org-toggle-tag "tag" 'off) (buffer-string)))) (should (equal "* H :tag:" (org-test-with-temp-text "* H :tag:" (let ((org-tags-column 1)) (org-toggle-tag "foo" 'off)) (buffer-string)))) ;; Special case: Handle properly tag inheritance. In particular, do ;; not set inherited tags. (should (equal "* H1 :tag:\n** H2 :tag2:tag:" (org-test-with-temp-text "* H1 :tag:\n** H2 :tag2:" (let ((org-use-tag-inheritance t) (org-tags-column 1)) (org-toggle-tag "tag")) (buffer-string)))) (should (equal "* H1 :tag1:tag2:\n** H2 :foo:" (org-test-with-temp-text "* H1 :tag1:tag2:\n** H2" (let ((org-use-tag-inheritance t) (org-tags-column 1)) (org-toggle-tag "foo")) (buffer-string))))) (ert-deftest test-org/tags-expand () "Test `org-tags-expand' specifications." ;; Expand tag groups as a regexp enclosed within curly brackets. (should (equal "{\\<[ABC]\\>}" (org-test-with-temp-text "#+TAGS: [ A : B C ]" (org-mode-restart) (let ((org-tag-alist-for-agenda nil)) (org-tags-expand "A"))))) (should (equal "{\\<\\(?:Aa\\|Bb\\|Cc\\)\\>}" (org-test-with-temp-text "#+TAGS: [ Aa : Bb Cc ]" (org-mode-restart) (let ((org-tag-alist-for-agenda nil)) (org-tags-expand "Aa"))))) ;; Preserve operator before the regexp. (should (equal "+{\\<[ABC]\\>}" (org-test-with-temp-text "#+TAGS: [ A : B C ]" (org-mode-restart) (let ((org-tag-alist-for-agenda nil)) (org-tags-expand "+A"))))) (should (equal "-{\\<[ABC]\\>}" (org-test-with-temp-text "#+TAGS: [ A : B C ]" (org-mode-restart) (let ((org-tag-alist-for-agenda nil)) (org-tags-expand "-A"))))) ;; Handle "|" syntax. (should (equal "{\\<[ABC]\\>}|D" (org-test-with-temp-text "#+TAGS: [ A : B C ]" (org-mode-restart) (let ((org-tag-alist-for-agenda nil)) (org-tags-expand "A|D"))))) ;; Handle nested groups. (should (equal "{\\<[A-D]\\>}" (org-test-with-temp-text "#+TAGS: [ A : B C ]\n#+TAGS: [ B : D ]" (org-mode-restart) (let ((org-tag-alist-for-agenda nil)) (org-tags-expand "A"))))) ;; Expand multiple occurrences of the same group. (should (equal "{\\<[ABC]\\>}|{\\<[ABC]\\>}" (org-test-with-temp-text "#+TAGS: [ A : B C ]" (org-mode-restart) (let ((org-tag-alist-for-agenda nil)) (org-tags-expand "A|A"))))) ;; Preserve regexp matches. (should (equal "{A+}" (org-test-with-temp-text "#+TAGS: [ A : B C ]" (org-mode-restart) (let ((org-tag-alist-for-agenda nil)) (org-tags-expand "{A+}")))))) ;;; TODO keywords (ert-deftest test-org/auto-repeat-maybe () "Test `org-auto-repeat-maybe' specifications." ;; Do not auto repeat when there is no valid time stamp with ;; a repeater in the entry. (should-not (string-prefix-p "* TODO H" (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* TODO H\n<2012-03-29 Thu>" (org-todo "DONE") (buffer-string))))) (should-not (string-prefix-p "* TODO H" (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* TODO H\n# <2012-03-29 Thu>" (org-todo "DONE") (buffer-string))))) ;; When switching to DONE state, switch back to first TODO keyword ;; in sequence, or the same keyword if they have different types. (should (string-prefix-p "* TODO H" (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* TODO H\n<2012-03-29 Thu +2y>" (org-todo "DONE") (buffer-string))))) (should (string-prefix-p "* KWD1 H" (let ((org-todo-keywords '((sequence "KWD1" "KWD2" "DONE")))) (org-test-with-temp-text "* KWD2 H\n<2012-03-29 Thu +2y>" (org-todo "DONE") (buffer-string))))) (should (string-prefix-p "* KWD2 H" (let ((org-todo-keywords '((type "KWD1" "KWD2" "DONE")))) (org-test-with-temp-text "* KWD2 H\n<2012-03-29 Thu +2y>" (org-todo "DONE") (buffer-string))))) ;; If there was no TODO keyword in the first place, do not insert ;; any either. (should (string-prefix-p "* H" (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* H\n<2012-03-29 Thu +2y>" (org-todo "DONE") (buffer-string))))) ;; Revert to REPEAT_TO_STATE, if set. (should (string-prefix-p "* KWD2 H" (let ((org-todo-keywords '((sequence "KWD1" "KWD2" "DONE")))) (org-test-with-temp-text "* KWD2 H :PROPERTIES: :REPEAT_TO_STATE: KWD2 :END: <2012-03-29 Thu +2y>" (org-todo "DONE") (buffer-string))))) ;; When switching to DONE state, update base date. If there are ;; multiple repeated time stamps, update them all. (should (string-match-p "<2014-03-29 .* \\+2y>" (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* TODO H\n<2012-03-29 Thu +2y>" (org-todo "DONE") (buffer-string))))) (should (string-match-p "<2015-03-04 .* \\+1y>" (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* TODO H\n<2012-03-29 Thu. +2y>\n<2014-03-04 Tue +1y>" (org-todo "DONE") (buffer-string))))) ;; Throw an error if repeater unit is the hour and no time is ;; provided in the timestamp. (should-error (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* TODO H\n<2012-03-29 Thu +2h>" (org-todo "DONE") (buffer-string)))) ;; Handle every repeater type using hours step. (should (string-match-p (regexp-quote "<2014-03-04 02:00 +8h>") (org-test-without-dow (org-test-at-time "<2014-03-04 02:35>" (org-test-with-temp-text "* TODO H\n<2014-03-03 18:00 +8h>" (org-todo "DONE") (buffer-string)))))) (should (string-match-p (regexp-quote "<2014-03-04 10:00 ++8h>") (org-test-without-dow (org-test-at-time "<2014-03-04 02:35>" (org-test-with-temp-text "* TODO H\n<2014-03-03 18:00 ++8h>" (org-todo "DONE") (buffer-string)))))) (should (string-match-p (regexp-quote "<2014-03-04 10:35 .+8h>") (org-test-without-dow (org-test-at-time "<2014-03-04 02:35>" (org-test-with-temp-text "* TODO H\n<2014-03-03 18:00 .+8h>" (org-todo "DONE") (buffer-string)))))) ;; Handle `org-extend-today-until'. (should (string-match-p (regexp-quote "<2014-03-04 ++1d>") (let ((org-extend-today-until 4)) (org-test-without-dow (org-test-at-time "<2014-03-04 02:35>" (org-test-with-temp-text "* TODO H\n<2014-03-03 ++1d>" (org-todo "DONE") (buffer-string))))))) (should (string-match-p (regexp-quote "<2014-03-06 17:00 ++1d>") (let ((org-extend-today-until 4)) (org-test-without-dow (org-test-at-time "<2014-03-05 18:00>" (org-test-with-temp-text "* TODO H\n<2014-03-04 17:00 ++1d>" (org-todo "DONE") (buffer-string))))))) (should (string-match-p (regexp-quote "<2014-03-04 10:00 ++8h>") (let ((org-extend-today-until 4)) (org-test-without-dow (org-test-at-time "<2014-03-04 02:35>" (org-test-with-temp-text "* TODO H\n<2014-03-03 18:00 ++8h>" (org-todo "DONE") (buffer-string))))))) (should (string-match-p (regexp-quote "<2014-03-04 18:00 .+1d>") (let ((org-extend-today-until 4)) (org-test-without-dow (org-test-at-time "<2014-03-04 02:35>" (org-test-with-temp-text "* TODO H\n<2014-03-03 18:00 .+1d>" (org-todo "DONE") (buffer-string))))))) (should (string-match-p (regexp-quote "<2014-03-04 10:35 .+8h>") (let ((org-extend-today-until 4)) (org-test-without-dow (org-test-at-time "<2014-03-04 02:35>" (org-test-with-temp-text "* TODO H\n<2014-03-03 18:00 .+8h>" (org-todo "DONE") (buffer-string))))))) ;; Do not repeat inactive time stamps with a repeater. (should-not (string-match-p "\\[2014-03-29 .* \\+2y\\]" (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* TODO H\n[2012-03-29 Thu. +2y]" (org-todo "DONE") (buffer-string))))) ;; Do not repeat commented time stamps. (should-not (string-prefix-p "<2015-03-04 .* \\+1y>" (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* TODO H\n<2012-03-29 Thu +2y>\n# <2014-03-04 Tue +1y>" (org-todo "DONE") (buffer-string))))) (should-not (string-prefix-p "<2015-03-04 .* \\+1y>" (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* TODO H <2012-03-29 Thu. +2y> #+BEGIN_EXAMPLE <2014-03-04 Tue +1y> #+END_EXAMPLE" (org-todo "DONE") (buffer-string))))) ;; When `org-log-repeat' is non-nil or there is a CLOCK in the ;; entry, record time of last repeat. (should-not (string-match-p ":LAST_REPEAT:" (let ((org-todo-keywords '((sequence "TODO" "DONE"))) (org-log-repeat nil)) (cl-letf (((symbol-function 'org-add-log-setup) (lambda (&rest _args) nil))) (org-test-with-temp-text "* TODO H\n<2012-03-29 Thu. +2y>" (org-todo "DONE") (buffer-string)))))) (should (string-match-p ":LAST_REPEAT:" (let ((org-todo-keywords '((sequence "TODO" "DONE"))) (org-log-repeat t)) (cl-letf (((symbol-function 'org-add-log-setup) (lambda (&rest _args) nil))) (org-test-with-temp-text "* TODO H\n<2012-03-29 Thu. +2y>" (org-todo "DONE") (buffer-string)))))) (should (string-match-p ":LAST_REPEAT:" (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (cl-letf (((symbol-function 'org-add-log-setup) (lambda (&rest _args) nil))) (org-test-with-temp-text "* TODO H\n<2012-03-29 Thu +2y>\nCLOCK: [2012-03-29 Thu 16:40]" (org-todo "DONE") (buffer-string)))))) ;; When a SCHEDULED entry has no repeater, remove it upon repeating ;; the entry as it is no longer relevant. (should-not (string-match-p "^SCHEDULED:" (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* TODO H\nSCHEDULED: <2014-03-04 Tue>\n<2012-03-29 Thu +2y>" (org-todo "DONE") (buffer-string))))) ;; Properly advance repeater even when a clock entry is specified ;; and `org-log-repeat' is nil. (should (string-match-p "SCHEDULED: <2014-03-29" (let ((org-log-repeat nil) (org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* TODO H SCHEDULED: <2012-03-29 Thu +2y> CLOCK: [2012-03-29 Thu 10:00]--[2012-03-29 Thu 16:40] => 6:40" (org-todo "DONE") (buffer-string))))) ;; Make sure that logbook state change record does not get ;; duplicated when `org-log-repeat' `org-log-done' are non-nil. (should (string-match-p (rx "* TODO Read book SCHEDULED: <2021-06-16 " (1+ (not space)) " +1d> :PROPERTIES: :LAST_REPEAT:" (1+ nonl) " :END: - State \"DONE\" from \"TODO\"" (1+ nonl) buffer-end) (let ((org-log-repeat 'time) (org-todo-keywords '((sequence "TODO" "|" "DONE(d!)"))) (org-log-into-drawer nil)) (org-test-with-temp-text "* TODO Read book SCHEDULED: <2021-06-15 Tue +1d>" (org-todo "DONE") (when (memq 'org-add-log-note post-command-hook) (org-add-log-note)) (buffer-string)))))) (ert-deftest test-org/org-log-done () "Test `org-log-done' specifications." ;; nil value. (should (string= "* DONE task" (let ((org-log-done nil) (org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* TODO task" (org-todo "DONE") (when (memq 'org-add-log-note post-command-hook) (org-add-log-note)) (buffer-string))))) ;; `time' value. (should (string= (format "* DONE task CLOSED: %s" (org-test-with-temp-text "" (org-insert-timestamp (current-time) t t) (buffer-string))) (let ((org-log-done 'time) (org-log-done-with-time t) (org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* TODO task" (org-todo "DONE") (when (memq 'org-add-log-note post-command-hook) (org-add-log-note)) (buffer-string))))) (should (string= (format "* DONE task CLOSED: %s" (org-test-with-temp-text "" (org-insert-timestamp (current-time) nil t) (buffer-string))) (let ((org-log-done 'time) (org-log-done-with-time nil) (org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* TODO task" (org-todo "DONE") (when (memq 'org-add-log-note post-command-hook) (org-add-log-note)) (buffer-string))))) ;; TODO: Test `note' value. ;; Test startup overrides. (should (string= "#+STARTUP: nologdone * DONE task" (let ((org-log-done 'time) (org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "#+STARTUP: nologdone * TODO task" (org-set-regexps-and-options) (org-todo "DONE") (when (memq 'org-add-log-note post-command-hook) (org-add-log-note)) (buffer-string))))) (should (string= (format "#+STARTUP: logdone * DONE task CLOSED: %s" (org-test-with-temp-text "" (org-insert-timestamp (current-time) t t) (buffer-string))) (let ((org-log-done nil) (org-log-done-with-time t) (org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "#+STARTUP: logdone * TODO task" (org-set-regexps-and-options) (org-todo "DONE") (when (memq 'org-add-log-note post-command-hook) (org-add-log-note)) (buffer-string))))) ;; Test local property overrides. (should (string= "* DONE task :PROPERTIES: :LOGGING: nil :END:" (let ((org-log-done 'time) (org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* TODO task :PROPERTIES: :LOGGING: nil :END:" (org-todo "DONE") (when (memq 'org-add-log-note post-command-hook) (org-add-log-note)) (buffer-string))))) (should (string= (format "* DONE task CLOSED: %s :PROPERTIES: :LOGGING: logdone :END:" (org-test-with-temp-text "" (org-insert-timestamp (current-time) t t) (buffer-string))) (let ((org-log-done nil) (org-log-done-with-time t) (org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* TODO task :PROPERTIES: :LOGGING: logdone :END:" (org-todo "DONE") (when (memq 'org-add-log-note post-command-hook) (org-add-log-note)) (buffer-string)))))) (ert-deftest test-org/org-todo-prefix () "Test `org-todo' prefix arg behavior." ;; FIXME: Add tests for all other allowed prefix arguments. ;; -1 prefix arg should cancel repeater and mark DONE. (should (string-match-p "DONE H\\(.*\n\\)*<2012-03-29 Thu \\+0y>" (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* TODO H\n<2012-03-29 Thu +2y>" (org-todo -1) (buffer-string))))) ;; - prefix arg should cancel repeater and mark DONE. (should (string-match-p "DONE H\\(.*\n\\)*<2012-03-29 Thu \\+0y>" (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* TODO H\n<2012-03-29 Thu +2y>" (org-todo '-) (buffer-string))))) ;; C-u forces logging note. ;; However, logging falls back to "time" when `org-inhibit-logging' ;; is 'note. (dolist (org-inhibit-logging '(nil t note)) (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* TODO H\n" (unwind-protect (progn (org-todo '(4)) (should (string-match-p "DONE" (buffer-string))) (should (member #'org-add-log-note post-command-hook)) (if (eq org-inhibit-logging 'note) (should (eq org-log-note-how 'time)) (should (eq org-log-note-how 'note)))) (remove-hook 'post-command-hook #'org-add-log-note)))))) ;;; Timestamps API (ert-deftest test-org/at-timestamp-p () "Test `org-at-timestamp-p' specifications." (should (org-test-with-temp-text "<2012-03-29 Thu>" (org-at-timestamp-p))) (should-not (org-test-with-temp-text "2012-03-29 Thu" (org-at-timestamp-p))) ;; Test return values. (should (eq 'bracket (org-test-with-temp-text "<2012-03-29 Thu>" (org-at-timestamp-p)))) (should (eq 'year (org-test-with-temp-text "<2012-03-29 Thu>" (org-at-timestamp-p)))) (should (eq 'month (org-test-with-temp-text "<2012-03-29 Thu>" (org-at-timestamp-p)))) (should (eq 'day (org-test-with-temp-text "<2012-03-29 Thu>" (org-at-timestamp-p)))) (should (eq 'day (org-test-with-temp-text "<2012-03-29 Thu>" (org-at-timestamp-p)))) (should (wholenump (org-test-with-temp-text "<2012-03-29 Thu +2y>" (org-at-timestamp-p)))) (should (eq 'bracket (org-test-with-temp-text "<2012-03-29 Thu>" (org-at-timestamp-p)))) (should (eq 'after (org-test-with-temp-text "<2012-03-29 Thu>»" (org-at-timestamp-p)))) ;; Test `inactive' optional argument. (should (org-test-with-temp-text "[2012-03-29 Thu]" (org-at-timestamp-p 'inactive))) (should-not (org-test-with-temp-text "[2012-03-29 Thu]" (org-at-timestamp-p))) ;; When optional argument is `agenda', recognize timestamps in ;; planning info line, property drawers and clocks. (should (org-test-with-temp-text "* H\nSCHEDULED: <2012-03-29 Thu>" (org-at-timestamp-p 'agenda))) (should-not (org-test-with-temp-text "* H\nSCHEDULED: <2012-03-29 Thu>" (org-at-timestamp-p))) (should (org-test-with-temp-text "* H\n:PROPERTIES:\n:PROP: <2012-03-29 Thu>\n:END:" (org-at-timestamp-p 'agenda))) (should-not (org-test-with-temp-text "* H\n:PROPERTIES:\n:PROP: <2012-03-29 Thu>\n:END:" (org-at-timestamp-p))) (should (org-test-with-temp-text "CLOCK: [2012-03-29 Thu]" (let ((org-agenda-include-inactive-timestamps t)) (org-at-timestamp-p 'agenda)))) (should-not (org-test-with-temp-text "CLOCK: [2012-03-29 Thu]" (let ((org-agenda-include-inactive-timestamps t)) (org-at-timestamp-p)))) (should-not (org-test-with-temp-text "CLOCK: [2012-03-29 Thu]" (let ((org-agenda-include-inactive-timestamps t)) (org-at-timestamp-p 'inactive)))) ;; When optional argument is `lax', match any part of the document ;; with Org timestamp syntax. (should (org-test-with-temp-text "# <2012-03-29 Thu>" (org-at-timestamp-p 'lax))) (should-not (org-test-with-temp-text "# <2012-03-29 Thu>" (org-at-timestamp-p))) (should (org-test-with-temp-text ": <2012-03-29 Thu>" (org-at-timestamp-p 'lax))) (should-not (org-test-with-temp-text ": <2012-03-29 Thu>" (org-at-timestamp-p))) (should (org-test-with-temp-text "#+BEGIN_EXAMPLE\n<2012-03-29 Thu>\n#+END_EXAMPLE" (org-at-timestamp-p 'lax))) (should-not (org-test-with-temp-text "#+BEGIN_EXAMPLE\n<2012-03-29 Thu>\n#+END_EXAMPLE" (org-at-timestamp-p))) ;; Optional argument `lax' also matches inactive timestamps. (should (org-test-with-temp-text "# [2012-03-29 Thu]" (org-at-timestamp-p 'lax)))) (ert-deftest test-org/timestamp () "Test `org-timestamp' specifications." ;; Insert chosen time stamp at point. (should (string-match "Te<2014-03-04 .*?>xt" (org-test-with-temp-text "Text" (cl-letf (((symbol-function 'org-read-date) (lambda (&rest _args) (org-time-string-to-time "2014-03-04")))) (org-timestamp nil) (buffer-string))))) ;; With a prefix argument, also insert time. (should (string-match "Te<2014-03-04 .*? 00:41>xt" (org-test-with-temp-text "Text" (cl-letf (((symbol-function 'org-read-date) (lambda (&rest _args) (org-time-string-to-time "2014-03-04 00:41")))) (org-timestamp '(4)) (buffer-string))))) ;; With two universal prefix arguments, insert an active timestamp ;; with the current time without prompting the user. (should (string-match "Te<2014-03-04 .*? 00:41>xt" (org-test-with-temp-text "Text" (org-test-at-time "2014-03-04 00:41" (org-timestamp '(16)) (buffer-string))))) ;; When optional argument is non-nil, insert an inactive timestamp. (should (string-match "Te\\[2014-03-04 .*?\\]xt" (org-test-with-temp-text "Text" (cl-letf (((symbol-function 'org-read-date) (lambda (&rest _args) (org-time-string-to-time "2014-03-04")))) (org-timestamp nil t) (buffer-string))))) ;; When called from a timestamp, replace existing one. (should (string-match "<2014-03-04 .*?>" (org-test-with-temp-text "<2012-03-29 thu.>" (cl-letf (((symbol-function 'org-read-date) (lambda (&rest _args) (org-time-string-to-time "2014-03-04")))) (org-timestamp nil) (buffer-string))))) (should (string-match "<2014-03-04 .*?>--<2014-03-04 .*?>" (org-test-with-temp-text "<2012-03-29 thu.>--<2014-03-04 tue.>" (cl-letf (((symbol-function 'org-read-date) (lambda (&rest _args) (org-time-string-to-time "2014-03-04")))) (org-timestamp nil) (buffer-string))))) ;; When replacing a timestamp, preserve repeater, if any. (should (string-match "<2014-03-04 .*? \\+2y>" (org-test-with-temp-text "<2012-03-29 thu. +2y>" (cl-letf (((symbol-function 'org-read-date) (lambda (&rest _args) (org-time-string-to-time "2014-03-04")))) (org-timestamp nil) (buffer-string))))) ;; When called twice in a raw, build a date range. (should (string-match "<2012-03-29 .*?>--<2014-03-04 .*?>" (org-test-with-temp-text "<2012-03-29 thu.>" (cl-letf (((symbol-function 'org-read-date) (lambda (&rest _args) (org-time-string-to-time "2014-03-04")))) (let ((last-command 'org-timestamp) (this-command 'org-timestamp)) (org-timestamp nil)) (buffer-string)))))) (ert-deftest test-org/timestamp-has-time-p () "Test `org-timestamp-has-time-p' specifications." ;; With time. (should (org-test-with-temp-text "<2012-03-29 Thu 16:40>" (org-timestamp-has-time-p (org-element-context)))) ;; Without time. (should-not (org-test-with-temp-text "<2012-03-29 Thu>" (org-timestamp-has-time-p (org-element-context))))) (ert-deftest test-org/get-repeat () "Test `org-get-repeat' specifications." (should (org-test-with-temp-text "* H\n<2012-03-29 Thu 16:40 +2y>" (org-get-repeat))) (should-not (org-test-with-temp-text "* H\n<2012-03-29 Thu 16:40>" (org-get-repeat))) ;; Return proper repeat string. (should (equal "+2y" (org-test-with-temp-text "* H\n<2014-03-04 Tue 16:40 +2y>" (org-get-repeat)))) ;; Prevent false positive (commented or verbatim time stamps) (should-not (org-test-with-temp-text "* H\n# <2012-03-29 Thu 16:40>" (org-get-repeat))) (should-not (org-test-with-temp-text "* H\n#+BEGIN_EXAMPLE\n<2012-03-29 Thu 16:40>\n#+END_EXAMPLE" (org-get-repeat))) ;; Return nil when called before first heading. (should-not (org-test-with-temp-text "<2012-03-29 Thu 16:40 +2y>" (org-get-repeat))) ;; When called with an optional argument, extract repeater from that ;; string instead. (should (equal "+2y" (org-get-repeat "<2012-03-29 Thu 16:40 +2y>"))) (should-not (org-get-repeat "<2012-03-29 Thu 16:40>"))) (ert-deftest test-org/timestamp-format () "Test `org-format-timestamp' specifications." ;; Regular test. (should (equal "2012-03-29 16:40" (org-test-with-temp-text "<2012-03-29 Thu 16:40>" (org-format-timestamp (org-element-context) "%Y-%m-%d %R")))) ;; Range end. (should (equal "2012-03-29" (org-test-with-temp-text "[2011-07-14 Thu]--[2012-03-29 Thu]" (org-format-timestamp (org-element-context) "%Y-%m-%d" t))))) (ert-deftest test-org/timestamp-split-range () "Test `org-timestamp-split-range' specifications." ;; Extract range start (active). (should (equal '(2012 3 29) (org-test-with-temp-text "<2012-03-29 Thu>--<2012-03-30 Fri>" (let ((ts (org-timestamp-split-range (org-element-context)))) (mapcar (lambda (p) (org-element-property p ts)) '(:year-end :month-end :day-end)))))) ;; Extract range start (inactive) (should (equal '(2012 3 29) (org-test-with-temp-text "[2012-03-29 Thu]--[2012-03-30 Fri]" (let ((ts (org-timestamp-split-range (org-element-context)))) (mapcar (lambda (p) (org-element-property p ts)) '(:year-end :month-end :day-end)))))) ;; Extract range end (active). (should (equal '(2012 3 30) (org-test-with-temp-text "<2012-03-29 Thu>--<2012-03-30 Fri>" (let ((ts (org-timestamp-split-range (org-element-context) t))) (mapcar (lambda (p) (org-element-property p ts)) '(:year-end :month-end :day-end)))))) ;; Extract range end (inactive) (should (equal '(2012 3 30) (org-test-with-temp-text "[2012-03-29 Thu]--[2012-03-30 Fri]" (let ((ts (org-timestamp-split-range (org-element-context) t))) (mapcar (lambda (p) (org-element-property p ts)) '(:year-end :month-end :day-end)))))) ;; Return the timestamp if not a range. (should (org-test-with-temp-text "[2012-03-29 Thu]" (let* ((ts-orig (org-element-context)) (ts-copy (org-timestamp-split-range ts-orig))) (eq ts-orig ts-copy)))) (should (org-test-with-temp-text "<%%(org-float t 4 2)>" (let* ((ts-orig (org-element-context)) (ts-copy (org-timestamp-split-range ts-orig))) (eq ts-orig ts-copy))))) (ert-deftest test-org/timestamp-translate () "Test `org-timestamp-translate' specifications." ;; Translate whole date range. (should (equal "<29>--<30>" (org-test-with-temp-text "<2012-03-29 Thu>--<2012-03-30 Fri>" (let ((org-display-custom-times t) (org-timestamp-custom-formats '("<%d>" . "<%d>"))) (org-timestamp-translate (org-element-context)))))) ;; Translate date range start. (should (equal "<29>" (org-test-with-temp-text "<2012-03-29 Thu>--<2012-03-30 Fri>" (let ((org-display-custom-times t) (org-timestamp-custom-formats '("<%d>" . "<%d>"))) (org-timestamp-translate (org-element-context) 'start))))) ;; Translate date range end. (should (equal "<30>" (org-test-with-temp-text "<2012-03-29 Thu>--<2012-03-30 Fri>" (let ((org-display-custom-times t) (org-timestamp-custom-formats '("<%d>" . "<%d>"))) (org-timestamp-translate (org-element-context) 'end))))) ;; Translate time range. (should (equal "<08>--<16>" (org-test-with-temp-text "<2012-03-29 Thu 8:30-16:40>" (let ((org-display-custom-times t) (org-timestamp-custom-formats '("<%d>" . "<%H>"))) (org-timestamp-translate (org-element-context)))))) ;; Translate non-range timestamp. (should (equal "<29>" (org-test-with-temp-text "<2012-03-29 Thu>" (let ((org-display-custom-times t) (org-timestamp-custom-formats '("<%d>" . "<%d>"))) (org-timestamp-translate (org-element-context)))))) ;; Do not change `diary' timestamps. (should (equal "<%%(org-float t 4 2)>" (org-test-with-temp-text "<%%(org-float t 4 2)>" (let ((org-display-custom-times t) (org-timestamp-custom-formats '("<%d>" . "<%d>"))) (org-timestamp-translate (org-element-context))))))) (ert-deftest test-org/timestamp-from-string () "Test `org-timestamp-from-string' specifications." ;; Return nil if argument is not a valid Org timestamp. (should-not (org-timestamp-from-string "")) (should-not (org-timestamp-from-string nil)) (should-not (org-timestamp-from-string "<2012-03-29")) ;; Otherwise, return a valid Org timestamp object. (should (string-match-p "<2012-03-29 .+>" (org-element-interpret-data (org-timestamp-from-string "<2012-03-29 Thu>")))) (should (string-match-p "[2014-03-04 .+]" (org-element-interpret-data (org-timestamp-from-string "[2014-03-04 Tue]"))))) (ert-deftest test-org/timestamp-from-time () "Test `org-timestamp-from-time' specifications." ;; Standard test. (should (string-match-p "<2012-03-29 .+>" (org-element-interpret-data (org-timestamp-from-time (org-time-string-to-time "<2012-03-29 Thu 16:40>"))))) ;; When optional argument WITH-TIME is non-nil, provide time ;; information. (should (string-match-p "<2012-03-29 .+ 16:40>" (org-element-interpret-data (org-timestamp-from-time (org-time-string-to-time "<2012-03-29 Thu 16:40>") t)))) ;; When optional argument INACTIVE is non-nil, return an inactive ;; timestamp. (should (string-match-p "[2012-03-29 .+]" (org-element-interpret-data (org-timestamp-from-time (org-time-string-to-time "<2012-03-29 Thu 16:40>") nil t))))) (ert-deftest test-org/timestamp-to-time () "Test `org-timestamp-to-time' specifications." (should (equal "2014-03-04" (format-time-string "%Y-%m-%d" (org-timestamp-to-time (org-timestamp-from-string "<2014-03-04 Tue>"))))) (should (equal "2014-03-04" (format-time-string "%Y-%m-%d" (org-timestamp-to-time (org-timestamp-from-string "[2014-03-04 Tue]"))))) (should (equal "2012-03-29 08:30" (format-time-string "%Y-%m-%d %H:%M" (org-timestamp-to-time (org-timestamp-from-string "<2012-03-29 Thu 08:30-16:40>"))))) (should (equal "2012-03-29" (format-time-string "%Y-%m-%d" (org-timestamp-to-time (org-timestamp-from-string "<2012-03-29 Thu>--<2014-03-04 Tue>"))))) (should (equal "2012-03-29" (format-time-string "%Y-%m-%d" (org-timestamp-to-time (org-timestamp-from-string "[2012-03-29 Thu]--[2014-03-04 Tue]"))))) ;; When optional argument END is non-nil, use end of date range or ;; time range. (should (equal "2012-03-29 16:40" (format-time-string "%Y-%m-%d %H:%M" (org-timestamp-to-time (org-timestamp-from-string "<2012-03-29 Thu 08:30-16:40>") t)))) (should (equal "2014-03-04" (format-time-string "%Y-%m-%d" (org-timestamp-to-time (org-timestamp-from-string "<2012-03-29 Thu>--<2014-03-04 Tue>") t)))) (should (equal "2014-03-04" (format-time-string "%Y-%m-%d" (org-timestamp-to-time (org-timestamp-from-string "[2012-03-29 Thu]--[2014-03-04 Tue]") t))))) ;;; Yank and Kill (ert-deftest test-org/paste-subtree () "Test `org-paste-subtree' specifications." ;; Return an error if text to yank is not a set of subtrees. (should-error (org-paste-subtree nil "Text")) ;; Adjust level according to current one. (should (equal "* H\n* Text\n" (org-test-with-temp-text "* H\n" (org-paste-subtree nil "* Text") (buffer-string)))) (should (equal "* H1\n** H2\n** Text\n" (org-test-with-temp-text "* H1\n** H2\n" (org-paste-subtree nil "* Text") (buffer-string)))) ;; When not on a heading, move to next heading before yanking. (should (equal "* H1\nParagraph\n* Text\n* H2" (org-test-with-temp-text "* H1\nParagraph\n* H2" (org-paste-subtree nil "* Text") (buffer-string)))) ;; With prefix argument, move to the end of subtree. (should (equal "* H1\nParagraph\n** H1.1\n* Text\n* H2" (org-test-with-temp-text "* H1\nParagraph\n** H1.1\n* H2" (org-paste-subtree '(4) "* Text") (buffer-string)))) ;; With double prefix argument, move to first sibling (should (equal "* H1\nParagraph\n** Text\n** H1.1\n* H2" (org-test-with-temp-text "* H1\nParagraph\n** H1.1\n* H2" (org-paste-subtree '(16) "* Text") (buffer-string)))) ;; If point is between two headings, use the deepest level. (should (equal "* H1\n\n* Text\n* H2" (org-test-with-temp-text "* H1\n\n* H2" (org-paste-subtree nil "* Text") (buffer-string)))) (should (equal "** H1\n\n** Text\n* H2" (org-test-with-temp-text "** H1\n\n* H2" (org-paste-subtree nil "* Text") (buffer-string)))) (should (equal "* H1\n\n** Text\n** H2" (org-test-with-temp-text "* H1\n\n** H2" (org-paste-subtree nil "* Text") (buffer-string)))) ;; When point is on heading at bol, insert before (should (equal "* Text\n* H1\n** H2" (org-test-with-temp-text "* H1\n** H2" (org-paste-subtree nil "*** Text") (buffer-string)))) ;; With prefix argument, ignore that we are at bol (should (equal "* H1\n** H2\n* Text\n" (org-test-with-temp-text "* H1\n** H2" (org-paste-subtree '(4) "*** Text") (buffer-string)))) ;; When point is on heading but not at bol, use smallest level among ;; current heading and next, inserting before the next heading. (should (equal "* H1\ncontents\n** Text\n** H2" (org-test-with-temp-text "* H1\ncontents\n** H2" (org-paste-subtree nil "*** Text") (buffer-string)))) (should (equal "*** H1\ncontents\n*** Text\n* H2" (org-test-with-temp-text "*** H1\ncontents\n* H2" (org-paste-subtree nil "* Text") (buffer-string)))) ;; When on an empty heading, after the stars, deduce the new level ;; from the number of stars. (should (equal "*** Text\n" (org-test-with-temp-text "*** " (org-paste-subtree nil "* Text") (buffer-string)))) ;; Remove the indicator line completely. (should (equal "* Top text more text *** Text " (org-test-with-temp-text "* Top text *** more text" (org-paste-subtree nil "* Text") (buffer-string)))) ;; Optional argument LEVEL forces a level for the subtree. (should (equal "* H\n*** Text\n" (org-test-with-temp-text "* H" (org-paste-subtree 3 "* Text") (buffer-string))))) (ert-deftest test-org/cut-and-paste-subtree () "Test `org-cut-subtree' and `org-paste-subtree'." (should (equal "* Two two * One " (org-test-with-temp-text "* One * Two two " (call-interactively #'org-cut-subtree) (goto-char (point-min)) (call-interactively #'org-paste-subtree) (buffer-string)))) (should (equal "* One * Two " (org-test-with-temp-text "* One * Two " (call-interactively #'org-cut-subtree) (backward-char) (call-interactively #'org-paste-subtree) (buffer-string))))) (ert-deftest test-org/org--open-file-format-command () "Test `org--open-file-format-command' helper for `org-open-file'." (let ((system-type 'gnu/linux)) ; Fix behavior of `shell-quote-argument'. ;; No additional groups in `org-file-apps' key. (let ((file "/file.pdf") (pattern "\\.pdf\\'")) (should (equal "simple /file.pdf" (and (string-match pattern file) (org--open-file-format-command "simple %s" file file (match-data))))) (should (equal "single-quotes /file.pdf" (and (string-match pattern file) (org--open-file-format-command "single-quotes '%s'" file file (match-data))))) (should (equal "double-quotes /file.pdf" (and (string-match pattern file) (org--open-file-format-command "double-quotes \"%s\"" file file (match-data))))) (should (equal "quotes 'mismatch \"/file.pdf'" (and (string-match pattern file) (org--open-file-format-command "quotes 'mismatch \"%s'" file file (match-data))))) (should (equal "no subst" (and (string-match pattern file) (org--open-file-format-command "no subst" file file (match-data))))) (should (equal "% literal percent 100% %s" (and (string-match pattern file) (org--open-file-format-command "\\% literal percent 100\\% \\%s" file file (match-data))))) (should (equal "escape \"/file.pdf\" \\ more" (and (string-match pattern file) (org--open-file-format-command ;; Second quote is not escaped. "escape \\\"%s\" \\\\ more" file file (match-data))))) (should (equal "/file.pdf file at start" (and (string-match pattern file) (org--open-file-format-command "%s file at start" file file (match-data))))) (should (equal "backslash-newline\n/file.pdf" (and (string-match pattern file) (org--open-file-format-command "backslash-newline\\\n%s" file file (match-data)))))) ;; Anchors within target file. (let ((file "/page-search.pdf") (link "/page-search.pdf::10::some words") (pattern "\\.pdf::\\([0-9]+\\)::\\(.*\\)\\'")) (should (equal "zathura --page 10 --find some\\ words /page-search.pdf" (and (string-match pattern link) (org--open-file-format-command "zathura --page '%1' --find %2 \"%s\"" file link (match-data))))) ;; Unused %2. (should (equal "firefox file:///page-search.pdf\\#page=10" (and (string-match pattern link) (org--open-file-format-command "firefox file://%s\\\\#page=%1" file link (match-data))))) (should (equal "adjucent-subst /page-search.pdfsome\\ words10some\\ words" (and (string-match pattern link) (org--open-file-format-command "adjucent-subst %s%2'%1'%2" file link (match-data)))))) ;; No more than 9 substitutions are supported. (let ((file "/many.pdf") (link "/many.pdf::one:2:3:4:5:6:7:8:9:a:b:c") (pattern (concat "\\.pdf:" (mapconcat (lambda (_) ":\\([^:]+\\)") (number-sequence 1 12) "") "\\'"))) (should (equal "overflow /many.pdf::one:2:3:4:5:6:7:8:9:one0:one1:one2" (and (string-match pattern link) (org--open-file-format-command "overflow %s::%1:%2:%3:%4:%5:%6:%7:%8:%9:%10:%11:%12" file link (match-data)))))) ;; Percent character in link fields does not cause any problem. (let ((file "/file-%2.pdf") (link "/file-%2.pdf::anchor-%3::search %1") (pattern "\\.pdf::\\([^:]+\\)::\\(.+\\)\\'")) (should (equal "percents --find search\\ \\%1 file:///file-\\%2.pdf\\#anchor-\\%3" (and (string-match pattern link) (org--open-file-format-command "percents --find %2 file://%s\\\\#%1" file link (match-data)))))) ;; Errors. (let ((file "/error.pdf") (pattern "\\.pdf\\'")) (let* ((err (should-error (and (string-match pattern file) (org--open-file-format-command "trailing-percent %s %" file file (match-data))) :type 'error)) (err-text (cadr err))) (should-not (unless (and (stringp err-text) (string-match-p "\\`Invalid format .*%" err-text)) err))) (let* ((err (should-error (and (string-match pattern file) (org--open-file-format-command "trailing-backslash %s \\" file file (match-data))) :type 'error)) (err-text (cadr err))) (should-not (unless (and (stringp err-text) (string-match-p "\\`Invalid format .*\\\\" err-text)) err))) (let* ((err (should-error (and (string-match pattern file) (org--open-file-format-command "percent-newline %\n%s" file file (match-data))) :type 'error)) (err-text (cadr err))) (should-not (unless (and (stringp err-text) (string-match-p "\\`Invalid format .*%\n" err-text)) err))) ;; Mailcap escape for "%" is "\%", not "%%". (let* ((err (should-error (and (string-match pattern file) (org--open-file-format-command "percent-percent %s%%" file file (match-data))) :type 'error)) (err-text (cadr err))) (should-not (unless (and (stringp err-text) (string-match-p "\\`Invalid format .*%%" err-text)) err))) ;; Mailcap allows "%t" for MIME type, but Org has no such information. (let* ((err (should-error (and (string-match pattern file) (org--open-file-format-command "percent-t-unsupported --type '%t' %s" file file (match-data))) :type 'error)) (err-text (cadr err))) (should-not (unless (and (stringp err-text) (string-match-p "\\`Invalid format .*%t" err-text)) err)))) ;; Optional regular expression groups have no point in `org-file-apps' patterns. (let* ((file "/error.pdf") (link "/error.pdf::1") (pattern "\\.pdf::\\([^:]+\\)\\(?:::\\(.+\\)\\)?\\'") (err (should-error (and (string-match pattern link) (org--open-file-format-command "no-such-match --search %2 %s" file link (match-data))) :type 'error)) (err-text (cadr err))) (should-not (unless (and (stringp err-text) (string-match-p "\\`Invalid format.*%2" err-text)) err))))) ;;; LaTeX-related functions. (ert-deftest test-org/format-latex-as-html () "Test shell special characters escaping in `org-format-latex-as-html'." ;; printf is only available in POSIX-compatible shells. (skip-unless (not (memq system-type '(ms-dos windows-nt)))) (let ((org-latex-to-html-convert-command "printf \"\" %i")) ;; No backslashes added by `shell-quote-argumet' ;; are leaked to command arguments. See e.g. dash(1) "Double Quotes": ;; ;; The backslash inside double quotes is historically weird, ;; and serves to quote only the following characters: ;; $ ` " \ . ;; Otherwise it remains literal. ;; ;; Actually extra backslashes may appear if a user adds ;; double quotes around "%i", however it is not subject ;; of this test. (should (equal "" (org-format-latex-as-html "(|)`[[\\]]{}#$'!"))) ;; The following tests generate shell errors if %i ;; substitution is not passed throuhg `shell-quote-argument'. ;; Multiple words. (should (equal "" (org-format-latex-as-html "words ; |"))) ;; Bypass single quote. ;; The same snippet causes shell error if '%i' is wrapped ;; in single quotes in user configuration. (should (equal "" (org-format-latex-as-html "apostrophe' ; |"))) ;; Bypass double quote. ;; Double quotes around "%i" in user configuration leads ;; to extra backslashes (see above), however likely ;; such user error can not cause execution of the argument ;; as raw shell commands. (should (equal "" (org-format-latex-as-html "quote\" ; |"))))) (defun test-org/extract-mathml-math (xml) "Extract body from result of `org-create-math-formula'." (and (string-match "]*>\\(\\(?:.\\|\n\\)*\\)" xml) (match-string 1 xml))) (ert-deftest test-org/create-math-formula () "Test shell special characters escaping in `org-create-math-formula'." ;; printf is only available in POSIX-compatible shells. (skip-unless (not (memq system-type '(ms-dos windows-nt)))) ;; The function requires ... elements. (let ((org-latex-to-mathml-convert-command "printf \"\" %i >%o")) ;; See comments in `test-org/format-latex-as-html'. ;; ;; No backslashes added by `shell-quote-argumet' ;; are leaked to command arguments. (should (equal "" (test-org/extract-mathml-math (org-create-math-formula "(|)`[[\\]]{}#$'!")))) ;; Multiple words. (should (equal "" (test-org/extract-mathml-math (org-create-math-formula "words ; |")))) ;; Bypass single quote. (should (equal "" (test-org/extract-mathml-math (org-create-math-formula "apostrophe' ; |")))) ;; Bypass double quote. (should (equal "" (test-org/extract-mathml-math (org-create-math-formula "quote\" ; |")))))) (provide 'test-org) ;;; test-org.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ox-ascii.el000066400000000000000000000043251500430433700217140ustar00rootroot00000000000000;;; test-ox-ascii.el --- tests for ox-ascii.el -*- lexical-binding: t; -*- ;; Copyright (C) 2023 Ihor Radchenko ;; Author: Ihor Radchenko ;; 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 . ;;; Commentary: ;; Tests checking validity of Org ASCII export output. ;;; Code: (require 'ox-ascii nil t) (unless (featurep 'ox-ascii) (signal 'missing-test-dependency "org-export-ascii")) (ert-deftest test-ox-ascii/list () "Test lists." ;; Number counter. (org-test-with-exported-text 'ascii "1. [@3] foo" (goto-char (point-min)) (should (search-forward "3. foo"))) ;; Number counter. Start from 1. (org-test-with-exported-text 'ascii "3. foo" (goto-char (point-min)) (should (search-forward "1. foo"))) ;; Alphanumeric counter. (let ((org-list-allow-alphabetical t)) (org-test-with-exported-text 'ascii "m. [@k] baz" (goto-char (point-min)) (should (search-forward "11. baz")))) ;; Start from 1. (let ((org-list-allow-alphabetical t)) (org-test-with-exported-text 'ascii "m. bar" (goto-char (point-min)) (should (search-forward "1. bar"))))) (ert-deftest test-ox-ascii/justify () "Test justification." ;; Right justify. (org-test-with-exported-text 'ascii "#+OPTIONS: author:nil *:t #+BEGIN_JUSTIFYRIGHT left or right #+END_JUSTIFYRIGHT " (goto-char (point-min)) (search-forward "left or right") (should (equal org-ascii-text-width (org-current-text-column))))) (provide 'test-ox-ascii) ;;; test-ox-ascii.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ox-beamer.el000066400000000000000000000072441500430433700220620ustar00rootroot00000000000000;;; test-ox-beamer.el --- tests for ox-beamer.el -*- lexical-binding: t; -*- ;; Copyright (C) 2024 Leo Butler ;; Author: Leo Butler ;; 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 . ;;; Commentary: ;; Tests checking validity of Org Beamer export output. ;;; Code: (require 'ox-beamer nil t) (unless (featurep 'ox-beamer) (signal 'missing-test-dependency "org-export-beamer")) (ert-deftest ox-beamer/orgframe () "Test that `org-beamer-frame-environment' is defined and used." (org-test-with-exported-text 'beamer "#+OPTIONS: toc:nil * A frame Here is an example: #+begin_example \\begin{frame} ... \\end{frame} #+end_example " (goto-char (point-min)) (should (search-forward (concat "\\newenvironment<>{" org-beamer-frame-environment "}") nil t)) (should (search-forward (concat "\\begin{" org-beamer-frame-environment "}") nil t)) (should (search-forward (concat "\\end{" org-beamer-frame-environment "}") nil t)))) (ert-deftest ox-beamer/orgframe-in-example () "Test that `org-beamer-frame-environment' is not defined." (org-test-with-exported-text 'beamer (concat "#+OPTIONS: toc:nil * A frame Here is an example: #+begin_example \\begin{" org-beamer-frame-environment "} ... \\end{" org-beamer-frame-environment "} #+end_example ") (goto-char (point-min)) (should-not (search-forward (concat "\\newenvironment<>{" org-beamer-frame-environment "}") nil t)) (forward-line) (should (search-forward (concat "\\begin{frame}") nil t)) (should (search-forward (concat "\\begin{" org-beamer-frame-environment "}"))) (should (search-forward (concat "\\end{" org-beamer-frame-environment "}"))) (should (search-forward (concat "\\end{frame}") nil t)))) (ert-deftest ox-beamer/orgframe-in-one-example () "Test that `org-beamer-frame-environment' is defined. First frame should use \"frame\" environment, the second uses `org-beamer-frame-environment'." (org-test-with-exported-text 'beamer (concat "#+OPTIONS: toc:nil * A frame Here is an example: #+begin_example \\begin{" org-beamer-frame-environment "} ... \\end{" org-beamer-frame-environment "} #+end_example * A second frame Here is a second example: #+begin_example \\begin{frame} ... \\end{frame} #+end_example ") (goto-char (point-min)) (should (search-forward (concat "\\newenvironment<>{" org-beamer-frame-environment "}") nil t)) (forward-line) (org-test-ignore-duplicate (should (search-forward (concat "\\begin{frame}") nil t)) (should (search-forward (concat "\\begin{" org-beamer-frame-environment "}"))) (should (search-forward (concat "\\end{" org-beamer-frame-environment "}"))) (should (search-forward (concat "\\end{frame}") nil t)) (should (search-forward (concat "\\begin{" org-beamer-frame-environment "}"))) (should (search-forward (concat "\\begin{frame}") nil t)) (should (search-forward (concat "\\end{frame}") nil t)) (should (search-forward (concat "\\end{" org-beamer-frame-environment "}")))))) (provide 'test-ox-beamer) ;;; test-ox-beamer.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ox-html.el000066400000000000000000001070751500430433700215760ustar00rootroot00000000000000;;; test-ox-html.el --- Tests for ox-html.el -*- lexical-binding: t; -*- ;; Copyright (C) 2022 Rudolf Adamkovič ;; Author: Rudolf Adamkovič ;; This file is part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (require 'ox-html) ;;; Loading MathJax (ert-deftest ox-html/mathjax-path-none () "Test that MathJax does not load when not needed." (should-not (org-test-with-temp-text "No LaTeX here." (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (let ((case-fold-search t)) (search-forward "MathJax" nil t))))))) (ert-deftest ox-html/mathjax-path-default () "Test the default path from which MathJax loads." (should (= 1 (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx "")))))))) (ert-deftest ox-html/mathjax-path-custom () "Test a customized path from which MathJax loads." (should (= 1 (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options '((path "./mathjax/es5/tex-mml-chtml.js")))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx "")))))))) (ert-deftest ox-html/mathjax-path-in-buffer () "Test a in-buffer customized path from which MathJax loads." (should (= 1 (org-test-with-temp-text " #+HTML_MATHJAX: path: ./mathjax/es5/tex-mml-chtml.js $x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx "")))))))) ;;; Configuring MathJax with options (ert-deftest ox-html/mathjax-options-default () "Test the default MathJax options." (should (= 1 (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx "")))))))) (ert-deftest ox-html/mathjax-options-custom () "Test customized MathJax options." (should (= 1 (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options '((path "") ; tested elsewhere (scale 0.5) (align "right") (font "mathjax-euler") (overflow "scale") (tags "all") (indent "1em") (multlinewidth "100%") (tagindent "2em") (tagside "left")))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx "")))))))) (ert-deftest ox-html/mathjax-options-in-buffer () "Test in-buffer customized MathJax options." (should (= 1 (org-test-with-temp-text "$x$ #+HTML_MATHJAX: scale: 0.5 #+HTML_MATHJAX: align: right #+HTML_MATHJAX: font: mathjax-euler #+HTML_MATHJAX: overflow: scale #+HTML_MATHJAX: tags: all #+HTML_MATHJAX: indent: 1em #+HTML_MATHJAX: multlinewidth: 100% #+HTML_MATHJAX: tagindent: 2em #+HTML_MATHJAX: tagside: left" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx "")))))))) ;;; Converting legacy MathJax scales ;; Define a legacy scale as any scale given as a percentage string, ;; such as "150", instead of a unit-interval float, such as 1.5. (ert-deftest ox-html/mathjax-legacy-scale-default () "Test the legacy scale conversion with the old default value." (should (= 2 (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (cons '(scale "100") org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "scale: 1.0" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-scale-custom () "Test the legacy scale conversion with a non-default value." (should (= 2 (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (cons '(scale "10") org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "scale: 0.1" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-scale-in-buffer () "Test the legacy scale conversion with an in-buffer value." (should (= 2 (org-test-with-temp-text "$x$ #+HTML_MATHJAX: scale: 10" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "scale: 0.1" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-scale-message () "Test the legacy scale conversion message." (should (= 1 (seq-count (lambda (message) (string= "Converting legacy MathJax scale: 20 to 0.2" message)) (org-test-capture-warnings (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (cons '(scale "20") org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode)))))))) (ert-deftest ox-html/mathjax-legacy-scale-message-in-buffer () "Test the legacy scale conversion message for an in-buffer value." (should (seq-count (lambda (message) (string= "Converting legacy MathJax scale: 20 to 0.2" message)) (org-test-capture-warnings (org-test-with-temp-text "$x$ #+HTML_MATHJAX: scale: 20" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode))))))) (ert-deftest ox-html/mathjax-legacy-scale-ignore () "Test the legacy scale conversion ignores small values." (should (= 2 (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options '((scale "9")))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "scale: 9" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-scale-invalid () "Test the legacy scale conversion with an invalid value." (should (= 2 (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (cons '(scale "xxx") org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "scale: 1.0" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-scale-invalid-message () "Test the invalid legacy scale conversion message." (should (= 1 (seq-count (lambda (message) (string= "Non-numerical MathJax scale: xxx" message)) (org-test-capture-warnings (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (cons '(scale "xxx") org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode)))))))) ;;; Converting legacy MathJax auto-numbering ;; NOTE: AMS stands for American Mathematical Society. (ert-deftest ox-html/mathjax-legacy-autonumber-ams () "Test legacy auto-numbering, when AMS." (should (= 1 (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (cons '(autonumber "AMS") org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "tags: 'ams'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-autonumber-ams-in-buffer () "Test legacy auto-numbering, when AMS in-buffer." (should (= 1 (org-test-with-temp-text "$x$ #+HTML_MATHJAX: autonumber: AMS" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "tags: 'ams'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-autonumber-none () "Test legacy auto-numbering, when disabled." (should (= 1 (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (cons '(autonumber "None") org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "tags: 'none'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-autonumber-none-in-buffer () "Test legacy auto-numbering, when disabled in-buffer." (should (= 1 (org-test-with-temp-text "$x$ #+HTML_MATHJAX: autonumber: None" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "tags: 'none'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-autonumber-all () "Test legacy auto-numbering, when enabled." (should (= 1 (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (cons '(autonumber "All") org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "tags: 'all'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-autonumber-all-in-buffer () "Test legacy auto-numbering, when enabled in-buffer." (should (= 1 (org-test-with-temp-text "$x$ #+HTML_MATHJAX: autonumber: All" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "tags: 'all'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-autonumber-message () "Test legacy auto-numbering conversion message." (should (= 1 (seq-count (lambda (message) (string= "Converting legacy MathJax option: autonumber" message)) (org-test-capture-warnings (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (cons '(autonumber "AMS") org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode)))))))) (ert-deftest ox-html/mathjax-legacy-autonumber-message-in-buffer () "Test legacy auto-numbering conversion message." (should (= 1 (seq-count (lambda (message) (string= "Converting legacy MathJax option: autonumber" message)) (org-test-capture-warnings (org-test-with-temp-text "$x$ #+HTML_MATHJAX: autonumber: AMS" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode)))))))) ;;; Converting legacy MathJax fonts (ert-deftest ox-html/mathjax-legacy-font-tex () "Test legacy font, when TeX." (should (= 1 (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (cons '(font "TeX") org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "font: 'mathjax-tex'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-font-tex-in-buffer () "Test legacy font, when TeX in-buffer." (should (= 1 (org-test-with-temp-text "$x$ #+HTML_MATHJAX: font: TeX" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "font: 'mathjax-tex'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-font-stix-web () "Test legacy font, when STIX-Web." (should (= 1 (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (cons '(font "STIX-Web") org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "font: 'mathjax-stix2'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-font-stix-web-in-buffer () "Test legacy font, when STIX-Web in-buffer." (should (= 1 (org-test-with-temp-text "$x$ #+HTML_MATHJAX: font: STIX-Web" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "font: 'mathjax-stix2'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-font-asana-math () "Test legacy font, when Asana-Math." (should (= 1 (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (cons '(font "Asana-Math") org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "font: 'mathjax-asana'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-font-asana-math-in-buffer () "Test legacy font, when Asana-Math in-buffer." (should (= 1 (org-test-with-temp-text "$x$ #+HTML_MATHJAX: font: Asana-Math" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "font: 'mathjax-asana'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-font-neo-euler () "Test legacy font, when Neo-Euler." (should (= 1 (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (cons '(font "Neo-Euler") org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "font: 'mathjax-euler'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-font-neo-euler-in-buffer () "Test legacy font, when Neo-Euler in-buffer." (should (= 1 (org-test-with-temp-text "$x$ #+HTML_MATHJAX: font: Neo-Euler" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "font: 'mathjax-euler'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-font-gyre-pagella () "Test legacy font, when Gyre-Pagella." (should (= 1 (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (cons '(font "Gyre-Pagella") org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "font: 'mathjax-pagella'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-font-gyre-pagella-in-buffer () "Test legacy font, when Gyre-Pagella in-buffer." (should (= 1 (org-test-with-temp-text "$x$ #+HTML_MATHJAX: font: Gyre-Pagella" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "font: 'mathjax-pagella'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-font-gyre-termes () "Test legacy font, when Gyre-Termes." (should (= 1 (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (cons '(font "Gyre-Termes") org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "font: 'mathjax-termes'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-font-gyre-termes-in-buffer () "Test legacy font, when Gyre-Termes in-buffer." (should (= 1 (org-test-with-temp-text "$x$ #+HTML_MATHJAX: font: Gyre-Termes" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "font: 'mathjax-termes'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-font-latin-modern () "Test legacy font, when Latin-Modern." (should (= 1 (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (cons '(font "Latin-Modern") org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "font: 'mathjax-modern'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-font-latin-modern-in-buffer () "Test legacy font, when Latin-Modern in-buffer." (should (= 1 (org-test-with-temp-text "$x$ #+HTML_MATHJAX: font: Latin-Modern" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "font: 'mathjax-modern'" (or "," "\n")))))))))) ;;; Converting legacy MathJax line breaks (ert-deftest ox-html/mathjax-legacy-line-breaks-true () "Test legacy line breaks, when true." (should (= 1 (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (append '((linebreaks "true") (overflow "overflow")) org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "displayOverflow: 'linebreak'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-line-breaks-true-in-buffer () "Test legacy line breaks, when true in-buffer." (should (= 1 (org-test-with-temp-text "$x$ #+HTML_MATHJAX: linebreaks: true" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (cons '(overflow "overflow") org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "displayOverflow: 'linebreak'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-line-breaks-false () "Test legacy line breaks, when false." (should (= 1 (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (append '((linebreaks "false") (overflow "linebreak")) org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "displayOverflow: 'overflow'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-line-breaks-false-in-buffer () "Test legacy line breaks, when true in-buffer." (should (= 1 (org-test-with-temp-text "$x$ #+HTML_MATHJAX: linebreaks: false" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (cons '(overflow "linebreak") org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode) (with-current-buffer export-buffer (how-many (rx (seq "displayOverflow: 'overflow'" (or "," "\n")))))))))) (ert-deftest ox-html/mathjax-legacy-line-breaks-message () "Test the legacy line breaks conversion message." (should (= 1 (seq-count (lambda (message) (string= "Converting legacy MathJax option: linebreaks" message)) (org-test-capture-warnings (org-test-with-temp-text "$x$" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil) (org-html-mathjax-options (cons '(linebreaks "true") org-html-mathjax-options))) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode)))))))) (ert-deftest ox-html/mathjax-legacy-line-breaks-message-in-buffer () "Test the legacy scale conversion message for an in-buffer value." (should (= 1 (seq-count (lambda (message) (string= "Converting legacy MathJax option: linebreaks" message)) (org-test-capture-warnings (org-test-with-temp-text "$x$ #+HTML_MATHJAX: linebreaks: true" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil nil #'html-mode)))))))) ;;; Rendering checkboxes (ert-deftest ox-html/checkbox-ascii () "Test ascii checkbox rendering" (skip-unless (libxml-available-p)) (should (equal `(ul ((class . "org-ul")) (li ((class . "off")) (code nil ,(format "[%c]" (char-from-name "NO-BREAK SPACE"))) " not yet") (li ((class . "on")) (code nil "[X]") " I am done") (li ((class . "trans")) (code nil "[-]") " unclear")) (org-test-with-temp-text " - [ ] not yet - [X] I am done - [-] unclear " (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil t nil) (with-current-buffer export-buffer (libxml-parse-xml-region (point-min) (point-max)))))))) (ert-deftest ox-html/checkbox-html () "Test HTML checkbox rendering" (skip-unless (libxml-available-p)) (should (equal '(ul ((class . "org-ul")) (li ((class . "off")) (input ((type . "checkbox"))) " not yet") (li ((class . "on")) (input ((type . "checkbox") (checked . "checked"))) " I am done") (li ((class . "trans")) (input ((type . "checkbox"))) " unclear")) (org-test-with-temp-text " - [ ] not yet - [X] I am done - [-] unclear " (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil t '(:html-checkbox-type html)) (with-current-buffer export-buffer (libxml-parse-xml-region (point-min) (point-max)))))))) (ert-deftest ox-html/checkbox-unicode () "Test HTML checkbox rendering" (skip-unless (libxml-available-p)) (should (equal '(ul ((class . "org-ul")) (li ((class . "off")) "☐ not yet") (li ((class . "on")) "☑ I am done") (li ((class . "trans")) "☐ unclear")) (org-test-with-temp-text " - [ ] not yet - [X] I am done - [-] unclear " (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil t '(:html-checkbox-type unicode)) (with-current-buffer export-buffer (libxml-parse-xml-region (point-min) (point-max)))))))) ;;; Postamble Format (ert-deftest ox-html/postamble-default () "Test default postamble" (org-test-with-temp-text "Test, hi" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil nil) (with-current-buffer export-buffer (should (= 1 (how-many "Validate"))) (should (= 1 (how-many "Created: "))))))) (ert-deftest ox-html/postamble-custom () "Test custom postamble" (org-test-with-temp-text "Test, hi" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil '(:html-postamble "Foobar")) (with-current-buffer export-buffer (should (= 0 (how-many "Validate"))) (should (= 0 (how-many "Created: "))) (should (= 1 (how-many "Foobar"))))))) (ert-deftest ox-html/postamble-custom-format () "Test a html-postamble option (not -format) containing a format string" (org-test-with-temp-text "Test, hi" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil '(:html-postamble "Author=%a" :author "Madame Orange")) (with-current-buffer export-buffer (should (= 0 (how-many "Validate"))) (should (= 0 (how-many "Created: "))) (should (= 1 (how-many "Author=Madame Orange"))))))) (ert-deftest ox-html/postamble-none () "Test no postamble" (org-test-with-temp-text "Test, hi" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil '(:html-postamble nil)) (with-current-buffer export-buffer (should (= 0 (how-many "Validate"))) (should (= 0 (how-many "Created: "))))))) (ert-deftest ox-html/postamble-format-wrong-config () "Test a html-postamble-format option, with incomplete config. This option is only picked up when html-postamble is set to T. This test leaves it unset, which means it is set to 'auto, which will make ox-html skip the html-postamble-format option entirely." (org-test-with-temp-text "Test, hi" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil '(:html-postamble-format (("en" "Foobar")))) (with-current-buffer export-buffer (should (= 1 (how-many "Validate"))) (should (= 1 (how-many "Created: "))) (should (= 0 (how-many "Foobar"))))))) (ert-deftest ox-html/postamble-format-proper-config () "Test a html-postamble-format option which is just a string" (org-test-with-temp-text "Test, hi" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil '(:html-postamble-format (("en" "Foobar")) :html-postamble t)) (with-current-buffer export-buffer (should (= 0 (how-many "Validate"))) (should (= 0 (how-many "Created: "))) (should (= 1 (how-many "Foobar"))))))) (ert-deftest ox-html/postamble-format-conflict () "Test conflicting postamble and postamble-format configs" (org-test-with-temp-text "Test, hi" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil '(:html-postamble-format "The format string" :html-postamble "Regular postamble")) (with-current-buffer export-buffer (should (= 0 (how-many "Validate"))) (should (= 0 (how-many "Created: "))) (should (= 0 (how-many "The format string"))) (should (= 1 (how-many "Regular postamble"))))))) (ert-deftest ox-html/postamble-format-author () "Test a html-postamble-format option containing the author" (org-test-with-temp-text "Test, hi" (let ((export-buffer "*Test HTML Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'html export-buffer nil nil nil nil '(:html-postamble-format (("en" "Author=%a")) :html-postamble t :author "Monsieur Oeuf")) (with-current-buffer export-buffer (should (= 0 (how-many "Validate"))) (should (= 0 (how-many "Created: "))) (should (= 1 (how-many "Author=Monsieur Oeuf"))))))) (provide 'test-ox-html) ;;; test-ox-html.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ox-icalendar.el000066400000000000000000000130131500430433700225400ustar00rootroot00000000000000;;; test-ox-icalendar.el --- tests for ox-icalendar.el -*- lexical-binding: t; -*- ;; Copyright (C) 2023 Jack Kamm ;; Author: Jack Kamm ;; 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 . ;;; Commentary: ;; Tests checking validity of Org iCalendar export output. ;;; Code: (require 'ox-icalendar) (ert-deftest test-ox-icalendar/crlf-endings () "Test every line of iCalendar export has CRLF ending." (let ((tmp-ics (org-test-with-temp-text-in-file "* Test event :PROPERTIES: :ID: b17d8f92-1beb-442e-be4d-d2060fa3c7ff :END: <2023-03-30 Thu>" (expand-file-name (org-icalendar-export-to-ics))))) (unwind-protect (with-temp-buffer (insert-file-contents tmp-ics) (should (eql 1 (coding-system-eol-type last-coding-system-used)))) (when (file-exists-p tmp-ics) (delete-file tmp-ics))))) (ert-deftest test-ox-icalendar/todo-repeater-shared () "Test shared repeater on todo scheduled and deadline." (let* ((org-icalendar-include-todo 'all) (tmp-ics (org-test-with-temp-text-in-file "* TODO Both repeating DEADLINE: <2023-04-02 Sun +1m> SCHEDULED: <2023-03-26 Sun +1m>" (expand-file-name (org-icalendar-export-to-ics))))) (unwind-protect (with-temp-buffer (insert-file-contents tmp-ics) (save-excursion (should (search-forward "DTSTART;VALUE=DATE:20230326"))) (save-excursion (should (search-forward "DUE;VALUE=DATE:20230402"))) (save-excursion (should (search-forward "RRULE:FREQ=MONTHLY;INTERVAL=1")))) (when (file-exists-p tmp-ics) (delete-file tmp-ics))))) (ert-deftest test-ox-icalendar/todo-repeating-deadline-warndays () "Test repeating deadline with DTSTART as warning days." (let* ((org-icalendar-include-todo 'all) (org-icalendar-todo-unscheduled-start 'recurring-deadline-warning) (tmp-ics (org-test-with-temp-text-in-file "* TODO Repeating deadline DEADLINE: <2023-04-02 Sun +2w -3d>" (expand-file-name (org-icalendar-export-to-ics))))) (unwind-protect (with-temp-buffer (insert-file-contents tmp-ics) (save-excursion (should (search-forward "DTSTART;VALUE=DATE:20230330"))) (save-excursion (should (search-forward "DUE;VALUE=DATE:20230402"))) (save-excursion (should (search-forward "RRULE:FREQ=WEEKLY;INTERVAL=2")))) (when (file-exists-p tmp-ics) (delete-file tmp-ics))))) (ert-deftest test-ox-icalendar/todo-repeater-until () "Test repeater on todo scheduled until deadline." (let* ((org-icalendar-include-todo 'all) (tmp-ics (org-test-with-temp-text-in-file "* TODO Repeating scheduled with nonrepeating deadline DEADLINE: <2023-05-01 Mon> SCHEDULED: <2023-03-26 Sun +3d>" (expand-file-name (org-icalendar-export-to-ics))))) (unwind-protect (with-temp-buffer (insert-file-contents tmp-ics) (save-excursion (should (search-forward "DTSTART;VALUE=DATE:20230326"))) (save-excursion (should (not (re-search-forward "^DUE" nil t)))) (save-excursion (should (search-forward "RRULE:FREQ=DAILY;INTERVAL=3;UNTIL=20230501")))) (when (file-exists-p tmp-ics) (delete-file tmp-ics))))) (ert-deftest test-ox-icalendar/todo-repeater-until-utc () "Test that UNTIL is in UTC when DTSTART is not in local time format." (let* ((org-icalendar-include-todo 'all) (org-icalendar-date-time-format ":%Y%m%dT%H%M%SZ") (tmp-ics (org-test-with-temp-text-in-file "* TODO Repeating scheduled with nonrepeating deadline DEADLINE: <2023-05-02 Tue> SCHEDULED: <2023-03-26 Sun 15:00 +3d>" (expand-file-name (org-icalendar-export-to-ics))))) (unwind-protect (with-temp-buffer (insert-file-contents tmp-ics) (save-excursion (should (re-search-forward "DTSTART:2023032.T..0000"))) (save-excursion (should (not (re-search-forward "^DUE" nil t)))) (save-excursion (should (re-search-forward "RRULE:FREQ=DAILY;INTERVAL=3;UNTIL=2023050.T..0000Z")))) (when (file-exists-p tmp-ics) (delete-file tmp-ics))))) (ert-deftest test-ox-icalendar/warn-unsupported-repeater () "Test warning is emitted for unsupported repeater type." (let ((org-icalendar-include-todo 'all)) (should (member "Repeater-type restart not currently supported by iCalendar export" (org-test-capture-warnings (let ((tmp-ics (org-test-with-temp-text-in-file "* TODO Unsupported restart repeater SCHEDULED: <2023-03-26 Sun .+1m>" (expand-file-name (org-icalendar-export-to-ics))))) (when (file-exists-p tmp-ics) (delete-file tmp-ics)))))))) (provide 'test-ox-icalendar) ;;; test-ox-icalendar.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ox-latex.el000066400000000000000000000063541500430433700217450ustar00rootroot00000000000000;;; test-ox-latex.el --- tests for ox-latex.el -*- lexical-binding: t; -*- ;; Copyright (C) 2022 Ihor Radchenko ;; Author: Ihor Radchenko ;; 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 . ;;; Commentary: ;; Tests checking validity of Org LaTeX export output. ;;; Code: (require 'ox-latex nil t) (unless (featurep 'ox-latex) (signal 'missing-test-dependency "org-export-latex")) (ert-deftest text-ox-latex/protect-square-brackets () "Test [foo] being interpreted as plain text even after LaTeX commands." (org-test-with-exported-text 'latex "* This is test lorem @@latex:\\pagebreak@@ [ipsum] #+begin_figure [lorem] figure #+end_figure | [foo] | 2 | | [bar] | 3 | - [bax] - [aur] " (goto-char (point-min)) (should (search-forward "lorem \\pagebreak {[}ipsum]")) (should (search-forward "{[}lorem] figure")) (should (search-forward "{[}foo]")) (should (search-forward "\\item {[}bax]")))) (ert-deftest test-ox-latex/verse () "Test verse blocks." (org-test-with-exported-text 'latex "#+begin_verse lorem ipsum dolor lorem ipsum dolor lorem ipsum dolor lorem ipsum dolor lorem ipsum dolor lorem ipsum dolor #+end_verse " (goto-char (point-min)) (should (search-forward "\\begin{verse} lorem ipsum dolor\\\\ lorem ipsum dolor lorem ipsum dolor\\\\ lorem ipsum dolor lorem ipsum dolor\\\\ lorem ipsum dolor\\\\ \\end{verse}")))) (ert-deftest test-ox-latex/longtable () "Test table export with longtable environment." (org-test-with-exported-text 'latex "#+attr_latex: :environment longtable | First | Second | | Column | Column | |--------------+--------| | a | 1 | | b | 2 | | \\pagebreak c | 3 | | d | 4 | " (goto-char (point-min)) (should (search-forward "\\begin{longtable}{lr} First & Second\\\\ Column & Column\\\\ \\hline \\endfirsthead")) (goto-char (point-min)) (should (search-forward "First & Second\\\\ Column & Column \\\\ \\hline \\endhead")) (goto-char (point-min)) (should (search-forward "\\hline\\multicolumn{2}{r}{Continued on next page} \\\\ \\endfoot")))) (ert-deftest test-ox-latex/inline-image () "Test inline images." (org-test-with-exported-text 'latex "#+caption: Schematic [[https://orgmode.org/worg/images/orgmode/org-mode-unicorn.svg][file:/wallpaper.png]]" (goto-char (point-min)) (should (search-forward "\\href{https://orgmode.org/worg/images/orgmode/org-mode-unicorn.svg}{\\includegraphics[width=.9\\linewidth]{/wallpaper.png}}")))) (provide 'test-ox-latex) ;;; test-ox-latex.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ox-md.el000066400000000000000000000074021500430433700212230ustar00rootroot00000000000000;;; test-ox-md.el --- Tests from ox-md.el -*- lexical-binding: t; -*- ;; Copyright (C) 2023 Ihor Radchenko ;; Author: Ihor Radchenko ;; 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 . ;;; Code: (require 'ox-md) (ert-deftest ox-md/footnotes-level () "Test `org-md-toplevel-hlevel' being honored by footnote section." (org-test-with-temp-text " ** level 1 Post starts here. [fn:1] *** level2 lorem ipsum ** Footnotes [fn:1] a footnote " (let ((org-md-toplevel-hlevel 4) (export-buffer "*Test MD Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'md export-buffer) (with-current-buffer export-buffer (goto-char (point-min)) (should (search-forward "#### Footnotes")))))) (ert-deftest ox-md/headline-style () "Test `org-md-headline-style' being honored." (dolist (org-md-headline-style '(atx setext mixed)) (let ((export-buffer "*Test MD Export*") (org-export-show-temporary-export-buffer nil)) (org-test-with-temp-text "#+options: toc:nil h:10 * level 1 ** level 2 *** level 3 **** level 4 ***** level 5 ****** level 6 ******* level 7 " (org-export-to-buffer 'md export-buffer) (with-current-buffer export-buffer (goto-char (point-min)) (pcase org-md-headline-style (`atx (should (search-forward "# level 1")) (should (search-forward "## level 2")) (should (search-forward "### level 3")) (should (search-forward "#### level 4")) (should (search-forward "##### level 5")) (should (search-forward "###### level 6")) (should (search-forward "1. level 7"))) (`setext (should (search-forward "level 1\n=======")) (should (search-forward "level 2\n------")) (should (search-forward "1. level 3")) (should (search-forward "1. level 4")) (should (search-forward "1. level 5")) (should (search-forward "1. level 6")) (should (search-forward "1. level 7"))) (`mixed (should (search-forward "level 1\n=======")) (should (search-forward "level 2\n------")) (should (search-forward "### level 3")) (should (search-forward "#### level 4")) (should (search-forward "##### level 5")) (should (search-forward "###### level 6")) (should (search-forward "1. level 7"))))))))) (ert-deftest ox-md/item () "Test `org-md-item'." ;; Align items at column 4. ;; Columns >=100 not aligned. (org-test-with-temp-text (mapconcat #'identity (cl-loop for n from 1 to 105 collect (format "%d. item" n)) "\n") (let ((export-buffer "*Test MD Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'md export-buffer) (with-current-buffer export-buffer (goto-char (point-min)) (should (search-forward "1. item")) (should (search-forward "10. item")) (should (search-forward "101. item")))))) (provide 'test-ox-md) ;;; test-ox-md.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ox-publish.el000066400000000000000000000471651500430433700223030ustar00rootroot00000000000000;;; test-ox-publish.el --- Tests for "ox-publish.el" -*- lexical-binding: t; -*- ;; Copyright (C) 2016, 2019 Nicolas Goaziou ;; Author: Nicolas Goaziou ;; 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 . ;;; Code: (require 'org-test "../testing/org-test") (require 'ox-publish) ;;; Helper functions (defun org-test-publish (properties handler &optional remove-prop) "Publish a project defined by PROPERTIES. Call HANDLER with the publishing directory as its sole argument. Unless set otherwise in PROPERTIES, `:base-directory' is set to \"examples/pub/\" sub-directory from test directory and `:publishing-function' is set to `org-publish-attachment'. Because `org-publish-property' uses `plist-member' to check the existence of a property, a property with a value nil is different from a non-existing property. Properties in REMOVE-PROP will be removed from the final plist." (declare (indent 1)) (let* ((org-publish-use-timestamps-flag nil) (org-publish-cache nil) (base-dir (expand-file-name "examples/pub/" org-test-dir)) (pub-dir (make-temp-file "org-test" t)) (org-publish-timestamp-directory (expand-file-name ".org-timestamps/" pub-dir)) (props (org-plist-delete-all (org-combine-plists `(:base-directory ,base-dir :publishing-function org-publish-attachment) properties `(:publishing-directory ,pub-dir)) remove-prop)) (project `("test" ,@props))) (unwind-protect (progn (org-publish-projects (list project)) (funcall handler pub-dir)) ;; Clear published data. (delete-directory pub-dir t) ;; Delete auto-generated site-map file, if applicable. (let ((site-map (and (plist-get properties :auto-sitemap) (expand-file-name (or (plist-get properties :sitemap-filename) "sitemap.org") base-dir)))) (when (and site-map (file-exists-p site-map)) (delete-file site-map)))))) ;;; Mandatory properties (ert-deftest test-org-publish/base-extension () "Test `:base-extension' specifications" ;; Regular tests. (should (equal '("a.org" "b.org") (org-test-publish '(:base-extension "org") (lambda (dir) (remove ".org-timestamps" (cl-remove-if #'file-directory-p (directory-files dir))))))) (should (equal '("file.txt") (org-test-publish '(:base-extension "txt") (lambda (dir) (remove ".org-timestamps" (cl-remove-if #'file-directory-p (directory-files dir))))))) ;; A nil value is equivalent to ".org". (should (equal '("a.org" "b.org") (org-test-publish '(:base-extension nil) (lambda (dir) (remove ".org-timestamps" (cl-remove-if #'file-directory-p (directory-files dir))))))) ;; Symbol `any' includes all files, even those without extension. (should (equal '("a.org" "b.org" "file.txt" "noextension") (org-test-publish '(:base-extension any) (lambda (dir) (remove ".org-timestamps" (cl-remove-if #'file-directory-p (directory-files dir))))))) ;; Check the default transformation function, ;; org-html-publish-to-html. Because org-test-publish uses ;; org-publish-attachment by default, we must not just override with ;; nil but tell it to remove the :publishing-function from the list. (should (let ((func (lambda (dir) (with-temp-buffer (insert-file-contents (expand-file-name "a.html" dir)) (buffer-string))))) (equal (org-test-publish nil func '(:publishing-function)) (org-test-publish '(:publishing-function org-html-publish-to-html) func))))) ;;; Site-map (ert-deftest test-org-publish/sitemap () "Test site-map specifications." ;; Site-map creation is controlled with `:auto-sitemap'. It ;; defaults to "sitemap.org". (should (org-test-publish '(:auto-sitemap t) (lambda (dir) (file-exists-p (expand-file-name "sitemap.org" dir))))) (should-not (org-test-publish '(:auto-sitemap nil) (lambda (dir) (file-exists-p (expand-file-name "sitemap.org" dir))))) ;; Site-map file name is controlled with `:sitemap-filename'. (should (org-test-publish '(:auto-sitemap t :sitemap-filename "mysitemap.org") (lambda (dir) (file-exists-p (expand-file-name "mysitemap.org" dir))))) ;; Site-map title is controlled with `:sitemap-title'. It defaults ;; to the project name. (should (equal "#+TITLE: Sitemap for project test" (org-test-publish '(:auto-sitemap t) (lambda (dir) (with-temp-buffer (insert-file-contents (expand-file-name "sitemap.org" dir)) (buffer-substring (point) (line-end-position))))))) (should (equal "#+TITLE: My title" (org-test-publish '(:auto-sitemap t :sitemap-title "My title") (lambda (dir) (with-temp-buffer (insert-file-contents (expand-file-name "sitemap.org" dir)) (buffer-substring (point) (line-end-position))))))) ;; Allowed site-map styles: `list' and `tree'. (should (equal " - [[file:a.org][A]] - [[file:b.org][b]] - [[file:sub/c.org][C]]" (org-test-publish '(:auto-sitemap t :sitemap-sort-folders ignore :sitemap-style list :exclude "." :include ("a.org" "b.org" "sub/c.org")) (lambda (dir) (with-temp-buffer (insert-file-contents (expand-file-name "sitemap.org" dir)) (buffer-substring (line-beginning-position 2) (point-max))))))) (should (equal " - [[file:a.org][A]] - [[file:b.org][b]] - sub - [[file:sub/c.org][C]]" (org-test-publish '(:auto-sitemap t :sitemap-style tree :exclude "." :include ("a.org" "b.org" "sub/c.org")) (lambda (dir) (with-temp-buffer (insert-file-contents (expand-file-name "sitemap.org" dir)) (buffer-substring (line-beginning-position 2) (point-max))))))) ;; When style is `list', `:sitemap-sort-folders' controls the order ;; of appearance of directories among published files. (should (equal " - sub/ - [[file:a.org][A]] - [[file:sub/c.org][C]]" (org-test-publish '(:auto-sitemap t :recursive t :sitemap-style list :sitemap-sort-folders first :exclude "." :include ("a.org" "sub/c.org")) (lambda (dir) (with-temp-buffer (insert-file-contents (expand-file-name "sitemap.org" dir)) (buffer-substring (line-beginning-position 2) (point-max))))))) (should (equal " - [[file:a.org][A]] - [[file:sub/c.org][C]] - sub/" (org-test-publish '(:auto-sitemap t :recursive t :sitemap-style list :sitemap-sort-folders last :exclude "." :include ("a.org" "sub/c.org")) (lambda (dir) (with-temp-buffer (insert-file-contents (expand-file-name "sitemap.org" dir)) (buffer-substring (line-beginning-position 2) (point-max))))))) ;; When style is `list', `:sitemap-sort-folders' can be used to ;; toggle visibility of directories in the site-map. (should (let ((case-fold-search t)) (string-match-p "- sub/$" (org-test-publish '(:auto-sitemap t :recursive t :sitemap-style list :sitemap-sort-folders t :exclude "." :include ("a.org" "sub/c.org")) (lambda (dir) (with-temp-buffer (insert-file-contents (expand-file-name "sitemap.org" dir)) (buffer-substring (line-beginning-position 2) (point-max)))))))) (should-not (string-match-p "- sub/$" (org-test-publish '(:auto-sitemap t :recursive t :sitemap-style list :sitemap-sort-folders ignore :exclude "." :include ("a.org" "sub/c.org")) (lambda (dir) (with-temp-buffer (insert-file-contents (expand-file-name "sitemap.org" dir)) (buffer-substring (line-beginning-position 2) (point-max))))))) ;; Using `:sitemap-sort-files', files can be sorted alphabetically ;; (according to their title, or file name when there is none), ;; chronologically a anti-chronologically. (should (equal " - [[file:a.org][A]] - [[file:b.org][b]] - [[file:sub/c.org][C]]" (org-test-publish '(:auto-sitemap t :recursive t :sitemap-style list :sitemap-sort-folders ignore :sitemap-sort-files alphabetically :exclude "." :include ("a.org" "b.org" "sub/c.org")) (lambda (dir) (with-temp-buffer (insert-file-contents (expand-file-name "sitemap.org" dir)) (buffer-substring (line-beginning-position 2) (point-max))))))) (should (equal " - [[file:b.org][b]] - [[file:sub/c.org][C]] - [[file:a.org][A]]" (org-test-publish '(:auto-sitemap t :recursive t :sitemap-style list :sitemap-sort-folders ignore :sitemap-sort-files chronologically :exclude "." :include ("a.org" "b.org" "sub/c.org")) (lambda (dir) (with-temp-buffer (insert-file-contents (expand-file-name "sitemap.org" dir)) (buffer-substring (line-beginning-position 2) (point-max))))))) (should (equal " - [[file:a.org][A]] - [[file:sub/c.org][C]] - [[file:b.org][b]]" (org-test-publish '(:auto-sitemap t :recursive t :sitemap-style list :sitemap-sort-folders ignore :sitemap-sort-files anti-chronologically :exclude "." :include ("a.org" "b.org" "sub/c.org")) (lambda (dir) (with-temp-buffer (insert-file-contents (expand-file-name "sitemap.org" dir)) (buffer-substring (line-beginning-position 2) (point-max))))))) ;; `:sitemap-format-entry' formats entries in the site-map whereas ;; `:sitemap-function' controls the full site-map. (should (equal " - a.org" (org-test-publish '(:auto-sitemap t :exclude "." :include ("a.org") :sitemap-format-entry (lambda (f _s _p) f)) (lambda (dir) (with-temp-buffer (insert-file-contents (expand-file-name "sitemap.org" dir)) (buffer-substring (line-beginning-position 2) (point-max))))))) (should (equal "Custom!" (org-test-publish '(:auto-sitemap t :exclude "." :include ("a.org") :sitemap-function (lambda (_title _f) "Custom!")) (lambda (dir) (with-temp-buffer (insert-file-contents (expand-file-name "sitemap.org" dir)) (buffer-string)))))) (should (equal "[[file:a.org][A]]" (org-test-publish '(:auto-sitemap t :exclude "." :include ("a.org") :sitemap-function (lambda (_title f) (org-list-to-generic f nil))) (lambda (dir) (with-temp-buffer (insert-file-contents (expand-file-name "sitemap.org" dir)) (buffer-string))))))) ;;; Cross references (ert-deftest test-org-publish/resolve-external-link () "Test `org-publish-resolve-external-link' specifications." ;; Function should preserve internal reference when used between ;; published files. (should (apply #'equal (let* (;; (ids nil) (backend (org-export-create-backend :transcoders `((headline . ,(lambda (h c i) (concat (org-export-get-reference h i) " " c))) (paragraph . ,(lambda (_p c _i) c)) (section . ,(lambda (_s c _i) c)) (link . ,(lambda (l _c _i) (let ((option (org-element-property :search-option l)) (path (org-element-property :path l))) (and option (org-publish-resolve-external-link option path)))))))) (publish (lambda (plist filename pub-dir) (org-publish-org-to backend filename ".test" plist pub-dir)))) (org-test-publish (list :publishing-function (list publish)) (lambda (dir) (cl-subseq (split-string (mapconcat (lambda (f) (org-file-contents (expand-file-name f dir))) (directory-files dir nil "\\.test\\'") " ")) 1 3)))))) ;; When optional argument PREFER-CUSTOM is non-nil, use custom ID ;; instead of internal reference, whenever possible. (should (equal '("a1" "b1") (let* ((ids nil) (link-transcoder (lambda (l _c _i) (let ((option (org-element-property :search-option l)) (path (org-element-property :path l))) (push (org-publish-resolve-external-link option path t) ids) ""))) (backend (org-export-create-backend :transcoders `((headline . (lambda (h c i) c)) (paragraph . (lambda (p c i) c)) (section . (lambda (s c i) c)) (link . ,link-transcoder)))) (publish (lambda (plist filename pub-dir) (org-publish-org-to backend filename ".test" plist pub-dir)))) (org-test-publish (list :publishing-function (list publish) :exclude "." :include '("a.org" "b.org")) #'ignore) (sort ids #'string<))))) ;;; Tools (ert-deftest test-org-publish/get-project-from-filename () "Test `org-publish-get-project-from-filename' specifications." ;; Check base directory. (should (let* ((base (expand-file-name "examples/pub/" org-test-dir)) (file (expand-file-name "a.org" base)) (org-publish-project-alist `(("p" :base-directory ,base)))) (org-publish-get-project-from-filename file))) ;; Return nil if no appropriate project is found. (should-not (let* ((base (expand-file-name "examples/pub/" org-test-dir)) ;; (file (expand-file-name "a.org" base)) (org-publish-project-alist `(("p" :base-directory ,base)))) (org-publish-get-project-from-filename "/other/file.org"))) ;; Return the first project effectively publishing the provided ;; file. (should (equal "p2" (let* ((base (expand-file-name "examples/pub/" org-test-dir)) (file (expand-file-name "a.org" base)) (org-publish-project-alist `(("p1" :base-directory "/other/") ("p2" :base-directory ,base) ("p3" :base-directory ,base)))) (car (org-publish-get-project-from-filename file))))) ;; When :recursive in non-nil, allow files in sub-directories. (should (let* ((base (expand-file-name "examples/pub/" org-test-dir)) (file (expand-file-name "sub/c.org" base)) (org-publish-project-alist `(("p" :base-directory ,base :recursive t)))) (org-publish-get-project-from-filename file))) (should-not (let* ((base (expand-file-name "examples/pub/" org-test-dir)) (file (expand-file-name "sub/c.org" base)) (org-publish-project-alist `(("p" :base-directory ,base :recursive nil)))) (org-publish-get-project-from-filename file))) ;; Also, when :recursive is non-nil, follow symlinks to directories. (should (let* ((base (expand-file-name "examples/pub/" org-test-dir)) (file (expand-file-name "link/link.org" base)) (org-publish-project-alist `(("p" :base-directory ,base :recursive t)))) (org-publish-get-project-from-filename file))) (should-not (let* ((base (expand-file-name "examples/pub/" org-test-dir)) (file (expand-file-name "link/link.org" base)) (org-publish-project-alist `(("p" :base-directory ,base :recursive nil)))) (org-publish-get-project-from-filename file))) ;; Check :base-extension. (should (let* ((base (expand-file-name "examples/pub/" org-test-dir)) (file (expand-file-name "file.txt" base)) (org-publish-project-alist `(("p" :base-directory ,base :base-extension "txt")))) (org-publish-get-project-from-filename file))) (should-not (let* ((base (expand-file-name "examples/pub/" org-test-dir)) (file (expand-file-name "file.txt" base)) (org-publish-project-alist `(("p" :base-directory ,base :base-extension "org")))) (org-publish-get-project-from-filename file))) ;; When :base-extension has the special value `any', allow any ;; extension, including none. (should (let* ((base (expand-file-name "examples/pub/" org-test-dir)) (file (expand-file-name "file.txt" base)) (org-publish-project-alist `(("p" :base-directory ,base :base-extension any)))) (org-publish-get-project-from-filename file))) (should (let* ((base (expand-file-name "examples/pub/" org-test-dir)) (file (expand-file-name "noextension" base)) (org-publish-project-alist `(("p" :base-directory ,base :base-extension any)))) (org-publish-get-project-from-filename file))) ;; Pathological case: Handle both :extension any and :recursive t. (should (let* ((base (expand-file-name "examples/pub/" org-test-dir)) (file (expand-file-name "sub/c.org" base)) (org-publish-project-alist `(("p" :base-directory ,base :recursive t :base-extension any)))) (org-publish-get-base-files (org-publish-get-project-from-filename file)))) ;; Check :exclude property. (should-not (let* ((base (expand-file-name "examples/pub/" org-test-dir)) (file (expand-file-name "a.org" base)) (org-publish-project-alist `(("p" :base-directory ,base :exclude "a")))) (org-publish-get-project-from-filename file))) (should (let* ((base (expand-file-name "examples/pub/" org-test-dir)) (file (expand-file-name "a.org" base)) (org-publish-project-alist `(("p" :base-directory ,base :exclude "other")))) (org-publish-get-project-from-filename file))) ;; The regexp matches against relative file name, not absolute one. (should (let* ((base (expand-file-name "examples/pub/" org-test-dir)) (file (expand-file-name "a.org" base)) (org-publish-project-alist `(("p" :base-directory ,base :exclude "examples/pub")))) (org-publish-get-project-from-filename file))) ;; Check :include property. (should (let* ((base (expand-file-name "examples/pub/" org-test-dir)) (file (expand-file-name "file.txt" base)) (org-publish-project-alist `(("p" :base-directory ,base :include (,file))))) (org-publish-get-project-from-filename file))) ;; :include property has precedence over :exclude one. (should (let* ((base (expand-file-name "examples/pub/" org-test-dir)) (file (expand-file-name "a.org" base)) (org-publish-project-alist `(("p" :base-directory ,base :include (,(file-name-nondirectory file)) :exclude "a")))) (org-publish-get-project-from-filename file))) ;; With optional argument, return a meta-project publishing provided ;; file. (should (equal "meta" (let* ((base (expand-file-name "examples/pub/" org-test-dir)) (file (expand-file-name "a.org" base)) (org-publish-project-alist `(("meta" :components ("p")) ("p" :base-directory ,base)))) (car (org-publish-get-project-from-filename file t)))))) (ert-deftest test-org-publish/file-relative-name () "Test `org-publish-file-relative-name' specifications." ;; Turn absolute file names into relative ones if file belongs to ;; base directory. (should (equal "a.org" (let* ((base (expand-file-name "examples/pub/" org-test-dir)) (file (expand-file-name "a.org" base))) (org-publish-file-relative-name file `(:base-directory ,base))))) (should (equal "pub/a.org" (let* ((base (expand-file-name "examples/" org-test-dir)) (file (expand-file-name "pub/a.org" base))) (org-publish-file-relative-name file `(:base-directory ,base))))) ;; Absolute file names that do not belong to base directory are ;; unchanged. (should (equal "/name.org" (let ((base (expand-file-name "examples/pub/" org-test-dir))) (org-publish-file-relative-name "/name.org" `(:base-directory ,base))))) ;; Relative file names are unchanged. (should (equal "a.org" (let ((base (expand-file-name "examples/pub/" org-test-dir))) (org-publish-file-relative-name "a.org" `(:base-directory ,base)))))) (provide 'test-ox-publish) ;;; test-ox-publish.el ends here org-mode-9.7.29+dfsg/testing/lisp/test-ox-texinfo.el000066400000000000000000000271571500430433700223100ustar00rootroot00000000000000;;; test-ox-texinfo.el --- Tests for ox-texinfo.el -*- lexical-binding: t; -*- ;; Copyright (C) 2022 Rudolf Adamkovič ;; Author: Rudolf Adamkovič ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (require 'cl-lib) (require 'ox-texinfo) (eval-when-compile (require 'subr-x)) (unless (featurep 'ox-texinfo) (signal 'missing-test-dependency "org-export-texinfo")) ;;; TeX fragments (ert-deftest test-ox-texinfo/tex-fragment-inline () "Test inline TeX fragment." (should (equal "@math{a^2 = b}" (let ((org-texinfo-with-latex t)) (org-texinfo-latex-fragment (org-element-create 'latex-fragment '(:value "$a^2 = b$")) nil '(:with-latex t)))))) (ert-deftest test-ox-texinfo/tex-fragment-inline-padded () "Test inline TeX fragment padded with whitespace." (should (equal "@math{a^2 = b}" (let ((org-texinfo-with-latex t)) (org-texinfo-latex-fragment (org-element-create 'latex-fragment '(:value "$ a^2 = b $")) nil '(:with-latex t)))))) (ert-deftest test-ox-texinfo/tex-fragment-displayed () "Test displayed TeX fragment." (should (equal (string-join (list "" "@displaymath" "a ^ 2 = b" "@end displaymath" "") "\n") (let ((org-texinfo-with-latex t)) (org-texinfo-latex-fragment (org-element-create 'latex-fragment (list :value "$$a ^ 2 = b$$")) nil '(:with-latex t)))))) (ert-deftest test-ox-texinfo/tex-fragment-displayed-padded () "Test displayed TeX fragment padded with whitespace." (should (equal (string-join (list "" "@displaymath" "a ^ 2 = b" "@end displaymath" "") "\n") (let ((org-texinfo-with-latex t)) (org-texinfo-latex-fragment (org-element-create 'latex-fragment (list :value "$$ a ^ 2 = b $$")) nil '(:with-latex t)))))) (ert-deftest test-ox-texinfo/tex-fragment-displayed-multi-line () "Test displayed TeX fragment with multiple lines." (should (equal (string-join (list "" "@displaymath" "a ^ 2 = b" "b ^ 2 = c" "@end displaymath" "") "\n") (let ((org-texinfo-with-latex t)) (org-texinfo-latex-fragment (org-element-create 'latex-fragment (list :value (string-join (list "$$" "a ^ 2 = b" "b ^ 2 = c" "$$") "\n"))) nil '(:with-latex t)))))) (ert-deftest test-ox-texinfo/tex-fragment-displayed-indented () "Test displayed TeX fragment with indentation." (should (equal (string-join (list "" "@displaymath" "a ^ 2 = b" "b ^ 2 = c" "@end displaymath" "") "\n") (let ((org-texinfo-with-latex t)) (org-texinfo-latex-fragment (org-element-create 'latex-fragment (list :value (string-join (list " $$" " a ^ 2 = b" " b ^ 2 = c" " $$") "\n"))) nil '(:with-latex t)))))) ;;; LaTeX fragments (ert-deftest test-ox-texinfo/latex-fragment-inline () "Test inline LaTeX fragment." (should (equal "@math{a^2 = b}" (let ((org-texinfo-with-latex t)) (org-texinfo-latex-fragment (org-element-create 'latex-fragment '(:value "\\(a^2 = b\\)")) nil '(:with-latex t)))))) (ert-deftest test-ox-texinfo/latex-fragment-inline-padded () "Test inline LaTeX fragment padded with whitespace." (should (equal "@math{a^2 = b}" (let ((org-texinfo-with-latex t)) (org-texinfo-latex-fragment (org-element-create 'latex-fragment '(:value "\\( a^2 = b \\)")) nil '(:with-latex t)))))) (ert-deftest test-ox-texinfo/latex-fragment-displayed () "Test displayed LaTeX fragment." (should (equal (string-join (list "" "@displaymath" "a ^ 2 = b" "@end displaymath" "") "\n") (let ((org-texinfo-with-latex t)) (org-texinfo-latex-fragment (org-element-create 'latex-fragment (list :value "\\[a ^ 2 = b\\]")) nil '(:with-latex t)))))) (ert-deftest test-ox-texinfo/latex-fragment-displayed-padded () "Test displayed LaTeX fragment with multiple lines." (should (equal (string-join (list "" "@displaymath" "a ^ 2 = b" "@end displaymath" "") "\n") (let ((org-texinfo-with-latex t)) (org-texinfo-latex-fragment (org-element-create 'latex-fragment (list :value "\\[ a ^ 2 = b \\]")) nil '(:with-latex t)))))) (ert-deftest test-ox-texinfo/latex-fragment-displayed-multi-line () "Test displayed LaTeX fragment with multiple lines." (should (equal (string-join (list "" "@displaymath" "a ^ 2 = b" "b ^ 2 = c" "@end displaymath" "") "\n") (let ((org-texinfo-with-latex t)) (org-texinfo-latex-fragment (org-element-create 'latex-fragment (list :value (string-join (list "\\[" "a ^ 2 = b" "b ^ 2 = c" "\\]") "\n"))) nil '(:with-latex t)))))) (ert-deftest test-ox-texinfo/latex-fragment-displayed-indented () "Test displayed LaTeX fragment with indentation." (should (equal (string-join (list "" "@displaymath" "a ^ 2 = b" "b ^ 2 = c" "@end displaymath" "") "\n") (let ((org-texinfo-with-latex t)) (org-texinfo-latex-fragment (org-element-create 'latex-fragment (list :value (string-join (list " \\[" " a ^ 2 = b" " b ^ 2 = c" " \\]") "\n"))) nil '(:with-latex t)))))) ;;; LaTeX environments (ert-deftest test-ox-texinfo/latex-environment () "Test LaTeX environment." (should (equal (string-join (list "@displaymath" "\\begin{equation}" "a ^ 2 = b" "b ^ 2 = c" "\\end{equation}" "@end displaymath") "\n") (let ((org-texinfo-with-latex t)) (org-texinfo-latex-environment (org-element-create 'latex-environment (list :value (string-join (list "\\begin{equation}" "a ^ 2 = b" "b ^ 2 = c" "\\end{equation}") "\n"))) nil '(:with-latex t)))))) (ert-deftest test-ox-texinfo/latex-environment-indented () "Test LaTeX environment with indentation." (should (equal (string-join (list "@displaymath" "\\begin{equation}" "a ^ 2 = b" "b ^ 2 = c" "\\end{equation}" "@end displaymath") "\n") (let ((org-texinfo-with-latex t)) (org-texinfo-latex-environment (org-element-create 'latex-environment (list :value (string-join (list " \\begin{equation}" " a ^ 2 = b" " b ^ 2 = c" " \\end{equation}") "\n"))) nil '(:with-latex t)))))) ;;; End-to-end (ert-deftest test-ox-texinfo/end-to-end-inline () "Test end-to-end with inline TeX fragment." (should (org-test-with-temp-text "$a^2 = b$" (let ((export-buffer "*Test Texinfo Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'texinfo export-buffer nil nil nil nil nil #'texinfo-mode))))) (ert-deftest test-ox-texinfo/end-to-end-sanity-check-displayed () "Test end-to-end with LaTeX environment." (should (org-test-with-temp-text (string-join (list "\\begin{equation}" "a ^ 2 = b" "b ^ 2 = c" "\\end{equation}") "\n") (let ((export-buffer "*Test Texinfo Export*") (org-export-show-temporary-export-buffer nil)) (org-export-to-buffer 'texinfo export-buffer nil nil nil nil nil #'texinfo-mode))))) ;;; Filters (ert-deftest test-ox-texinfo/normalize-headlines () "Test adding empty sections to headlines without one." (org-test-with-temp-text "* only subsections, no direct content ** sub 1 body ** sub 2 body " (let ((tree (org-element-parse-buffer))) (setq tree (org-texinfo--normalize-headlines tree nil nil)) (let* ((first-heading (car (org-element-contents tree))) (section (car (org-element-contents first-heading)))) (should (org-element-type-p first-heading 'headline)) (should (org-element-type-p section 'section)) (should-not (org-element-contents section)) (should (eq first-heading (org-element-parent section))))))) (provide 'test-ox-texinfo) ;;; test-ox-texinfo.el end here org-mode-9.7.29+dfsg/testing/lisp/test-ox.el000066400000000000000000005351531500430433700206360ustar00rootroot00000000000000;;; test-ox.el --- Tests for ox.el -*- lexical-binding: t; -*- ;; Copyright (C) 2012-2016, 2019 Nicolas Goaziou ;; Author: Nicolas Goaziou ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (require 'cl-lib) (require 'ox) (require 'org-inlinetask) (defun org-test-default-backend () "Return a default export backend. This backend simply returns parsed data as Org syntax." (org-export-create-backend :transcoders (mapcar (lambda (type) (cons type (lambda (o c _) (funcall (intern (format "org-element-%s-interpreter" type)) o c)))) (append org-element-all-elements org-element-all-objects)))) (defmacro org-test-with-parsed-data (data &rest body) "Execute body with parsed data available. DATA is a string containing the data to be parsed. BODY is the body to execute. Parse tree is available under the `tree' variable, and communication channel under `info'." (declare (debug (form body)) (indent 1)) `(org-test-with-temp-text ,data (org-export--delete-comment-trees) (let* ((tree (org-element-parse-buffer)) (info (org-combine-plists (org-export--get-export-attributes) (org-export-get-environment)))) (org-export--prune-tree tree info) (org-export--remove-uninterpreted-data tree info) (let ((info (org-combine-plists info (org-export--collect-tree-properties tree info)))) (ignore info) ;; Don't warn if the var is unused. ,@body)))) ;;; Internal Tests (ert-deftest test-org-export/org-export-copy-buffer () "Test `org-export-copy-buffer' specifications." ;; The copy must not overwrite the original file. (org-test-with-temp-text-in-file "* Heading" (let ((file (buffer-file-name))) (with-current-buffer (org-export-copy-buffer) (insert "This must not go into the original file.") (save-buffer) (should (equal "* Heading" (with-temp-buffer (insert-file-contents file) (buffer-string))))))) ;; The copy must not show when re-opening the original file. (org-test-with-temp-text-in-file "* Heading" (let ((file (buffer-file-name)) (buffer-copy (generate-new-buffer " *Org export copy*"))) (org-export-with-buffer-copy :to-buffer buffer-copy (insert "This must not show as the original file.") (save-buffer)) ;; Unassign the original buffer from file. (setq buffer-file-name nil) (should-not (equal buffer-copy (get-file-buffer file)))))) (ert-deftest test-org-export/bind-keyword () "Test reading #+BIND: keywords." ;; Test with `org-export-allow-bind-keywords' set to t. (should (org-test-with-temp-text "#+BIND: test-ox-var value" (let ((org-export-allow-bind-keywords t)) (org-export-get-environment) (eq test-ox-var 'value)))) ;; Test with `org-export-allow-bind-keywords' set to nil. (should-not (org-test-with-temp-text "#+BIND: test-ox-var value" (let ((org-export-allow-bind-keywords nil)) (org-export-get-environment) (boundp 'test-ox-var)))) ;; BIND keywords are case-insensitive. (should (org-test-with-temp-text "#+bind: test-ox-var value" (let ((org-export-allow-bind-keywords t)) (org-export-get-environment) (eq test-ox-var 'value)))) ;; Preserve order of BIND keywords. (should (org-test-with-temp-text "#+BIND: test-ox-var 1\n#+BIND: test-ox-var 2" (let ((org-export-allow-bind-keywords t)) (org-export-get-environment) (eq test-ox-var 2)))) ;; Read BIND keywords in setup files. (should (org-test-with-temp-text (format "#+SETUPFILE: \"%s/examples/setupfile.org\"" org-test-dir) (let ((org-export-allow-bind-keywords t)) (org-export-get-environment) ;; `variable' is bound inside the setupfile. (eq variable 'value)))) ;; Verify that bound variables are seen during export. (should (equal "Yes\n" (org-test-with-temp-text "#+BIND: test-ox-var value" (let ((org-export-allow-bind-keywords t)) (org-export-as (org-export-create-backend :transcoders '((section . (lambda (s c i) (if (eq test-ox-var 'value) "Yes" "No")))))))))) ;; Seen from elisp code blocks as well. (should (string-match-p "::: \"test value\"" (org-test-with-temp-text "#+BIND: test-ox-var \"test value\" #+begin_src emacs-lisp :results value :exports results :eval yes (format \"::: %S\" test-ox-var) #+end_src" (let ((org-export-allow-bind-keywords t)) (org-export-as (org-test-default-backend))))))) (ert-deftest test-org-export/parse-option-keyword () "Test reading all standard #+OPTIONS: items." (should (let ((options (org-export--parse-option-keyword "H:1 num:t \\n:t timestamp:t arch:t author:t creator:t d:t email:t \ *:t e:t ::t f:t pri:t -:t ^:t toc:t |:t tags:t tasks:t <:t todo:t inline:nil \ stat:t title:t"))) (and (eq (plist-get options :headline-levels) 1) (eq (plist-get options :section-numbers) t) (eq (plist-get options :preserve-breaks) t) (eq (plist-get options :time-stamp-file) t) (eq (plist-get options :with-archived-trees) t) (eq (plist-get options :with-author) t) (eq (plist-get options :with-drawers) t) (eq (plist-get options :with-email) t) (eq (plist-get options :with-emphasize) t) (eq (plist-get options :with-entities) t) (eq (plist-get options :with-fixed-width) t) (eq (plist-get options :with-footnotes) t) (eq (plist-get options :with-priority) t) (eq (plist-get options :with-special-strings) t) (eq (plist-get options :with-sub-superscript) t) (eq (plist-get options :with-toc) t) (eq (plist-get options :with-tables) t) (eq (plist-get options :with-tags) t) (eq (plist-get options :with-tasks) t) (eq (plist-get options :with-timestamps) t) (eq (plist-get options :with-todo-keywords) t) (eq (plist-get options :with-inlinetasks) nil) (eq (plist-get options :with-statistics-cookies) t) (eq (plist-get options :with-title) t)))) ;; Test some special values. (should (let ((options (org-export--parse-option-keyword "arch:headline d:(\"TEST\") ^:{} toc:1 tags:not-in-toc tasks:todo \ num:2 <:active"))) (and (eq (plist-get options :with-archived-trees) 'headline) (eq (plist-get options :with-sub-superscript) '{}) (eq (plist-get options :with-toc) 1) (eq (plist-get options :with-tags) 'not-in-toc) (eq (plist-get options :with-tasks) 'todo) (eq (plist-get options :section-numbers) 2) (eq (plist-get options :with-timestamps) 'active) (equal (plist-get options :with-drawers) '("TEST"))))) ;; Test backend specific values. (should (equal (org-export--parse-option-keyword "opt:t" (org-export-create-backend :options '((:option nil "opt")))) '(:option t))) ;; More than one property can refer to the same option item. (should (equal '(:opt1 t :opt2 t) (org-export--parse-option-keyword "opt:t" (org-export-create-backend :options '((:opt1 nil "opt") (:opt2 nil "opt")))))) ;; Ignore options with a missing value. (should (let ((options (org-export--parse-option-keyword "H: num:t"))) (and (not (plist-get options :headline-levels)) (plist-get options :section-numbers)))) ;; Parse spaces inside brackets. (let ((options (org-export--parse-option-keyword "html-postamble:\"test space\"" 'html))) (should (equal "test space" (plist-get options :html-postamble))))) (ert-deftest test-org-export/get-inbuffer-options () "Test reading all standard export keywords." ;; Properties should follow buffer order. (should (equal (org-test-with-temp-text "#+LANGUAGE: fr\n#+CREATOR: Me\n#+EMAIL: email" (org-export--get-inbuffer-options)) '(:language "fr" :creator "Me" :email "email"))) ;; Test `space' behaviour. (should (equal (let ((backend (org-export-create-backend :options '((:keyword "KEYWORD" nil nil space))))) (org-test-with-temp-text "#+KEYWORD: With\n#+KEYWORD: spaces" (org-export--get-inbuffer-options backend))) '(:keyword "With spaces"))) ;; Test `newline' behaviour. (should (equal (let ((backend (org-export-create-backend :options '((:keyword "KEYWORD" nil nil newline))))) (org-test-with-temp-text "#+KEYWORD: With\n#+KEYWORD: two lines" (org-export--get-inbuffer-options backend))) '(:keyword "With\ntwo lines"))) ;; Test `split' behaviour. (should (equal (org-test-with-temp-text "#+SELECT_TAGS: a\n#+SELECT_TAGS: b" (org-export--get-inbuffer-options)) '(:select-tags ("a" "b")))) ;; Test `parse' behaviour. `parse' implies `space' but preserve ;; line breaks. Multi-line objects are allowed. (should (org-element-map (org-test-with-temp-text "#+TITLE: *bold*" (plist-get (org-export--get-inbuffer-options) :title)) 'bold #'identity nil t)) (should (equal (org-test-with-temp-text "#+TITLE: Some title\n#+TITLE: with spaces" (plist-get (org-export--get-inbuffer-options) :title)) '("Some title with spaces"))) (should (org-element-map (org-test-with-temp-text "#+TITLE: Some title\\\\\n#+TITLE: with spaces" (plist-get (org-export--get-inbuffer-options) :title)) 'line-break #'identity nil t)) (should (org-element-map (org-test-with-temp-text "#+TITLE: *bold\n#+TITLE: sentence*" (plist-get (org-export--get-inbuffer-options) :title)) 'bold #'identity nil t)) ;; Options set through SETUPFILE. (should (equal (org-test-with-temp-text (format "#+DESCRIPTION: l1 #+LANGUAGE: es #+SELECT_TAGS: a #+TITLE: a #+SETUPFILE: \"%s/examples/setupfile.org\" #+LANGUAGE: fr #+SELECT_TAGS: c #+TITLE: c" org-test-dir) (org-export--get-inbuffer-options)) '(:language "fr" :select-tags ("a" "b" "c") :title ("a b c")))) ;; Options set through SETUPFILE specified using a URL. (let ((buffer (generate-new-buffer "url-retrieve-output")) (org-resource-download-policy t)) (unwind-protect ;; Simulate successful retrieval of a setupfile from URL. (cl-letf (((symbol-function 'url-retrieve-synchronously) (lambda (&rest_) (with-current-buffer buffer (insert "HTTP/1.1 200 OK # Contents of http://link-to-my-setupfile.org #+BIND: variable value #+DESCRIPTION: l2 #+LANGUAGE: en #+SELECT_TAGS: b #+TITLE: b #+PROPERTY: a 1 ")) buffer))) (should (equal (org-test-with-temp-text "#+DESCRIPTION: l1 #+LANGUAGE: es #+SELECT_TAGS: a #+TITLE: a #+SETUPFILE: \"http://link-to-my-setupfile.org\" #+LANGUAGE: fr #+SELECT_TAGS: c #+TITLE: c" (org-export--get-inbuffer-options)) '(:language "fr" :select-tags ("a" "b" "c") :title ("a b c"))))) (kill-buffer buffer))) ;; More than one property can refer to the same buffer keyword. (should (equal '(:k2 "value" :k1 "value") (let ((backend (org-export-create-backend :options '((:k1 "KEYWORD") (:k2 "KEYWORD"))))) (org-test-with-temp-text "#+KEYWORD: value" (org-export--get-inbuffer-options backend))))) ;; Keywords in commented subtrees are ignored. (should-not (equal "Me" (org-test-with-parsed-data "* COMMENT H1\n#+AUTHOR: Me" (plist-get info :author)))) (should-not (equal "Mine" (org-test-with-parsed-data "* COMMENT H1\n** H2\n#+EMAIL: Mine" (plist-get info :email)))) ;; Keywords can be set to an empty value. (should-not (let ((user-full-name "Me")) (org-test-with-parsed-data "#+AUTHOR:" (plist-get info :author))))) (ert-deftest test-org-export/get-subtree-options () "Test setting options from headline's properties." ;; EXPORT_TITLE. (should (equal '("Subtree Title") (org-test-with-temp-text "#+TITLE: Title * Headline :PROPERTIES: :EXPORT_TITLE: Subtree Title :END: Paragraph" (plist-get (org-export-get-environment nil t) :title)))) ;; EXPORT_OPTIONS. (should (= 2 (org-test-with-temp-text "#+OPTIONS: H:1 * Headline :PROPERTIES: :EXPORT_OPTIONS: H:2 :END: Paragraph" (plist-get (org-export-get-environment nil t) :headline-levels)))) ;; EXPORT_DATE. (should (equal '("29-03-2012") (org-test-with-temp-text "#+DATE: today * Headline :PROPERTIES: :EXPORT_DATE: 29-03-2012 :END: Paragraph" (plist-get (org-export-get-environment nil t) :date)))) ;; Properties with `split' behaviour are stored as a list of ;; strings. (should (equal '("a" "b") (org-test-with-temp-text "#+EXCLUDE_TAGS: noexport * Headline :PROPERTIES: :EXPORT_EXCLUDE_TAGS: a b :END: Paragraph" (plist-get (org-export-get-environment nil t) :exclude-tags)))) ;; Handle :PROPERTY+: syntax. (should (equal '("a" "b") (org-test-with-temp-text "#+EXCLUDE_TAGS: noexport * Headline :PROPERTIES: :EXPORT_EXCLUDE_TAGS: a :EXPORT_EXCLUDE_TAGS+: b :END: Paragraph" (plist-get (org-export-get-environment nil t) :exclude-tags)))) ;; Export properties are case-insensitive. (should (equal '("29-03-2012") (org-test-with-temp-text "* Headline :PROPERTIES: :EXPORT_Date: 29-03-2012 :END: Paragraph" (plist-get (org-export-get-environment nil t) :date)))) ;; Still grab correct options when section above is empty. (should (equal '("H12") (org-test-with-temp-text "* H1\n** H11\n** H12" (plist-get (org-export-get-environment nil t) :title)))) ;; More than one property can refer to the same node property. (should (equal '("1" "1") (org-test-with-temp-text "* H\n:PROPERTIES:\n:EXPORT_A: 1\n:END:\n" (let* ((backend (org-export-create-backend :options '((:k1 "A") (:k2 "A")))) (options (org-export-get-environment backend t))) (list (plist-get options :k1) (plist-get options :k2))))))) (ert-deftest test-org-export/get-ordinal () "Test specifications for `org-export-get-ordinal'." ;; Table numbering with, without predicates, and with other types. (org-test-with-temp-text "#+title: Table numbering test #+options: author:nil toc:nil #+caption: Should be Table 1 | h1 | h2 | h3 | |----------+----------+----------| | abcdefgh | ijklmnop | qrstuvwx | #+caption: Should be Table 2 | h1 | h2 | h3 | |----------+----------+----------| | abcdefgh | ijklmnop | qrstuvwx | #+caption: Should be Table 3 | h1 | h2 | h3 | |----------+----------+----------| | abcdefgh | ijklmnop | qrstuvwx | #+caption: Should be Table 4 | h1 | h2 | h3 | |----------+----------+----------| | abcdefgh | ijklmnop | qrstuvwx |" (org-export-as (org-export-create-backend :parent 'org :transcoders '((table . (lambda (table contents info) (let ((from-third (lambda (table info) (<= 3 (org-export-get-ordinal table info))))) (pcase (org-element-interpret-data (org-export-get-caption table)) ("Should be Table 1" (should (= 1 (org-export-get-ordinal table info))) (should (= 2 (org-export-get-ordinal table info '(section)))) (should (= 1 (org-export-get-ordinal table info nil #'org-ascii--has-caption-p))) (should-not (org-export-get-ordinal table info nil from-third))) ("Should be Table 2" (should (= 2 (org-export-get-ordinal table info))) (should (= 3 (org-export-get-ordinal table info '(section)))) (should (= 2 (org-export-get-ordinal table info nil #'org-ascii--has-caption-p))) (should-not (org-export-get-ordinal table info nil from-third))) ("Should be Table 3" (should (= 3 (org-export-get-ordinal table info))) (should (= 4 (org-export-get-ordinal table info '(section)))) (should (= 3 (org-export-get-ordinal table info nil #'org-ascii--has-caption-p))) (should (= 1 (org-export-get-ordinal table info nil from-third)))) ("Should be Table 4" (should (= 4 (org-export-get-ordinal table info))) (should (= 5 (org-export-get-ordinal table info '(section)))) (should (= 4 (org-export-get-ordinal table info nil #'org-ascii--has-caption-p))) (should (= 2 (org-export-get-ordinal table info nil from-third)))))) ""))))))) (ert-deftest test-org-export/set-title () "Test title setting." ;; Without TITLE keyword. (should (equal "" (let (org-export-filter-body-functions org-export-filter-final-output-functions) (org-test-with-temp-text "Test" (org-export-as (org-export-create-backend :transcoders '((template . (lambda (text info) (org-element-interpret-data (plist-get info :title))))))))))) ;; With a blank TITLE keyword. (should (equal "" (let (org-export-filter-body-functions org-export-filter-final-output-functions) (org-test-with-temp-text "#+TITLE:\nTest" (org-export-as (org-export-create-backend :transcoders '((template . (lambda (text info) (org-element-interpret-data (plist-get info :title))))))))))) ;; With a non-empty TITLE keyword. (should (equal "Title" (org-test-with-temp-text "#+TITLE: Title\nTest" (org-export-as (org-export-create-backend :transcoders '((template . (lambda (text info) (org-element-interpret-data (plist-get info :title)))))))))) ;; When exporting a subtree, its heading becomes the headline of the ;; document... (should (equal "Headline" (org-test-with-temp-text "* Headline\nBody" (org-export-as (org-export-create-backend :transcoders '((template . (lambda (text info) (org-element-interpret-data (plist-get info :title)))))) 'subtree)))) ;; ... unless there is an EXPORT_TITLE property at the root of the ;; subtree. (should (equal "B" (org-test-with-temp-text "* A\n :PROPERTIES:\n :EXPORT_TITLE: B\n :END:\nBody" (org-export-as (org-export-create-backend :transcoders '((template . (lambda (text info) (org-element-interpret-data (plist-get info :title)))))) 'subtree))))) (ert-deftest test-org-export/handle-options () "Test if export options have an impact on output." ;; Test exclude tags for headlines and inlinetasks. (should (equal "" (let (org-export-filter-body-functions org-export-filter-final-output-functions) (org-test-with-temp-text "* Head1 :noexp:" (org-export-as (org-test-default-backend) nil nil nil '(:exclude-tags ("noexp"))))))) (should (equal "#+filetags: noexp\n" (let (org-export-filter-body-functions org-export-filter-final-output-functions) (org-test-with-temp-text "#+FILETAGS: noexp\n* Head1" (org-export-as (org-test-default-backend) nil nil nil '(:exclude-tags ("noexp"))))))) ;; Excluding a tag excludes its whole group. (should (equal "" (let (org-export-filter-body-functions org-export-filter-final-output-functions) (org-test-with-temp-text "* Head1 :baz:" (let ((org-tag-alist '((:startgrouptag) ("foo") (:grouptags) ("bar") ("baz") (:endgrouptag)))) (org-export-as (org-test-default-backend) nil nil nil '(:exclude-tags ("foo")))))))) ;; Test include tags for headlines and inlinetasks. (should (equal (org-test-with-temp-text "* H1\n* H2\n** Sub :exp:\n*** Sub Sub\n* H3" (let ((org-tags-column 0)) (org-export-as (org-test-default-backend) nil nil nil '(:select-tags ("exp"))))) "* H2\n** Sub :exp:\n*** Sub Sub\n")) ;; Including a tag includes its whole group. (should (string-match-p "\\`\\* H2" (let (org-export-filter-body-functions org-export-filter-final-output-functions) (org-test-with-temp-text "* H1\n* H2 :bar:" (let ((org-tag-alist '((:startgrouptag) ("foo") (:grouptags) ("bar") ("baz") (:endgrouptag)))) (org-export-as (org-test-default-backend) nil nil nil '(:select-tags ("foo")))))))) ;; If there is an include tag, ignore the section before the first ;; headline, if any. (should (equal (org-test-with-temp-text "First section\n* H1 :exp:\nBody" (let ((org-tags-column 0)) (org-export-as (org-test-default-backend) nil nil nil '(:select-tags ("exp"))))) "* H1 :exp:\nBody\n")) (should (equal (org-test-with-temp-text "#+FILETAGS: exp\nFirst section\n* H1\nBody" (org-export-as (org-test-default-backend) nil nil nil '(:select-tags ("exp")))) "* H1\nBody\n")) (should-not (equal (org-test-with-temp-text "* H1 :exp:\nBody" (let ((org-tags-column 0)) (org-export-as (org-test-default-backend) nil nil nil '(:select-tags ("exp"))))) "* H1 :exp:\n")) ;; Test mixing include tags and exclude tags. (should (string-match "\\* Head1[ \t]+:export:\n\\*\\* Sub-Head2\n" (org-test-with-temp-text " * Head1 :export: ** Sub-Head1 :noexport: ** Sub-Head2 * Head2 :noexport: ** Sub-Head1 :export:" (org-export-as (org-test-default-backend) nil nil nil '(:select-tags ("export") :exclude-tags ("noexport")))))) ;; Ignore tasks. (should (equal "" (let ((org-todo-keywords '((sequence "TODO" "DONE"))) org-export-filter-body-functions org-export-filter-final-output-functions) (org-test-with-temp-text "* TODO Head1" (org-export-as (org-test-default-backend) nil nil nil '(:with-tasks nil)))))) (should (equal "* TODO Head1\n" (let ((org-todo-keywords '((sequence "TODO" "DONE")))) (org-test-with-temp-text "* TODO Head1" (org-export-as (org-test-default-backend) nil nil nil '(:with-tasks t)))))) ;; Archived tree. (should (equal "" (let (org-export-filter-body-functions org-export-filter-final-output-functions) (org-test-with-temp-text "* Head1 :ARCHIVE:" (org-export-as (org-test-default-backend) nil nil nil '(:with-archived-trees nil)))))) (should (string-match "\\* Head1[ \t]+:ARCHIVE:" (org-test-with-temp-text "* Head1 :archive:\nbody\n** Sub-head 2" (org-export-as (org-test-default-backend) nil nil nil '(:with-archived-trees headline))))) (should (string-match "\\`\\* Head1[ \t]+:ARCHIVE:\n\\'" (org-test-with-temp-text "* Head1 :ARCHIVE:" (org-export-as (org-test-default-backend) nil nil nil '(:with-archived-trees t))))) ;; Broken links. Depending on `org-export-with-broken-links', raise ;; an error, ignore link or mark is as broken in output. (should-error (org-test-with-temp-text "[[#broken][link]]" (let ((backend (org-export-create-backend :transcoders '((section . (lambda (_e c _i) c)) (paragraph . (lambda (_e c _i) c)) (link . (lambda (l c i) (org-export-resolve-id-link l i))))))) (org-export-as backend nil nil nil '(:with-broken-links nil))))) (should (org-test-with-temp-text "[[#broken][link]]" (let ((backend (org-export-create-backend :transcoders '((section . (lambda (_e c _i) c)) (paragraph . (lambda (_e c _i) c)) (link . (lambda (l c i) (org-export-resolve-id-link l i))))))) (org-export-as backend nil nil nil '(:with-broken-links t))))) (should (org-test-with-temp-text "[[#broken][link]]" (let ((backend (org-export-create-backend :transcoders '((section . (lambda (_e c _i) c)) (paragraph . (lambda (_e c _i) c)) (link . (lambda (l c i) (org-export-resolve-id-link l i))))))) (org-string-nw-p (org-export-as backend nil nil nil '(:with-broken-links mark)))))) ;; Clocks. (should (string-match "CLOCK: \\[2012-04-29 .* 10:45\\]" (org-test-with-temp-text "CLOCK: [2012-04-29 sun. 10:45]" (org-export-as (org-test-default-backend) nil nil nil '(:with-clocks t))))) (should (equal "" (let (org-export-filter-body-functions org-export-filter-final-output-functions) (org-test-with-temp-text "CLOCK: [2012-04-29 sun. 10:45]" (org-export-as (org-test-default-backend) nil nil nil '(:with-clocks nil)))))) ;; Drawers. (should (equal "" (let (org-export-filter-body-functions org-export-filter-final-output-functions) (org-test-with-temp-text ":TEST:\ncontents\n:END:" (org-export-as (org-test-default-backend) nil nil nil '(:with-drawers nil)))))) (should (equal ":TEST:\ncontents\n:END:\n" (org-test-with-temp-text ":TEST:\ncontents\n:END:" (org-export-as (org-test-default-backend) nil nil nil '(:with-drawers t))))) (should (equal ":FOO:\nkeep\n:END:\n" (org-test-with-temp-text ":FOO:\nkeep\n:END:\n:BAR:\nremove\n:END:" (org-export-as (org-test-default-backend) nil nil nil '(:with-drawers ("FOO")))))) (should (equal ":FOO:\nkeep\n:END:\n" (org-test-with-temp-text ":FOO:\nkeep\n:END:\n:BAR:\nremove\n:END:" (org-export-as (org-test-default-backend) nil nil nil '(:with-drawers (not "BAR")))))) ;; Fixed-width. (should (equal ": A\n" (org-test-with-temp-text ": A" (org-export-as (org-test-default-backend) nil nil nil '(:with-fixed-width t))))) (should (equal "" (let (org-export-filter-body-functions org-export-filter-final-output-functions) (org-test-with-temp-text ": A" (org-export-as (org-test-default-backend) nil nil nil '(:with-fixed-width nil)))))) ;; Footnotes. (should (equal "Footnote?" (let ((org-footnote-section nil)) (org-test-with-temp-text "Footnote?[fn:1]\n\n[fn:1] Def" (org-trim (org-export-as (org-test-default-backend) nil nil nil '(:with-footnotes nil))))))) (should (equal "Footnote?[fn:1]\n\n[fn:1] Def" (let ((org-footnote-section nil)) (org-test-with-temp-text "Footnote?[fn:1]\n\n[fn:1] Def" (org-trim (org-export-as (org-test-default-backend) nil nil nil '(:with-footnotes t))))))) ;; Inlinetasks. (when (featurep 'org-inlinetask) (should (equal "" (let ((org-inlinetask-min-level 15) org-export-filter-body-functions org-export-filter-final-output-functions) (org-test-with-temp-text "*************** Task" (org-export-as (org-test-default-backend) nil nil nil '(:with-inlinetasks nil)))))) (should (equal "" (let ((org-inlinetask-min-level 15) org-export-filter-body-functions org-export-filter-final-output-functions) (org-test-with-temp-text "*************** Task\nContents\n*************** END" (org-export-as (org-test-default-backend) nil nil nil '(:with-inlinetasks nil))))))) ;; Plannings. (should (string-match "* H\nCLOSED: \\[2012-04-29 .* 10:45\\]" (let ((org-closed-string "CLOSED:")) (org-test-with-temp-text "* H\nCLOSED: [2012-04-29 sun. 10:45]" (org-export-as (org-test-default-backend) nil nil nil '(:with-planning t)))))) (should (equal "* H\n" (let ((org-closed-string "CLOSED:")) (org-test-with-temp-text "* H\nCLOSED: [2012-04-29 sun. 10:45]" (org-export-as (org-test-default-backend) nil nil nil '(:with-planning nil)))))) ;; Property Drawers. (should (equal "* H1\n" (org-test-with-temp-text "* H1\n :PROPERTIES:\n :PROP: value\n :END:" (org-export-as (org-test-default-backend) nil nil nil '(:with-properties nil))))) (should (equal "* H1\n:PROPERTIES:\n:PROP: value\n:END:\n" (org-test-with-temp-text "* H1\n :PROPERTIES:\n :PROP: value\n :END:" (org-export-as (org-test-default-backend) nil nil nil '(:with-properties t))))) (should (equal "* H1\n:PROPERTIES:\n:B: 2\n:END:\n" (org-test-with-temp-text "* H1\n :PROPERTIES:\n :A: 1\n :B: 2\n:END:" (org-export-as (org-test-default-backend) nil nil nil '(:with-properties ("B")))))) ;; Statistics cookies. (should (equal "* Stats" (let (org-export-filter-body-functions org-export-filter-final-output-functions) (org-trim (org-test-with-temp-text "* Stats [0/0]" (org-export-as (org-test-default-backend) nil nil nil '(:with-statistics-cookies nil))))))) ;; Tables. (should (equal "| A |\n" (org-test-with-temp-text "| A |" (org-export-as (org-test-default-backend) nil nil nil '(:with-tables t))))) (should (equal "" (let (org-export-filter-body-functions org-export-filter-final-output-functions) (org-test-with-temp-text "| A |" (org-export-as (org-test-default-backend) nil nil nil '(:with-tables nil))))))) (ert-deftest test-org-export/with-timestamps () "Test `org-export-with-timestamps' specifications." ;; t value. (should (string-match "\\[2012-04-29 .*? 10:45\\]<2012-04-29 .*? 10:45>" (org-test-with-temp-text "[2012-04-29 sun. 10:45]<2012-04-29 sun. 10:45>" (org-export-as (org-test-default-backend) nil nil nil '(:with-timestamps t))))) ;; nil value. (should (equal "" (let (org-export-filter-body-functions org-export-filter-final-output-functions) (org-trim (org-test-with-temp-text "[2012-04-29 sun. 10:45]<2012-04-29 sun. 10:45>" (org-export-as (org-test-default-backend) nil nil nil '(:with-timestamps nil))))))) ;; `active' value. (should (string-match "<2012-03-29 .*?>\n\nParagraph <2012-03-29 .*?>\\[2012-03-29 .*?\\]" (org-test-with-temp-text "<2012-03-29 Thu>[2012-03-29 Thu] Paragraph <2012-03-29 Thu>[2012-03-29 Thu]" (org-export-as (org-test-default-backend) nil nil nil '(:with-timestamps active))))) ;; `inactive' value. (should (string-match "\\[2012-03-29 .*?\\]\n\nParagraph <2012-03-29 .*?>\\[2012-03-29 .*?\\]" (org-test-with-temp-text "<2012-03-29 Thu>[2012-03-29 Thu] Paragraph <2012-03-29 Thu>[2012-03-29 Thu]" (org-export-as (org-test-default-backend) nil nil nil '(:with-timestamps inactive)))))) (ert-deftest test-org-export/comment-tree () "Test if export process ignores commented trees." (should (equal "" (let (org-export-filter-body-functions org-export-filter-final-output-functions) (org-test-with-temp-text "* COMMENT Head1" (org-export-as (org-test-default-backend))))))) (ert-deftest test-org-export/uninterpreted () "Test handling of uninterpreted elements." ;; Entities. (should (equal "dummy\n" (org-test-with-temp-text "\\alpha" (org-export-as (org-export-create-backend :transcoders '((entity . (lambda (e c i) "dummy")) (paragraph . (lambda (p c i) c)) (section . (lambda (s c i) c)))) nil nil nil '(:with-entities t))))) (should (equal "\\alpha\n" (org-test-with-temp-text "\\alpha" (org-export-as (org-export-create-backend :transcoders '((entity . (lambda (e c i) "dummy")) (paragraph . (lambda (p c i) c)) (section . (lambda (s c i) c)))) nil nil nil '(:with-entities nil))))) ;; Emphasis. (should (equal "dummy\n" (org-test-with-temp-text "*bold*" (org-export-as (org-export-create-backend :transcoders '((bold . (lambda (b c i) "dummy")) (paragraph . (lambda (p c i) c)) (section . (lambda (s c i) c)))) nil nil nil '(:with-emphasize t))))) (should (equal "*bold*\n" (org-test-with-temp-text "*bold*" (org-export-as (org-export-create-backend :transcoders '((bold . (lambda (b c i) "dummy")) (paragraph . (lambda (p c i) c)) (section . (lambda (s c i) c)))) nil nil nil '(:with-emphasize nil))))) (should (equal "/simple/ /example/\n" (org-test-with-temp-text "/simple/ /example/" (org-export-as (org-export-create-backend :transcoders '((bold . (lambda (b c i) "dummy")) (paragraph . (lambda (p c i) c)) (section . (lambda (s c i) c)))) nil nil nil '(:with-emphasize nil))))) ;; LaTeX environment. (should (equal "dummy\n" (org-test-with-temp-text "\\begin{equation}\n1+1=2\n\\end{equation}" (org-export-as (org-export-create-backend :transcoders '((latex-environment . (lambda (l c i) "dummy")) (section . (lambda (s c i) c)))) nil nil nil '(:with-latex t))))) (should (equal "\\begin{equation}\n1+1=2\n\\end{equation}\n" (org-test-with-temp-text "\\begin{equation}\n1+1=2\n\\end{equation}" (org-export-as (org-export-create-backend :transcoders '((latex-environment . (lambda (l c i) "dummy")) (section . (lambda (s c i) c)))) nil nil nil '(:with-latex verbatim))))) ;; LaTeX fragment. (should (equal "dummy\n" (org-test-with-temp-text "$1$" (org-export-as (org-export-create-backend :transcoders '((latex-fragment . (lambda (l c i) "dummy")) (paragraph . (lambda (p c i) c)) (section . (lambda (s c i) c)))) nil nil nil '(:with-latex t))))) (should (equal "$1$\n" (org-test-with-temp-text "$1$" (org-export-as (org-export-create-backend :transcoders '((latex-fragment . (lambda (l c i) "dummy")) (paragraph . (lambda (p c i) c)) (section . (lambda (s c i) c)))) nil nil nil '(:with-latex verbatim))))) (should (equal "$1$ \n" (org-test-with-temp-text "$1$ " (org-export-as (org-export-create-backend :transcoders '((latex-fragment . (lambda (l c i) "dummy")) (paragraph . (lambda (p c i) c)) (section . (lambda (s c i) c)))) nil nil nil '(:with-latex verbatim))))) ;; Sub/superscript. (should (equal "adummy\n" (org-test-with-temp-text "a_b" (org-export-as (org-export-create-backend :transcoders '((subscript . (lambda (s c i) "dummy")) (paragraph . (lambda (p c i) c)) (section . (lambda (s c i) c)))) nil nil nil '(:with-sub-superscript t))))) (should (equal "a_b\n" (org-test-with-temp-text "a_b" (org-export-as (org-export-create-backend :transcoders '((subscript . (lambda (s c i) "dummy")) (paragraph . (lambda (p c i) c)) (section . (lambda (s c i) c)))) nil nil nil '(:with-sub-superscript nil))))) (should (equal "a_b\n" (org-test-with-temp-text "a_b" (org-export-as (org-export-create-backend :transcoders '((subscript . (lambda (s c i) "dummy")) (paragraph . (lambda (p c i) c)) (section . (lambda (s c i) c)))) nil nil nil '(:with-sub-superscript {}))))) (should (equal "adummy\n" (org-test-with-temp-text "a_{b}" (org-export-as (org-export-create-backend :transcoders '((subscript . (lambda (s c i) "dummy")) (paragraph . (lambda (p c i) c)) (section . (lambda (s c i) c)))) nil nil nil '(:with-sub-superscript {}))))) (should (equal "a_entity\n" (org-test-with-temp-text "a_\\alpha" (org-export-as (org-export-create-backend :transcoders '((entity . (lambda (e c i) "entity")) (subscript . (lambda (s c i) "dummy")) (paragraph . (lambda (p c i) c)) (section . (lambda (s c i) c)))) nil nil nil '(:with-sub-superscript nil))))) ;; Handle uninterpreted objects in parsed keywords. (should (equal "a_b" (org-test-with-temp-text "#+TITLE: a_b" (org-export-as (org-export-create-backend :transcoders '((subscript . (lambda (s c i) "dummy")) (template . (lambda (c i) (org-export-data (plist-get i :title) i))) (section . (lambda (s c i) c)))) nil nil nil '(:with-sub-superscript nil))))) (should (equal "a_b" (org-test-with-temp-text "#+FOO: a_b" (org-export-as (org-export-create-backend :options '((:foo "FOO" nil nil parse)) :transcoders '((subscript . (lambda (s c i) "dummy")) (template . (lambda (c i) (org-export-data (plist-get i :foo) i))) (section . (lambda (s c i) c)))) nil nil nil '(:with-sub-superscript nil))))) ;; Objects in parsed keywords are "uninterpreted" before filters are ;; applied. (should (org-test-with-temp-text "#+TITLE: a_b" (org-export-as (org-export-create-backend :filters '((:filter-options (lambda (i _) (org-element-map (plist-get i :title) 'subscript (lambda (_) (error "There should be no subscript here"))))))) nil nil nil '(:with-sub-superscript nil)))) ;; Handle uninterpreted objects in captions. (should (equal "adummy\n" (org-test-with-temp-text "#+CAPTION: a_b\nParagraph" (org-export-as (org-export-create-backend :transcoders '((subscript . (lambda (s c i) "dummy")) (paragraph . (lambda (p c i) (org-export-data (org-export-get-caption p) i))) (section . (lambda (s c i) c)))) nil nil nil '(:with-sub-superscript t))))) (should (equal "a_b\n" (org-test-with-temp-text "#+CAPTION: a_b\nParagraph" (org-export-as (org-export-create-backend :transcoders '((subscript . (lambda (s c i) "dummy")) (paragraph . (lambda (p c i) (org-export-data (org-export-get-caption p) i))) (section . (lambda (s c i) c)))) nil nil nil '(:with-sub-superscript nil))))) ;; Special case: multiples uninterpreted objects in a row. (should (equal "a_b_c_d\n" (org-test-with-temp-text "a_b_c_d" (org-export-as (org-export-create-backend :transcoders '((subscript . (lambda (s c i) "dummy")) (paragraph . (lambda (p c i) c)) (section . (lambda (s c i) c)))) nil nil nil '(:with-sub-superscript {})))))) (ert-deftest test-org-export/export-scope () "Test all export scopes." ;; Subtree. (should (equal "text\n*** H3\n" (org-test-with-temp-text "* H1\n** H2\ntext\n*** H3" (org-export-as (org-test-default-backend) 'subtree)))) (should (equal "text\n*** H3\n" (org-test-with-temp-text "* H1\n** H2\ntext\n*** H3" (org-export-as (org-test-default-backend) 'subtree)))) ;; Subtree with a code block calling another block outside. (should (equal ": 3\n" (org-test-with-temp-text " * Head1 #+BEGIN_SRC emacs-lisp :noweb yes :exports results <> #+END_SRC * Head2 #+NAME: test #+BEGIN_SRC emacs-lisp \(+ 1 2) #+END_SRC" (let ((org-export-use-babel t)) (org-export-as (org-test-default-backend) 'subtree))))) ;; Subtree export should ignore leading planning line and property ;; drawer. (should (equal "Text\n" (org-test-with-temp-text " * H SCHEDULED: <2012-03-29 Thu> :PROPERTIES: :A: 1 :END: Text" (org-export-as (org-test-default-backend) 'subtree nil nil '(:with-planning t :with-properties t))))) (should (equal "" (org-test-with-temp-text " * H :PROPERTIES: :A: 1 :END: * H2" (org-export-as (org-test-default-backend) 'subtree)))) ;; Visible. (should (equal "* H1\n" (org-test-with-temp-text "* H1\n** H2\ntext\n*** H3" (org-cycle) (org-export-as (org-test-default-backend) nil 'visible)))) ;; Region. (should (equal "text\n" (org-test-with-temp-text "* H1\n** H2\ntext\n*** H3" (transient-mark-mode 1) (push-mark (point) t t) (end-of-line) (org-export-as (org-test-default-backend))))) ;; Body only. (should (equal "Text\n" (org-test-with-temp-text "Text" (org-export-as (org-export-create-backend :transcoders '((template . (lambda (b _i) (format "BEGIN\n%sEND" b))) (section . (lambda (_s c _i) c)) (paragraph . (lambda (_p c _i) c)))) nil nil 'body-only)))) (should (equal "BEGIN\nText\nEND" (org-test-with-temp-text "Text" (org-export-as (org-export-create-backend :transcoders '((template . (lambda (b _i) (format "BEGIN\n%sEND" b))) (section . (lambda (_s c _i) c)) (paragraph . (lambda (_p c _i) c)))))))) ;; Pathological case: Body only on an empty buffer is expected to ;; return an empty string, not nil. (should (org-test-with-temp-text "" (org-export-as (org-test-default-backend) nil nil t)))) (ert-deftest test-org-export/output-file-name () "Test `org-export-output-file-name' specifications." ;; Export from a file: name is built from original file name. (should (org-test-with-temp-text-in-file "Test" (equal (file-name-base (buffer-file-name)) (file-name-base (org-export-output-file-name ".ext"))))) ;; When #+EXPORT_FILE_NAME is defined, use it. (should (equal "test.ext" (org-test-with-temp-text-in-file "#+EXPORT_FILE_NAME: test" (org-export-output-file-name ".ext" t)))) ;; When exporting to subtree, check EXPORT_FILE_NAME property first. (should (equal "test.ext" (org-test-with-temp-text-in-file "* Test\n :PROPERTIES:\n :EXPORT_FILE_NAME: test\n :END:" (org-export-output-file-name ".ext" t)))) (should (equal "property.ext" (org-test-with-temp-text "#+EXPORT_FILE_NAME: keyword * Test :PROPERTIES: :EXPORT_FILE_NAME: property :END:" (org-export-output-file-name ".ext" t)))) ;; From a buffer not associated to a file, too. (should (equal "test.ext" (org-test-with-temp-text "* Test\n :PROPERTIES:\n :EXPORT_FILE_NAME: test\n :END:" (org-export-output-file-name ".ext" t)))) ;; When provided name is absolute, preserve it. (should (org-test-with-temp-text (format "* Test\n :PROPERTIES:\n :EXPORT_FILE_NAME: %s\n :END:" (expand-file-name "test")) (file-name-absolute-p (org-export-output-file-name ".ext" t)))) ;; When PUB-DIR argument is provided, use it. (should (equal "dir/" (org-test-with-temp-text-in-file "Test" (file-name-directory (org-export-output-file-name ".ext" nil "dir/"))))) ;; PUB-DIR has precedence over EXPORT_FILE_NAME keyword or property. (should (equal "pub-dir/" (org-test-with-temp-text-in-file "#+EXPORT_FILE_NAME: /dir/keyword\nTest" (file-name-directory (org-export-output-file-name ".ext" nil "pub-dir/"))))) ;; When returned name would overwrite original file, add EXTENSION ;; another time. (should (equal "normal.org.org" (org-test-at-id "75282ba2-f77a-4309-a970-e87c149fe125" (org-export-output-file-name ".org"))))) (ert-deftest test-org-export/expand-include () "Test file inclusion in an Org buffer." ;; Error when file isn't specified. (should-error (org-test-with-temp-text "#+INCLUDE: dummy.org" (org-export-expand-include-keyword))) ;; Refuse to expand keywords in commented headings. (should (org-test-with-temp-text "* COMMENT H1\n#+INCLUDE: dummy.org" (org-export-expand-include-keyword) t)) ;; Full insertion with recursive inclusion. (should (equal (with-temp-buffer (insert-file (expand-file-name "examples/include.org" org-test-dir)) (replace-regexp-in-string (regexp-quote "#+INCLUDE: \"include2.org\"") "Success!" (buffer-string))) (org-test-with-temp-text (format "#+INCLUDE: \"%s/examples/include.org\"" org-test-dir) (org-export-expand-include-keyword) (buffer-string)))) ;; Localized insertion. (org-test-with-temp-text (format "#+INCLUDE: \"%s/examples/include.org\" :lines \"1-2\"" org-test-dir) (org-export-expand-include-keyword) (should (equal (buffer-string) "Small Org file with an include keyword.\n"))) ;; Insertion with constraints on headlines level. (should (equal "* Top heading\n** Heading\nbody\n" (org-test-with-temp-text (format "* Top heading\n#+INCLUDE: \"%s/examples/include.org\" :lines \"9-11\"" org-test-dir) (org-export-expand-include-keyword) (buffer-string)))) (should (equal "* Top heading\n* Heading\nbody\n" (org-test-with-temp-text (format "* Top heading\n#+INCLUDE: \"%s/examples/include.org\" :lines \"9-11\" :minlevel 1" org-test-dir) (org-export-expand-include-keyword) (buffer-string)))) ;; Inclusion within an example block. (should (equal "#+BEGIN_EXAMPLE\nSmall Org file with an include keyword.\n#+END_EXAMPLE\n" (org-test-with-temp-text (format "#+INCLUDE: \"%s/examples/include.org\" :lines \"1-2\" EXAMPLE" org-test-dir) (org-export-expand-include-keyword) (buffer-string)))) ;; Inclusion within a src-block. (should (equal "#+BEGIN_SRC emacs-lisp\n(+ 2 1)\n#+END_SRC\n" (org-test-with-temp-text (format "#+INCLUDE: \"%s/examples/include.org\" :lines \"4-5\" SRC emacs-lisp" org-test-dir) (org-export-expand-include-keyword) (buffer-string)))) ;; Inclusion within an html export-block. (should (equal "#+BEGIN_EXPORT html\n

HTML!

\n#+END_EXPORT\n" (org-test-with-temp-text (format "#+INCLUDE: \"%s/examples/include.html\" EXPORT html" org-test-dir) (org-export-expand-include-keyword) (buffer-string)))) ;; Inclusion within an center paragraph (should (equal "#+BEGIN_CENTER\nSuccess!\n#+END_CENTER\n" (org-test-with-temp-text (format "#+INCLUDE: \"%s/examples/include2.org\" CENTER" org-test-dir) (org-export-expand-include-keyword) (buffer-string)))) ;; Footnotes labels are local to each included file. (should (= 6 (length (delete-dups (let ((contents " Footnotes[fn:1], [fn:test], [fn:test] and [fn:inline:inline footnote]. \[fn:1] Footnote 1 \[fn:test] Footnote \"test\"")) (org-test-with-temp-text-in-file contents (let ((file1 (buffer-file-name))) (org-test-with-temp-text-in-file contents (let ((file2 (buffer-file-name))) (org-test-with-temp-text (format "#+INCLUDE: \"%s\"\n#+INCLUDE: \"%s\"" file1 file2) (org-export-expand-include-keyword) (org-element-map (org-element-parse-buffer) 'footnote-reference (lambda (r) (org-element-property :label r))))))))))))) ;; Footnotes labels are not local to each include keyword. (should (= 3 (length (delete-dups (let ((contents " Footnotes[fn:1], [fn:test] and [fn:inline:inline footnote]. \[fn:1] Footnote 1 \[fn:test] Footnote \"test\"")) (org-test-with-temp-text-in-file contents (let ((file (buffer-file-name))) (org-test-with-temp-text (format "#+INCLUDE: \"%s\"\n#+INCLUDE: \"%s\"" file file) (org-export-expand-include-keyword) (org-element-map (org-element-parse-buffer) 'footnote-reference (lambda (ref) (org-element-property :label ref))))))))))) ;; Footnotes are supported by :lines-like elements and unnecessary ;; footnotes are dropped. (should (= 3 (length (delete-dups (let ((contents " * foo Footnotes[fn:1] * bar Footnotes[fn:2], foot[fn:test] and [fn:inline:inline footnote] \[fn:1] Footnote 1 \[fn:2] Footnote 1 * Footnotes \[fn:test] Footnote \"test\" ")) (org-test-with-temp-text-in-file contents (let ((file (buffer-file-name))) (org-test-with-temp-text (format "#+INCLUDE: \"%s::*bar\"\n" file) (org-export-expand-include-keyword) (org-element-map (org-element-parse-buffer) 'footnote-definition (lambda (ref) (org-element-property :label ref))))))))))) ;; If only-contents is non-nil only include contents of element. (should (equal "body\n" (org-test-with-temp-text (concat (format "#+INCLUDE: \"%s/examples/include.org::*Heading\" " org-test-dir) ":only-contents t") (org-export-expand-include-keyword) (buffer-string)))) ;; Headings can be included via CUSTOM_ID. (should (org-test-with-temp-text (format "#+INCLUDE: \"%s/examples/include.org::#ah\"" org-test-dir) (org-export-expand-include-keyword) (goto-char (point-min)) (looking-at "* Another heading"))) ;; Named objects can be included. (should (equal "| 1 |\n" (org-test-with-temp-text (format "#+INCLUDE: \"%s/examples/include.org::tbl\" :only-contents t" org-test-dir) (org-export-expand-include-keyword) (buffer-string)))) ;; Including non-existing elements should result in an error. (should-error (org-test-with-temp-text (format "#+INCLUDE: \"%s/examples/include.org::*non-existing heading\"" org-test-dir) (org-export-expand-include-keyword))) ;; Lines work relatively to an included element. (should (equal "2\n3\n" (org-test-with-temp-text (format "#+INCLUDE: \"%s/examples/include.org::#ah\" :only-contents t \ :lines \"2-3\"" org-test-dir) (org-export-expand-include-keyword) (buffer-string)))) ;; Properties should be dropped from headlines. (should (equal (org-test-with-temp-text (format "#+INCLUDE: \"%s/examples/include.org::#ht\" :only-contents t" org-test-dir) (org-export-expand-include-keyword) (buffer-string)) (org-test-with-temp-text (format "#+INCLUDE: \"%s/examples/include.org::tbl\"" org-test-dir) (org-export-expand-include-keyword) (buffer-string)))) ;; Properties should be dropped, drawers should not be. (should (equal ":LOGBOOK:\ndrawer\n:END:\ncontent\n" (org-test-with-temp-text (format "#+INCLUDE: \"%s/examples/include.org::#dh\" :only-contents t" org-test-dir) (org-export-expand-include-keyword) (buffer-string)))) ;; Adjacent INCLUDE-keywords should have the same :minlevel if unspecified. (should (cl-every (lambda (level) (zerop (1- level))) (org-test-with-temp-text (concat (format "#+INCLUDE: \"%s/examples/include.org::#ah\"\n" org-test-dir) (format "#+INCLUDE: \"%s/examples/include.org::*Heading\"" org-test-dir)) (org-export-expand-include-keyword) (org-element-map (org-element-parse-buffer) 'headline (lambda (head) (org-element-property :level head)))))) ;; INCLUDE does not insert induced :minlevel for src-blocks. (should-not (equal (org-test-with-temp-text (format "#+INCLUDE: \"%s/examples/include2.org\" src emacs-lisp" org-test-dir) (org-export-expand-include-keyword) (buffer-string)) (org-test-with-temp-text (format "#+INCLUDE: \"%s/examples/include2.org\" src emacs-lisp :minlevel 1" org-test-dir) (org-export-expand-include-keyword) (buffer-string)))) ;; INCLUDE assigns the relative :minlevel conditional on narrowing. (should (org-test-with-temp-text-in-file (format "* h1\n#+INCLUDE: \"%s/examples/include.org::#ah\"" org-test-dir) (narrow-to-region (point) (point-max)) (org-export-expand-include-keyword) (eq 2 (org-current-level)))) ;; If :minlevel is present do not alter it. (should (org-test-with-temp-text (format "* h1\n#+INCLUDE: \"%s/examples/include.org::#ah\" :minlevel 3" org-test-dir) (narrow-to-region (point) (point-max)) (org-export-expand-include-keyword) (eq 3 (org-current-level))))) (ert-deftest test-org-export/expand-include/links () "Test links modifications when including files." ;; Preserve relative plain links. (should (string-prefix-p "file:org-includee-" (let* ((subdir (make-temp-file "org-includee-" t)) (includee (expand-file-name "includee.org" subdir)) (includer (make-temp-file "org-includer-"))) (write-region "file:foo.org" nil includee) (write-region (format "#+INCLUDE: %S" (file-relative-name includee temporary-file-directory)) nil includer) (let ((buffer (find-file-noselect includer t))) (unwind-protect (with-current-buffer buffer (org-mode) (org-export-expand-include-keyword) (org-trim (buffer-string))) (when (buffer-live-p buffer) (with-current-buffer buffer (set-buffer-modified-p nil)) (kill-buffer buffer)) (when (file-exists-p subdir) (delete-directory subdir t)) (when (file-exists-p includer) (delete-file includer))))))) ;; Preserve relative angular links. (should (string-prefix-p "" nil includee) (write-region (format "#+INCLUDE: %S" (file-relative-name includee temporary-file-directory)) nil includer) (let ((buffer (find-file-noselect includer t))) (unwind-protect (with-current-buffer buffer (org-mode) (org-export-expand-include-keyword) (org-trim (buffer-string))) (when (buffer-live-p buffer) (with-current-buffer buffer (set-buffer-modified-p nil)) (kill-buffer buffer)) (when (file-exists-p subdir) (delete-directory subdir t)) (when (file-exists-p includer) (delete-file includer))))))) ;; Preserve relative bracket links without description. (should (string-prefix-p "[[file:org-includee-" (let* ((subdir (make-temp-file "org-includee-" t)) (includee (expand-file-name "includee.org" subdir)) (includer (make-temp-file "org-includer-"))) (write-region "[[file:foo.org]]" nil includee) (write-region (format "#+INCLUDE: %S" (file-relative-name includee temporary-file-directory)) nil includer) (let ((buffer (find-file-noselect includer t))) (unwind-protect (with-current-buffer buffer (org-mode) (org-export-expand-include-keyword) (org-trim (buffer-string))) (when (buffer-live-p buffer) (with-current-buffer buffer (set-buffer-modified-p nil)) (kill-buffer buffer)) (when (file-exists-p subdir) (delete-directory subdir t)) (when (file-exists-p includer) (delete-file includer))))))) ;; Preserve blanks after the link. (should (string-suffix-p "foo.org]] :tag:" (let* ((subdir (make-temp-file "org-includee-" t)) (includee (expand-file-name "includee.org" subdir)) (includer (make-temp-file "org-includer-"))) (write-region "[[file:foo.org]] :tag:" nil includee) (write-region (format "#+INCLUDE: %S" (file-relative-name includee temporary-file-directory)) nil includer) (let ((buffer (find-file-noselect includer t))) (unwind-protect (with-current-buffer buffer (org-mode) (org-export-expand-include-keyword) (org-trim (buffer-string))) (when (buffer-live-p buffer) (with-current-buffer buffer (set-buffer-modified-p nil)) (kill-buffer buffer)) (when (file-exists-p subdir) (delete-directory subdir t)) (when (file-exists-p includer) (delete-file includer))))))) ;; Preserve relative bracket links with description. (should (string-prefix-p "[[file:org-includee-" (let* ((subdir (make-temp-file "org-includee-" t)) (includee (expand-file-name "includee.org" subdir)) (includer (make-temp-file "org-includer-"))) (write-region "[[file:foo.org][description]]" nil includee) (write-region (format "#+INCLUDE: %S" (file-relative-name includee temporary-file-directory)) nil includer) (let ((buffer (find-file-noselect includer t))) (unwind-protect (with-current-buffer buffer (org-mode) (org-export-expand-include-keyword) (org-trim (buffer-string))) (when (buffer-live-p buffer) (with-current-buffer buffer (set-buffer-modified-p nil)) (kill-buffer buffer)) (when (file-exists-p subdir) (delete-directory subdir t)) (when (file-exists-p includer) (delete-file includer))))))) ;; Preserve absolute links. (should (string= "[[file:/foo/bar.org]]" (let* ((subdir (make-temp-file "org-includee-" t)) (includee (expand-file-name "includee.org" subdir)) (includer (make-temp-file "org-includer-"))) (write-region "[[file:/foo/bar.org]]" nil includee) (write-region (format "#+INCLUDE: %S" (file-relative-name includee temporary-file-directory)) nil includer) (let ((buffer (find-file-noselect includer t))) (unwind-protect (with-current-buffer buffer (org-mode) (org-export-expand-include-keyword) (org-trim (buffer-string))) (when (buffer-live-p buffer) (with-current-buffer buffer (set-buffer-modified-p nil)) (kill-buffer buffer)) (when (file-exists-p subdir) (delete-directory subdir t)) (when (file-exists-p includer) (delete-file includer))))))) ;; Pathological case: Do not error when fixing a path in a headline. (should (let* ((subdir (make-temp-file "org-includee-" t)) (includee (expand-file-name "includee.org" subdir)) (includer (make-temp-file "org-includer-"))) (write-region "* [[file:foo.org]]" nil includee) (write-region (format "#+INCLUDE: %S" (file-relative-name includee temporary-file-directory)) nil includer) (let ((buffer (find-file-noselect includer t))) (unwind-protect (with-current-buffer buffer (org-mode) (org-export-expand-include-keyword) (org-trim (buffer-string))) (when (buffer-live-p buffer) (with-current-buffer buffer (set-buffer-modified-p nil)) (kill-buffer buffer)) (when (file-exists-p subdir) (delete-directory subdir t)) (when (file-exists-p includer) (delete-file includer))))))) (ert-deftest test-org-export/expand-macro () "Test macro expansion in an Org buffer." (require 'ox-org) ;; Standard macro expansion. (should (equal "#+macro: macro1 value\nvalue\n" (org-test-with-temp-text "#+MACRO: macro1 value\n{{{macro1}}}" (org-export-as (org-test-default-backend))))) ;; Include global macros. However, local macros override them. (should (equal "global\n" (org-test-with-temp-text "{{{M}}}" (let ((org-export-global-macros '(("M" . "global")))) (org-export-as (org-test-default-backend)))))) (should (equal "global arg\n" (org-test-with-temp-text "{{{M(arg)}}}" (let ((org-export-global-macros '(("M" . "global $1")))) (org-export-as (org-test-default-backend)))))) (should (equal "2\n" (org-test-with-temp-text "{{{M}}}" (let ((org-export-global-macros '(("M" . "(eval (+ 1 1))")))) (org-export-as (org-test-default-backend)))))) (should (equal "#+macro: M local\nlocal\n" (org-test-with-temp-text "#+macro: M local\n{{{M}}}" (let ((org-export-global-macros '(("M" . "global")))) (org-export-as (org-test-default-backend)))))) ;; Allow macro in parsed keywords and associated properties. ;; Standard macro expansion. (should (string-match "#\\+k: value" (let ((backend (org-export-create-backend :parent 'org :options '((:k "K" nil nil parse))))) (org-test-with-temp-text "#+MACRO: macro value\n#+K: {{{macro}}}" (org-export-as backend))))) (should (string-match ":EXPORT_K: v" (let ((backend (org-export-create-backend :parent 'org :options '((:k "K" nil nil parse))))) (org-test-with-temp-text "#+macro: m v\n* H\n:PROPERTIES:\n:EXPORT_K: {{{m}}}\n:END:" (org-export-as backend nil nil nil '(:with-properties t)))))) ;; Expand specific macros. (should (equal "me 2012-03-29 me@here Title\n" (org-test-with-temp-text " #+TITLE: Title #+DATE: 2012-03-29 #+AUTHOR: me #+EMAIL: me@here {{{author}}} {{{date}}} {{{email}}} {{{title}}}" (let ((output (org-export-as (org-test-default-backend)))) (substring output (string-match ".*\n\\'" output)))))) ;; Expand specific macros when property contained a regular macro ;; already. (should (equal "value\n" (org-test-with-temp-text " #+MACRO: macro1 value #+TITLE: {{{macro1}}} {{{title}}}" (let ((output (org-export-as (org-test-default-backend)))) (substring output (string-match ".*\n\\'" output)))))) ;; Expand macros with templates in included files. (should (equal "success\n" (org-test-with-temp-text (format "#+INCLUDE: \"%s/examples/macro-templates.org\" {{{included-macro}}}" org-test-dir) (let ((output (org-export-as (org-test-default-backend)))) (substring output (string-match ".*\n\\'" output)))))) ;; Date macro takes a optional formatting argument (should (equal "09-02-15\n" (org-test-with-temp-text "{{{date(%d-%m-%y)}}}\n* d :noexport:\n#+DATE: <2015-02-09>" (org-export-as (org-test-default-backend))))) ;; Only single timestamps are formatted (should (equal "<2015-02x-09>\n" (org-test-with-temp-text "{{{date(%d-%m-%y)}}}\n* d :noexport:\n#+DATE: <2015-02x-09>" (org-export-as (org-test-default-backend))))) ;; Throw an error when a macro definition is missing. (should-error (org-test-with-temp-text "{{{missing}}}" (org-export-as (org-test-default-backend)))) ;; Inline source blocks generate {{{results}}} macros. Evaluate ;; those. (should (equal "=2=\n" (org-test-with-temp-text "src_emacs-lisp{(+ 1 1)}" (let ((org-export-use-babel t) (org-babel-inline-result-wrap "=%s=")) (org-export-as (org-test-default-backend)))))) ;; If inline source block is already associated to a "results" ;; macro, do not duplicate it. (should (equal "src_emacs-lisp{(+ 1 1)} {{{results(=2=)}}}" (org-test-with-temp-text "src_emacs-lisp{(+ 1 1)} {{{results(=2=)}}}" (let ((org-export-use-babel t) (org-babel-inline-result-wrap "=%s=")) (org-export-as (org-test-default-backend))) (buffer-string))))) (ert-deftest test-org-export/before-processing-hook () "Test `org-export-before-processing-hook'." (should (equal "#+macro: mac val\nTest\n" (org-test-with-temp-text "#+MACRO: mac val\n{{{mac}}} Test" (let ((org-export-before-processing-hook '((lambda (backend) (while (re-search-forward "{{{" nil t) (let ((object (org-element-context))) (when (org-element-type-p object 'macro) (delete-region (org-element-property :begin object) (org-element-property :end object))))))))) (org-export-as (org-test-default-backend))))))) (ert-deftest test-org-export/before-parsing-hook () "Test `org-export-before-parsing-hook'." (should (equal "Body 1\nBody 2\n" (org-test-with-temp-text "* Headline 1\nBody 1\n* Headline 2\nBody 2" (let ((org-export-before-parsing-hook '((lambda (backend) (goto-char (point-min)) (while (re-search-forward org-outline-regexp-bol nil t) (delete-region (point-at-bol) (progn (forward-line) (point)))))))) (org-export-as (org-test-default-backend))))))) ;;; Affiliated Keywords (ert-deftest test-org-export/read-attribute () "Test `org-export-read-attribute' specifications." ;; Standard test. (should (equal (org-export-read-attribute :attr_html (org-test-with-temp-text "#+ATTR_HTML: :a 1 :b 2\nParagraph" (org-element-at-point))) '(:a "1" :b "2"))) ;; Return nil on empty attribute. (should-not (org-export-read-attribute :attr_html (org-test-with-temp-text "Paragraph" (org-element-at-point)))) ;; Return nil on "nil" string. (should (equal '(:a nil :b nil) (org-export-read-attribute :attr_html (org-test-with-temp-text "#+ATTR_HTML: :a nil :b nil\nParagraph" (org-element-at-point))))) ;; Return nil on empty string. (should (equal '(:a nil :b nil) (org-export-read-attribute :attr_html (org-test-with-temp-text "#+ATTR_HTML: :a :b\nParagraph" (org-element-at-point))))) ;; Return empty string when value is "". (should (equal '(:a "") (org-export-read-attribute :attr_html (org-test-with-temp-text "#+ATTR_HTML: :a \"\"\nParagraph" (org-element-at-point))))) ;; Return \"\" when value is """". (should (equal '(:a "\"\"") (org-export-read-attribute :attr_html (org-test-with-temp-text "#+ATTR_HTML: :a \"\"\"\"\nParagraph" (org-element-at-point))))) ;; Ignore text before first property. (should-not (member "ignore" (org-export-read-attribute :attr_html (org-test-with-temp-text "#+ATTR_HTML: ignore :a 1\nParagraph" (org-element-at-point)))))) (ert-deftest test-org-export/get-caption () "Test `org-export-get-caption' specifications." ;; Without optional argument, return long caption (should (equal '("l") (org-test-with-temp-text "#+CAPTION[s]: l\nPara" (org-export-get-caption (org-element-at-point))))) ;; With optional argument, return short caption. (should (equal '("s") (org-test-with-temp-text "#+CAPTION[s]: l\nPara" (org-export-get-caption (org-element-at-point) t)))) ;; Multiple lines are separated by white spaces. (should (equal '("a" " " "b") (org-test-with-temp-text "#+CAPTION: a\n#+CAPTION: b\nPara" (org-export-get-caption (org-element-at-point)))))) ;;; Backend Tools (ert-deftest test-org-export/define-backend () "Test backend definition and accessors." ;; Translate table. (should (equal '((headline . my-headline-test)) (let (org-export-registered-backends) (org-export-define-backend 'test '((headline . my-headline-test))) (org-export-get-all-transcoders 'test)))) ;; Filters. (should (equal '((:filter-headline . my-filter)) (let (org-export-registered-backends) (org-export-define-backend 'test '((headline . my-headline-test)) :filters-alist '((:filter-headline . my-filter))) (org-export-backend-filters (org-export-get-backend 'test))))) ;; Options. (should (equal '((:prop value)) (let (org-export-registered-backends) (org-export-define-backend 'test '((headline . my-headline-test)) :options-alist '((:prop value))) (org-export-backend-options (org-export-get-backend 'test))))) ;; Menu. (should (equal '(?k "Test Export" test) (let (org-export-registered-backends) (org-export-define-backend 'test '((headline . my-headline-test)) :menu-entry '(?k "Test Export" test)) (org-export-backend-menu (org-export-get-backend 'test)))))) (ert-deftest test-org-export/define-derived-backend () "Test `org-export-define-derived-backend' specifications." ;; Error when parent backend is not defined. (should-error (let (org-export-registered-backends) (org-export-define-derived-backend 'test 'parent))) ;; Append translation table to parent's. (should (equal '((:headline . test) (:headline . parent)) (let (org-export-registered-backends) (org-export-define-backend 'parent '((:headline . parent))) (org-export-define-derived-backend 'test 'parent :translate-alist '((:headline . test))) (org-export-get-all-transcoders 'test)))) ;; Options defined in the new back have priority over those defined ;; in parent. (should (eq 'test (let (org-export-registered-backends) (org-export-define-backend 'parent '((:headline . parent)) :options-alist '((:a nil nil 'parent))) (org-export-define-derived-backend 'test 'parent :options-alist '((:a nil nil 'test))) (plist-get (org-export--get-global-options (org-export-get-backend 'test)) :a))))) (ert-deftest test-org-export/derived-backend-p () "Test `org-export-derived-backend-p' specifications." ;; Non-nil with direct match. (should (let (org-export-registered-backends) (org-export-define-backend 'test '((headline . test))) (org-export-derived-backend-p 'test 'test))) (should (let (org-export-registered-backends) (org-export-define-backend 'test '((headline . test))) (org-export-define-derived-backend 'test2 'test) (org-export-derived-backend-p 'test2 'test2))) ;; Non-nil with a direct parent. (should (let (org-export-registered-backends) (org-export-define-backend 'test '((headline . test))) (org-export-define-derived-backend 'test2 'test) (org-export-derived-backend-p 'test2 'test))) ;; Non-nil with an indirect parent. (should (let (org-export-registered-backends) (org-export-define-backend 'test '((headline . test))) (org-export-define-derived-backend 'test2 'test) (org-export-define-derived-backend 'test3 'test2) (org-export-derived-backend-p 'test3 'test))) ;; Nil otherwise. (should-not (let (org-export-registered-backends) (org-export-define-backend 'test '((headline . test))) (org-export-define-backend 'test2 '((headline . test2))) (org-export-derived-backend-p 'test2 'test))) (should-not (let (org-export-registered-backends) (org-export-define-backend 'test '((headline . test))) (org-export-define-backend 'test2 '((headline . test2))) (org-export-define-derived-backend 'test3 'test2) (org-export-derived-backend-p 'test3 'test)))) (ert-deftest test-org-export/get-all-transcoders () "Test `org-export-get-all-transcoders' specifications." ;; Return nil when backend cannot be found. (should-not (org-export-get-all-transcoders nil)) ;; Same as `org-export-transcoders' if no parent. (should (equal '((headline . ignore)) (org-export-get-all-transcoders (org-export-create-backend :transcoders '((headline . ignore)))))) ;; But inherit from all ancestors whenever possible. (should (equal '((section . ignore) (headline . ignore)) (let (org-export-registered-backends) (org-export-define-backend 'b1 '((headline . ignore))) (org-export-get-all-transcoders (org-export-create-backend :parent 'b1 :transcoders '((section . ignore))))))) (should (equal '((paragraph . ignore) (section . ignore) (headline . ignore)) (let (org-export-registered-backends) (org-export-define-backend 'b1 '((headline . ignore))) (org-export-define-derived-backend 'b2 'b1 :translate-alist '((section . ignore))) (org-export-get-all-transcoders (org-export-create-backend :parent 'b2 :transcoders '((paragraph . ignore))))))) ;; Backend transcoders overrule inherited ones. (should (eq 'b (let (org-export-registered-backends) (org-export-define-backend 'b1 '((headline . a))) (cdr (assq 'headline (org-export-get-all-transcoders (org-export-create-backend :parent 'b1 :transcoders '((headline . b)))))))))) (ert-deftest test-org-export/get-all-options () "Test `org-export-get-all-options' specifications." ;; Return nil when backend cannot be found. (should-not (org-export-get-all-options nil)) ;; Same as `org-export-options' if no parent. (should (equal '((headline . ignore)) (org-export-get-all-options (org-export-create-backend :options '((headline . ignore)))))) ;; But inherit from all ancestors whenever possible. (should (equal '((:key2 value2) (:key1 value1)) (let (org-export-registered-backends) (org-export-define-backend 'b1 nil :options-alist '((:key1 value1))) (org-export-get-all-options (org-export-create-backend :parent 'b1 :options '((:key2 value2))))))) (should (equal '((:key3 value3) (:key2 value2) (:key1 value1)) (let (org-export-registered-backends) (org-export-define-backend 'b1 nil :options-alist '((:key1 value1))) (org-export-define-derived-backend 'b2 'b1 :options-alist '((:key2 value2))) (org-export-get-all-options (org-export-create-backend :parent 'b2 :options '((:key3 value3))))))) ;; Backend options overrule inherited ones. (should (eq 'b (let (org-export-registered-backends) (org-export-define-backend 'b1 nil :options-alist '((:key1 . a))) (cdr (assq :key1 (org-export-get-all-options (org-export-create-backend :parent 'b1 :options '((:key1 . b)))))))))) (ert-deftest test-org-export/get-all-filters () "Test `org-export-get-all-filters' specifications." ;; Return nil when backend cannot be found. (should-not (org-export-get-all-filters nil)) ;; Same as `org-export-filters' if no parent. (should (equal '((:filter-headline . ignore)) (org-export-get-all-filters (org-export-create-backend :filters '((:filter-headline . ignore)))))) ;; But inherit from all ancestors whenever possible. (should (equal '((:filter-section . ignore) (:filter-headline . ignore)) (let (org-export-registered-backends) (org-export-define-backend 'b1 nil :filters-alist '((:filter-headline . ignore))) (org-export-get-all-filters (org-export-create-backend :parent 'b1 :filters '((:filter-section . ignore))))))) (should (equal '((:filter-paragraph . ignore) (:filter-section . ignore) (:filter-headline . ignore)) (let (org-export-registered-backends) (org-export-define-backend 'b1 nil :filters-alist '((:filter-headline . ignore))) (org-export-define-derived-backend 'b2 'b1 :filters-alist '((:filter-section . ignore))) (org-export-get-all-filters (org-export-create-backend :parent 'b2 :filters '((:filter-paragraph . ignore))))))) ;; Backend filters overrule inherited ones. (should (eq 'b (let (org-export-registered-backends) (org-export-define-backend 'b1 '((:filter-headline . a))) (cdr (assq :filter-headline (org-export-get-all-filters (org-export-create-backend :parent 'b1 :filters '((:filter-headline . b)))))))))) (ert-deftest test-org-export/with-backend () "Test `org-export-with-backend' definition." ;; Error when calling an undefined backend (should-error (org-export-with-backend nil "Test")) ;; Error when called backend doesn't have an appropriate ;; transcoder. (should-error (org-export-with-backend (org-export-create-backend :transcoders '((headline . ignore))) "Test")) ;; Otherwise, export using correct transcoder (should (equal "Success" (let (org-export-registered-backends) (org-export-define-backend 'test '((verbatim . (lambda (text contents info) "Failure")))) (org-export-define-backend 'test2 '((verbatim . (lambda (text contents info) "Success")))) (org-export-with-backend 'test2 '(verbatim (:value "=Test=")))))) ;; Corner case: plain-text transcoders have a different arity. (should (equal "Success" (org-export-with-backend (org-export-create-backend :transcoders '((plain-text . (lambda (text info) "Success")))) "Test"))) ;; Provide correct backend if transcoder needs to use recursive ;; calls. (should (equal "Success\n" (let ((test-backend (org-export-create-backend :transcoders (list (cons 'headline (lambda (headline _contents info) (org-export-data (org-element-property :title headline) info))) (cons 'plain-text (lambda (_text _info) "Success")))))) (org-export-string-as "* Test" (org-export-create-backend :transcoders (list (cons 'headline (lambda (headline contents info) (org-export-with-backend test-backend headline contents info)))))))))) (ert-deftest test-org-export/data-with-backend () "Test `org-export-data-with-backend' specifications." ;; Error when calling an undefined backend. (should-error (org-export-data-with-backend nil "nil" nil)) ;; Otherwise, export data recursively, using correct backend. (should (equal "Success!" (org-export-data-with-backend '(bold nil "Test") (org-export-create-backend :transcoders '((plain-text . (lambda (text info) "Success")) (bold . (lambda (bold contents info) (concat contents "!"))))) '(:with-emphasize t))))) ;;; Comments (ert-deftest test-org-export/comments () "Test comments handling during export. In particular, structure of the document mustn't be altered after comments removal." (should (equal "Para1\n\nPara2\n" (org-test-with-temp-text "Para1 # Comment # Comment Para2" (org-export-as (org-test-default-backend))))) (should (equal "Para1\n\nPara2\n" (org-test-with-temp-text "Para1 # Comment Para2" (org-export-as (org-test-default-backend))))) (should (equal "[fn:1] Para1\n\n\nPara2\n" (org-test-with-temp-text "[fn:1] Para1 # Inside definition # Outside definition Para2" (org-export-as (org-test-default-backend))))) (should (equal "[fn:1] Para1\n\nPara2\n" (org-test-with-temp-text "[fn:1] Para1 # Inside definition # Inside definition Para2" (org-export-as (org-test-default-backend))))) (should (equal "[fn:1] Para1\n\nPara2\n" (org-test-with-temp-text "[fn:1] Para1 # Inside definition Para2" (org-export-as (org-test-default-backend))))) (should (equal "[fn:1] Para1\n\nPara2\n" (org-test-with-temp-text "[fn:1] Para1 # Inside definition Para2" (org-export-as (org-test-default-backend))))) (should (equal "- item 1\n\n- item 2\n" (org-test-with-temp-text "- item 1 # Comment - item 2" (org-export-as (org-test-default-backend)))))) ;;; Export blocks (ert-deftest test-org-export/export-block () "Test export blocks transcoding." (should (equal "Success!\n" (org-test-with-temp-text "#+BEGIN_EXPORT backend\nSuccess!\n#+END_EXPORT" (org-export-as (org-export-create-backend :transcoders '((export-block . (lambda (b _c _i) (org-element-property :value b))) (section . (lambda (_s c _i) c)))))))) (should (equal "Success!\n" (org-test-with-temp-text "#+BEGIN_EXPORT backend\nSuccess!\n#+END_EXPORT" (org-export-as (org-export-create-backend :transcoders (list (cons 'export-block (lambda (b _c _i) (and (equal (org-element-property :type b) "BACKEND") (org-element-property :value b)))) (cons 'section (lambda (_s c _i) c))))))))) ;;; Export Snippets (ert-deftest test-org-export/export-snippet () "Test export snippets transcoding." ;; Standard test. (org-test-with-temp-text "@@test:A@@@@t:B@@" (let ((backend (org-test-default-backend))) (setf (org-export-backend-name backend) 'test) (setf (org-export-backend-transcoders backend) (cons (cons 'export-snippet (lambda (snippet _contents _info) (when (eq (org-export-snippet-backend snippet) 'test) (org-element-property :value snippet)))) (org-export-backend-transcoders backend))) (let ((org-export-snippet-translation-alist nil)) (should (equal (org-export-as backend) "A\n"))) (let ((org-export-snippet-translation-alist '(("t" . "test")))) (should (equal (org-export-as backend) "AB\n"))))) ;; Ignored export snippets do not remove any blank. (should (equal "begin end\n" (org-test-with-parsed-data "begin@@test:A@@ end" (org-export-data-with-backend tree (org-export-create-backend :transcoders '((paragraph . (lambda (paragraph contents info) contents)) (section . (lambda (section contents info) contents)))) info))))) ;;; Filters (ert-deftest test-org-export/filter-apply-functions () "Test `org-export-filter-apply-functions' specifications." ;; Functions are applied in order and return values are reduced. (should (equal "210" (org-export-filter-apply-functions (list (lambda (value &rest _) (concat "1" value)) (lambda (value &rest _) (concat "2" value))) "0" nil))) ;; Functions returning nil are skipped. (should (equal "20" (org-export-filter-apply-functions (list #'ignore (lambda (value &rest _) (concat "2" value))) "0" nil))) ;; If all functions are skipped, return the initial value. (should (equal "0" (org-export-filter-apply-functions (list #'ignore) "0" nil))) ;; If any function returns the empty string, final value is the ;; empty string. (should (equal "" (org-export-filter-apply-functions (list (lambda (_value &rest _) "") (lambda (value &rest _) (concat "2" value))) "0" nil))) ;; Any function returning the empty string short-circuits the ;; process. (should (org-export-filter-apply-functions (list (lambda (_value &rest _) "") (lambda (_value &rest _) (error "This shouldn't happen"))) "0" nil))) ;;; Footnotes (ert-deftest test-org-export/footnote-first-reference-p () "Test `org-export-footnote-first-reference-p' specifications." (should (equal '(t nil) (org-test-with-temp-text "Text[fn:1][fn:1]\n\n[fn:1] Definition" (let (result) (org-export-as (org-export-create-backend :transcoders `(,(cons 'footnote-reference (lambda (f _c i) (push (org-export-footnote-first-reference-p f i) result) "")) (section . (lambda (s c i) c)) (paragraph . (lambda (p c i) c)))) nil nil nil '(:with-footnotes t)) (nreverse result))))) ;; Limit check to DATA, when non-nil. (should (equal '(nil t) (org-test-with-parsed-data "Text[fn:1]\n* H\nText[fn:1]\n\n[fn:1] D1" (let (result) (org-element-map tree 'footnote-reference (lambda (ref) (push (org-export-footnote-first-reference-p ref info (org-element-map tree 'headline #'identity info t)) result)) info) (nreverse result))))) (should (equal '(t nil) (org-test-with-parsed-data "Text[fn:1]\n* H\nText[fn:1]\n\n[fn:1] D1" (let (result) (org-element-map tree 'footnote-reference (lambda (ref) (push (org-export-footnote-first-reference-p ref info) result)) info) (nreverse result))))) ;; If optional argument BODY-FIRST is non-nil, first find footnote ;; in the main body of the document. Otherwise, enter footnote ;; definitions when they are encountered. (should (equal '(t nil) (org-test-with-temp-text ":BODY:\nText[fn:1][fn:2]\n:END:\n\n[fn:1] Definition[fn:2]\n\n[fn:2] Inner" (let (result) (org-export-as (org-export-create-backend :transcoders `(,(cons 'footnote-reference (lambda (f _c i) (when (org-element-lineage f 'drawer) (push (org-export-footnote-first-reference-p f i nil) result)) "")) (drawer . (lambda (d c i) c)) (footnote-definition . (lambda (d c i) c)) (section . (lambda (s c i) c)) (paragraph . (lambda (p c i) c)))) nil nil nil '(:with-footnotes t)) (nreverse result))))) (should (equal '(t t) (org-test-with-temp-text ":BODY:\nText[fn:1][fn:2]\n:END:\n\n[fn:1] Definition[fn:2]\n\n[fn:2] Inner" (let (result) (org-export-as (org-export-create-backend :transcoders `(,(cons 'footnote-reference (lambda (f _c i) (when (org-element-lineage f 'drawer) (push (org-export-footnote-first-reference-p f i nil t) result)) "")) (drawer . (lambda (d c i) c)) (footnote-definition . (lambda (d c i) c)) (section . (lambda (s c i) c)) (paragraph . (lambda (p c i) c)))) nil nil nil '(:with-footnotes t)) (nreverse result)))))) (ert-deftest test-org-export/get-footnote-number () "Test `org-export-get-footnote-number' specifications." (should (equal '(1 2 1) (org-test-with-parsed-data "Text[fn:1][fn:2][fn:1]\n\n[fn:1] Def\n[fn:2] Def" (org-element-map tree 'footnote-reference (lambda (ref) (org-export-get-footnote-number ref info)) info)))) ;; Anonymous footnotes all get a new number. (should (equal '(1 2) (org-test-with-parsed-data "Text[fn::anon1][fn::anon2]" (org-element-map tree 'footnote-reference (lambda (ref) (org-export-get-footnote-number ref info)) info)))) ;; Test nested footnotes order. (should (equal '((1 . "1") (2 . "2") (3 . "3") (3 . "3") (4)) (org-test-with-parsed-data "Text[fn:1:A[fn:2]] [fn:3].\n\n[fn:2] B [fn:3] [fn::D].\n\n[fn:3] C." (org-element-map tree 'footnote-reference (lambda (ref) (cons (org-export-get-footnote-number ref info) (org-element-property :label ref))) info)))) ;; Limit number to provided DATA, when non-nil. (should (equal '(1) (org-test-with-parsed-data "Text[fn:1]\n* H\nText[fn:2]\n\n[fn:1] D1\n[fn:2] D2" (org-element-map tree 'footnote-reference (lambda (ref) (org-export-get-footnote-number ref info (org-element-map tree 'headline #'identity info t))) info)))) (should (equal '(1 2) (org-test-with-parsed-data "Text[fn:1]\n* H\nText[fn:2]\n\n[fn:1] D1\n[fn:2]" (org-element-map tree 'footnote-reference (lambda (ref) (org-export-get-footnote-number ref info)) info)))) ;; With a non-nil BODY-FIRST optional argument, first check body, ;; then footnote definitions. (should (equal '(("1" . 1) ("2" . 2) ("3" . 3) ("3" . 3)) (org-test-with-parsed-data "Text[fn:1][fn:2][fn:3]\n\n[fn:1] Def[fn:3]\n[fn:2] Def\n[fn:3] Def" (org-element-map tree 'footnote-reference (lambda (ref) (cons (org-element-property :label ref) (org-export-get-footnote-number ref info nil t))) info)))) (should (equal '(("1" . 1) ("2" . 3) ("3" . 2) ("3" . 2)) (org-test-with-parsed-data "Text[fn:1][fn:2][fn:3]\n\n[fn:1] Def[fn:3]\n[fn:2] Def\n[fn:3] Def" (org-element-map tree 'footnote-reference (lambda (ref) (cons (org-element-property :label ref) (org-export-get-footnote-number ref info nil))) info))))) (ert-deftest test-org-export/get-footnote-definition () "Test `org-export-get-footnote-definition' specifications." ;; Standard test. (should (equal "A\n" (org-element-interpret-data (org-test-with-parsed-data "Text[fn:1]\n\n[fn:1] A" (org-export-get-footnote-definition (org-element-map tree 'footnote-reference #'identity nil t) info))))) ;; Raise an error if no definition is found. (should-error (org-test-with-parsed-data "Text[fn:1]" (org-export-get-footnote-definition (org-element-map tree 'footnote-reference #'identity nil t) info))) ;; Find inline definitions. (should (equal "A" (org-element-interpret-data (org-test-with-parsed-data "Text[fn:1:A]" (org-export-get-footnote-definition (org-element-map tree 'footnote-reference #'identity nil t) info))))) ;; Find anonymous definitions. (should (equal "A" (org-element-interpret-data (org-test-with-parsed-data "Text[fn::A]" (org-export-get-footnote-definition (org-element-map tree 'footnote-reference #'identity nil t) info))))) ;; Find empty definitions. (should (equal "" (org-element-interpret-data (org-test-with-parsed-data "Text[fn:1]\n\n[fn:1]" (org-export-get-footnote-definition (org-element-map tree 'footnote-reference #'identity nil t) info))))) (should (equal "" (org-element-interpret-data (org-test-with-parsed-data "Text[fn:1:]" (org-export-get-footnote-definition (org-element-map tree 'footnote-reference #'identity nil t) info))))) (should (equal "" (org-element-interpret-data (org-test-with-parsed-data "Text[fn::]" (org-export-get-footnote-definition (org-element-map tree 'footnote-reference #'identity nil t) info)))))) (ert-deftest test-org-export/collect-footnote-definitions () "Test `org-export-collect-footnote-definitions' specifications." (should (= 4 (org-test-with-parsed-data "Text[fn:1:A[fn:2]] [fn:3]. \[fn:2] B [fn:3] [fn::D]. \[fn:3] C." (length (org-export-collect-footnote-definitions info))))) ;; Limit number to provided DATA, when non-nil. (should (equal '((1 "2")) (org-test-with-parsed-data "Text[fn:1]\n* H\nText[fn:2]\n\n[fn:1] D1\n[fn:2] D2" (mapcar #'butlast (org-export-collect-footnote-definitions info (org-element-map tree 'headline #'identity info t)))))) (should (equal '((1 "1") (2 "2")) (org-test-with-parsed-data "Text[fn:1]\n* H\nText[fn:2]\n\n[fn:1] D1\n[fn:2] D2" (mapcar #'butlast (org-export-collect-footnote-definitions info))))) ;; With optional argument BODY-FIRST, first check body, then ;; footnote definitions. (should (equal '("1" "3" "2" nil) (org-test-with-parsed-data "Text[fn:1:A[fn:2]] [fn:3]. \[fn:2] B [fn:3] [fn::D]. \[fn:3] C." (mapcar (lambda (e) (nth 1 e)) (org-export-collect-footnote-definitions info nil t))))) (should-not (equal '("1" "3" "2" nil) (org-test-with-parsed-data "Text[fn:1:A[fn:2]] [fn:3]. \[fn:2] B [fn:3] [fn::D]. \[fn:3] C." (mapcar (lambda (e) (nth 1 e)) (org-export-collect-footnote-definitions info)))))) (ert-deftest test-org-export/footnotes () "Miscellaneous tests on footnotes." (let ((org-footnote-section nil) (org-export-with-footnotes t)) ;; Read every type of footnote. (should (equal '((1 . "A\n") (2 . "C") (3 . "D")) (org-test-with-parsed-data "Text[fn:1] [fn:label:C] [fn::D]\n\n[fn:1] A\n" (org-element-map tree 'footnote-reference (lambda (ref) (let ((def (org-export-get-footnote-definition ref info))) (cons (org-export-get-footnote-number ref info) (if (eq (org-element-property :type ref) 'inline) (car def) (car (org-element-contents (car (org-element-contents def)))))))) info)))) ;; Export nested footnote in invisible definitions. (should (= 2 (org-test-with-temp-text "Text[fn:1]\n\n[fn:1] B [fn:2]\n\n[fn:2] C." (narrow-to-region (point) (line-end-position)) (catch 'exit (org-export-as (org-export-create-backend :transcoders '((section . (lambda (s c i) (throw 'exit (length (org-export-collect-footnote-definitions i)))))))))))) ;; Export footnotes defined outside parsing scope. (should (string-match "Out of scope" (org-test-with-temp-text "[fn:1] Out of scope * Title Paragraph[fn:1]" (org-export-as (org-test-default-backend) 'subtree)))) (should (string-match "Out of scope" (org-test-with-temp-text "[fn:1] Out of scope * Title Paragraph[fn:1]" (narrow-to-region (point) (point-max)) (org-export-as (org-test-default-backend))))) ;; Export nested footnotes defined outside parsing scope. (should (string-match "Very out of scope" (org-test-with-temp-text " \[fn:1] Out of scope[fn:2] \[fn:2] Very out of scope * Title Paragraph[fn:1]" (org-export-as (org-test-default-backend) 'subtree)))) (should (string-match "Very out of scope" (org-test-with-temp-text " \[fn:1] Out of scope[fn:2] \[fn:2] Very out of scope * Title Paragraph[fn:1]" (narrow-to-region (point) (point-max)) (org-export-as (org-test-default-backend))))) (should (string-match "D2" (org-test-with-temp-text " \[fn:1] Out of scope[fn:2:D2] * Title Paragraph[fn:1]" (narrow-to-region (point) (point-max)) (org-export-as (org-test-default-backend))))) ;; Export footnotes in pruned parts of tree. (should (string-match "Definition" (let ((org-export-exclude-tags '("noexport"))) (org-test-with-temp-text "* H\nText[fn:1]\n* H2 :noexport:\n[fn:1] Definition" (org-export-as (org-test-default-backend)))))) (should (string-match "Definition" (let ((org-export-select-tags '("export"))) (org-test-with-temp-text "* H :export:\nText[fn:1]\n* H2\n[fn:1] Definition" (org-export-as (org-test-default-backend)))))) ;; Export nested footnotes in pruned parts of tree. (should (string-match "D2" (let ((org-export-exclude-tags '("noexport"))) (org-test-with-temp-text "* H\nText[fn:1]\n* H2 :noexport:\n[fn:1] D1[fn:2]\n\n[fn:2] D2" (org-export-as (org-test-default-backend)))))) (should (string-match "D2" (let ((org-export-select-tags '("export"))) (org-test-with-temp-text "* H :export:\nText[fn:1]\n* H2\n[fn:1] D1[fn:2]\n\n[fn:2] D2" (org-export-as (org-test-default-backend)))))) ;; Handle uninterpreted data in pruned footnote definitions. (should-not (string-match "|" (let ((org-export-with-tables nil)) (org-test-with-temp-text "* H\nText[fn:1]\n* H2 :noexport:\n[fn:1]\n| a |" (org-export-as (org-test-default-backend)))))) ;; Footnotes without a definition should throw an error. (should-error (org-test-with-parsed-data "Text[fn:1]" (org-export-get-footnote-definition (org-element-map tree 'footnote-reference #'identity info t) info))) ;; Footnote section should be ignored in TOC and in headlines ;; numbering. (should (= 1 (let ((org-footnote-section "Footnotes")) (length (org-test-with-parsed-data "* H1\n* Footnotes\n" (org-export-collect-headlines info)))))) (should (equal '(2) (let ((org-footnote-section "Footnotes")) (org-test-with-parsed-data "* H1\n* Footnotes\n* H2" (org-element-map tree 'headline (lambda (hl) (when (equal (org-element-property :raw-value hl) "H2") (org-export-get-headline-number hl info))) info t))))))) ;;; Headlines and Inlinetasks (ert-deftest test-org-export/get-relative-level () "Test `org-export-get-relative-level' specifications." ;; Standard test. (should (equal '(1 2) (let ((org-odd-levels-only nil)) (org-test-with-parsed-data "* Headline 1\n** Headline 2" (org-element-map tree 'headline (lambda (h) (org-export-get-relative-level h info)) info))))) ;; Missing levels (should (equal '(1 3) (let ((org-odd-levels-only nil)) (org-test-with-parsed-data "** Headline 1\n**** Headline 2" (org-element-map tree 'headline (lambda (h) (org-export-get-relative-level h info)) info)))))) (ert-deftest test-org-export/low-level-p () "Test `org-export-low-level-p' specifications." (should (equal '(no yes) (let ((org-odd-levels-only nil)) (org-test-with-parsed-data "* Headline 1\n** Headline 2" (org-element-map tree 'headline (lambda (h) (if (org-export-low-level-p h info) 'yes 'no)) (plist-put info :headline-levels 1))))))) (ert-deftest test-org-export/get-headline-number () "Test `org-export-get-headline-number' specifications." ;; Standard test. (should (equal '((1) (1 1)) (let ((org-odd-levels-only nil)) (org-test-with-parsed-data "* Headline 1\n** Headline 2" (org-element-map tree 'headline (lambda (h) (org-export-get-headline-number h info)) info))))) ;; Missing levels are replaced with 0. (should (equal '((1) (1 0 1)) (let ((org-odd-levels-only nil)) (org-test-with-parsed-data "* Headline 1\n*** Headline 2" (org-element-map tree 'headline (lambda (h) (org-export-get-headline-number h info)) info)))))) (ert-deftest test-org-export/numbered-headline-p () "Test `org-export-numbered-headline-p' specifications." ;; If `:section-numbers' is nil, never number headlines. (should-not (org-test-with-parsed-data "* Headline" (org-element-map tree 'headline (lambda (h) (org-export-numbered-headline-p h info)) (plist-put info :section-numbers nil)))) ;; If `:section-numbers' is a number, only number headlines with ;; a level greater that it. (should (equal '(yes no) (org-test-with-parsed-data "* Headline 1\n** Headline 2" (org-element-map tree 'headline (lambda (h) (if (org-export-numbered-headline-p h info) 'yes 'no)) (plist-put info :section-numbers 1))))) ;; Otherwise, headlines are always numbered. (should (org-test-with-parsed-data "* Headline" (org-element-map tree 'headline (lambda (h) (org-export-numbered-headline-p h info)) (plist-put info :section-numbers t)))) ;; With #+OPTIONS: num:nil all headlines are unnumbered. (should-not (org-test-with-parsed-data "* H\n#+OPTIONS: num:nil" (org-export-numbered-headline-p (org-element-map tree 'headline 'identity info t) info))) ;; Headlines with a non-nil UNNUMBERED property are not numbered. (should-not (org-test-with-parsed-data "* H\n:PROPERTIES:\n:UNNUMBERED: t\n:END:" (org-export-numbered-headline-p (org-element-map tree 'headline #'identity info t) info))) ;; UNNUMBERED is inherited. (should (equal '(unnumbered numbered unnumbered) (org-test-with-parsed-data "* H :PROPERTIES: :UNNUMBERED: t :END: ** H2 :PROPERTIES: :UNNUMBERED: nil :END: ** H3" (org-element-map tree 'headline (lambda (h) (if (org-export-numbered-headline-p h info) 'numbered 'unnumbered)) info))))) (ert-deftest test-org-export/number-to-roman () "Test `org-export-number-to-roman' specifications." ;; If number is negative, return it as a string. (should (equal (org-export-number-to-roman -1) "-1")) ;; Otherwise, return it as a roman number. (should (equal (org-export-number-to-roman 1449) "MCDXLIX"))) (ert-deftest test-org-export/get-optional-title () "Test `org-export-get-alt-title' specifications." ;; If ALT_TITLE property is defined, use it. (should (equal '("opt") (org-test-with-parsed-data "* Headline\n:PROPERTIES:\n:ALT_TITLE: opt\n:END:" (org-export-get-alt-title (org-element-map tree 'headline 'identity info t) info)))) ;; Otherwise, fall-back to regular title. (should (equal '("Headline") (org-test-with-parsed-data "* Headline" (org-export-get-alt-title (org-element-map tree 'headline 'identity info t) info))))) (ert-deftest test-org-export/get-tags () "Test `org-export-get-tags' specifications." ;; Standard test: tags which are not a select tag, an exclude tag, ;; or specified as optional argument shouldn't be ignored. (should (org-test-with-parsed-data "* Headline :tag:" (org-export-get-tags (org-element-map tree 'headline 'identity info t) info))) ;; Tags provided in the optional argument are ignored. (should-not (org-test-with-parsed-data "* Headline :ignore:" (org-export-get-tags (org-element-map tree 'headline 'identity info t) info '("ignore")))) ;; Allow tag inheritance. (should (equal '(("tag") ("tag")) (org-test-with-parsed-data "* Headline :tag:\n** Sub-heading" (org-element-map tree 'headline (lambda (hl) (org-export-get-tags hl info nil t)) info)))) ;; Tag inheritance checks FILETAGS keywords. (should (equal '(("a" "b" "tag")) (org-test-with-parsed-data "#+FILETAGS: :a:b:\n* Headline :tag:" (org-element-map tree 'headline (lambda (hl) (org-export-get-tags hl info nil t)) info))))) (ert-deftest test-org-export/get-node-property () "Test`org-export-get-node-property' specifications." ;; Standard test. (should (equal "value" (org-test-with-parsed-data "* Headline :PROPERTIES: :prop: value :END:" (org-export-get-node-property :PROP (org-element-map tree 'headline 'identity nil t))))) ;; Test inheritance. (should (equal "value" (org-test-with-parsed-data "* Parent :PROPERTIES: :prop: value :END: ** Headline Paragraph" (org-export-get-node-property :PROP (org-element-map tree 'paragraph 'identity nil t) t)))) ;; Cannot return a value before the first headline. (should-not (org-test-with-parsed-data "Paragraph * Headline :PROPERTIES: :prop: value :END:" (org-export-get-node-property :PROP (org-element-map tree 'paragraph 'identity nil t))))) (ert-deftest test-org-export/get-category () "Test `org-export-get-category' specifications." ;; Standard test. (should (equal "value" (org-test-with-parsed-data "* Headline :PROPERTIES: :CATEGORY: value :END:" (org-export-get-category (org-element-map tree 'headline 'identity nil t) info)))) ;; Test inheritance from a parent headline. (should (equal '("value" "value") (org-test-with-parsed-data "* Headline1 :PROPERTIES: :CATEGORY: value :END: ** Headline2" (org-element-map tree 'headline (lambda (hl) (org-export-get-category hl info)) info)))) ;; Test inheritance from #+CATEGORY keyword (should (equal "value" (org-test-with-parsed-data "#+CATEGORY: value * Headline" (org-export-get-category (org-element-map tree 'headline 'identity nil t) info)))) ;; Test inheritance from file name. (should (equal "test" (org-test-with-parsed-data "* Headline" (let ((info (plist-put info :input-file "~/test.org"))) (org-export-get-category (org-element-map tree 'headline 'identity nil t) info))))) ;; Fall-back value. (should (equal "???" (org-test-with-parsed-data "* Headline" (org-export-get-category (org-element-map tree 'headline 'identity nil t) info))))) (ert-deftest test-org-export/first-sibling-p () "Test `org-export-first-sibling-p' specifications." ;; Standard test. (should (equal '(yes yes no) (org-test-with-parsed-data "* H\n** H 2\n** H 3" (org-element-map tree 'headline (lambda (h) (if (org-export-first-sibling-p h info) 'yes 'no)) info)))) (should (equal '(yes no) (org-test-with-parsed-data "- item\n\n para" (org-element-map tree 'paragraph (lambda (h) (if (org-export-first-sibling-p h info) 'yes 'no)) info)))) ;; Ignore sections for headlines. (should (equal '(yes yes) (org-test-with-parsed-data "* H\nSection\n** H 2" (org-element-map tree 'headline (lambda (h) (if (org-export-first-sibling-p h info) 'yes 'no)) info)))) ;; Ignore headlines not exported. (should (equal '(yes) (let ((org-export-exclude-tags '("ignore"))) (org-test-with-parsed-data "* Headline :ignore:\n* Headline 2" (org-element-map tree 'headline (lambda (h) (if (org-export-first-sibling-p h info) 'yes 'no)) info)))))) (ert-deftest test-org-export/last-sibling-p () "Test `org-export-last-sibling-p' specifications." ;; Standard test. (should (equal '(yes no yes) (org-test-with-parsed-data "* Headline\n** Headline 2\n** Headline 3" (org-element-map tree 'headline (lambda (h) (if (org-export-last-sibling-p h info) 'yes 'no)) info)))) (should (equal '(no yes) (org-test-with-parsed-data "- item\n\n para" (org-element-map tree 'paragraph (lambda (h) (if (org-export-last-sibling-p h info) 'yes 'no)) info)))) ;; Ignore headlines not exported. (should (equal '(yes) (let ((org-export-exclude-tags '("ignore"))) (org-test-with-parsed-data "* Headline\n* Headline 2 :ignore:" (org-element-map tree 'headline (lambda (h) (if (org-export-last-sibling-p h info) 'yes 'no)) info))))) ;; Handle gracefully discontinuous headings. (should (equal '(yes yes) (org-test-with-parsed-data "** S\n* H" (org-element-map tree 'headline (lambda (h) (if (org-export-last-sibling-p h info) 'yes 'no))))))) (ert-deftest test-org-export/handle-inlinetasks () "Test inlinetask export." ;; Inlinetask with an exclude tag. (when (featurep 'org-inlinetask) (should (equal "" (let ((org-inlinetask-min-level 3) org-export-filter-body-functions org-export-filter-final-output-functions) (org-test-with-temp-text "*** Inlinetask :noexp:\nContents\n*** end" (org-export-as (org-test-default-backend) nil nil nil '(:exclude-tags ("noexp"))))))) ;; Inlinetask with an include tag. (should (equal "* H2\n*** Inline :exp:\n" (let ((org-inlinetask-min-level 3) (org-tags-column 0)) (org-test-with-temp-text "* H1\n* H2\n*** Inline :exp:" (org-export-as (org-test-default-backend) nil nil nil '(:select-tags ("exp"))))))) ;; Ignore inlinetask with a TODO keyword and tasks excluded. (should (equal "" (let ((org-todo-keywords '((sequence "TODO" "DONE"))) (org-inlinetask-min-level 3) org-export-filter-body-functions org-export-filter-final-output-functions) (org-test-with-temp-text "*** TODO Inline" (org-export-as (org-test-default-backend) nil nil nil '(:with-tasks nil)))))))) ;;; Keywords (ert-deftest test-org-export/get-date () "Test `org-export-get-date' specifications." ;; Return a properly formatted string when ;; `org-export-date-timestamp-format' is non-nil and DATE keyword ;; consists in a single timestamp. (should (equal "29 03 2012" (let ((org-export-date-timestamp-format "%d %m %Y")) (org-test-with-parsed-data "#+DATE: <2012-03-29 Thu>" (org-export-get-date info))))) ;; Return a secondary string otherwise. (should-not (stringp (let ((org-export-date-timestamp-format nil)) (org-test-with-parsed-data "#+DATE: <2012-03-29 Thu>" (org-export-get-date info))))) (should (equal '("Date") (org-test-with-parsed-data "#+DATE: Date" (org-export-get-date info)))) ;; Optional argument has precedence over ;; `org-export-date-timestamp-format'. (should (equal "29 03" (let ((org-export-date-timestamp-format "%d %m %Y")) (org-test-with-parsed-data "#+DATE: <2012-03-29 Thu>" (org-export-get-date info "%d %m")))))) ;;; Links (ert-deftest test-org-export/custom-protocol-maybe () "Test `org-export-custom-protocol-maybe' specifications." (should (string-match "success" (progn (org-link-set-parameters "foo" :export (lambda (_p _d _f _i) "success")) (org-export-string-as "[[foo:path]]" (org-export-create-backend :name 'test :transcoders '((section . (lambda (s c i) c)) (paragraph . (lambda (p c i) c)) (link . (lambda (l c i) (or (org-export-custom-protocol-maybe l c 'test i) "failure"))))))))) (should-not (string-match "success" (progn (org-link-set-parameters "foo" :export (lambda (_p _d f _i) (and (eq f 'test) "success"))) (org-export-string-as "[[foo:path]]" (org-export-create-backend :name 'no-test :transcoders '((section . (lambda (s c i) c)) (paragraph . (lambda (p c i) c)) (link . (lambda (l c i) (or (org-export-custom-protocol-maybe l c 'no-test i) "failure"))))))))) ;; Ignore anonymous backends. (should-not (string-match "success" (progn (org-link-set-parameters "foo" :export (lambda (_p _d f _i) (and (eq f 'test) "success"))) (org-export-string-as "[[foo:path]]" (org-export-create-backend :transcoders '((section . (lambda (s c i) c)) (paragraph . (lambda (p c i) c)) (link . (lambda (l c i) (or (org-export-custom-protocol-maybe l c nil i) "failure")))))))))) (ert-deftest test-org-export/get-coderef-format () "Test `org-export-get-coderef-format' specifications." ;; A link without description returns "%s" (should (equal (org-export-get-coderef-format "(ref:line)" nil) "%s")) ;; Return "%s" when path is matched within description. (should (equal (org-export-get-coderef-format "path" "desc (path)") "desc %s")) ;; Otherwise return description. (should (equal (org-export-get-coderef-format "path" "desc") "desc"))) (ert-deftest test-org-export/inline-image-p () "Test `org-export-inline-image-p' specifications." (should (org-export-inline-image-p (org-test-with-temp-text "[[#id]]" (org-element-map (org-element-parse-buffer) 'link 'identity nil t)) '(("custom-id" . "id"))))) (ert-deftest test-org-export/insert-image-links () "Test `org-export-insert-image-links' specifications." (should-not (member "file" (org-test-with-parsed-data "[[https://orgmode.org][file:image.png]]" (org-element-map tree 'link (lambda (l) (org-element-property :type l)))))) (should (member "file" (org-test-with-parsed-data "[[https://orgmode.org][file:image.png]]" (org-element-map (org-export-insert-image-links tree info) 'link (lambda (l) (org-element-property :type l)))))) ;; Properly set `:parent' property when replace contents with image ;; link. (should (memq 'link (org-test-with-parsed-data "[[https://orgmode.org][file:image.png]]" (org-element-map (org-export-insert-image-links tree info) 'link (lambda (l) (org-element-type (org-element-property :parent l))))))) ;; With optional argument RULES, recognize different links as ;; images. (should-not (member "file" (org-test-with-parsed-data "[[https://orgmode.org][file:image.xxx]]" (org-element-map (org-export-insert-image-links tree info) 'link (lambda (l) (org-element-property :type l)))))) (should (member "file" (org-test-with-parsed-data "[[https://orgmode.org][file:image.xxx]]" (org-element-map (org-export-insert-image-links tree info '(("file" . "xxx"))) 'link (lambda (l) (org-element-property :type l)))))) ;; If an image link was included from another file, make sure to ;; shift any relative path accordingly. (should (string-prefix-p "file:org-includee-" (let* ((subdir (make-temp-file "org-includee-" t)) (includee (expand-file-name "includee.org" subdir)) (includer (make-temp-file "org-includer-"))) (write-region "file:foo.png" nil includee) (write-region (format "#+INCLUDE: %S" (file-relative-name includee temporary-file-directory)) nil includer) (let ((buffer (find-file-noselect includer t))) (unwind-protect (with-current-buffer buffer (org-export-as (org-export-create-backend :transcoders '((section . (lambda (_s c _i) c)) (paragraph . (lambda (_p c _i) c)) (link . (lambda (l c _i) (org-element-link-interpreter l c)))) :filters '((:filter-parse-tree (lambda (d _b i) (org-export-insert-image-links d i))))))) (when (buffer-live-p buffer) (with-current-buffer buffer (set-buffer-modified-p nil)) (kill-buffer buffer)) (when (file-exists-p subdir) (delete-directory subdir t)) (when (file-exists-p includer) (delete-file includer))))))) (should (string-match-p "file:org-includee-.+?foo\\.png" (let* ((subdir (make-temp-file "org-includee-" t)) (includee (expand-file-name "includee.org" subdir)) (includer (make-temp-file "org-includer-"))) (write-region "[[https://orgmode.org][file:foo.png]]" nil includee) (write-region (format "#+INCLUDE: %S" (file-relative-name includee temporary-file-directory)) nil includer) (let ((buffer (find-file-noselect includer t))) (unwind-protect (with-current-buffer buffer (org-export-as (org-export-create-backend :transcoders '((section . (lambda (_s c _i) c)) (paragraph . (lambda (_p c _i) c)) (link . (lambda (l c _i) (org-element-link-interpreter l c)))) :filters '((:filter-parse-tree (lambda (d _b i) (org-export-insert-image-links d i))))))) (when (buffer-live-p buffer) (with-current-buffer buffer (set-buffer-modified-p nil)) (kill-buffer buffer)) (when (file-exists-p subdir) (delete-directory subdir t)) (when (file-exists-p includer) (delete-file includer))))))) (should (string-match-p "file:org-includee.+?file:org-includee" (let* ((subdir (make-temp-file "org-includee-" t)) (includee (expand-file-name "includee.org" subdir)) (includer (make-temp-file "org-includer-"))) (write-region "[[file:bar.png][file:foo.png]]" nil includee) (write-region (format "#+INCLUDE: %S" (file-relative-name includee temporary-file-directory)) nil includer) (let ((buffer (find-file-noselect includer t))) (unwind-protect (with-current-buffer buffer (org-export-as (org-export-create-backend :transcoders '((section . (lambda (_s c _i) c)) (paragraph . (lambda (_p c _i) c)) (link . (lambda (l c _i) (org-element-link-interpreter l c)))) :filters '((:filter-parse-tree (lambda (d _b i) (org-export-insert-image-links d i))))))) (when (buffer-live-p buffer) (with-current-buffer buffer (set-buffer-modified-p nil)) (kill-buffer buffer)) (when (file-exists-p subdir) (delete-directory subdir t)) (when (file-exists-p includer) (delete-file includer)))))))) (ert-deftest test-org-export/fuzzy-link () "Test fuzzy links specifications." ;; Link to an headline should return headline's number. (should ;; Note: Headline's number is in fact a list of numbers. (equal '(2) (org-test-with-parsed-data "Paragraph.\n* Head1\n* Head2\n* Head3\n[[Head2]]" (org-element-map tree 'link (lambda (link) (org-export-get-ordinal (org-export-resolve-fuzzy-link link info) info)) info t)))) ;; Link to a target in an item should return item's number. (should ;; Note: Item's number is in fact a list of numbers. (equal '(1 2) (org-test-with-parsed-data "- Item1\n - Item11\n - <>Item12\n- Item2\n\n\n[[test]]" (org-element-map tree 'link (lambda (link) (org-export-get-ordinal (org-export-resolve-fuzzy-link link info) info)) info t)))) ;; Link to a target in a footnote should return footnote's number. (should (equal '(2 3) (org-test-with-parsed-data " Paragraph[fn:1][fn:2][fn:lbl3:C<>][[test]][[target]] \[fn:1] A \[fn:2] <>B" (org-element-map tree 'link (lambda (link) (org-export-get-ordinal (org-export-resolve-fuzzy-link link info) info)) info)))) ;; Link to a named element should return sequence number of that ;; element. (should (= 2 (org-test-with-parsed-data "#+NAME: tbl1\n|1|2|\n#+NAME: tbl2\n|3|4|\n#+NAME: tbl3\n|5|6|\n[[tbl2]]" (org-element-map tree 'link (lambda (link) (org-export-get-ordinal (org-export-resolve-fuzzy-link link info) info)) info t)))) ;; Link to a target not within an item, a table, a footnote ;; reference or definition should return section number. (should (equal '(2) (org-test-with-parsed-data "* Head1\n* Head2\nParagraph<>\n* Head3\n[[target]]" (org-element-map tree 'link (lambda (link) (org-export-get-ordinal (org-export-resolve-fuzzy-link link info) info)) info t)))) ;; Space are not significant when matching a fuzzy link. (should (org-test-with-parsed-data "* Head 1\n[[Head\n 1]]" (org-element-map tree 'link (lambda (link) (org-export-resolve-fuzzy-link link info)) info t))) ;; Statistics cookies are ignored for headline match. (should (org-test-with-parsed-data "* Head [0/0]\n[[Head]]" (org-element-map tree 'link (lambda (link) (org-export-resolve-fuzzy-link link info)) info t))) (should (org-test-with-parsed-data "* Head [100%]\n[[Head]]" (org-element-map tree 'link (lambda (link) (org-export-resolve-fuzzy-link link info)) info t))) ;; Case is not significant when matching headings and radio targets. (should (org-test-with-parsed-data "* Head line\n[[head line]]" (org-element-map tree 'link (lambda (link) (org-export-resolve-fuzzy-link link info)) info t)))) (ert-deftest test-org-export/resolve-link () "Test `org-export-resolve-link' specifications." (should ;; Match ID links (equal "Headline1" (org-test-with-parsed-data "* Headline1 :PROPERTIES: :ID: aaaa :END: * Headline2" (org-element-property :raw-value (org-export-resolve-link "#aaaa" info))))) ;; Match Custom ID links (should (equal "Headline1" (org-test-with-parsed-data "* Headline1 :PROPERTIES: :CUSTOM_ID: test :END: * Headline2" (org-element-property :raw-value (org-export-resolve-link "#test" info))))) ;; Match fuzzy links (should (equal "B" (org-test-with-parsed-data "* A\n* B\n* C" (org-element-property :raw-value (org-export-resolve-link "B" info)))))) (defun test-org-gen-loc-list(text type) (org-test-with-parsed-data text (org-element-map tree type (lambda (el) (or (org-export-get-loc el info) 'no-loc))))) (ert-deftest test-org-export/get-loc () "Test `org-export-get-loc' specifications." (should ;; "-n" resets line number. (equal '(0) (test-org-gen-loc-list "#+BEGIN_EXAMPLE -n\n Text\n#+END_EXAMPLE" 'example-block))) ;; The first "+n" has 0 lines before it (should (equal '(0) (test-org-gen-loc-list "#+BEGIN_EXAMPLE +n\n Text\n#+END_EXAMPLE" 'example-block))) ;; "-n 10" resets line number but has "9 lines" before it. (should (equal '(9) (test-org-gen-loc-list "#+BEGIN_EXAMPLE -n 10\n Text\n#+END_EXAMPLE" 'example-block))) ;; -n10 with two lines then +n 15 (should (equal '(9 25) (test-org-gen-loc-list " #+BEGIN_EXAMPLE -n 10 Text_10 Second line(11) #+END_EXAMPLE #+BEGIN_EXAMPLE +n 15 Text line (11 + 15) #+END_EXAMPLE" 'example-block))) (should (equal '(9 19 0) (test-org-gen-loc-list " #+BEGIN_EXAMPLE -n 10 Text #+END_EXAMPLE #+BEGIN_EXAMPLE +n 10 Text #+END_EXAMPLE #+BEGIN_EXAMPLE -n Text #+END_EXAMPLE" 'example-block))) ;; an Example Block without -n does not add to the line count. (should (equal '(9 no-loc 19) (test-org-gen-loc-list " #+BEGIN_EXAMPLE -n 10 Text #+END_EXAMPLE #+BEGIN_EXAMPLE Text #+END_EXAMPLE #+BEGIN_EXAMPLE +n 10 Text #+END_EXAMPLE" 'example-block))) ;; "-n" resets line number. (should (equal '(0) (test-org-gen-loc-list "#+BEGIN_SRC emacs-lisp -n \n (- 1 1) \n#+END_SRC" 'src-block))) ;; The first "+n" has 0 lines before it. (should (equal '(0) (test-org-gen-loc-list "#+BEGIN_SRC emacs-lisp +n \n (+ 0 (- 1 1))\n#+END_SRC" 'src-block))) ;; "-n 10" resets line number but has "9 lines" before it. (should (equal '(9) (test-org-gen-loc-list "#+BEGIN_SRC emacs-lisp -n 10\n (- 10 1)\n#+END_SRC" 'src-block))) (should (equal '(9 25) (test-org-gen-loc-list " #+BEGIN_SRC emacs-lisp -n 10 (- 10 1) (+ (- 10 1) 1) #+END_SRC #+BEGIN_SRC emacs-lisp +n 15 (+ (- 10 1) 2 (- 15 1)) #+END_SRC" 'src-block))) (should (equal '(9 19 0) (test-org-gen-loc-list " #+BEGIN_SRC emacs-lisp -n 10 (- 10 1) #+END_SRC #+BEGIN_SRC emacs-lisp +n 10 (+ (- 10 1) 1 (- 10 1)) #+END_SRC #+BEGIN_SRC emacs-lisp -n (- 1 1) #+END_SRC" 'src-block))) ;; A SRC Block without -n does not add to the line count. (should (equal '(9 no-loc 19) (test-org-gen-loc-list "#+BEGIN_SRC emacs-lisp -n 10 (+ (-10 1) 1) #+END_SRC #+BEGIN_SRC emacs-lisp (+ 2 2) #+END_SRC #+BEGIN_SRC emacs-lisp +n 10 (+ (- 10 1) 1 (- 10 1)) #+END_SRC" 'src-block)))) (ert-deftest test-org-export/resolve-coderef () "Test `org-export-resolve-coderef' specifications." (let ((org-coderef-label-format "(ref:%s)")) ;; A link to a "-n -k -r" block returns line number. (should (= 1 (org-test-with-parsed-data "#+BEGIN_EXAMPLE -n -k -r\nText (ref:coderef)\n#+END_EXAMPLE" (org-export-resolve-coderef "coderef" info)))) (should (= 10 (org-test-with-parsed-data "#+BEGIN_EXAMPLE -n 10 -k -r\nText (ref:coderef)\n#+END_EXAMPLE" (org-export-resolve-coderef "coderef" info)))) (should (= 135 (org-test-with-parsed-data "#+BEGIN_EXAMPLE -n 10 -k -r\nText \n#+END_EXAMPLE\n #+BEGIN_EXAMPLE +n 125 -k -r\nText (ref:coderef)\n#+END_EXAMPLE" (org-export-resolve-coderef "coderef" info)))) (should (= 1 (org-test-with-parsed-data "#+BEGIN_SRC emacs-lisp -n -k -r\n(+ 1 1) (ref:coderef)\n#+END_SRC" (org-export-resolve-coderef "coderef" info)))) (should (= 10 (org-test-with-parsed-data "#+BEGIN_SRC emacs-lisp -n 10 -k -r\n(+ 1 1) (ref:coderef)\n#+END_SRC" (org-export-resolve-coderef "coderef" info)))) (should (= 135 (org-test-with-parsed-data "#+BEGIN_SRC emacs-lisp -n 10 -k -r\n(+ 1 1) \n#+END_SRC\n #+BEGIN_SRC emacs-lisp +n 125 -k -r\n(+ 1 1) (ref:coderef)\n#+END_SRC" (org-export-resolve-coderef "coderef" info)))) ;; A link to a "-n -r" block returns line number. (should (= 1 (org-test-with-parsed-data "#+BEGIN_EXAMPLE -n -r\nText (ref:coderef)\n#+END_EXAMPLE" (org-export-resolve-coderef "coderef" info)))) (should (= 10 (org-test-with-parsed-data "#+BEGIN_EXAMPLE -n 10 -r\nText (ref:coderef)\n#+END_EXAMPLE" (org-export-resolve-coderef "coderef" info)))) (should (= 135 (org-test-with-parsed-data "#+BEGIN_EXAMPLE +n 10 -r\nText \n#+END_EXAMPLE #+BEGIN_EXAMPLE +n 125 -r\nText (ref:coderef)\n#+END_EXAMPLE" (org-export-resolve-coderef "coderef" info)))) (should (= 1 (org-test-with-parsed-data "#+BEGIN_SRC emacs-lisp -n -r\n(+ 1 1) (ref:coderef)\n#+END_SRC" (org-export-resolve-coderef "coderef" info)))) (should (= 10 (org-test-with-parsed-data "#+BEGIN_SRC emacs-lisp -n10 -r\n(+ 1 1) (ref:coderef)\n#+END_SRC" (org-export-resolve-coderef "coderef" info)))) (should (= 135 (org-test-with-parsed-data "#+BEGIN_SRC emacs-lisp -n10 -r\n(+ 1 1) \n#+END_SRC #+BEGIN_SRC emacs-lisp +n125 -r\n(+ 1 1) (ref:coderef)\n#+END_SRC" (org-export-resolve-coderef "coderef" info)))) ;; A link to a "-n" block returns coderef. (should (equal "coderef" (org-test-with-parsed-data "#+BEGIN_SRC emacs-lisp -n\n(+ 1 1) (ref:coderef)\n#+END_SRC" (org-export-resolve-coderef "coderef" info)))) (should (equal "coderef" (org-test-with-parsed-data "#+BEGIN_EXAMPLE -n\nText (ref:coderef)\n#+END_EXAMPLE" (org-export-resolve-coderef "coderef" info)))) ;; A link to a "-r" block returns line number. (should (= 1 (org-test-with-parsed-data "#+BEGIN_SRC emacs-lisp -r\n(+ 1 1) (ref:coderef)\n#+END_SRC" (org-export-resolve-coderef "coderef" info)))) (should (= 1 (org-test-with-parsed-data "#+BEGIN_EXAMPLE -r\nText (ref:coderef)\n#+END_EXAMPLE" (org-export-resolve-coderef "coderef" info)))) ;; A link to a block without a switch returns coderef. (should (equal "coderef" (org-test-with-parsed-data "#+BEGIN_SRC emacs-lisp\n(+ 1 1) (ref:coderef)\n#+END_SRC" (org-export-resolve-coderef "coderef" info)))) (org-test-with-parsed-data "#+BEGIN_EXAMPLE\nText (ref:coderef)\n#+END_EXAMPLE" (should (equal (org-export-resolve-coderef "coderef" info) "coderef"))) ;; Correctly handle continued line numbers. A "+n" switch should ;; resume numbering from previous block with numbered lines, ;; ignoring blocks not numbering lines in the process. A "-n" ;; switch resets count. (should (equal '(2 1) (org-test-with-parsed-data " #+BEGIN_EXAMPLE -n Text. #+END_EXAMPLE #+BEGIN_SRC emacs-lisp \(- 1 1) #+END_SRC #+BEGIN_SRC emacs-lisp +n -r \(+ 1 1) (ref:addition) #+END_SRC #+BEGIN_EXAMPLE -n -r Another text. (ref:text) #+END_EXAMPLE" (list (org-export-resolve-coderef "addition" info) (org-export-resolve-coderef "text" info))))) ;; Recognize coderef with user-specified syntax. (should (equal "text" (org-test-with-parsed-data "#+BEGIN_EXAMPLE -l \"[ref:%s]\"\nText. [ref:text]\n#+END_EXAMPLE" (org-export-resolve-coderef "text" info)))) ;; Unresolved coderefs raise a `org-link-broken' signal. (should (condition-case nil (org-test-with-parsed-data "#+BEGIN_SRC emacs-lisp\n(+ 1 1)\n#+END_SRC" (org-export-resolve-coderef "unknown" info)) (org-link-broken t))))) (ert-deftest test-org-export/resolve-fuzzy-link () "Test `org-export-resolve-fuzzy-link' specifications." ;; Match target objects. (should (org-test-with-parsed-data "<> [[target]]" (org-export-resolve-fuzzy-link (org-element-map tree 'link 'identity info t) info))) ;; Match named elements. (should (org-test-with-parsed-data "#+NAME: target\nParagraph\n\n[[target]]" (org-export-resolve-fuzzy-link (org-element-map tree 'link 'identity info t) info))) ;; Match exact headline's name. (should (org-test-with-parsed-data "* My headline\n[[My headline]]" (org-export-resolve-fuzzy-link (org-element-map tree 'link 'identity info t) info))) ;; Targets objects have priority over headline titles. (should (eq 'target (org-test-with-parsed-data "* target\n<>[[target]]" (org-element-type (org-export-resolve-fuzzy-link (org-element-map tree 'link 'identity info t) info))))) ;; Named elements have priority over headline titles. (should (eq 'paragraph (org-test-with-parsed-data "* target\n#+NAME: target\nParagraph\n\n[[target]]" (org-element-type (org-export-resolve-fuzzy-link (org-element-map tree 'link 'identity info t) info))))) ;; If link's path starts with a "*", only match headline titles, ;; though. (should (eq 'headline (org-test-with-parsed-data "* target\n#+NAME: target\n<>\n\n[[*target]]" (org-element-type (org-export-resolve-fuzzy-link (org-element-map tree 'link 'identity info t) info))))) ;; Raise a `org-link-broken' signal if no match. (should (org-test-with-parsed-data "[[target]]" (condition-case nil (org-export-resolve-fuzzy-link (org-element-map tree 'link #'identity info t) info) (org-link-broken t)))) ;; Match fuzzy link even when before first headline. (should (eq 'headline (org-test-with-parsed-data "[[hl]]\n* hl" (org-element-type (org-export-resolve-fuzzy-link (org-element-map tree 'link 'identity info t) info))))) ;; Handle escaped fuzzy links. (should (org-test-with-parsed-data "* [foo]\n[[\\[foo\\]]]" (org-export-resolve-fuzzy-link (org-element-map tree 'link #'identity info t) info)))) (ert-deftest test-org-export/resolve-id-link () "Test `org-export-resolve-id-link' specifications." ;; Regular test for custom-id link. (should (equal '("Headline1") (org-test-with-parsed-data "* Headline1 :PROPERTIES: :CUSTOM_ID: test :END: * Headline 2 \[[#test]]" (org-element-property :title (org-export-resolve-id-link (org-element-map tree 'link 'identity info t) info))))) ;; Raise a `org-link-broken' signal on failing searches. (should (org-test-with-parsed-data "* Headline1 :PROPERTIES: :CUSTOM_ID: test :END: * Headline 2 \[[#no-match]]" (condition-case nil (org-export-resolve-id-link (org-element-map tree 'link #'identity info t) info) (org-link-broken t)))) ;; Test for internal id target. (should (equal '("Headline1") (org-test-with-parsed-data "* Headline1 :PROPERTIES: :ID: aaaa :END: * Headline 2 \[[id:aaaa]]" (org-element-property :title (org-export-resolve-id-link (org-element-map tree 'link 'identity info t) info))))) ;; Test for external id target. (should (equal "external-file" (org-test-with-parsed-data "[[id:aaaa]]" (org-export-resolve-id-link (org-element-map tree 'link 'identity info t) (org-combine-plists info '(:id-alist (("aaaa" . "external-file"))))))))) (ert-deftest test-org-export/resolve-radio-link () "Test `org-export-resolve-radio-link' specifications." ;; Standard test. (should (org-test-with-temp-text "<<>> radio" (org-update-radio-target-regexp) (let* ((tree (org-element-parse-buffer)) (info `(:parse-tree ,tree))) (org-export-resolve-radio-link (org-element-map tree 'link 'identity info t) info)))) ;; Radio targets are case-insensitive. (should (org-test-with-temp-text "<<>> radio" (org-update-radio-target-regexp) (let* ((tree (org-element-parse-buffer)) (info `(:parse-tree ,tree))) (org-export-resolve-radio-link (org-element-map tree 'link 'identity info t) info)))) ;; Radio target with objects. (should (org-test-with-temp-text "<<>> radio \\alpha" (org-update-radio-target-regexp) (let* ((tree (org-element-parse-buffer)) (info `(:parse-tree ,tree))) (org-export-resolve-radio-link (org-element-map tree 'link 'identity info t) info)))) ;; Radio target with objects at its beginning. (should (org-test-with-temp-text "<<<\\alpha radio>>> \\alpha radio" (org-update-radio-target-regexp) (let* ((tree (org-element-parse-buffer)) (info `(:parse-tree ,tree))) (org-export-resolve-radio-link (org-element-map tree 'link 'identity info t) info)))) ;; Radio link next to an apostrophe. (should (org-test-with-temp-text "<<>> radio's" (org-update-radio-target-regexp) (let* ((tree (org-element-parse-buffer)) (info `(:parse-tree ,tree))) (org-export-resolve-radio-link (org-element-map tree 'link 'identity info t) info)))) ;; Multiple radio targets. (should (equal '("radio1" "radio2") (org-test-with-temp-text "<<>> <<>> radio1 radio2" (org-update-radio-target-regexp) (let* ((tree (org-element-parse-buffer)) (info `(:parse-tree ,tree))) (org-element-map tree 'link (lambda (link) (org-element-property :value (org-export-resolve-radio-link link info))) info))))) ;; Radio target is whitespace insensitive. (should (org-test-with-temp-text "<<>> a\n radio" (org-update-radio-target-regexp) (let* ((tree (org-element-parse-buffer)) (info `(:parse-tree ,tree))) (org-element-map tree 'link (lambda (link) (org-export-resolve-radio-link link info)) info t))))) (ert-deftest test-org-export/file-uri () "Test `org-export-file-uri' specifications." ;; Preserve relative filenames. (should (equal "relative.org" (org-export-file-uri "relative.org"))) ;; Local files start with "file://" (should (equal (concat (if (memq system-type '(windows-nt cygwin)) "file:///" "file://") (expand-file-name "/local.org")) (org-export-file-uri "/local.org"))) ;; Remote files start with "file://" (should (equal "file://ssh:myself@some.where:papers/last.pdf" (org-export-file-uri "/ssh:myself@some.where:papers/last.pdf"))) (should (equal "file://localhost/etc/fstab" (org-export-file-uri "//localhost/etc/fstab"))) ;; Expand filename starting with "~". (should (equal (org-export-file-uri "~/file.org") (concat (if (memq system-type '(windows-nt cygwin)) "file:///" "file://") (expand-file-name "~/file.org"))))) (ert-deftest test-org-export/get-reference () "Test `org-export-get-reference' specifications." (should (org-test-with-parsed-data "* Headline" (org-export-get-reference (org-element-map tree 'headline #'identity nil t) info))) ;; For a given element always return the same reference. (should (org-test-with-parsed-data "* Headline" (let ((headline (org-element-map tree 'headline #'identity nil t))) (equal (org-export-get-reference headline info) (org-export-get-reference headline info))))) ;; References get through local export backends. (should (org-test-with-parsed-data "* Headline" (let ((headline (org-element-map tree 'headline #'identity nil t)) (backend (org-export-create-backend :transcoders '((headline . (lambda (h _c i) (org-export-get-reference h i))))))) (equal (org-trim (org-export-data-with-backend headline backend info)) (org-export-get-reference headline info))))) (should (org-test-with-parsed-data "* Headline" (let ((headline (org-element-map tree 'headline #'identity nil t)) (backend (org-export-create-backend :transcoders '((headline . (lambda (h _c i) (org-export-get-reference h i))))))) (equal (org-export-with-backend backend headline nil info) (org-export-get-reference headline info))))) ;; Use search cells defined in `:crossrefs'. However, handle ;; duplicate search cells. (should (equal "org0000001" (org-test-with-parsed-data "* Headline" (let* ((headline (org-element-map tree 'headline #'identity nil t)) (search-cell (car (org-export-search-cells headline)))) (setq info (plist-put info :crossrefs (list (cons search-cell 1)))) (org-export-get-reference headline info))))) (should-not (equal '("org0000001" "org0000001") (org-test-with-parsed-data "* H\n** H" (org-element-map tree 'headline (lambda (h) (let* ((search-cell (car (org-export-search-cells h))) (info (plist-put info :crossrefs (list (cons search-cell 1))))) (org-export-get-reference h info)))))))) ;;; Pseudo objects and pseudo elements (ert-deftest test-org-export/pseudo-elements () "Test exporting pseudo-elements." ;; Handle blank lines after pseudo-elements. In particular, do not ;; replace them with white spaces. (should (equal "contents\n\nparagraph\n" (let ((backend (org-export-create-backend :transcoders '((pseudo-element . (lambda (_p c _i) c)) (paragraph . (lambda (_p c _i) c)) (plain-text . (lambda (c _i) c))))) (element '(pseudo-element (:post-blank 1) "contents")) (paragraph '(paragraph nil "paragraph")) (data '(org-data nil))) (org-element-adopt data element paragraph) (org-export-data-with-backend data backend nil))))) (ert-deftest test-org-export/pseudo-objects () "Test exporting pseudo-objects." ;; Handle blank spaces after pseudo-objects. In particular, do not ;; replace them with newlines. (should (equal "begin x end\n" (let ((backend (org-export-create-backend :transcoders '((pseudo-object . (lambda (_p c _i) c)) (paragraph . (lambda (_p c _i) c)) (plain-text . (lambda (c _i) c))))) (object '(pseudo-object (:post-blank 1) "x")) (paragraph '(paragraph nil))) (org-element-adopt paragraph "begin " object "end") (org-export-data-with-backend paragraph backend nil))))) ;;; Raw objects (ert-deftest test-org-export/raw-strings () "Test exporting raw objects." (should (equal "foo" (let ((backend (org-export-create-backend)) (object (org-export-raw-string "foo"))) (org-export-data-with-backend object backend nil))))) ;;; Src-block and example-block (ert-deftest test-org-export/unravel-code () "Test `org-export-unravel-code' function." ;; Code without reference. (should (equal '("(+ 1 1)") (org-test-with-temp-text "#+BEGIN_EXAMPLE\n(+ 1 1)\n#+END_EXAMPLE" (org-export-unravel-code (org-element-at-point))))) ;; Code with reference. (should (equal '("(+ 1 1)" (1 . "test")) (org-test-with-temp-text "#+BEGIN_EXAMPLE\n(+ 1 1) (ref:test)\n#+END_EXAMPLE" (let ((org-coderef-label-format "(ref:%s)")) (org-export-unravel-code (org-element-at-point)))))) ;; Code with user-defined reference. (should (equal '("(+ 1 1)" (1 . "test")) (org-test-with-temp-text "#+BEGIN_EXAMPLE -l \"[ref:%s]\"\n(+ 1 1) [ref:test]\n#+END_EXAMPLE" (let ((org-coderef-label-format "(ref:%s)")) (org-export-unravel-code (org-element-at-point)))))) ;; Code references keys are relative to the current block. (should (equal '("(+ 2 2)\n(+ 3 3)" (2 . "one")) (org-test-with-temp-text " #+BEGIN_EXAMPLE -n \(+ 1 1) #+END_EXAMPLE #+BEGIN_EXAMPLE +n \(+ 2 2) \(+ 3 3) (ref:one) #+END_EXAMPLE" (goto-line 5) (let ((org-coderef-label-format "(ref:%s)")) (org-export-unravel-code (org-element-at-point))))))) (ert-deftest test-org-export/format-code-default () "Test `org-export-format-code-default' specifications." ;; Preserve blank lines, even when code is empty. (should (equal "\n\n" (org-test-with-parsed-data "#+BEGIN_SRC emacs-lisp\n\n\n#+END_SRC" (org-export-format-code-default (org-element-map tree 'src-block #'identity info t) info)))) ;; Likewise, preserve leading and trailing blank lines in the code. (should (equal "\n(+ 1 1)\n" (org-test-with-parsed-data "#+BEGIN_SRC emacs-lisp\n\n(+ 1 1)\n#+END_SRC" (org-export-format-code-default (org-element-map tree 'src-block #'identity info t) info)))) (should (equal "(+ 1 1)\n\n" (org-test-with-parsed-data "#+BEGIN_SRC emacs-lisp\n(+ 1 1)\n\n#+END_SRC" (org-export-format-code-default (org-element-map tree 'src-block #'identity info t) info)))) ;; Number lines, two whitespace characters before the actual loc. (should (equal "1 a\n2 b\n" (org-test-with-parsed-data "#+BEGIN_SRC emacs-lisp +n\na\nb\n#+END_SRC" (org-export-format-code-default (org-element-map tree 'src-block #'identity info t) info)))) ;; Numbering includes blank lines. (should (equal "1 \n2 a\n3 \n4 b\n5 \n" (org-test-with-parsed-data "#+BEGIN_SRC emacs-lisp +n\n\na\n\nb\n\n#+END_SRC" (org-export-format-code-default (org-element-map tree 'src-block #'identity info t) info)))) ;; Put references 6 whitespace characters after the widest line, ;; wrapped within parenthesis. (should (equal "123 (a)\n1 (b)\n" (let ((org-coderef-label-format "(ref:%s)")) (org-test-with-parsed-data "#+BEGIN_SRC emacs-lisp\n123 (ref:a)\n1 (ref:b)\n#+END_SRC" (org-export-format-code-default (org-element-map tree 'src-block #'identity info t) info)))))) (ert-deftest test-org-export/latex-src-block-verbatim-caption () "Test `org-latex-src-block' caption for verbatim environment. Check that percent sign does not become a part of format. This test does not cover listings and custom environments." (let ((export (lambda (buffer-text) (org-test-with-parsed-data buffer-text (let* ((backend (org-export-get-backend 'latex)) (info (org-combine-plists (org-export--get-export-attributes backend) (org-export-get-environment backend))) (result (org-latex-src-block (org-element-map tree 'src-block #'identity info t) t info))) ;; Remove properties to make failure reports more clear. (set-text-properties 0 (length result) nil result) result))))) (should (equal "\ \\begin{verbatim} \"No float, no listings, 20%S\" \\end{verbatim} \\captionof{figure}{Caption of verbatim is below, 20\\%s} " (funcall export "\ #+CAPTION: Caption of verbatim is below, 20%s #+BEGIN_SRC emacs-lisp \"No float, no listings, 20%S\" #+END_SRC"))) ;; `org-latex-caption-above' has no associated property or keyword. (should (equal "\ \\captionof{figure}{Caption of verbatim is above, 40\\%s} \\begin{verbatim} \"No float, no listings, 40%S\" \\end{verbatim}" (let ((org-latex-caption-above t)) (funcall export "\ #+CAPTION: Caption of verbatim is above, 40%s #+BEGIN_SRC emacs-lisp \"No float, no listings, 40%S\" #+END_SRC")))) (should (equal "\ \\begin{figure*}[tp] \\caption{Caption is above, 60\\%s} \\begin{verbatim} \"Float, no listings, 60%S\" \\end{verbatim} \\end{figure*}" (let ((org-latex-caption-above t) (org-latex-default-figure-position "tp")) (funcall export "\ #+CAPTION: Caption is above, 60%s #+ATTR_LATEX: :float multicolumn #+BEGIN_SRC emacs-lisp \"Float, no listings, 60%S\" #+END_SRC")))) (should (equal "\ \\begin{figure*}[tp] \\begin{verbatim} \"Float, no lang, listings, 80%S\" \\end{verbatim} \\caption{Caption is below, 60\\%s} \\end{figure*}" (let ((org-latex-src-block-backend 'minted) ; inactive due to missing lang (org-latex-default-figure-position "tp")) ;; Namely "multicolumn" value to get just figure environment ;; looks like a bug. (funcall export "\ #+CAPTION: Caption is below, 60%s #+ATTR_LATEX: :float multicolumn #+BEGIN_SRC \"Float, no lang, listings, 80%S\" #+END_SRC")))) (should (equal "\ \\begin{verbatim} \"No caption, no float, no listings, 100%S\" \\end{verbatim}" (funcall export "\ #+BEGIN_SRC emacs-lisp \"No caption, no float, no listings, 100%S\" #+END_SRC"))))) ;;; Smart Quotes (ert-deftest test-org-export/activate-smart-quotes () "Test `org-export-activate-smart-quotes' specifications." ;; Double quotes: standard test. (should (equal '("some “quoted” text") (let ((org-export-default-language "en")) (org-test-with-parsed-data "some \"quoted\" text" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :html info)) info))))) ;; Opening quotes: at the beginning of a paragraph. (should (equal '("“begin”") (let ((org-export-default-language "en")) (org-test-with-parsed-data "\"begin\"" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :html info)) info))))) ;; Opening quotes: after an object. (should (equal '("“quoted” text") (let ((org-export-default-language "en")) (org-test-with-parsed-data "=verb= \"quoted\" text" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :html info)) info))))) ;; Closing quotes: at the end of a paragraph. (should (equal '("Quoted “text”") (let ((org-export-default-language "en")) (org-test-with-parsed-data "Quoted \"text\"" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :html info)) info))))) ;; Inner quotes: standard test. (should (equal '("« outer “inner” outer »") (let ((org-export-default-language "fr")) (org-test-with-parsed-data "\"outer 'inner' outer\"" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :utf-8 info)) info))))) ;; Inner quotes: close to special symbols. (should (equal '("« outer (“inner”) outer »") (let ((org-export-default-language "fr")) (org-test-with-parsed-data "\"outer ('inner') outer\"" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :utf-8 info)) info))))) (should (equal '("« “inner” »") (let ((org-export-default-language "fr")) (org-test-with-parsed-data "\"'inner'\"" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :utf-8 info)) info))))) ;; Apostrophe: standard test. (should (equal '("It « shouldn’t » fail") (let ((org-export-default-language "fr")) (org-test-with-parsed-data "It \"shouldn't\" fail" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :utf-8 info)) info))))) (should (equal '("It shouldn’t fail") (let ((org-export-default-language "fr")) (org-test-with-parsed-data "It shouldn't fail" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :utf-8 info)) info))))) ;; Apostrophe: before an object. (should (equal '("« a’" " »") (let ((org-export-default-language "fr")) (org-test-with-parsed-data "\"a'=b=\"" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :utf-8 info)) info))))) ;; Apostrophe: after an object. (should (equal '("« " "’s »") (let ((org-export-default-language "fr")) (org-test-with-parsed-data "\"=code='s\"" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :utf-8 info)) info))))) ;; Special case: isolated quotes. (should (equal '("“" "”") (let ((org-export-default-language "en")) (org-test-with-parsed-data "\"$x$\"" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :html info)) info))))) ;; Smart quotes in secondary strings. (should (equal '("“" "”") (let ((org-export-default-language "en")) (org-test-with-parsed-data "* \"$x$\"" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :html info)) info))))) ;; Smart quotes when object has multiple secondary strings. (should (equal '(" “prefix” " " “suffix”") (let ((org-export-default-language "en")) (org-test-with-parsed-data "[cite:; \"prefix\" @key \"suffix\";]" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :html info)) info))))) ;; Smart quotes in document keywords. (should (equal '("“" "”") (let ((org-export-default-language "en")) (org-test-with-parsed-data "#+TITLE: \"$x$\"" (org-element-map (plist-get info :title) 'plain-text (lambda (s) (org-export-activate-smart-quotes s :html info)) info))))) ;; Smart quotes in parsed affiliated keywords. (should (equal '("“" "”" "Paragraph") (let ((org-export-default-language "en")) (org-test-with-parsed-data "#+CAPTION: \"$x$\"\nParagraph" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :html info)) info nil nil t))))) ;; Smart quotes within objects. (should (equal '("“foo”") (let ((org-export-default-language "en")) (org-test-with-parsed-data "| \"foo\" |" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :html info)) info nil nil t))))) (should (equal '("“foo”") (let ((org-export-default-language "en")) (org-test-with-parsed-data "*\"foo\"*" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :html info)) info nil nil t))))) ;; Unmatched quotes. (should (equal '("\\guillemotleft{}my friends' party and the students' papers\\guillemotright{} \\guillemotleft{}``mothers''\\guillemotright{}") (let ((org-export-default-language "es")) (org-test-with-parsed-data "\"my friends' party and the students' papers\" \"'mothers'\"" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :latex info)) info nil nil t))))) (should (equal '("\"'mothers'") (let ((org-export-default-language "es")) (org-test-with-parsed-data "\"'mothers'" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :latex info)) info nil nil t))))) (should (equal '("\"'mothers " "end'") (let ((org-export-default-language "es")) (org-test-with-parsed-data "\"'mothers =verbatim= end'" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :latex info)) info nil nil t))))) (should (equal '("\\guillemotleft{}να 'ρθώ το βράδυ\\guillemotright{}") (let ((org-export-default-language "el")) (org-test-with-parsed-data "\"να 'ρθώ το βράδυ\"" (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :latex info)) info nil nil t)))))) ;;; Tables (ert-deftest test-org-export/special-column () "Test if the table's special column is properly recognized." ;; 1. First column is special if it contains only a special marking ;; characters or empty cells. (org-test-with-temp-text " | ! | 1 | | | 2 |" (should (org-export-table-has-special-column-p (org-element-map (org-element-parse-buffer) 'table 'identity nil 'first-match)))) ;; 2. If the column contains anything else, it isn't special. (org-test-with-temp-text " | ! | 1 | | b | 2 |" (should-not (org-export-table-has-special-column-p (org-element-map (org-element-parse-buffer) 'table 'identity nil 'first-match)))) ;; 3. Special marking characters are "#", "^", "*", "_", "/", "$" ;; and "!". (org-test-with-temp-text " | # | 1 | | ^ | 2 | | * | 3 | | _ | 4 | | / | 5 | | $ | 6 | | ! | 7 |" (should (org-export-table-has-special-column-p (org-element-map (org-element-parse-buffer) 'table 'identity nil 'first-match)))) ;; 4. A first column with only empty cells isn't considered as ;; special. (org-test-with-temp-text " | | 1 | | | 2 |" (should-not (org-export-table-has-special-column-p (org-element-map (org-element-parse-buffer) 'table 'identity nil 'first-match))))) (ert-deftest test-org-export/table-row-is-special-p () "Test `org-export-table-row-is-special-p' specifications." ;; 1. A row is special if it has a special marking character in the ;; special column. (org-test-with-parsed-data "| ! | 1 |" (should (org-export-table-row-is-special-p (org-element-map tree 'table-row 'identity nil 'first-match) info))) ;; 2. A row is special when its first field is "/" (org-test-with-parsed-data " | / | 1 | | a | b |" (should (org-export-table-row-is-special-p (org-element-map tree 'table-row 'identity nil 'first-match) info))) ;; 3. A row only containing alignment cookies is also considered as ;; special. (org-test-with-parsed-data "| <5> | | | |" (should (org-export-table-row-is-special-p (org-element-map tree 'table-row 'identity nil 'first-match) info))) ;; 4. Everything else isn't considered as special. (org-test-with-parsed-data "| \alpha | | c |" (should-not (org-export-table-row-is-special-p (org-element-map tree 'table-row 'identity nil 'first-match) info))) ;; 5. Table's rules are never considered as special rows. (org-test-with-parsed-data "|---+---|" (should-not (org-export-table-row-is-special-p (org-element-map tree 'table-row 'identity nil 'first-match) info)))) (ert-deftest test-org-export/has-header-p () "Test `org-export-table-has-header-p' specifications." ;; With an header. (should (org-test-with-parsed-data " | a | b | |---+---| | c | d |" (org-export-table-has-header-p (org-element-map tree 'table 'identity info 'first-match) info))) ;; With a multi-line header. (should (org-test-with-parsed-data " | a | b | | 0 | 1 | |---+---| | a | w |" (org-export-table-has-header-p (org-element-map tree 'table 'identity info 'first-match) info))) ;; Without an header. (should-not (org-test-with-parsed-data " | a | b | | c | d |" (org-export-table-has-header-p (org-element-map tree 'table 'identity info 'first-match) info))) ;; Don't get fooled with starting and ending rules. (should-not (org-test-with-parsed-data " |---+---| | a | b | | c | d | |---+---|" (org-export-table-has-header-p (org-element-map tree 'table 'identity info 'first-match) info)))) (ert-deftest test-org-export/table-row-group () "Test `org-export-table-row-group' specifications." ;; A rule creates a new group. (should (equal '(1 rule 2) (org-test-with-parsed-data " | a | b | |---+---| | 1 | 2 |" (org-element-map tree 'table-row (lambda (row) (if (eq (org-element-property :type row) 'rule) 'rule (org-export-table-row-group row info))))))) ;; Special rows are ignored in count. (should (equal '(rule 1) (org-test-with-parsed-data " | / | < | > | |---|---+---| | | 1 | 2 |" (org-element-map tree 'table-row (lambda (row) (if (eq (org-element-property :type row) 'rule) 'rule (org-export-table-row-group row info))) info)))) ;; Double rules also are ignored in count. (should (equal '(1 rule rule 2) (org-test-with-parsed-data " | a | b | |---+---| |---+---| | 1 | 2 |" (org-element-map tree 'table-row (lambda (row) (if (eq (org-element-property :type row) 'rule) 'rule (org-export-table-row-group row info)))))))) (ert-deftest test-org-export/table-row-number () "Test `org-export-table-row-number' specifications." ;; Standard test. Number is 0-indexed. (should (equal '(0 1) (org-test-with-parsed-data "| a | b | c |\n| d | e | f |" (org-element-map tree 'table-row (lambda (row) (org-export-table-row-number row info)) info)))) ;; Number ignores separators. (should (equal '(0 1) (org-test-with-parsed-data " | a | b | c | |---+---+---| | d | e | f |" (org-element-map tree 'table-row (lambda (row) (org-export-table-row-number row info)) info)))) ;; Number ignores special rows. (should (equal '(0 1) (org-test-with-parsed-data " | / | < | > | | | b | c | |---+-----+-----| | | | | | | e | f |" (org-element-map tree 'table-row (lambda (row) (org-export-table-row-number row info)) info))))) (ert-deftest test-org-export/table-cell-width () "Test `org-export-table-cell-width' specifications." ;; Width is primarily determined by width cookies. If no cookie is ;; found, cell's width is nil. (should (equal '(nil 6 7) (org-test-with-parsed-data " | / | | <6> | | | | a | b | c |" (mapcar (lambda (cell) (org-export-table-cell-width cell info)) (org-element-map tree 'table-cell 'identity info))))) ;; Valid width cookies must have a specific row. (should (equal '(nil nil) (org-test-with-parsed-data "| <6> | cell |" (mapcar (lambda (cell) (org-export-table-cell-width cell info)) (org-element-map tree 'table-cell 'identity))))) ;; Do not error on malformed tables. (should (org-test-with-parsed-data " | a | | b | c |" (mapcar (lambda (cell) (org-export-table-cell-width cell info)) (org-element-map tree 'table-cell 'identity info))))) (ert-deftest test-org-export/table-cell-alignment () "Test `org-export-table-cell-alignment' specifications." ;; 1. Alignment is primarily determined by alignment cookies. (should (equal '(left center right) (let ((org-table-number-fraction 0.5) (org-table-number-regexp "^[0-9]+$")) (org-test-with-parsed-data "| | | |" (mapcar (lambda (cell) (org-export-table-cell-alignment cell info)) (org-element-map tree 'table-cell 'identity)))))) ;; 2. The last alignment cookie has precedence. (should (equal '(right right right) (org-test-with-parsed-data " | | | cell | | |" (mapcar (lambda (cell) (org-export-table-cell-alignment cell info)) (org-element-map tree 'table-cell 'identity))))) ;; 3. If there's no cookie, cell's contents determine alignment. ;; A column mostly made of cells containing numbers will align ;; its cells to the right. (should (equal '(right right right) (let ((org-table-number-fraction 0.5) (org-table-number-regexp "^[0-9]+$")) (org-test-with-parsed-data " | 123 | | some text | | 12345 |" (mapcar (lambda (cell) (org-export-table-cell-alignment cell info)) (org-element-map tree 'table-cell 'identity)))))) ;; 4. Otherwise, they will be aligned to the left. (should (equal '(left left left) (org-test-with-parsed-data " | text | | some text | | \alpha |" (mapcar (lambda (cell) (org-export-table-cell-alignment cell info)) (org-element-map tree 'table-cell 'identity info)))))) (ert-deftest test-org-export/table-cell-borders () "Test `org-export-table-cell-borders' specifications." ;; 1. Recognize various column groups indicators. (org-test-with-parsed-data "| / | < | > | <> |" (should (equal '((right bottom top) (left bottom top) (right bottom top) (right left bottom top)) (mapcar (lambda (cell) (org-export-table-cell-borders cell info)) (org-element-map tree 'table-cell 'identity))))) ;; 2. Accept shortcuts to define column groups. (org-test-with-parsed-data "| / | < | < |" (should (equal '((right bottom top) (right left bottom top) (left bottom top)) (mapcar (lambda (cell) (org-export-table-cell-borders cell info)) (org-element-map tree 'table-cell 'identity))))) ;; 3. A valid column groups row must start with a "/". (org-test-with-parsed-data " | | < | | a | b |" (should (equal '((top) (top) (bottom) (bottom)) (mapcar (lambda (cell) (org-export-table-cell-borders cell info)) (org-element-map tree 'table-cell 'identity))))) ;; 4. Take table rules into consideration. (org-test-with-parsed-data " | 1 | |---| | 2 |" (should (equal '((below top) (bottom above)) (mapcar (lambda (cell) (org-export-table-cell-borders cell info)) (org-element-map tree 'table-cell 'identity))))) ;; 5. Top and (resp. bottom) rules induce both `top' and `above' ;; (resp. `bottom' and `below') borders. Any special row is ;; ignored. (org-test-with-parsed-data " |---+----| | / | | | | 1 | |---+----|" (should (equal '((bottom below top above)) (last (mapcar (lambda (cell) (org-export-table-cell-borders cell info)) (org-element-map tree 'table-cell 'identity))))))) (ert-deftest test-org-export/table-dimensions () "Test `org-export-table-dimensions' specifications." ;; 1. Standard test. (org-test-with-parsed-data " | 1 | 2 | 3 | | 4 | 5 | 6 |" (should (equal '(2 . 3) (org-export-table-dimensions (org-element-map tree 'table 'identity info 'first-match) info)))) ;; 2. Ignore horizontal rules and special columns. (org-test-with-parsed-data " | / | < | > | | 1 | 2 | 3 | |---+---+---| | 4 | 5 | 6 |" (should (equal '(2 . 3) (org-export-table-dimensions (org-element-map tree 'table 'identity info 'first-match) info))))) (ert-deftest test-org-export/table-cell-address () "Test `org-export-table-cell-address' specifications." ;; 1. Standard test: index is 0-based. (org-test-with-parsed-data "| a | b |" (should (equal '((0 . 0) (0 . 1)) (org-element-map tree 'table-cell (lambda (cell) (org-export-table-cell-address cell info)) info)))) ;; 2. Special column isn't counted, nor are special rows. (org-test-with-parsed-data " | / | <> | | | c |" (should (equal '(0 . 0) (org-export-table-cell-address (car (last (org-element-map tree 'table-cell 'identity info))) info)))) ;; 3. Tables rules do not count either. (org-test-with-parsed-data " | a | |---| | b | |---| | c |" (should (equal '(2 . 0) (org-export-table-cell-address (car (last (org-element-map tree 'table-cell 'identity info))) info)))) ;; 4. Return nil for special cells. (org-test-with-parsed-data "| / | a |" (should-not (org-export-table-cell-address (org-element-map tree 'table-cell 'identity nil 'first-match) info)))) (ert-deftest test-org-export/get-table-cell-at () "Test `org-export-get-table-cell-at' specifications." ;; 1. Address ignores special columns, special rows and rules. (org-test-with-parsed-data " | / | <> | | | a | |---+----| | | b |" (should (equal '("b") (org-element-contents (org-export-get-table-cell-at '(1 . 0) (org-element-map tree 'table 'identity info 'first-match) info))))) ;; 2. Return value for a non-existent address is nil. (org-test-with-parsed-data "| a |" (should-not (org-export-get-table-cell-at '(2 . 2) (org-element-map tree 'table 'identity info 'first-match) info))) (org-test-with-parsed-data "| / |" (should-not (org-export-get-table-cell-at '(0 . 0) (org-element-map tree 'table 'identity info 'first-match) info)))) (ert-deftest test-org-export/table-cell-starts-colgroup-p () "Test `org-export-table-cell-starts-colgroup-p' specifications." ;; 1. A cell at a beginning of a row always starts a column group. (org-test-with-parsed-data "| a |" (should (org-export-table-cell-starts-colgroup-p (org-element-map tree 'table-cell 'identity info 'first-match) info))) ;; 2. Special column should be ignored when determining the ;; beginning of the row. (org-test-with-parsed-data " | / | | | | a |" (should (org-export-table-cell-starts-colgroup-p (org-element-map tree 'table-cell 'identity info 'first-match) info))) ;; 2. Explicit column groups. (org-test-with-parsed-data " | / | | < | | a | b | c |" (should (equal '(yes no yes) (org-element-map tree 'table-cell (lambda (cell) (if (org-export-table-cell-starts-colgroup-p cell info) 'yes 'no)) info))))) (ert-deftest test-org-export/table-cell-ends-colgroup-p () "Test `org-export-table-cell-ends-colgroup-p' specifications." ;; 1. A cell at the end of a row always ends a column group. (org-test-with-parsed-data "| a |" (should (org-export-table-cell-ends-colgroup-p (org-element-map tree 'table-cell 'identity info 'first-match) info))) ;; 2. Special column should be ignored when determining the ;; beginning of the row. (org-test-with-parsed-data " | / | | | | a |" (should (org-export-table-cell-ends-colgroup-p (org-element-map tree 'table-cell 'identity info 'first-match) info))) ;; 3. Explicit column groups. (org-test-with-parsed-data " | / | < | | | a | b | c |" (should (equal '(yes no yes) (org-element-map tree 'table-cell (lambda (cell) (if (org-export-table-cell-ends-colgroup-p cell info) 'yes 'no)) info))))) (ert-deftest test-org-export/table-row-starts-rowgroup-p () "Test `org-export-table-row-starts-rowgroup-p' specifications." ;; 1. A row at the beginning of a table always starts a row group. ;; So does a row following a table rule. (org-test-with-parsed-data " | a | |---| | b |" (should (equal '(yes no yes) (org-element-map tree 'table-row (lambda (row) (if (org-export-table-row-starts-rowgroup-p row info) 'yes 'no)) info)))) ;; 2. Special rows should be ignored when determining the beginning ;; of the row. (org-test-with-parsed-data " | / | < | | | a | |---+---| | / | < | | | b |" (should (equal '(yes no yes) (org-element-map tree 'table-row (lambda (row) (if (org-export-table-row-starts-rowgroup-p row info) 'yes 'no)) info))))) (ert-deftest test-org-export/table-row-ends-rowgroup-p () "Test `org-export-table-row-ends-rowgroup-p' specifications." ;; 1. A row at the end of a table always ends a row group. So does ;; a row preceding a table rule. (org-test-with-parsed-data " | a | |---| | b |" (should (equal '(yes no yes) (org-element-map tree 'table-row (lambda (row) (if (org-export-table-row-ends-rowgroup-p row info) 'yes 'no)) info)))) ;; 2. Special rows should be ignored when determining the beginning ;; of the row. (org-test-with-parsed-data " | | a | | / | < | |---+---| | | b | | / | < |" (should (equal '(yes no yes) (org-element-map tree 'table-row (lambda (row) (if (org-export-table-row-ends-rowgroup-p row info) 'yes 'no)) info))))) (ert-deftest test-org-export/table-row-in-header-p () "Test `org-export-table-row-in-header-p' specifications." ;; Standard test. Separators are always nil. (should (equal '(yes no no) (org-test-with-parsed-data "| a |\n|---|\n| b |" (org-element-map tree 'table-row (lambda (row) (if (org-export-table-row-in-header-p row info) 'yes 'no)) info)))) ;; Nil when there is no header. (should (equal '(no no) (org-test-with-parsed-data "| a |\n| b |" (org-element-map tree 'table-row (lambda (row) (if (org-export-table-row-in-header-p row info) 'yes 'no)) info))))) (ert-deftest test-org-export/table-row-starts-header-p () "Test `org-export-table-row-starts-header-p' specifications." ;; 1. Only the row starting the first row group starts the table ;; header. (org-test-with-parsed-data " | a | | b | |---| | c |" (should (equal '(yes no no no) (org-element-map tree 'table-row (lambda (row) (if (org-export-table-row-starts-header-p row info) 'yes 'no)) info)))) ;; 2. A row cannot start an header if there's no header in the ;; table. (org-test-with-parsed-data " | a | |---|" (should-not (org-export-table-row-starts-header-p (org-element-map tree 'table-row 'identity info 'first-match) info)))) (ert-deftest test-org-export/table-row-ends-header-p () "Test `org-export-table-row-ends-header-p' specifications." ;; 1. Only the row starting the first row group starts the table ;; header. (org-test-with-parsed-data " | a | | b | |---| | c |" (should (equal '(no yes no no) (org-element-map tree 'table-row (lambda (row) (if (org-export-table-row-ends-header-p row info) 'yes 'no)) info)))) ;; 2. A row cannot start an header if there's no header in the ;; table. (org-test-with-parsed-data " | a | |---|" (should-not (org-export-table-row-ends-header-p (org-element-map tree 'table-row 'identity info 'first-match) info)))) ;;; Tables of Contents (ert-deftest test-org-export/collect-headlines () "Test `org-export-collect-headlines' specifications." ;; Standard test. (should (equal '("H1" "H2") (org-test-with-parsed-data "* H1\n** H2" (mapcar (lambda (h) (org-element-property :raw-value h)) (org-export-collect-headlines info))))) ;; Do not collect headlines below optional argument. (should (equal '("H1") (org-test-with-parsed-data "* H1\n** H2" (mapcar (lambda (h) (org-element-property :raw-value h)) (org-export-collect-headlines info 1))))) ;; Never collect headlines below maximum headline level. (should (equal '("H1") (org-test-with-parsed-data "#+OPTIONS: H:1\n* H1\n** H2" (mapcar (lambda (h) (org-element-property :raw-value h)) (org-export-collect-headlines info))))) (should (equal '("H1") (org-test-with-parsed-data "#+OPTIONS: H:1\n* H1\n** H2" (mapcar (lambda (h) (org-element-property :raw-value h)) (org-export-collect-headlines info 2))))) ;; Do not collect footnote section. (should (equal '("H1") (let ((org-footnote-section "Footnotes")) (org-test-with-parsed-data "* H1\n** Footnotes" (mapcar (lambda (h) (org-element-property :raw-value h)) (org-export-collect-headlines info)))))) ;; Do not collect headlines with UNNUMBERED property set to "notoc". ;; Headlines with another value for the property are still ;; collected. UNNUMBERED property is inherited. (should (equal '("H1") (org-test-with-parsed-data "* H1\n* H2\n:PROPERTIES:\n:UNNUMBERED: notoc\n:END:" (mapcar (lambda (h) (org-element-property :raw-value h)) (org-export-collect-headlines info))))) (should-not (org-test-with-parsed-data "* H1\n:PROPERTIES:\n:UNNUMBERED: notoc\n:END:\n** H2" (mapcar (lambda (h) (org-element-property :raw-value h)) (org-export-collect-headlines info)))) (should (equal '("H1" "H2") (org-test-with-parsed-data "* H1\n* H2\n:PROPERTIES:\n:UNNUMBERED: t\n:END:" (mapcar (lambda (h) (org-element-property :raw-value h)) (org-export-collect-headlines info))))) ;; Collect headlines locally. (should (equal '("H2" "H3") (org-test-with-parsed-data "* H1\n** H2\n** H3" (let ((scope (org-element-map tree 'headline #'identity info t))) (mapcar (lambda (h) (org-element-property :raw-value h)) (org-export-collect-headlines info nil scope)))))) ;; Collect headlines from a scope specified by a fuzzy match (should (equal '("H3" "H4") (org-test-with-parsed-data "* HA ** H1 ** H2 * Target :PROPERTIES: :CUSTOM_ID: TargetSection :END: ** H3 ** H4 * HB ** H5 " (mapcar (lambda (h) (org-element-property :raw-value h)) (org-export-collect-headlines info nil (org-export-resolve-fuzzy-link (with-temp-buffer (save-excursion (insert "[[Target]]")) (org-element-link-parser)) info)))))) ;; Collect headlines from a scope specified by CUSTOM_ID (should (equal '("H3" "H4") (org-test-with-parsed-data "* Not this section ** H1 ** H2 * Target :PROPERTIES: :CUSTOM_ID: TargetSection :END: ** H3 ** H4 * Another ** H5 " (mapcar (lambda (h) (org-element-property :raw-value h)) (org-export-collect-headlines info nil (org-export-resolve-id-link (with-temp-buffer (save-excursion (insert "[[#TargetSection]]")) (org-element-link-parser)) info)))))) ;; When collecting locally, optional level is relative. (should (equal '("H2") (org-test-with-parsed-data "* H1\n** H2\n*** H3" (let ((scope (org-element-map tree 'headline #'identity info t))) (mapcar (lambda (h) (org-element-property :raw-value h)) (org-export-collect-headlines info 1 scope))))))) (ert-deftest test-org-export/excluded-from-toc-p () "Test `org-export-excluded-from-toc-p' specifications." ;; By default, headlines are not excluded. (should-not (org-test-with-parsed-data "* H1" (org-element-map tree 'headline (lambda (h) (org-export-excluded-from-toc-p h info)) info t))) ;; Exclude according to a maximum level. (should (equal '(in out) (org-test-with-parsed-data "#+OPTIONS: H:1\n* H1\n** H2" (org-element-map tree 'headline (lambda (h) (if (org-export-excluded-from-toc-p h info) 'out 'in)) info)))) ;; Exclude according to UNNUMBERED property. (should (org-test-with-parsed-data "* H1\n:PROPERTIES:\n:UNNUMBERED: notoc\n:END:" (org-element-map tree 'headline (lambda (h) (org-export-excluded-from-toc-p h info)) info t))) ;; UNNUMBERED property is inherited, so is "notoc" value. (should (equal '(out out) (org-test-with-parsed-data "* H1\n:PROPERTIES:\n:UNNUMBERED: notoc\n:END:\n** H2" (org-element-map tree 'headline (lambda (h) (if (org-export-excluded-from-toc-p h info) 'out 'in)) info))))) (ert-deftest test-org-export/toc-entry-backend () "Test `org-export-toc-entry-backend' specifications." ;; Ignore targets. (should (equal "H \n" (org-test-with-temp-text "* H <>" (let (org-export-registered-backends) (org-export-define-backend 'test '((headline . (lambda (h _c i) (org-export-data-with-backend (org-element-property :title h) (org-export-toc-entry-backend 'test) i))))) (org-export-as 'test))))) ;; Ignore footnote references. (should (equal "H \n" (org-test-with-temp-text "[fn:1] Definition\n* H [fn:1]" (let (org-export-registered-backends) (org-export-define-backend 'test '((headline . (lambda (h _c i) (org-export-data-with-backend (org-element-property :title h) (org-export-toc-entry-backend 'test) i))))) (org-export-as 'test))))) ;; Replace plain links with contents, or with path. (should (equal "H Org mode\n" (org-test-with-temp-text "* H [[https://orgmode.org][Org mode]]" (let (org-export-registered-backends) (org-export-define-backend 'test '((headline . (lambda (h _c i) (org-export-data-with-backend (org-element-property :title h) (org-export-toc-entry-backend 'test) i))))) (org-export-as 'test))))) (should (equal "H https://orgmode.org\n" (org-test-with-temp-text "* H [[https://orgmode.org]]" (let (org-export-registered-backends) (org-export-define-backend 'test '((headline . (lambda (h _c i) (org-export-data-with-backend (org-element-property :title h) (org-export-toc-entry-backend 'test) i))))) (org-export-as 'test))))) ;; Replace radio targets with contents. (should (equal "H radio\n" (org-test-with-temp-text "* H <<>>" (let (org-export-registered-backends) (org-export-define-backend 'test '((headline . (lambda (h _c i) (org-export-data-with-backend (org-element-property :title h) (org-export-toc-entry-backend 'test) i))))) (org-export-as 'test))))) ;; With optional argument TRANSCODERS, specify other ;; transformations. (should (equal "H bold\n" (org-test-with-temp-text "* H *bold*" (let (org-export-registered-backends) (org-export-define-backend 'test '((headline . (lambda (h _c i) (org-export-data-with-backend (org-element-property :title h) (org-export-toc-entry-backend 'test '(bold . (lambda (_b c _i) c))) i))))) (org-export-as 'test)))))) ;;; Templates (ert-deftest test-org-export/inner-template () "Test `inner-template' translator specifications." (should (equal "Success!" (org-test-with-temp-text "* Headline" (org-export-as (org-export-create-backend :transcoders '((inner-template . (lambda (contents info) "Success!")) (headline . (lambda (h c i) "Headline")))))))) ;; Inner template is applied even in a "body-only" export. (should (equal "Success!" (org-test-with-temp-text "* Headline" (org-export-as (org-export-create-backend :transcoders '((inner-template . (lambda (c i) "Success!")) (headline . (lambda (h c i) "Headline")))) nil nil 'body-only))))) (ert-deftest test-org-export/template () "Test `template' translator specifications." (should (equal "Success!" (org-test-with-temp-text "* Headline" (org-export-as (org-export-create-backend :transcoders '((template . (lambda (contents info) "Success!")) (headline . (lambda (h c i) "Headline")))))))) ;; Template is not applied in a "body-only" export. (should-not (equal "Success!" (org-test-with-temp-text "* Headline" (org-export-as (org-export-create-backend :transcoders '((template . (lambda (contents info) "Success!")) (headline . (lambda (h c i) "Headline")))) nil nil 'body-only))))) ;;; Topology (ert-deftest test-org-export/get-next-element () "Test `org-export-get-next-element' specifications." ;; Standard test. (should (equal "b" (org-test-with-parsed-data "* Headline\n*a* b" (org-export-get-next-element (org-element-map tree 'bold 'identity info t) info)))) ;; Return nil when no previous element. (should-not (org-test-with-parsed-data "* Headline\na *b*" (org-export-get-next-element (org-element-map tree 'bold 'identity info t) info))) ;; Non-exportable elements are ignored. (should-not (let ((org-export-with-timestamps nil)) (org-test-with-parsed-data "\alpha <2012-03-29 Thu>" (org-export-get-next-element (org-element-map tree 'entity 'identity info t) info)))) ;; Find next element in secondary strings. (should (eq 'verbatim (org-test-with-parsed-data "* a =verb=" (org-element-type (org-export-get-next-element (org-element-map tree 'plain-text 'identity info t) info))))) (should (eq 'verbatim (org-test-with-parsed-data "* /italic/ =verb=" (org-element-type (org-export-get-next-element (org-element-map tree 'italic 'identity info t) info))))) ;; Find next element in document keywords. (should (eq 'verbatim (org-test-with-parsed-data "#+TITLE: a =verb=" (org-element-type (org-export-get-next-element (org-element-map (plist-get info :title) 'plain-text 'identity info t) info))))) ;; Find next element in parsed affiliated keywords. (should (eq 'verbatim (org-test-with-parsed-data "#+CAPTION: a =verb=\nParagraph" (org-element-type (org-export-get-next-element (org-element-map tree 'plain-text 'identity info t nil t) info))))) ;; With optional argument N, return a list containing all the ;; following elements. (should (equal '(bold code underline) (org-test-with-parsed-data "_a_ /b/ *c* ~d~ _e_" (mapcar #'car (org-export-get-next-element (org-element-map tree 'italic 'identity info t) info t))))) ;; When N is a positive integer, return a list containing up to ;; N following elements. (should (equal '(bold code) (org-test-with-parsed-data "_a_ /b/ *c* ~d~ _e_" (mapcar #'car (org-export-get-next-element (org-element-map tree 'italic 'identity info t) info 2)))))) (ert-deftest test-org-export/get-previous-element () "Test `org-export-get-previous-element' specifications." ;; Standard test. (should (equal "a " (org-test-with-parsed-data "* Headline\na *b*" (org-export-get-previous-element (org-element-map tree 'bold 'identity info t) info)))) ;; Return nil when no previous element. (should-not (org-test-with-parsed-data "* Headline\n*a* b" (org-export-get-previous-element (org-element-map tree 'bold 'identity info t) info))) ;; Non-exportable elements are ignored. (should-not (let ((org-export-with-timestamps nil)) (org-test-with-parsed-data "<2012-03-29 Thu> \alpha" (org-export-get-previous-element (org-element-map tree 'entity 'identity info t) info)))) ;; Find previous element in secondary strings. (should (eq 'verbatim (org-test-with-parsed-data "* =verb= a" (org-element-type (org-export-get-previous-element (org-element-map tree 'plain-text 'identity info t) info))))) (should (eq 'verbatim (org-test-with-parsed-data "* =verb= /italic/" (org-element-type (org-export-get-previous-element (org-element-map tree 'italic 'identity info t) info))))) ;; Find previous element in document keywords. (should (eq 'verbatim (org-test-with-parsed-data "#+TITLE: =verb= a" (org-element-type (org-export-get-previous-element (org-element-map (plist-get info :title) 'plain-text 'identity info t) info))))) ;; Find previous element in parsed affiliated keywords. (should (eq 'verbatim (org-test-with-parsed-data "#+CAPTION: =verb= a\nParagraph" (org-element-type (org-export-get-previous-element (org-element-map tree 'plain-text 'identity info t nil t) info))))) ;; With optional argument N, return a list containing up to ;; N previous elements. (should (equal '(underline italic bold) (org-test-with-parsed-data "_a_ /b/ *c* ~d~" (mapcar #'car (org-export-get-previous-element (org-element-map tree 'code 'identity info t) info t))))) ;; When N is a positive integer, return a list containing up to ;; N previous elements. (should (equal '(italic bold) (org-test-with-parsed-data "_a_ /b/ *c* ~d~" (mapcar #'car (org-export-get-previous-element (org-element-map tree 'code 'identity info t) info 2)))))) (provide 'test-ox) ;;; test-org-export.el end here org-mode-9.7.29+dfsg/testing/lisp/test-property-inheritance.el000066400000000000000000000034151500430433700243520ustar00rootroot00000000000000;;; test-property-inheritance.el --- tests of property inheritance -*- lexical-binding: t; -*- ;; Copyright (c) 2011-2014, 2019 Eric Schulte ;; Authors: Eric Schulte ;; This file is not part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Code: (defmacro test-org-in-property-buffer (&rest body) `(with-temp-buffer (insert-file-contents (expand-file-name "property-inheritance.org" org-test-example-dir)) (org-mode) ,@body)) (def-edebug-spec test-org-in-property-buffer (body)) (ert-deftest test-org-property-accumulation-top-use () (test-org-in-property-buffer (goto-char (point-min)) (org-babel-next-src-block 1) (should (equal 3 (org-babel-execute-src-block))))) (ert-deftest test-org-property-accumulation-overwrite-use () (test-org-in-property-buffer (goto-char (point-min)) (org-babel-next-src-block 2) (should (= 7 (org-babel-execute-src-block))))) (ert-deftest test-org-property-accumulation-append-use () (test-org-in-property-buffer (goto-char (point-min)) (org-babel-next-src-block 3) (should (= 6 (org-babel-execute-src-block))))) (provide 'test-property-inheritance) ;;; test-property-inheritance.el ends here org-mode-9.7.29+dfsg/testing/org-batch-test-init.el000066400000000000000000000007511500430433700220370ustar00rootroot00000000000000;; -*- lexical-binding: t; -*- ;; Remove Org remnants built into Emacs ;; ;; clean load-path (setq load-path (delq nil (mapcar (lambda (p) (unless (string-match "lisp\\(/packages\\)?/org$" p) p)) load-path))) ;; remove property list to defeat cus-load and remove autoloads (mapatoms (lambda (s) (let ((sn (symbol-name s))) (when (string-match "\\`\\(org\\|ob\\|ox\\)\\(-.*\\)?\\'" sn) (setplist s nil))))) ;; we should now start from a clean slate org-mode-9.7.29+dfsg/testing/org-test.el000066400000000000000000000530571500430433700200260ustar00rootroot00000000000000;;;; org-test.el --- Tests for Org -*- lexical-binding: t; -*- ;; Copyright (c) 2010-2015 Sebastian Rose, Eric Schulte ;; Authors: ;; Sebastian Rose, Hannover, Germany, sebastian_rose gmx de ;; Eric Schulte, Santa Fe, New Mexico, USA, schulte.eric gmail com ;; David Maus, Brunswick, Germany, dmaus ictsoc de ;; Released under the GNU General Public License version 3 ;; see: https://www.gnu.org/licenses/gpl-3.0.html ;; Definition of `special-mode' copied from Emacs23's simple.el to be ;; provide a testing environment for Emacs22. ;;;; Comments: ;; Interactive testing for Org mode. ;; The heart of all this is the commands `org-test-current-defun'. If ;; called while in a `defun' all ert tests with names matching the ;; name of the function are run. ;;; Test Development ;; For test development purposes a number of navigation and test ;; function construction routines are available as a git submodule ;; (jump.el) ;; Install with... ;; $ git submodule init ;; $ git submodule update ;;;; Code: (require 'org) (require 'org-id) (require 'org-macs) ;;; Ob constants (defconst org-test-file-ob-anchor "94839181-184f-4ff4-a72f-94214df6f5ba") (defconst org-test-link-in-heading-file-ob-anchor "a8b1d111-eca8-49f0-8930-56d4f0875155") (unless (and (boundp 'org-batch-test) org-batch-test) (let* ((org-test-dir (expand-file-name (file-name-directory (or load-file-name buffer-file-name)))) (org-lisp-dir (expand-file-name (concat org-test-dir "../lisp")))) (unless (featurep 'org) (setq load-path (cons org-lisp-dir load-path)) (require 'org) (require 'org-id) (require 'ox) (org-babel-do-load-languages 'org-babel-load-languages '((shell . t) (org . t)))) (let ((load-path (cons org-test-dir (cons (expand-file-name "jump" org-test-dir) load-path)))) (require 'cl-lib) (require 'ert) (require 'ert-x) (when (file-exists-p (expand-file-name "jump/jump.el" org-test-dir)) (require 'jump) (require 'which-func))))) (defconst org-test-default-test-file-name "tests.el" "For each defun a separate file with tests may be defined. tests.el is the fallback or default if you like.") (defconst org-test-default-directory-name "testing" "Basename or the directory where the tests live. org-test searches this directory up the directory tree.") (defconst org-test-dir (expand-file-name (file-name-directory (or load-file-name buffer-file-name)))) (defconst org-test-base-dir (expand-file-name ".." org-test-dir)) (defconst org-test-example-dir (expand-file-name "examples" org-test-dir)) (defconst org-test-file (expand-file-name "normal.org" org-test-example-dir)) (defconst org-test-no-heading-file (expand-file-name "no-heading.org" org-test-example-dir)) (defconst org-test-attachments-file (expand-file-name "attachments.org" org-test-example-dir)) (defconst org-test-link-in-heading-file (expand-file-name "link-in-heading.org" org-test-dir)) ;;; Functions for writing tests (define-error 'missing-test-dependency "org-test: Test dependency missing.") (defun org-test-for-executable (exe) "Throw an error if EXE is not available. This can be used at the top of code-block-language specific test files to avoid loading the file on systems without the executable." (unless (cl-reduce (lambda (acc dir) (or acc (file-exists-p (expand-file-name exe dir)))) exec-path :initial-value nil) (signal 'missing-test-dependency (list exe)))) (defun org-test-buffer (&optional _file) "TODO: Setup and return a buffer to work with. If file is non-nil insert its contents in there.") (defun org-test-compare-with-file (&optional _file) "TODO: Compare the contents of the test buffer with FILE. If file is not given, search for a file named after the test currently executed.") (defmacro org-test-at-id (id &rest body) "Run body after placing the point in the headline identified by ID." (declare (indent 1) (debug t)) `(let* ((id-location (org-id-find ,id)) (id-file (car id-location)) (visited-p (get-file-buffer id-file)) to-be-removed) (unwind-protect (save-window-excursion (save-match-data (org-id-goto ,id) (setq to-be-removed (current-buffer)) (condition-case nil (progn (org-show-subtree) (org-show-all '(blocks))) (error nil)) (save-restriction ,@body))) (unless (or visited-p (not to-be-removed)) (kill-buffer to-be-removed))))) (defmacro org-test-in-example-file (file &rest body) "Execute body in the Org example file." (declare (indent 1) (debug t)) `(let* ((my-file (or ,file org-test-file)) (visited-p (get-file-buffer my-file)) to-be-removed results) (save-window-excursion (save-match-data (find-file my-file) (unless (eq major-mode 'org-mode) (org-mode)) (setq to-be-removed (current-buffer)) (goto-char (point-min)) (condition-case nil (progn (outline-next-visible-heading 1) (org-show-subtree) (org-show-all '(blocks))) (error nil)) (setq results (save-restriction ,@body)))) (unless visited-p (kill-buffer to-be-removed)) results)) (defmacro org-test-at-marker (file marker &rest body) "Run body after placing the point at MARKER in FILE. Note the uuidgen command-line command can be useful for generating unique markers for insertion as anchors into org files." (declare (indent 2) (debug t)) `(org-test-in-example-file ,file (goto-char (point-min)) (re-search-forward (regexp-quote ,marker)) ,@body)) (defmacro org-test-with-temp-text (text &rest body) "Run body in a temporary buffer with Org mode as the active mode holding TEXT. If the string \"\" appears in TEXT then remove it and place the point there before running BODY, otherwise place the point at the beginning of the inserted text." (declare (indent 1) (debug t)) `(let ((inside-text (if (stringp ,text) ,text (eval ,text))) (org-mode-hook nil)) (with-temp-buffer (org-mode) (let ((point (string-match "" inside-text))) (if point (progn (insert (replace-match "" nil nil inside-text)) (goto-char (1+ (match-beginning 0)))) (insert inside-text) (goto-char (point-min)))) (font-lock-ensure (point-min) (point-max)) ,@body))) (defmacro org-test-with-temp-text-in-file (text &rest body) "Run body in a temporary file buffer with Org mode as the active mode. If the string \"\" appears in TEXT then remove it and place the point there before running BODY, otherwise place the point at the beginning of the buffer." (declare (indent 1) (debug t)) `(let ((file (make-temp-file "org-test")) (inside-text (if (stringp ,text) ,text (eval ,text))) buffer) (with-temp-file file (insert inside-text)) (unwind-protect (progn ;; FIXME: For the rare cases where we do need to mess with windows, ;; we should let `body' take care of displaying this buffer! (setq buffer (find-file file)) (when (re-search-forward "" nil t) (replace-match "")) (org-mode) (progn ,@body)) (let ((kill-buffer-query-functions nil)) (when buffer (set-buffer buffer) ;; Ignore changes, we're deleting the file in the next step ;; anyways. (set-buffer-modified-p nil) (kill-buffer)) (delete-file file))))) (defun org-test-table-target-expect (target &optional expect laps &rest tblfm) "For all TBLFM: Apply the formula to TARGET, compare EXPECT with result. Either LAPS and TBLFM are nil and the table will only be aligned or LAPS is the count of recalculations that should be made on each TBLFM. To save ERT run time keep LAPS as low as possible to get the table stable. Anyhow, if LAPS is `iterate' then iterate, but this will run one recalculation longer. When EXPECT is nil it will be set to TARGET. When running a test interactively in ERT is not enough and you need to examine the target table with e. g. the Org formula debugger or an Emacs Lisp debugger (e. g. with point in a data field and calling the instrumented `org-table-eval-formula') then copy and paste the table with formula from the ERT results buffer or temporarily substitute the `org-test-with-temp-text' of this function with `org-test-with-temp-text-in-file'. Also consider setting `pp-escape-newlines' to nil manually." (require 'pp) (require 'ert) (let ((back pp-escape-newlines) (current-tblfm)) (unless tblfm (should-not laps) (push "" tblfm)) ; Dummy formula. (unless expect (setq expect target)) (while (setq current-tblfm (pop tblfm)) (org-test-with-temp-text (concat target current-tblfm) ;; Search the last of possibly several tables, let the ERT ;; test fail if not found. (goto-char (point-max)) (while (not (org-at-table-p)) (should (eq 0 (forward-line -1)))) (when laps (if (and (symbolp laps) (eq laps 'iterate)) (should (org-table-recalculate 'iterate t)) (should (integerp laps)) (should (< 0 laps)) (let ((cnt laps)) (while (< 0 cnt) (should (org-table-recalculate 'all t)) (setq cnt (1- cnt)))))) (org-table-align) (setq pp-escape-newlines nil) ;; Declutter the ERT results buffer by giving only variables ;; and not directly the forms to `should'. (let ((expect (concat expect current-tblfm)) (result (buffer-substring-no-properties (point-min) (point-max)))) (should (equal expect result))) ;; If `should' passed then set back `pp-escape-newlines' here, ;; else leave it nil as a side effect to see the failed table ;; on multiple lines in the ERT results buffer. (setq pp-escape-newlines back))))) (defun org-test-with-tramp-remote-dir--worker (body) "Worker for `org-test-with-tramp-remote-dir'." (let ((env-def (getenv "REMOTE_TEMPORARY_FILE_DIRECTORY"))) (cond (env-def (funcall body env-def)) ((eq system-type 'windows-nt) (funcall body null-device)) (t (require 'tramp) (defvar tramp-methods) (defvar tramp-default-host-alist) (let ((tramp-methods (cons '("mock" (tramp-login-program "sh") (tramp-login-args (("-i"))) (tramp-remote-shell "/bin/sh") (tramp-remote-shell-args ("-c")) (tramp-connection-timeout 10)) tramp-methods)) (tramp-default-host-alist `(("\\`mock\\'" nil ,(system-name))))) (funcall body (format "/mock::%s" temporary-file-directory))))))) (defmacro org-test-with-tramp-remote-dir (dir &rest body) "Bind the symbol DIR to a remote directory and execute BODY. Return the value of the last form in BODY. The directory DIR will be something like \"/mock::/tmp/\", which allows to test Tramp related features. We mostly follow `tramp-test-temporary-file-directory' from GNU Emacs tests." (declare (debug (sexp body)) (indent 2)) `(org-test-with-tramp-remote-dir--worker (lambda (,dir) ,@body))) (defun org-test-string-collate-lessp-ignore-case-supported-p (&optional locale) "`string-collate-lessp' supports ignore case for LOCALE. According to the docstring of `string-collate-lessp' it does not implement ignore case for some locale on some operating systems, actually depending on libc of Emacs. `string-collate-lessp' ignores when its parameter IGNORE-CASE is non-nil e. g. for the C locale in Emacs 29.4 of Homebrew on macOS. See also https://debbugs.gnu.org/cgi/bugreport.cgi?bug=59275 and https://list.orgmode.org/orgmode/m2ilkwso8r.fsf@me.com" (let ((ignore-case t)) (string-collate-lessp "a" "B" locale ignore-case))) ;;; Navigation Functions (defmacro org--compile-when (test &rest body) (declare (debug t) (indent 1)) (let ((exp `(progn ,@body))) (if (eval test t) exp `(when ,test (eval ',exp t))))) (org--compile-when (featurep 'jump) (defjump org-test-jump (("lisp/\\1.el" . "testing/lisp/test-\\1.el") ("lisp/\\1.el" . "testing/lisp/\\1.el/test.*.el") ("testing/lisp/test-\\1.el" . "lisp/\\1.el") ("testing/lisp/\\1.el" . "lisp/\\1.el/test.*.el")) (concat org-test-base-dir "/") "Jump between Org files and their tests." (lambda (path) (let* ((full-path (expand-file-name path org-test-base-dir)) (file-name (file-name-nondirectory path)) (name (file-name-sans-extension file-name))) (find-file full-path) (insert ";;; " file-name "\n\n" ";; Copyright (c) " (nth 5 (decode-time (current-time))) " " user-full-name "\n" ";; Authors: " user-full-name "\n\n" ";; Released under the GNU General Public License version 3\n" ";; see: https://www.gnu.org/licenses/gpl-3.0.html\n\n" ";;;; Comments:\n\n" ";; Template test file for Org tests\n\n" " \n" ";;; Code:\n" "(let ((load-path (cons (expand-file-name\n" " \"..\" (file-name-directory\n" " (or load-file-name buffer-file-name)))\n" " load-path)))\n" " (require 'org-test)\n\n" " \n" ";;; Tests\n" "(ert-deftest " name "/example-test ()\n" " \"Just an example to get you started.\"\n" " (should t)\n" " (should-not nil)\n" " (should-error (error \"errr...\")))\n\n\n" "(provide '" name ")\n\n" ";;; " file-name " ends here\n") full-path)) (lambda () ((lambda (res) (if (listp res) (car res) res)) (which-function))))) (define-key emacs-lisp-mode-map "\M-\C-j" #'org-test-jump) ;;; Miscellaneous helper functions (defun org-test-strip-text-props (s) "Return S without any text properties." (let ((noprop (copy-sequence s))) (set-text-properties 0 (length noprop) nil noprop) noprop)) (defun org-test-string-exact-match (regex string &optional start) "Case sensitive string-match" (let ((case-fold-search nil) (case-replace nil)) (if(and (equal regex "") (not(equal string ""))) nil (if (equal 0 (string-match regex string start)) t nil)))) ;;; Load and Run tests (defun org-test-load () "Load up the Org test suite." (interactive) (cl-flet ((rld (base) ;; Recursively load all files, if files throw errors ;; then silently ignore the error and continue to the ;; next file. This allows files to error out if ;; required executables aren't available. (mapc (lambda (path) (if (file-directory-p path) (rld path) (condition-case nil (when (string-match "\\`[A-Za-z].*\\.el\\'" (file-name-nondirectory path)) (let ((feature-name (intern (file-name-base (file-name-nondirectory path))))) (require feature-name path))) (missing-test-dependency (let ((name (intern (concat "org-missing-dependency/" (file-name-nondirectory (file-name-sans-extension path)))))) (eval `(ert-deftest ,name () (skip-unless nil) ;; Make it prominent. :expected-result :failed (should nil)))))))) (directory-files base 'full "\\`\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*\\.el\\'")))) (rld (expand-file-name "lisp" org-test-dir)))) (defun org-test-current-defun () "Test the current function." (interactive) (ert (which-function))) (defun org-test-current-file () "Run all tests for current file." (interactive) (ert (concat "test-" (file-name-sans-extension (file-name-nondirectory (buffer-file-name))) "/"))) (defvar org-test-buffers nil "Hold buffers open for running Org tests.") (defun org-test-touch-all-examples () (dolist (file (directory-files org-test-example-dir 'full "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*\\.org$")) (unless (get-file-buffer file) (add-to-list 'org-test-buffers (find-file file))))) (defun org-test-kill-all-examples () (while org-test-buffers (let ((b (pop org-test-buffers))) (when (buffer-live-p b) (kill-buffer b))))) (defun org-test-update-id-locations () (setq org-id-locations-file (expand-file-name ".test-org-id-locations" org-test-dir)) (org-id-update-id-locations (directory-files org-test-example-dir 'full "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*\\.org$"))) (defun org-test-run-batch-tests (&optional org-test-selector) "Run all tests matching an optional regex which defaults to \"\\(org\\|ob\\)\". Load all test files first." (interactive) (let ((org-id-track-globally t) (org-test-selector (if org-test-selector org-test-selector "\\(org\\|ob\\)")) org-confirm-babel-evaluate org-startup-folded vc-handled-backends ;; Catch errors in diary sexps better. (calendar-debug-sexp t)) (org-test-touch-all-examples) (org-test-update-id-locations) (org-test-load) (message "selected tests: %s" org-test-selector) (ert-run-tests-batch-and-exit org-test-selector))) (defun org-test-run-all-tests () "Run all defined tests matching \"\\(org\\|ob\\)\". Load all test files first." (interactive) (org-test-touch-all-examples) (org-test-update-id-locations) (org-test-load) (let (;; Catch errors in diary sexps better. (calendar-debug-sexp t)) (ert "\\(org\\|ob\\)")) (org-test-kill-all-examples)) (defmacro org-test-at-time (time &rest body) "Run body while pretending that the current time is TIME. TIME can be a non-nil Lisp time value, or a string specifying a date and time." (declare (indent 1)) (let ((tm (cl-gensym)) (at (cl-gensym))) `(let* ((,tm ,time) (,at (if (stringp ,tm) (org-time-string-to-time ,tm) ,tm))) (cl-letf ;; Wrap builtins whose behavior can depend on the current time. (((symbol-function 'current-time) (lambda () ,at)) ((symbol-function 'current-time-string) (lambda (&optional time &rest args) (apply ,(symbol-function 'current-time-string) (or time ,at) args))) ((symbol-function 'current-time-zone) (lambda (&optional time &rest args) (apply ,(symbol-function 'current-time-zone) (or time ,at) args))) ((symbol-function 'decode-time) (lambda (&optional time zone form) (condition-case nil (funcall ,(symbol-function 'decode-time) (or time ,at) zone form) (wrong-number-of-arguments (funcall ,(symbol-function 'decode-time) (or time ,at)))))) ((symbol-function 'encode-time) (lambda (time &rest args) (apply ,(symbol-function 'encode-time) (or time ,at) args))) ((symbol-function 'float-time) (lambda (&optional time) (funcall ,(symbol-function 'float-time) (or time ,at)))) ((symbol-function 'format-time-string) (lambda (format &optional time &rest args) (apply ,(symbol-function 'format-time-string) format (or time ,at) args))) ((symbol-function 'set-file-times) (lambda (file &optional time) (funcall ,(symbol-function 'set-file-times) file (or time ,at)))) ((symbol-function 'time-add) (lambda (a b) (funcall ,(symbol-function 'time-add) (or a ,at) (or b ,at)))) ((symbol-function 'time-equal-p) (lambda (a b) (funcall ,(symbol-function 'time-equal-p) (or a ,at) (or b ,at)))) ((symbol-function 'time-less-p) (lambda (a b) (funcall ,(symbol-function 'time-less-p) (or a ,at) (or b ,at)))) ((symbol-function 'time-subtract) (lambda (a b) (funcall ,(symbol-function 'time-subtract) (or a ,at) (or b ,at))))) ,@body)))) (defmacro org-test-capture-warnings (&rest body) "Capture all warnings passed to `org-display-warning' within BODY." (declare (indent 0) (debug t)) `(let ((messages (list))) (cl-letf (((symbol-function 'org-display-warning) (lambda (message) (setq messages (cons message messages))))) ,@body) (nreverse messages))) (defconst org-test-day-of-weeks-seconds [302400 ; Sun 388800 ; Mon 475200 ; Tue 561600 ; Wed 648000 ; Thu 734400 ; Fri 820800] ; Sat "Epoch seconds for generating days of week strings. Starts at Sunday, ends at Saturday.") (defconst org-test-day-of-weeks-abbrev (apply #'vector (seq-map (lambda (s) (format-time-string "%a" s t)) org-test-day-of-weeks-seconds)) "Vector of abbreviated names of days of week. See `org-test-day-of-weeks-seconds'.") (defconst org-test-day-of-weeks-full (apply #'vector (seq-map (lambda (s) (format-time-string "%A" s t)) org-test-day-of-weeks-seconds)) "Vector of full names for days of week. See `org-test-day-of-weeks-seconds'.") (defun org-test-get-day-name (day &optional full) "Return string containing locale-specific DAY abbrev. DAY Should be Mon, Tue, ... When FULL is non-nil, return the full name like Monday, Tuesday, ..." (aref (if full org-test-day-of-weeks-full org-test-day-of-weeks-abbrev) (pcase day ((or "Sun" "Sunday") 0) ((or "Mon" "Monday") 1) ((or "Tue" "Tuesday") 2) ((or "Wed" "Wednesday") 3) ((or "Thu" "Thursday") 4) ((or "Fri" "Friday") 5) ((or "Sat" "Saturday") 6) (_ (error "Unknown day of week: %s" day))))) (defmacro org-test-with-exported-text (backend source &rest body) "Run BODY in export buffer for SOURCE string via BACKEND." (declare (indent 2)) `(org-test-with-temp-text ,source (let ((export-buffer (generate-new-buffer "Org temporary export"))) (unwind-protect (progn (org-export-to-buffer ,backend export-buffer) (with-current-buffer export-buffer ,@body)) (kill-buffer export-buffer))))) (provide 'org-test) ;;; org-test.el ends here