pax_global_header00006660000000000000000000000064145370772110014521gustar00rootroot0000000000000052 comment=c91451f2d17453c19d3fa76faa4945cbe54e14ce emacs-eat/000077500000000000000000000000001453707721100127245ustar00rootroot00000000000000emacs-eat/.dir-locals.el000066400000000000000000000005121453707721100153530ustar00rootroot00000000000000;;; Directory Local Variables -*- no-byte-compile: t -*- ;;; For more information see (info "(emacs) Directory Variables") ((nil . ((fill-column . 70) (indent-tabs-mode . nil))) (sh-mode . ((sh-basic-offset . 2))) (makefile-mode . ((indent-tabs-mode . t))) (".git" . ((text-mode . ((fill-column . 63)))))) emacs-eat/.elpaignore000066400000000000000000000001201453707721100150430ustar00rootroot00000000000000CONTRIBUTE Makefile eat-tests.el gitlog-to-changelog make-changelog texinfo.tex emacs-eat/.gitignore000066400000000000000000000001141453707721100147100ustar00rootroot00000000000000eat.elc eat-tests.elc term/eat.elc dir eat.info eat-pkg.el eat-autoloads.el emacs-eat/CONTRIBUTE000066400000000000000000000244751453707721100143410ustar00rootroot00000000000000* How developers contribute to Eat Here is how software developers can contribute to Eat. ** Documenting your changes Any change that matters to end-users should have an entry in etc/NEWS. Try to start each NEWS entry with a sentence that summarizes the entry and takes just one line -- this will allow to read NEWS in Outline mode after hiding the body of each entry. Doc-strings should be updated together with the code. Think about whether your change requires updating the manuals. If you know it does not, mark the NEWS entry with "---". If you know that *all* the necessary documentation updates have been made as part of your changes or those by others, mark the entry with "+++". Otherwise do not mark it. If your change requires updating the manuals to document new functions/commands/variables/faces, then use the proper Texinfo command to index them; for instance, use @vindex for variables and @findex for functions/commands. For the full list of predefined indices, see https://www.gnu.org/software/texinfo/manual/texinfo/html_node/Predefined-Indices.html or run the shell command 'info "(texinfo)Predefined Indices"'. We prefer American English both in doc strings and in the manuals, just like Emacs. That includes both spelling (e.g., "behavior", not "behaviour") and the convention of leaving 2 spaces between sentences. Eat follows Emacs doc style. For more specific tips on Emacs's doc style, see https://www.gnu.org/software/emacs/manual/html_node/elisp/Documentation-Tips.html Use 'checkdoc' to check for documentation errors before submitting a patch. ** Testing your changes Please test your changes before committing them or sending them to the list. "make check" runs all the tests. You will need to set "EMACSFLAGS" so that the checker can find the dependencies. If possible, add a new test along with any bug fix or new functionality you commit (of course, some changes cannot be easily tested, but the terminal emulator part should be as much tested as possible). Eat uses ERT, Emacs Lisp Regression Testing, for testing. See https://www.gnu.org/software/emacs/manual/html_node/ert/ or run 'info "(ert)"' for more information on writing and running tests. If your test lasts longer than some few seconds, mark it in its 'ert-deftest' definition with ":tags '(:expensive-test)". ** Commit messages Ordinarily, a change you commit should contain a log entry in its commit message and should not touch the repository's ChangeLog files. Here is an example commit message (indented): Deactivate shifted region Do not silently extend a region that is not highlighted; this can happen after a shift (Bug#19003). * doc/emacs/mark.texi (Shift Selection): Document the change. * lisp/window.el (handle-select-window): * src/frame.c (Fhandle_switch_frame, Fselected_frame): Deactivate the mark. Here are guidelines for formatting commit message: - Start with a single unindented summary line explaining the change; do not end this line with a period. If possible, try to keep the summary line to 50 characters or fewer; this is for compatibility with certain Git commands that print that line in width-constrained contexts. If the summary line starts with a semicolon and a space "; ", the commit message will be ignored when generating the ChangeLog file. Use this for minor commits that do not need separate ChangeLog entries, such as changes in etc/NEWS. - After the summary line, there should be an empty line. - Unindented ChangeLog entries normally come next. However, if the commit couldn't be properly summarized in the brief summary line, you can put a paragraph (after the empty line and before the individual ChangeLog entries) that further describes the commit. - Lines in ChangeLog entries should preferably be not longer than 63 characters, and must not exceed 78 characters, unless they consist of a single word of at most 140 characters; this 78/140 limit is enforced by a commit hook. - If only a single file is changed, the summary line can be the normal file first line (starting with the asterisk). Then there is no individual files section. - If the commit has more than one author, the commit message should contain separate lines to mention the other authors, like the following: Co-authored-by: Joe Schmoe - If the commit is a tiny change that is exempt from copyright paperwork, the commit message should contain a separate line like the following: Copyright-paperwork-exempt: yes - The commit message should not contain any reference to any Codeberg issue. - The commit message should contain "Bug#NNNNN" if it is related to bug number NNNNN in the debbugs database. This string is often parenthesized, as in "(Bug#19003)". - When citing URLs, prefer https: to http: when either will do. - Commit messages should contain only printable UTF-8 characters. - Commit messages should not contain the "Signed-off-by:" lines that are used in some other projects. - Any lines of the commit message that start with "; " are omitted from the generated ChangeLog. - Explaining the rationale for a design choice is best done in comments in the source code. However, sometimes it is useful to describe just the rationale for a change; that can be done in the commit message between the summary line and the file entries. - Eat follows the GNU coding standards for ChangeLogs: see https://www.gnu.org/prep/standards/html_node/Change-Logs.html or run 'info "(standards)Change Logs"'. - Some commenting rules in the GNU coding standards also apply to ChangeLog entries: they must be in English, and be complete sentences starting with a capital and ending with a period (except the summary line should not end in a period). See https://www.gnu.org/prep/standards/html_node/Comments.html or run 'info "(standards)Comments"'. American English is preferred in Eat, just like Emacs; that includes spelling and leaving 2 blanks between sentences. They are preserved indefinitely, and have a reasonable chance of being read in the future, so it's better that they have good presentation. - Use the present tense; describe "what the change does", not "what the change did". - Preferred form for several entries with the same content: * lisp/menu-bar.el (clipboard-yank, clipboard-kill-ring-save) (clipboard-kill-region): * lisp/eshell/esh-io.el (eshell-virtual-targets) (eshell-clipboard-append): Replace option gui-select-enable-clipboard with select-enable-clipboard; renamed October 2014. (Bug#25145) (Rather than anything involving "ditto" and suchlike.) - There is no standard or recommended way to identify revisions in ChangeLog entries. Using Git SHA1 values limits the usability of the references to Git, and will become much less useful if Eat. switches to a different VCS. So we recommend against doing only that. One way to identify revisions is by quoting their summary line. Prefixing the summary with the commit date can give useful context (use 'git show -s "--pretty=format:%cd \"%s\"" --date=short HASH' to produce that). Often, "my previous commit" will suffice. - There is no need to mention files such as NEWS and MAINTAINERS, or to indicate regeneration of files such as 'lib/gnulib.mk', in the ChangeLog entry. "There is no need" means you don't have to, but you can if you want to. ** Committing your changes. Your commit message must meet the following critiria: - commit log message must not be empty; - the first line of the commit log message doesn't start with whitespace characters; - the second line of the commit log message must be empty; - commit log message should include only valid printable ASCII and UTF-8 characters; - commit log message lines must be shorter than 79 characters, unless a line consists of a single long word, in which case that word can be up to 140 characters long; - there shouldn't be any "Signed-off-by:" tags in the commit log message, and "git commit" should not be invoked with the '-s' option (which automatically adds "Signed-off-by:"); - if the commit adds new files, the file names must not begin with '-' and must consist of ASCII letters, digits, and characters of the set [-+./_]; - the changes don't include unresolved merge conflict markers; - the changes don't introduce whitespace errors: trailing whitespace, lines that include nothing but whitespace characters, and indented lines where a SPC character is immediately followed by a TAB in the line's initial indentation This is not enforced right now, but it'll happen eventually. ** Committing changes by others If committing changes written by someone else, commit in their name, not yours. You can use 'git commit --author="AUTHOR"' to specify a change's author. When using Emacs VC to commit, the author can be specified in the log-edit buffer by adding an "Author: AUTHOR" header line (set 'log-edit-setup-add-author' non-nil to have this header line added automatically). Note that the validity checks described in the previous section are still applied, so you will have to correct any problems they uncover in the changes submitted by others. ** git vs rename Git does not explicitly represent a file renaming; it uses a percent changed heuristic to deduce that a file was renamed. So if you are planning to make extensive changes to a file after renaming it (or moving it to another directory), you should: - Create a feature branch. - Commit the rename without any changes. - Make other changes. - Merge the feature branch to the master branch, instead of squashing the commits into one. The commit message on this merge should summarize the renames and all the changes. Adapted from CONTRIBUTE in Emacs source tree. This file is part of Eat and is not part of GNU Emacs. Eat 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. Eat 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 . Local variables: mode: outline paragraph-separate: "[ ]*$" coding: utf-8 end: emacs-eat/COPYING000066400000000000000000001045151453707721100137650ustar00rootroot00000000000000 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 . emacs-eat/ChangeLog000066400000000000000000002117311453707721100145030ustar00rootroot000000000000002023-12-15 Akib Azmain Turja Don't let automatic scrolling mess up the display * eat.el (eat-mode, eat--eshell-local-mode): Set both 'scroll-margin' and 'hscroll-margin' to zero. 2023-10-26 Akib Azmain Turja Don't queue input * eat.el (eat--pending-input-chunks) (eat--defer-input-processing, eat--process-input-queue-timer): Remove. All references updated. * eat.el (eat--send-input): Send the input immediately. * eat.el (eat--process-input-queue): Remove. * eat.el (eat--eshell-process-output-queue) (eat--process-output-queue): Loop while output queue isn't empty, set 'eat--output-queue-first-chunk-time' to 't' in the loop. * eat.el (eat--filter, eat--eshell-filter): Don't start a timer if 'eat--output-queue-first-chunk-time' is 't'. 2023-10-19 Akib Azmain Turja * eat.ti (eat-mono): Remove unused capability 'Ed' 2023-10-17 Akib Azmain Turja Fix input processing & auto line mode * eat.el (eat--process-input-queue): Set 'eat--process-input-queue-timer' to 'nil'. * eat.el (eat--auto-line-mode-pending-toggles): New variable. * eat.el (eat--line-mode-enter-auto-1): Don't take any argument. * eat.el (eat--line-mode-exit-auto-1): Don't take any argument. Don't call 'eat-line-mode'. * eat.el (eat--line-mode-enter-auto, eat--line-mode-exit-auto): Queue the toggle in 'eat--auto-line-mode-pending-toggles' instead of starting a timer. * eat.el (eat--line-mode-do-toggles): New function. * eat.el (eat--process-output-queue): Let-bind 'eat--auto-line-mode-pending-toggles' to nil. Call 'eat--line-mode-do-toggles'. 2023-10-17 Akib Azmain Turja Fix compiler warnings * eat.el: Wrap all 'emacs-major-version' checks with 'eval-when-compile'. * eat.el (eat--process-input-queue): Don't use obsolete generalized variable 'buffer-local-value'. 2023-10-17 Akib Azmain Turja Don't let 'eat--process-input-queue' to recurse * eat.el (eat--defer-input-processing): New variable. * eat.el (eat-mode, eat--eshell-local-mode): Make 'eat--defer-input-processing' buffer-local. * eat.el (eat--process-input-queue): If 'eat--defer-input-processing' is non-nil, do nothing, otherwise bind it to non-nil while sending input. 2023-10-17 Akib Azmain Turja Don't mess up terminal when switching to line mode * eat.el (eat--line-mode-enter-auto-1) (eat--line-mode-exit-auto-1): New function. * eat.el (eat--line-mode-enter-auto, eat--line-mode-exit-auto): Do everything after the output is processed. 2023-10-10 Akib Azmain Turja Don't flicker due to screen line breaking * eat.el (eat--eshell-synchronize-scroll) (eat--synchronize-scroll): Use 'recenter' instead of 'set-window-start' to synchronize scrolling. 2023-10-10 Akib Azmain Turja Fix side-effect in 'eat--set-term-sixel-params' * eat.el (eat--set-term-sixel-params): Fix unintended side-effect. 2023-10-10 Akib Azmain Turja Fix 'eat--set-term-sixel-params' on text display * eat.el (eat--set-term-sixel-params): Don't error on text display. 2023-10-09 Akib Azmain Turja Fix 'eat--eshell-set-input-process' advice * eat.el (eat--eshell-set-input-process): Take any number of arguments and ignore them. 2023-10-09 Akib Azmain Turja Make motion commands work properly * eat.el (eat--line-mode): Set 'front-sticky' and 'rear-nonsticky' text properties to 't' when enabling mode, and revert them when disabling. * eat.el (eat--process-output-queue): Don't add 'front-sticky' and 'rear-nonsticky' text properties if not in line mode. * eat.el (eat--sentinel): Don't remove 'front-sticky' and 'rear-nonsticky' text properties, they shouldn't be in the buffer. 2023-10-09 Akib Azmain Turja Remove internal text properties when killing text * eat.el (eat-term-filter-string): Remove 'read-only', 'rear-nonsticky', 'front-sticky' and 'field' text properties. * eat.el (eat--filter-buffer-substring): Remove 'eat--t-char-width', 'eat--t-sixel-bitmap-size' and 'eat--t-sixel-bitmap' text properties. 2023-10-08 Akib Azmain Turja Fix Makefile * Makefile (eat.elc, check): Pass $(EMACSFLAGS) to $(EMACS). 2023-10-07 Akib Azmain Turja Fix garbage image after the end of Sixel image * eat.el (eat--t-sixel-init): Increase Sixel buffer size. 2023-10-07 Akib Azmain Turja Support Emacs 26.1 * eat.el: Depend on Emacs 26.1. * eat.el (eat--t-handle-output, eat--handle-uic) (eat--eshell-handle-uic): Don't use '(not CHAR)' form in 'rx' forms. * eat.el (eat--eshell-process-output-queue): Use 'combine-change-calls' if available. * README.org: Update to reflect support for Emacs 26. 2023-10-07 Akib Azmain Turja Update tests to make them pass * eat-tests.el: Use 'eat-term-parameter'. * eat-tests.el (eat-test-set-cwd): Test OSC 7 only. 2023-10-07 Akib Azmain Turja Modify eshell-variable-aliases-list buffer-locally * eat.el (eat-eshell-mode): Don't modify 'eshell-variable-aliases-list'. * eat.el (eat--eshell-local-mode): Modify 'eshell-variable-aliases-list'. 2023-10-07 Akib Azmain Turja Support Emacs 27.1 * eat.el: Depend on Emacs 27.1. Require 'compat' and 'term'. * eat.el (eat-term-color-*): Inherit 'term-color-*' in Emacs 27. * eat.el (eat--t-sixel-render-bitmap): Call 'mapconcat' with three arguments. * eat.el (eat-yank-from-kill-ring): Signal error in Emacs 27. * eat.el (eat--eshell-setup-proc-and-term): Don't error if process mark is invalid. * eat.el (eat-eshell-mode): Properly revert 'eshell-variable-aliases-list' when disabling the mode. * eat.el (eat--trace-exec) (eat--trace-eshell-adjust-make-process-args): Make sure 'lisp-data-mode' is defined before calling. 2023-10-06 Akib Azmain Turja Enable shell command completion by default * eat.el (eat-mode): Use shell mode's completion facilities. 2023-10-04 Akib Azmain Turja Don't put overlay on prompt while history Isearch * eat.el (eat--line-history-isearch-message-overlay) (eat--line-history-isearch-message): Remove. * eat.el (eat--line-history-isearch-setup) (eat--line-history-isearch-end): Don't modify 'isearch-message-function'. * eat.el (eat-mode): Don't make 'isearch-message-function' and 'eat--line-history-isearch-message-overlay' buffer-local. 2023-10-04 Akib Azmain Turja Put the cursor below Sixel image after showing it * eat.el (eat--t-sixel-cleanup): Make sure the cursor is below the image. 2023-10-03 Akib Azmain Turja Apply scaling and ascept ratio on Sixel properly * eat.el (eat--set-term-sixel-params): Make sure both numbers of char-dimension is non-zero. 2023-10-03 Akib Azmain Turja Use terminal parameters to set callback functions Also make sure the terminal passed as arguments to API functions is live. * eat.el (eat--t-term): Update default value of slot 'params'. * eat.el (eat-term-p, eat-term-live-p): New function. * eat.el (eat--t-ensure-live-term): New macro. * eat.el (eat--t-with-env, eat-term-parameter, eat-term-size) (eat-term-set-parameter, eat-term-cursor-type, eat-term-end) (eat-term-beginning, eat-term-display-cursor, eat-term-title) (eat-term-in-alternative-display-p, eat-term-input-event) (eat-term-send-string, eat-term-send-string-as-yank): Ensure the terminal passed as argument is live. * eat.el (eat-term-delete): Ensure the terminal passed as argument is live. Mark terminal as deleted. * eat.el (eat-term-parameters): New function. * eat.el (eat-term-set-parameter): Handle more special parameters: 'input-function', 'ring-bell-function', 'grab-mouse-function', 'grab-focus-events-function', 'manipulate-selection-function', 'set-title-function' and 'set-cwd-function'. * eat.el (eat-term-input-function, eat-term-ring-bell-function) (eat-term-set-cursor-function, eat-term-grab-mouse-function) (eat-term-grab-focus-events-function) (eat-term-manipulate-selection-function) (eat-term-set-title-function, eat-term-set-cwd-function): Remove function. * eat.el (eat-exec, eat--eshell-setup-proc-and-term) (eat--trace-replay-eval): Update to use parameters. 2023-10-03 Akib Azmain Turja Support rendering Sixel with XPM images * eat.el (eat--t-sixel-render-bitmap): Support conversion to XPM format. * eat.el (eat-sixel-render-formats): Add XPM to the default. * eat.el (eat-term-set-parameter, eat--sixel-render-format): Support XPM as an allowed format. * eat.texi (Sixel): Update. 2023-10-03 Akib Azmain Turja Refactor 'eat--t-sixel-flush-line' * eat.el (eat--t-sixel-render-bitmap): New function. * eat.el (eat--t-sixel-flush-line): Move bitmap rendering code to 'eat--t-sixel-render-bitmap'. 2023-10-02 Akib Azmain Turja * eat.el (eat-enable-alternative-display): Obsolete 2023-10-02 Akib Azmain Turja Allow customizing Sixel scaling and aspect ratio * eat.el (eat-sixel-scale, eat-sixel-aspect-ratio): New user option. * eat.el (eat--t-term): Remove slot 'sixel-image-height'. New slot 'sixel-image-extra-props'. * eat.el (eat--t-sixel-flush-line): Give more control to the UI on the image properties. * eat.el (eat-term-set-parameter): Don't treat parameter 'sixel-image-height' specially. Treat parameter 'sixel-image-extra-properties' specially. * eat.el (eat--set-term-sixel-params): New function. * eat.el (eat-exec, eat--eshell-setup-proc-and-term): Set Sixel related terminal parameters with 'eat--set-term-sixel-params'. * eat.texi (Sixel): Document 'eat-sixel-scale' and 'eat-sixel-aspect-ratio'. 2023-09-28 Akib Azmain Turja Support * eat.el (eat-term-input-event): Handle '' and '' events. * eat.el (eat-term-make-keymap): Bind '' and ''. * eat.ti (eat-mono): Add 'kcbt' capability. 2023-09-27 Akib Azmain Turja Handle output correctly in emacs mode in Eshell * eat.el (eat--eshell-output-filter): Inhibit read only. 2023-09-24 Akib Azmain Turja Add command 'eat-send-password' * eat.el (eat-send-password): New command. * eat.texi (Password Input): New chapter. 2023-09-24 Akib Azmain Turja Add the underlying terminal object to public API * eat.el (eat--terminal): Rename to 'eat-terminal'. All references changed. 2023-09-23 Akib Azmain Turja New command eat-line-load-input-history-from-file * eat.el (eat-line-load-input-history-from-file): New command. 2023-09-23 Akib Azmain Turja Integrate Isearch with line mode input history * eat.el (eat-line-input-history-isearch): New user option. * eat.el (eat-line-mode-map): Bind 'M-r' to 'eat-line-history-isearch-backward-regexp'. * eat.el (eat-line-mode): Error if process isn't running. * eat.el (eat--line-history-isearch-message-overlay) (eat--saved-line-input-history-isearch): New variable. * eat.el (eat--line-delete-input): Remove. All callers changed. * eat.el (eat-line-history-isearch-backward-regexp) (eat-line-history-isearch-backward): New command. * eat.el (eat--line-history-isearch-setup) (eat--line-history-isearch-end, eat--line-history-isearch-wrap) (eat--line-history-isearch-search, eat--line-goto-input) (eat--line-history-isearch-message) (eat--line-history-isearch-push-state) (eat--line-history-isearch-pop-state): New function. * eat.el (eat-mode): Make 'eat--line-history-isearch-message-overlay', 'isearch-search-fun-function', 'isearch-message-function', 'isearch-wrap-function' and 'isearch-push-state-function' buffer-local. Add 'eat--line-history-isearch-setup' to 'isearch-mode-hook' locally. * eat.texi (Line Mode): Document this new feature. 2023-09-23 Akib Azmain Turja Move to the input line automatically in line mode * eat.el (eat-line-auto-move-to-input): New user option. * eat.el (eat--line-move-to-input): New function. * eat.el (eat--line-mode): Add/remove 'eat--line-move-to-input' to 'pre-command-hook' locally. * eat.texi (Line Mode): Document 'eat-line-auto-move-to-input'. 2023-09-23 Akib Azmain Turja Rename prompt mode to line mode and generalize it * eat.el: Rename prompt mode to line mode, also rename related symbols to have 'eat-line-' or 'eat--line-' prefix. * eat.el (eat-enable-native-shell-prompt-editing): Rename to 'eat-enable-auto-line-mode'. * eat.el (eat--line-mode-enter-auto) (eat--line-mode-exit-auto): New function. * eat.el (eat--post-prompt, eat--post-cont-prompt): Call 'eat--line-mode-enter-auto' to enter line mode. * eat.el (eat--pre-cmd): Call 'eat--line-mode-exit-auto'. * eat.el (eat--get-shell-history): Populate input ring unconditionally. * eat.el (eat-emacs-mode, eat-semi-char-mode, eat-char-mode): Call 'eat--line-mode-exit' to exit line mode. * eat.el (eat--line-mode): Move all logic to 'eat-line-mode', 'eat--line-mode-exit' and 'eat--line-mode-exit-auto'. * eat.el (eat-line-send): Make non-interactive. Reset input history cycling variables. * eat.el (eat-line-send-input): New argument NO-NEWLINE. * eat.el (eat-mode): Don't disable undo information recording. * eat.el (eat--process-output-queue): Disable undo information recording while process output. * eat.texi (Line Mode): New section in chapter 'Input Modes'. * eat.texi (Line Mode Integration): New section in chapter 'Shell Integration'. * eat.texi (Native Shell Prompt Editing): Remove section. * README.org (Usage): Avoid the term "keybinding mode", use "input mode" instead as the manual uses it. Document line mode. * README.org (NonGNU ELPA): Emacs 28 has NonGNU ELPA enabled by default, so remove unnecessary instructions. 2023-09-22 Akib Azmain Turja Skip comments in Bash history file * eat.el (eat--prompt-populate-input-ring): Skip Bash history comments. * eat.el: Add Compat as dependency; some functions from Emacs 29 are used. 2023-09-18 Akib Azmain Turja Allow customizing Sixel rendering format * eat.el (eat-sixel-render-formats): New user option. * eat.el (eat-eshell-fallback-if-stty-not-available): Fix customization type. * eat.el (eat--t-term): Rename slot 'sixel-display-method' to 'sixel-render-format'. All callers changed. * eat.el (eat--t-sixel-flush-line): New support render format, 'none'. * eat.el (eat-term-set-parameter): Rename parameter 'sixel-display-method' to 'sixel-render-format'. All callers changed. * eat.el (eat--sixel-render-format): New function. * eat.el (eat-exec, eat--eshell-setup-proc-and-term): Set 'sixel-render-format' according to user customization. 2023-09-17 Akib Azmain Turja Add message passing support * eat.el (eat-message-handler-alist): New user option. * eat.el (eat--handle-message): New function. * eat.el (eat--handle-uic, eat--eshell-handle-uic): Handle message passing sequence. * eat.texi (Message Passing): New section in chapter "Shell Integration". * integration/bash (__eat_enable_integration): Remove the unnecessary complex code to update PROMPT_COMMAND. * integration/bash (_eat_msg): * integration/zsh (_eat_msg): New function. 2023-09-16 Akib Azmain Turja Don't enter prompt mode after exit till new prompt * eat.el (eat--inhibit-prompt-mode): New variable. * eat.el (eat--post-prompt, eat--post-cont-prompt): Check 'eat--inhibit-prompt-mode' before switching to prompt mode. * eat.el (eat--before-new-prompt): New function. * eat.el (eat--handle-uic): Support "before new prompt" sequence. * eat.el (eat-emacs-mode, eat-semi-char-mode, eat-char-mode): Set 'eat--inhibit-prompt-mode' to t if prompt mode is enabled. * eat.el (eat-mode): Make 'eat--inhibit-prompt-mode' buffer-local. * integration/bash (__eat_prompt_command): * integration/zsh (__eat_precmd): Send the "before new prompt" sequence. 2023-09-16 Akib Azmain Turja Add history reporting in Zsh integration script * eat.el (eat--get-shell-history): New argument FORMAT, pass it to 'eat--prompt-populate-input-ring'. * eat.el (eat--handle-uic): Accept FORMAT argument in input history reporting sequence and pass it to 'eat--get-shell-history'. * eat.el (eat--prompt-populate-input-ring): New argument FORMAT. Support Zsh extended history file format. * integration/bash (__eat_enable_integration): Send "bash" as the history file format. * integration/zsh (__eat_enable_integration): Send shell history. 2023-09-16 Akib Azmain Turja Fix input handling in Eshell * eat.el (eat--eshell-local-mode): Make input processing variables buffer-local. 2023-09-14 Akib Azmain Turja * eat.el (eat-prompt-newline): Make ring if needed 2023-09-14 Illia Ostapyshyn * eat.el (eat-mode): Escape '%' character in title 2023-09-14 Akib Azmain Turja Fix recursive output processing * eat.el (eat--pending-input-chunks) (eat--process-input-queue-timer): New variable. * eat.el (eat-mode): Make 'eat--pending-input-chunks' and 'eat--process-input-queue-timer' buffer-local. * eat.el (eat--send-input): Queue input instead of sending immediately. * eat.el (eat--process-input-queue): New function. 2023-09-14 Akib Azmain Turja Document prompt mode * eat.texi (Shell Integration): Divide into sections. Add new section 'Native Shell Prompt Editing'. * README.org (Usage): Document "prompt" mode. * README.org (Comparison With Other Terminal Emulators): Update comparison. 2023-09-14 Akib Azmain Turja Allow terminal text modification after it's killed * eat.el (eat--sentinel): Remove 'read-only' and other text properties from the terminal region. 2023-09-13 Akib Azmain Turja Fix Bash long startup when run from Eshell * eat.el (eat--eshell-handle-uic): Acknowledge when input history file is sent by the shell. 2023-09-13 Akib Azmain Turja Disable native shell prompt editing by default * eat.el (eat-enable-native-shell-prompt-editing): Set to nil. 2023-09-13 Akib Azmain Turja Add prompt mode and process OSC 51 as UI Command * eat.el (eat-enable-native-shell-prompt-editing) (eat-prompt-input-ring-size) (eat-prompt-move-point-for-matching-input): New user option. * eat.el (eat--t-term): Remove slots 'cwd', 'prompt-start-fn', 'prompt-end-fn', 'cont-prompt-start-fn', 'cont-prompt-end-fn', 'cmd-start-fn' and 'cmd-finish-fn'. Add new slot 'ui-cmd-fn'. * eat.el (eat--t-set-cwd): Support only the format used by OSC 7. * eat.el (eat--t-prompt-start, eat--t-prompt-end) (eat--t-cont-prompt-start, eat--t-cont-prompt-end) (eat--t-set-cmd, eat--t-cmd-start, eat--t-cmd-finish): Remove function. * eat.el (eat--t-ui-cmd): New function. * eat.el (eat--t-handle-output): Remove all specialized OSC 51 handlers and call 'eat--t-ui-cmd' to handle any OSC 51 sequence. * eat.el (eat-term-set-parameter): Set 'ui-cmd-fn' slot of terminal when 'ui-command-function' is set. * eat.el (eat-term-cwd): Remove function. * eat.el (eat-term-prompt-start-function) (eat-term-prompt-end-function, eat-term-cmd-start-function) (eat-term-cont-prompt-start-function) (eat-term-cont-prompt-end-function, eat-term-set-cmd-function) (eat-term-cmd-finish-function): Remove generalized variables. * eat.el (eat-term-send-string): New function. * eat.el (eat-send-string-as-yank): Rename to 'eat-term-send-string-as-yank'. All callers changed. * eat.el (eat--set-cwd-uic, eat--post-cont-prompt): New function. * eat.el (eat--pre-prompt, eat--post-prompt, eat--set-cmd) (eat--pre-cmd, eat--set-cmd-status): Remove unused first argument. All callers changed. * eat.el (eat--get-shell-history, eat--handle-uic): New function. * eat.el (eat-prompt-mode-map): New variable. * eat.el (eat-emacs-mode, eat-semi-char-mode, eat-char-mode): Handle the case when prompt mode is active. * eat.el (eat--prompt-mode-previous-mode): New variable. * eat.el (eat--prompt-mode): New non-interactive minor mode. * eat.el (eat-prompt-send-default, eat-prompt-send) (eat-prompt-newline, eat-prompt-delchar-or-eof) (eat-prompt-send-interrupt): New command. * eat.el (eat--prompt-input-ring, eat--prompt-input-ring-index) (eat--prompt-stored-incomplete-input) (eat--prompt-matching-input-from-input-string): New variable. * eat.el (eat--prompt-populate-input-ring) (eat--prompt-reset-input-ring-vars, eat--prompt-ask-for-regexp-arg) (eat--prompt-search-arg, eat--prompt-search-start) (eat--prompt-prev-input-string, eat--prompt-prev-matching-input-str) (eat--prompt-delete-input, eat--prompt-prev-matching-input-str-pos): New function. * eat.el (eat-prompt-previous-input, eat-prompt-next-input) (eat-prompt-restore-input, eat-prompt-previous-matching-input) (eat-prompt-next-matching-input, eat-prompt-find-input) (eat-prompt-previous-matching-input-from-input) (eat-prompt-next-matching-input-from-input): New command. * eat.el (eat-mode): Make 'eat--prompt-mode-previous-mode', 'eat--prompt-input-ring', 'eat--prompt-input-ring-index', 'eat--prompt-stored-incomplete-input' and 'eat--prompt-matching-input-from-input-string' buffer-local. Record undo information if 'eat-enable-native-shell-prompt-editing' is enabled. Set 'mode-line-process' properly so that prompt mode is shown when enabled. * eat.el (eat--process-output-queue): Remove any narrowing temporarily when called. Don't * eat.el (eat--sentinel): Disable prompt mode. Also fix the bug where the point centers when the program exits, instead of going to the end of buffer. * eat.el (eat-exec): Don't set removed generalized variables 'eat-term-prompt-start-function', 'eat-term-set-cmd-function', 'eat-term-prompt-end-function', 'eat-term-cmd-start-function' and 'eat-term-cmd-finish-function'. Set terminal parameter 'ui-command-function' to 'eat--handle-uic'. * eat.el (eat--eshell-handle-uic): New function. * eat.el (eat--eshell-output-filter): Let-bind 'inhibit-read-only' only for required parts. * eat.el (eat--eshell-setup-proc-and-term): Set terminal parameter 'ui-command-function' to 'eat--eshell-handle-uic'. * eat.el (eat--eshell-sentinel): Fix the bug where the point centers when the program exits, instead of going to the end of buffer. * integration/bash (__eat_enable_integration): Send shell history. 2023-09-03 Akib Azmain Turja * eat.el (eat-project-other-window): New command 2023-08-31 Akib Azmain Turja Add command 'eat-other-window' * eat.el (eat--1, eat-other-window): New function. * eat.el (eat): Move all logic to 'eat--1' and call it. 2023-08-27 Akib Azmain Turja Support Sixel properly in Eshell * eat.el (eat--eshell-setup-proc-and-term): Set Sixel attributes of the terminal object. 2023-08-25 Akib Azmain Turja Add experimental Sixel support * eat.el (eat--t-cur): New slots 'sixel-x', 'sixel-y' and 'sixel-beg'. * eat.el (eat--t-term): New slots 'sixel-buffer', 'sixel-buffer-size', 'sixel-palette', 'sixel-color', 'sixel-display-method', 'sixel-image-height', 'sixel-scroll-mode', 'sixel-initial-cursor-pos', 'char-width' and 'char-height'. * eat.el (eat--t-reset): Reset 'sixel-scroll-mode' to 't'. * eat.el (eat--t-fix-partial-multi-col-char): Preserve original face if PRESERVE-FACE is non-nil. * eat.el (eat--t-send-device-attrs): Send correct attributes. * eat.el (eat--t-report-foreground-color) (eat--t-report-background-color): Use correct format. * eat.el (eat--t-sixel-init, eat--t-send-graphics-attrs) (eat--t-sixel-write, eat--t-sixel-flush-line) (eat--t-sixel-newline, eat--t-sixel-set-color-reg) (eat--t-sixel-cleanup, eat--t-sixel-enable-scrolling) (eat--t-sixel-disable-scrolling): New function. * eat.el (eat--t-set-modes, eat--t-reset-modes): Handle Sixel scroll mode. * eat.el (eat--t-handle-output): Update 'eat--t-send-device-attrs' call. Handle 'send graphics attributes' CSI function. Parse and dispatch DCS sequence properly. Handle Sixel sequence. * eat.el (eat-term-set-parameter): Handle 'char-dimensions', 'sixel-display-method', and 'sixel-image-height' parameters. * eat.el (eat-exec): Set 'char-dimensions', 'sixel-display-method', and 'sixel-image-height' parameters. 2023-07-20 Akib Azmain Turja Implement device status report function properly * eat.el (eat--t-device-status-report): Implement properly. Take an argument. * eat.el (eat--t-handle-output): Update call to 'eat--t-device-status-report'. 2023-07-12 Akib Azmain Turja Bind 'S-' to 'eat-yank' * eat.el (eat--eshell-prepare-semi-char-mode-map) (eat--prepare-semi-char-mode-map): Bind 'S-' to 'eat-yank'. 2023-07-06 Akib Azmain Turja Bind keys 'DEL' and 'M-DEL' keys * eat.el (eat-term-input-event): Handle 'DEL' and 'M-DEL' keys. * eat.el (eat-term-make-keymap): Bind 'DEL' and 'M-DEL' keys. 2023-07-03 Akib Azmain Turja Fix recursive load error * eat.el (eat-semi-char-non-bound-keys) (eat-eshell-semi-char-non-bound-keys): Let-bind 'after-load-alist' and 'after-load-functions' before reloading Eat. 2023-05-15 Akib Azmain Turja Yank active region with middle-click yank * eat.el (eat-mouse-yank-primary): Select active region if 'select-active-regions' is non-nil. 2023-05-06 Akib Azmain Turja Fix middle-click yanking commands * eat.el (eat-mouse-yank-primary): Switch to the window where the event was initiated. * eat.el (eat-mouse-yank-secondary): Switch to the window where the event was initiated. Ensure that the secondary selection is non-empty before sending it. 2023-05-06 Akib Azmain Turja Support middle-click yank in terminal * eat.el (eat-mouse-yank-primary, eat-mouse-yank-secondary): New command. * eat.el (eat--prepare-semi-char-mode-map) (eat--eshell-prepare-semi-char-mode-map): Remap commands 'mouse-yank-primary' and 'mouse-yank-secondary' to 'eat-mouse-yank-primary' and 'eat-mouse-yank-secondary' respectively. 2023-04-17 Akib Azmain Turja Update documentation in commentary section * README.org (Usage): List keybindings in semi-char mode to switch to other keybinding modes. * eat.el (Commentary): Put the documentation of semi-char mode first as it is the default. List keybindings in semi-char mode to switch to other keybinding modes. Update stale keybindings listed in emacs mode documentation. 2023-04-09 Akib Azmain Turja Pass process to eat-exec-hook and eat-exit-hook * eat.el (eat-exec-hook, eat-exit-hook): Update docstring. * eat.el (eat--sentinel): Pass process to 'eat-exit-hook'. Delete process after running 'eat-exit-hook'. * eat.el (eat--kill-buffer): New helper function. * eat.el (eat-exec): Pass process to 'eat-exec-hook'. Add or remove 'eat--kill-process' from 'eat-exit-hook' instead of 'kill-buffer'. 2023-04-09 Akib Azmain Turja New command 'eat-narrow-to-shell-prompt' * eat.el (eat--post-prompt): Put the 'eat--shell-prompt-begin' text property at the beginning of shell prompt. * eat.el (eat--correct-shell-prompt-mark-overlays): Don't try to go outside accessible portion of buffer when the buffer is narrowed. * eat.el (eat-narrow-to-shell-prompt): New command. * eat.el (eat-mode-map): Bind key sequence 'C-x n d' to 'eat-narrow-to-shell-prompt'. * eat.el (eat--filter-buffer-substring): Remove all text properties added on UI side before passing buffer substring to 'eat-term-filter-string'. * eat.texi (Shell Integration): Document 'eat-narrow-to-shell-prompt'. 2023-04-07 Akib Azmain Turja Run hooks on terminal update and process exit * eat.el (eat-exit-hook, eat-update-hook, eat-eshell-exit-hook) (eat-eshell-update-hook): New user option. * eat.el (eat-reset, eat--process-output-queue): Run 'eat-update-hook'. * eat.el (eat--sentinel): Don't test 'eat-kill-buffer-on-exit', run 'eat-exit-hook'. * eat.el (eat--adjust-process-window-size): Run 'eat-update-hook' when the major mode is 'eat-mode'. Run 'eat-eshell-update-hook' in 'eshell-mode' buffers. * eat.el (eat-exec): Don't let-bind 'eat-kill-buffer-on-exit' while killing old process, remove 'kill-buffer' from hook 'eat-exit-hook' instead. When 'eat-kill-buffer-on-exit' is non-nil, add 'kill-buffer' to 'eat-exit-hook' with priority 90. Run 'eat-exec-hook' outside any let-block. * eat.el (eat--eshell-output-filter): Run 'eat-eshell-update-hook'. * eat.el (eat--eshell-cleanup): Run 'eat-eshell-exit-hook'. 2023-04-05 Akib Azmain Turja Fix resizing when alternative display is enabled * eat.el (eat--t-term): Fix error while resizing when alternative display is enabled. 2023-04-02 Akib Azmain Turja Force directionality of paragraphs in Eat buffer * eat.el (eat-mode): Set 'bidi-paragraph-direction' to 'left-to-right'. 2023-04-02 Akib Azmain Turja Inform about unlisted semi-char mode exceptions * eat.texi (Input Modes): Inform about unlisted semi-char mode exceptions. * README.org: Inform about unlisted semi-char mode exceptions. Add Zsh integration instructions. 2023-03-31 Akib Azmain Turja Support DECSCUSR control function * eat.el (eat-very-visible-vertical-bar-cursor-type) (eat-vertical-bar-cursor-type, eat-horizontal-bar-cursor-type) (eat-very-visible-horizontal-bar-cursor-type): New user options. * eat.el (eat--t-term): New slot 'cur-visible-p'. Remove slot 'cur-blinking-p'. Change the default value of 'cur-state' to ':block'. * eat.el (eat--t-reset): Don't reset slot 'cur-blinking-p'. Reset slot 'cur-visible-p'. Reset 'cur-state' to ':block'. Reset cursor state to ':block'. * eat.el (eat--t-set-cursor-state): Set slot 'cur-visible-p' to the current visibility of cursor. Set slot 'cur-state' to the shape of cursor, regardless of visibility. * eat.el (eat--t-default-cursor): Rename to 'eat--t-show-cursor'. Don't change any cursor state except visibility. * eat.el (eat--t-invisible-cursor): Rename to 'eat--t-hide-cursor'. Don't change any cursor state except visibility. * eat.el (eat--t-set-cursor-style): New function. * eat.el (eat--t-blinking-cursor, eat--t-non-blinking-cursor): Update to handle new cursor shapes properly. * eat.el (eat--t-set-modes): Call 'eat--t-show-cursor' instead of 'eat--t-default-cursor'. * eat.el (eat--t-reset-modes): Call 'eat--t-hide-cursor' instead of 'eat--t-invisible-cursor'. * eat.el (eat--t-handle-output): Handle DECSCUSR control function. * eat.el (eat-term-cursor-type): Return ':invisible' if cursor is invisible, otherwise the value of slot 'cur-state'. Update the list of possible cursor states in the docstring. * eat.el (eat-term-set-cursor-function): Update the list of possible cursor states and fallback cursor state in the docstring. * eat.el (eat--cursor-blink-mode): Add 'eat--cursor-blink-stop-timers' to 'kill-buffer-hook'. * eat.el (eat--set-cursor): Support new cursor states. Update the list of possible cursor states in the docstring. * eat.texi (Cursor Types): Document new user options 'eat-vertical-bar-cursor-type', 'eat-very-visible-vertical-bar-cursor-type', 'eat-horizontal-bar-cursor-type' and 'eat-very-visible-horizontal-bar-cursor-type'. 2023-03-31 Akib Azmain Turja Capture keys 'M-backspace' and 'C-M-backspace' * eat.el (eat-term-input-event): Support 'M-backspace' and 'C-M-backspace'. * eat.el (eat-term-make-keymap): Bind 'M-backspace' and 'C-M-backspace'. 2023-03-31 Akib Azmain Turja Suggest to invoke 'eat-compile-terminfo' in manual * eat.texi (Not Recognized, Garbled Text): Suggest to do 'M-x eat-compile-terminfo' when the Terminfo databases aren't working properly. 2023-03-31 Akib Azmain Turja Fix directory tracking in Zsh * integration/zsh (__eat_precmd): Use '$HOST' instead of '$HOSTNAME'. 2023-03-31 Akib Azmain Turja Implement Zsh integration * integration/zsh: New file. * eat.texi (Shell Integration): Add instructions for Zsh. * integration/bash: Always return zero on succuss. 2023-03-30 Akib Azmain Turja Add new command 'eat-compile-terminfo' * eat.el (eat-compile-terminfo): New command. * eat.texi (Hello Terminal): Refer to 'Common Problems'. * eat.texi (Common Problems): Add instructions to compile Terminfo databases with 'eat-compile-terminfo'. 2023-03-29 Akib Azmain Turja Update manual to correctly describe command 'eat' * eat.texi (Hello Terminal): Update to match with the current behavior of command 'eat'. 2023-03-29 Akib Azmain Turja Don't prompt for program in M-x eat by default * eat.el (eat): Prompt for program to run only if double prefix argument is given, otherwise use the default one. 2023-03-28 Akib Azmain Turja Fix bugs in selection data manipulation code * eat.el (eat--t-term): Put a vector of length 8 (instead of 10) in 'cut-buffers' slot. * eat.el (eat--t-manipulate-selection): Don't process the targets '8' and '9', since they are not in the protocol. Return correct list of queried targets to the client. Make sure base64 encoded data doesn't contain line breaks. * eat.el (eat--t-handle-output): Tolerate invalid targets in OSC 52 (manipulate selection data). * eat.el (eat--manipulate-kill-ring): Handle 'nil' correctly. 2023-03-21 Akib Azmain Turja Fix incompatiblity with Eshell on Emacs 28 * eat.el (eat--eshell-setup-proc-and-term): Set 'eat--input-process' terminal parameter on Emacs 28. * eat.el (eat-eshell-mode): Don't add advice 'eat--eshell-set-input-process' on Emacs 28. 2023-03-20 Akib Azmain Turja Send input to correct process in Eshell pipeline * eat.el (eat--process): Remove. * eat.el (eat-mode): Don't make 'eat--process' buffer-local. Use 'eat--terminal' to determine whether terminal is live. * eat.el (eat--eshell-set-input-process): New function. * eat.el (eat--eshell-local-mode): Don't make 'eat--process' buffer-local. Add 'eat--eshell-set-input-process' as a advice after 'eshell-resume-eval'. * eat.el (eat-exec): Use the terminal parameters 'eat--process', 'eat--input-process' and 'eat--output-process' instead of the buffer-local variable 'eat--process' store the process object. * eat.el (eat--sentinel): Don't set 'eat--process' to nil. * eat.el (eat--eshell-setup-proc-and-term): Set the terminal parameters instead of buffer-local 'eat--process'. * eat.el (eat--pre-prompt, eat--pre-cmd, eat-kill-process, eat) (eat--send-input, eat--trace-eshell-adjust-make-process-args) (eat--eshell-cleanup, eat--eshell-output-filter): Get the process from terminal parameter. 2023-02-08 Akib Azmain Turja * eat.texi (Querying Before Kill): New chapter 2023-02-07 Akib Azmain Turja Query when killing buffer when command is running * eat.el (eat-query-before-killing-running-terminal): New user option. * eat.el (eat--pre-prompt): Unset process query on exit flag if eat-query-before-killing-running-terminal is 'auto'. * eat.el (eat--pre-cmd): Set process query on exit flag if eat-query-before-killing-running-terminal is 'auto'. 2023-01-22 Akib Azmain Turja Add many more semi-char mode exceptions * eat.el (eat-semi-char-non-bound-keys) (eat-eshell-semi-char-non-bound-keys): Add 81 more keys. 2023-01-19 Akib Azmain Turja Auto reload Eat when "semi-char" map is customized * eat.el (eat--load-file-path, eat--being-loaded): New variable. * eat.el (eat-semi-char-non-bound-keys) (eat-eshell-semi-char-non-bound-keys): Reload Eat when customized. Document what to do if changed from Lisp. * eat.el (eat-update-semi-char-mode-map) (eat-eshell-update-semi-char-mode-map): New function. * eat.el (eat-reload): New command. * eat.texi (Semi-char Mode): Document what to do if the lists of not bound keys are changed from Lisp. 2023-01-18 Akib Azmain Turja Document some new user options * eat.texi (Semi-char Mode): Document 'eat-semi-char-non-bound-keys' and 'eat-eshell-semi-char-non-bound-keys'. * eat.texi (Input Invisible): New section. 2023-01-16 Akib Azmain Turja Allow modifying semi-char key exceptions * eat.el (eat-semi-char-non-bound-keys) (eat-eshell-semi-char-non-bound-keys): New user option. * eat.el (eat--prepare-semi-char-mode-map) (eat--eshell-prepare-semi-char-mode-map): New function. * eat.el (eat-semi-char-mode-map): Use 'eat--prepare-semi-char-mode-map' to make the keymap. * eat.el (eat-eshell-semi-char-mode-map): Use 'eat--eshell-prepare-semi-char-mode-map' to make the keymap. 2023-01-15 Akib Azmain Turja Allow pasting from parent XTerm to Eat terminal * eat.el (eat-xterm-paste): New command. * eat.el (eat-mode-map, eat-eshell-emacs-mode-map): Bind to 'ignore'. * eat.el (eat-semi-char-mode-map, eat-char-mode-map) (eat-eshell-semi-char-mode-map, eat-eshell-char-mode-map): Bind to 'eat-xterm-paste'. 2022-12-28 Akib Azmain Turja Don't error while handling the sequence '\e[>c' * eat.el (eat--t-send-device-attrs): Accept '((nil))' as the value of the parameter PARAMS, but don't accept 'nil'. 2022-12-28 Akib Azmain Turja * eat.el (eat): Use buffer name for process name * eat.el (eat-eshell-exec-hook): Make customizable * eat.el (eat-exec-hook): Make customizable 2022-12-28 Akib Azmain Turja Run eat-eshell-exec-hook as Eshell process is run * eat.el (eat--eshell-setup-proc-and-term): Run 'eat-eshell-exec-hook' at the very end. 2022-12-22 Akib Azmain Turja * README.org (Straight.el): New section 2022-12-22 Akib Azmain Turja Don't let font-lock to mess up text face * eat.el (eat--t-repeated-insert, eat--t-write) (eat--t-fix-partial-multi-col-char): Add 'font-lock-face' text property along with 'face' to stop font-lock from removing the face. 2022-12-21 Akib Azmain Turja Fix terminfo path on case-insensitive filesystems * eat.el (eat--terminfo-path): New variable. * eat.el (eat-term-terminfo-directory): Change default value to 'PACKAGE-ROOT/terminfo/'. * Makefile (terminfo): Don't depend on other targets. Make directory 'terminfo'. Write terminfo to both 'e' and '65' (hexadecimal number of ASCII 'e') directories. * Makefile (e/eat-mono e/eat-color eat-256color e/eat-truecolor): Remove target. * README.org (Quelpa): Update recipe. 2022-12-20 Akib Azmain Turja * eat.el (eat--t-handle-output): Fix NULL handling 2022-12-17 Akib Azmain Turja Fix typo in 'eat--t-scroll-up' * eat.el (eat--t-scroll-up): Replace 'scroll-begin' with 'scroll-end' in '(< scroll-end (eat--t-disp-width disp))', since this will always evaluate to t, and thus it was most probably a typo. 2022-12-17 Akib Azmain Turja Make sure symbol is used as face parameter value * eat.el (eat-term-set-parameter): Make sure face parameter values are symbols. 2022-12-16 Akib Azmain Turja Allow changing terminal faces terminal-locally * eat.el (eat--t-term): New slots: bold-face, faint-face, italic-face, slow-blink-face, fast-blink-face, color-0-face, color-faces, font-faces. Use hash table for 'params' slot. * eat.el (eat--t-set-sgr-params): Use new slot instead using the faces directly. * eat.el (eat-term-parameter): Update to work with 'params' hash table. * eat.el (eat-term-set-parameter): Update to work with 'params' hash table. Handle the following parameters specially: bold-face, faint-face, italic-face, slow-blink-face, fast-blink-face, color-0-face, color-1-face, ..., color-255-face, font-0-face, font-1-face, ..., font-9-face. 2022-12-16 Akib Azmain Turja Decoding UTF-8 encoded strings encoded as base64 * eat.el (eat--t-set-cwd): Decoding UTF-8 encoded strings encoded as base64. * eat.el (eat--t-manipulate-selection): Use 'ignore-errors' instead of '(ignore-error ...)', since both macro expand to the same code. * eat.el (eat--t-set-cmd): Decoding UTF-8 encoded strings encoded as base64. Use 'when-let*' instead of 'when' nested in 'let'. 2022-12-15 Akib Azmain Turja Decrease regexp usage while parsing output * eat.el (eat--t-handle-output): Don't use regular expressions while parsing plain text and CSI sequences. Remove useless code from VT300-specific charset set sequence parser. 2022-12-15 Akib Azmain Turja Fix handling non-interactive processes in Eshell * eat.el (eat--eshell-adjust-make-process-args): Check whether the process going to be run will be interactive process. * eat.el: Declare function 'eshell-interactive-output-p'.a 2022-12-15 Akib Azmain Turja Don't use '.*-char-property-.*' functions * eat.el (eat--t-move-before-to-safe): Use 'previous-single-property-change' instead of '(previous-single-char-property-change'. * eat.el (eat--t-join-long-line): Use 'get-text-property' instead of 'get-char-property'. Handle properly the case where 'next-single-property-change' returns nil. * eat.el (eat--t-fix-partial-multi-col-char): Handle properly the case where 'next-single-property-change' returns nil. 2022-12-15 Akib Azmain Turja Refactor 'eat--eshell-adjust-make-process-args' * eat.el (eat--eshell-adjust-make-process-args): Move the 'add-hook' out of 'unwind-protect'. 2022-12-15 Akib Azmain Turja Remove invisible spaces when killing text * eat.el (eat-term-filter-string): Remove invisible spaces preceding multi-column width characters. * eat.el (eat--t-join-long-line) (eat--t-fix-partial-multi-col-char): Use 'next-single-property-change' instead of 'next-single-char-property-change'. 2022-12-14 Akib Azmain Turja Refactor 'eat--eshell-adjust-make-process-args' * eat.el (eat--eshell-adjust-make-process-args): Avoid unnecessary function overrides. Overriding functions with 'cl-letf*' is not well documented, and self-overriding a function (override a function with its own definition) may not a nop. 2022-12-14 Akib Azmain Turja Ask if 'stty' is unavailable in Eshell * eat.el (eat-eshell-fallback-if-stty-not-available): New user option. * eat.el (eat--eshell-adjust-make-process-args): If 'stty' is not available, use 'eat-eshell-fallback-if-stty-not-available' to determine whether to continue terminal emulation or not. 2022-12-12 Akib Azmain Turja Fix shell prompt navigation * eat.el (eat--post-prompt): Set 'eat--shell-prompt-begin' to nil at the very end to ensure prompt end text properties are put. 2022-12-12 Akib Azmain Turja Synchronize scroll on all windows showing terminal * eat.el (eat--synchronize-scroll-windows): New function. * eat.el (eat--synchronize-scroll) (eat--eshell-synchronize-scroll): Take a single argument, WINDOWS, list of windows to synchronize. The special value 'buffer' can also be included, to synchronize the point in buffer. * eat.el (eat-self-input, eat-yank, eat-yank-from-kill-ring): Pass the return value of 'eat--synchronize-scroll-windows' to the function in 'eat--synchronize-scroll-function'. * eat.el (eat--process-output-queue) (eat--adjust-process-window-size, eat--eshell-output-filter) Call 'eat--synchronize-scroll-windows' before doing anything, save the result, and pass it as the first argument to the function in 'eat--synchronize-scroll-function'. * eat.el (eat--trace-replay-eval): Pass the return value of 'get-buffer-window-list' to 'eat--synchronize-scroll'. 2022-12-12 Akib Azmain Turja Don't intercept background processes in Eshell * eat.el (eat--eshell-adjust-make-process-args): Don't do any change to the environment if 'eshell-current-subjob-p' is non-nil. 2022-12-11 Akib Azmain Turja Check whether 'yank-transform-functions' is bound * eat.el (eat-yank, eat-yank-from-kill-ring): Check whether 'yank-transform-functions' is bound before using it's value. The variable is not available in Emacs 28. 2022-12-11 Akib Azmain Turja * integration/bash: Check TERM before enabling * eat.el (eat-eshell-emacs-mode-map): Disable undo 2022-12-11 Akib Azmain Turja Use Eshell "emacs" mode map when process is live * eat.el (eat-eshell-emacs-mode-map): Update docstring. * eat.el (eat--eshell-process-running-mode): New non-interactive minor mode. * eat.el (eat--eshell-setup-proc-and-term): Enable 'eat--eshell-process-running-mode'. * eat.el (eat--eshell-cleanup): Disable 'eat--eshell-process-running-mode'. * eat.le (eat--eshell-local-mode): Don't use 'eat-eshell-emacs-mode-map' as the mode keymap. 2022-12-11 Akib Azmain Turja Synchronize scrolling and point properly * eat.el (eat--synchronize-scroll) (eat--eshell-synchronize-scroll): Call 'set-window-point' in addition to 'goto-char'. 2022-12-11 Akib Azmain Turja Fix invoking commands with eshell/sudo from Tramp * eat.el (eat--eshell-adjust-make-process-args): Override 'start-file-process' on Emacs 28, used by Eshell on Emacs 28. Don't override 'make-process' on Emacs 28, Eshell doesn't use that on Emacs 28. Set process filter and sentinel in 'eshell-exec-hook' instead of 'make-process' advice. 2022-12-11 Akib Azmain Turja Fix Eshell working directory tracking * eat.el (eat--eshell-local-mode): Call 'eat--eshell-update-cwd'. * eat.el (eat-eshell-mode): Don't call 'eat--eshell-update-cwd'. 2022-12-10 Akib Azmain Turja Fix yanking text into terminal * eat.el (eat-yank): Use simpler, saner and better approach to get the yanked string instead of the previous stupid, insane and buggy approach. * eat.el (eat-yank-pop): Remove, because it's not cancel the text already sent to the terminal. * eat.el (eat-yank-from-kill-ring): New function. Behaves essentially the same the previous (not properly working) 'eat-yank-pop'. * eat.el (eat-semi-char-mode-map, eat-eshell-semi-char-mode-map): Replace 'eat-yank-pop' with 'eat-yank-from-kill-ring'. 2022-12-10 Akib Azmain Turja Implement terminal parameters * eat.el (eat--t-term): New slot: 'params'. * eat.el (eat-term-parameter): New function and generalized variable. * eat.el (eat-set-term-parameter): New function. 2022-12-09 Akib Azmain Turja Fix terminal color reporting in text terminals * eat.el (eat--t-report-foreground-color): Fix foreground color reporting in text terminals. * eat.el (eat--t-report-background-color): Fix background color reporting in text terminals. 2022-12-08 Akib Azmain Turja Break combined 'setq'/'setf'/'setq-local's When giving more than two arguments to 'setq', it becomes hard to figure out what is assigned to what. As the number of argument increases, the readablity of code decreases. This is also true for 'setq-local'. For 'setf', the problem is even greater, unlike 'setq', all arguments to 'setf' look the same. * eat.el (eat--t-reset, eat--t-write, eat--t-save-cur) (eat--t-restore-cur, eat--t-enable-alt-disp) (eat--t-disable-alt-disp, eat--t-change-scroll-region) (eat--t-set-sgr-params, eat--t-manipulate-selection) (eat--t-resize, eat--blink-start-timers, eat-blink-mode) (eat--cursor-blink-mode, eat--set-cursor, eat-self-input) (eat-mode, eat-exec, eat--eshell-setup-proc-and-term) (eat--eshell-cleanup, eat--eshell-adjust-make-process-args) (eat--eshell-local-mode, eat--eshell-exec-visual) (eat--trace-exec, eat--trace-eshell-adjust-make-process-args) (eat--trace-replay-eval, eat-trace-replay, eat-trace--cleanup): Break combined 'setq'/'setf'/'setq-local's. 2022-12-08 Akib Azmain Turja Don't 'cd' as root when using 'sudo' from Tramp * eat.el (eat--eshell-setup-proc-and-term): Don't set 'eat--eshell-invocation-directory'. * eat.el (eat--eshell-update-cwd): New function. * eat.el (eat--eshell-local-mode): Call 'eat--eshell-update-cwd' when enabling the mode and add it to 'eshell-directory-change-hook' to keep track of Eshell's working directory. 2022-12-08 Akib Azmain Turja Fix prompt annotation bug trigger when shell exits * eat.el (eat--correct-shell-prompt-mark-overlays): Make sure 'eat--terminal' is non-nil. * eat.el (eat--sentinel): Cancel timers, correct shell prompt annotations for the final time, reset shell prompt annotation related local variables. 2022-12-07 Akib Azmain Turja Synchronize scrolling only when point is on cursor * eat.el (eat--adjust-process-window-size): Synchronize scrolling only when point is on cursor. 2022-12-07 Akib Azmain Turja Escape control characters in trace output * eat.el (eat--trace-log): Escape control characters (codepoints from 0 to 31) in trace output. 2022-12-07 Akib Azmain Turja Auto update 'eat-term-shell-integration-directory' * eat.el: Auto update 'eat-term-shell-integration-directory' if not modified. 2022-12-07 Akib Azmain Turja Move Bash global variables to function * integration/bash (__eat_current_command, __eat_exit_status) (__eat_inhibit_preexec): Move to '__eat_enable_integration'. * integration/bash (__eat_prompt_start, __eat_prompt_end) (__eat_continuation_start, __eat_continuation_end): Move to '__eat_enable_integration' and make local there. 2022-12-07 Akib Azmain Turja Document annotation correction delay user option * eat.texi (Performance Tuning): Document the purpose and the use of 'eat-shell-prompt-annotation-correction-delay' user option. 2022-12-07 Akib Azmain Turja Don't accept 'nil' as annotation correction delay * eat.el (eat-shell-prompt-annotation-correction-delay): Don't accept 'nil'. * eat.el (eat--process-output-queue): Don't accept value 'nil' for 'eat-shell-prompt-annotation-correction-delay'. 2022-12-07 Akib Azmain Turja Rename some symbols to reflect their purposes * eat.el (eat-shell-prompt-annotation-delay): Rename to 'eat-shell-prompt-annotation-correction-delay'. * eat.el (eat--update-shell-prompt-mark-overlays): Rename to 'eat--correct-shell-prompt-mark-overlays'. * eat.el (eat--shell-prompt-annotation-update-timer): Rename to 'eat--shell-prompt-annotation-correction-timer'. * eat.el (eat-mode, eat--process-output-queue, eat--filter): Update references to renamed symbols. 2022-12-07 Akib Azmain Turja * README.org: Add shell integration instructions * eat-tests.el (eat-test-set-cwd): Update test * eat.el (eat--t-term): Fix initial value of 'cwd' 2022-12-06 Akib Azmain Turja Document everything about shell integration * eat.texi (Shell Integration): Move to be the first chapter of Part III. Document everything about shell integration. * eat.texi (Directory Tracking): Merge with the node 'Shell Integration'. 2022-12-06 Akib Azmain Turja Fix prompt navigation when annotation is disabled * eat.el (eat--pre-prompt): Always set 'eat--shell-prompt-begin'. * eat.el (eat--post-prompt): Always put special prompt end marking text property. 2022-12-06 Akib Azmain Turja Add shell prompt navigation commands * eat.el (eat--post-prompt): Add special text property on the last character of shell prompt. * eat.el (eat-previous-shell-prompt, eat-next-shell-prompt): New command. * eat.el (eat-mode-map): Bind 'eat-previous-shell-prompt' and 'eat-next-shell-prompt' to 'C-c C-p' and 'C-c C-n' respectively. 2022-12-06 Akib Azmain Turja Annotate shell prompts * eat.el (eat-enable-shell-prompt-annotation) (eat-shell-prompt-annotation-position) (eat-shell-prompt-annotation-running-margin-indicator) (eat-shell-prompt-annotation-success-margin-indicator) (eat-shell-prompt-annotation-failure-margin-indicator) (eat-shell-prompt-annotation-delay): New user option. * eat.el (eat-shell-prompt-annotation-running) (eat-shell-prompt-annotation-success) (eat-shell-prompt-annotation-failure): New face. * eat.el (eat--shell-command-status, eat--shell-prompt-begin) (eat--shell-prompt-mark, eat--shell-prompt-mark-overlays): New variable. * eat.el (eat--pre-prompt, eat--post-prompt) (eat--update-shell-prompt-mark-overlays, eat--pre-cmd) (eat--set-cmd-status): New function. * eat.el (eat-mode): Make 'eat--shell-command-status', 'eat--shell-prompt-begin', 'eat--shell-prompt-mark', 'eat--shell-prompt-mark-overlays' and 'eat--shell-prompt-annotation-update-timer' local. Set margin width if shell prompt annotation is enabled. * eat.el (eat--shell-prompt-annotation-update-timer): New variable. * eat.el (eat--process-output-queue): Call or schedule 'eat--update-shell-prompt-mark-overlays' call. * eat.el (eat--filter): Cancel timer 'eat--shell-prompt-annotation-update-timer'. * eat.el (eat-exec): Set prompt start & end and command start & end functions. 2022-12-06 Akib Azmain Turja Add shell commands run in Eat to Emacs history * eat.el (eat-enable-shell-command-history): New user option. * eat.el (eat--set-cmd): New function. * eat.el (eat-exec, eat--eshell-setup-proc-and-term): Call 'eat--set-cmd' when the shell reports the command being run. 2022-12-06 Akib Azmain Turja Tighter shell integration * eat.el (eat--t-term): New slots: 'prompt-start-fn', 'prompt-end-fn', 'cont-prompt-start-fn', 'cont-prompt-end-fn', 'set-cmd-fn', 'cmd-start-fn', 'cmd-finish-fn' * eat.el (eat--t-set-cwd): Accept three arguments in two different formats. * eat.el (eat--t-prompt-start, eat--t-prompt-end) (eat--t-cont-prompt-start, eat--t-cont-prompt-end) (eat--t-set-cmd, eat--t-cmd-start, eat--t-cmd-finish): New function. * eat.el (eat--t-handle-output): Accept Eat's own OSC 51 ; e ; ... ST sequences. * eat.el (eat-term-prompt-start-function) (eat-term-prompt-end-function) (eat-term-continuation-prompt-start-function) (eat-term-continuation-prompt-end-function) (eat-term-set-cmd-function, eat-term-cmd-start-function) (eat-term-cmd-finish-function): New generalized variable. * integration/bash (__eat_current_command, __eat_exit_status): New variable. * integration/bash (__eat_prompt_command): Send exit status of last command, if applicable. Use Eat specific sequence to report working directory. Set title. * integration/bash (__eat_preexec): Report current command and execution start. * integration/bash (__eat_before_prompt_command): Set '__eat_exit_status' to the exit status of the last command. * integration/bash (__eat_prompt_start, __eat_prompt_end) (__eat_continuation_start, __eat_continuation_end): New variable, used as constant only to make the code more readable. * integration/bash (__eat_enable_integration): Wrap 'PS1' and 'PS2'. Don't set title from 'PS1'. 2022-12-04 Akib Azmain Turja Calculate the number of lines on window properly * eat.el (eat-exec, eat--eshell-setup-proc-and-term) (eat--eshell-adjust-make-process-args) (eat--eshell-synchronize-scroll): Calculate the number of available lines on window properly. 2022-12-04 Akib Azmain Turja Remap 'insert-char' to read and input a character * eat.el (eat-input-char): New function. * eat.el (eat-semi-char-mode-map) (eat-eshell-semi-char-mode-map): Remap 'insert-char' to 'eat-input-char'. 2022-12-04 Akib Azmain Turja Use full command as title in shell integration * integration/bash (__eat_in_prompt_command): Rename to '__eat_inhibit_preexec'. * integration/bash (__eat_before_exec): Inhibit further '__eat_preexec' calls until next prompt. * integration/bash (__eat_preexec): Use 'history' to get the real command typed by the user. 2022-12-04 Akib Azmain Turja Fix Bash integration * integration/bash (__eat_enable_integration): Fix moving 'PROMPT_COMMAND' array elements. 2022-12-04 Akib Azmain Turja * eat.el: Fix bright faces face definitions 2022-12-04 Akib Azmain Turja Refactor shell integration enabling code * integration/bash (__eat_enable_integration): New function. * integration/bash: Move enabling code to dedicated function. 2022-12-04 Akib Azmain Turja Update title automatically in shell integration * integration/bash (__eat_prompt_command): Remove extra '\'s. * integration/bash (__eat_in_prompt_command): New variable. * integration/bash (__eat_preexec, __eat_before_prompt_command) (__eat_after_prompt_command, __eat_before_exec): New function. * integration/bash: Modify PS1 to update terminal title when displaying prompt. Add '__eat_before_exec' as 'DEBUG' trap handler to update terminal title just before executing a command. Prepend and append '__eat_before_prompt_command' and '__eat_after_prompt_command' to 'PROMPT_COMMAND' to avoid getting trapped in 'DEBUG' trap. 2022-12-04 Akib Azmain Turja * README.org (Quelpa): Update recipe. * term/eat.el: New file. 2022-12-04 Akib Azmain Turja Refer to NonGNU ELPA in README * README.org (NonGNU ELPA Devel): Rename to NonGNU ELPA. Change the code-snippet to use NonGNU ELPA (stable). 2022-12-03 Akib Azmain Turja * integration/bash: Add copyright notice. * integration/bash: Use '__' prefix * integration/bash: Append to array more cleanly * integration/bash: Don't setup again on reload 2022-12-03 Akib Azmain Turja Use more uniform names for tracer functions * eat.el: Rename 'eat--eshell-trace-...' symbols to 'eat--trace-eshell-...'. 2022-12-03 Akib Azmain Turja Add shell integration script for GNU Bash * integration/bash: New file. * eat.el (eat-term-shell-integration-directory): New variable. Contains path to the directory containing the scripts, automatically updated when Eat is updated. * eat.el (eat-exec, eat-eshell-mode): New enviroment variable 'EAT_SHELL_INTEGRATION_DIR'. * eat.texi (Shell Integration): New chapter. * eat.texi (Directory Tracking): Refer to 'Shell Integration'. 2022-12-01 Akib Azmain Turja Check terminal contents while testing bell * eat-tests.el (eat-test-character-sets): Check terminal contents. 2022-12-01 Akib Azmain Turja Refer to Info manual to setup directory tracking * eat.el (eat-enable-directory-tracking): Refer to Info manual. 2022-12-01 Akib Azmain Turja Support directory tracking with OSC 7 * eat.el: Require 'url'. * eat.el (eat-enable-directory-tracking): New user option. * eat.el (eat--t-term): New slots: 'cwd' and 'set-cwd-fn'. * eat.el (eat--t-set-cwd, eat-term-cwd): New function. * eat.el (eat-term-set-cwd-function): New function and generalized variable. * eat.el (eat--t-handle-output): Handle OSC 7. * eat.el (eat--set-cwd): New function. * eat.el (eat-exec): Use 'eat--set-cwd' to change current working directory. * eat.el (eat--eshell-invocation-directory): New variable, local in Eshell buffers when 'eat-eshell-mode' is enabled. * eat.el (eat--eshell-setup-proc-and-term): Save the invocation directory of process. * eat.el (eat--eshell-cleanup): Revert working directory to invocation directory. * eat.el (eat--eshell-local-mode): When enabling, make 'eat--eshell-invocation-directory' local variable and kill when disabling. * eat-tests.el (eat-test-set-cwd): New test. * eat.texi (Directory Tracking): New chapter. 2022-11-30 Akib Azmain Turja Fix byte-compilation warnings on Emacs 28 * eat.el (eat-term-input-event): Fix byte-compilation warnings on Emacs 28, caused by calling 'posn-col-row' with two arguments on Emacs >= 29, which is not available on Emacs 28. 2022-11-30 Akib Azmain Turja * eat.el: Use loop to define color and font faces 2022-11-30 Akib Azmain Turja Document eat-eshell-visual-command-mode in manual * eat.texi (Eshell Terminal): Document 'eat-eshell-visual-command-mode' and suggest to set 'eshell-visual-commands' to nil. 2022-11-30 Akib Azmain Turja Fix compatibility issues with Emacs 28 * eat.el: Require 'subr-x'. * eat.el (eat-yank, eat-yank-pop): Pass three arguments to 'mapconcat'. * eat.el (eat--eshell-term-name): New function. * eat.el (eat-eshell-mode): Use 'eat--eshell-term-name' instead of using 'eat-term-name' directly. * eat.el (eat-eshell-mode) [(< emacs-major-version 29)]: Use 'eshell-last-async-proc' instead of 'eshell-last-async-procs'. * eat.el (eat--eshell-adjust-make-process-args) [(< emacs-major-version 29)]: Don't check and set ':filter' and ':sentinel' of 'make-process' argument plist. Set process filter and sentinel from 'eshell-exec-hook'. * eat.el (eat--eshell-adjust-make-process-args): Call 'eat--eshell-setup-proc-and-term' from 'eshell-exec-hook', not just after 'make-process'. 2022-11-29 Akib Azmain Turja Don't put 'error' as the only then part of 'if' * eat.el (eat-semi-char-mode, eat-char-mode): Don't put 'error' as only then part of 'if'. 2022-11-29 Akib Azmain Turja Use 'mapc' to make & kill multiple local variables * eat.el (eat-blink-mode, eat--cursor-blink-mode, eat-mode) (eat--eshell-local-mode, eat-trace-replay-mode): Use 'mapc' to make & kill multiple local variables. 2022-11-29 Akib Azmain Turja Put declare-function and defvar outside functions * eat.el (eat--flip-slow-blink-state) (eat--flip-fast-blink-state, eat-blink-mode) (eat--eshell-output-filter, eat--eshell-cleanup) (eat--eshell-process-output-queue, eat--eshell-sentinel) (eat-eshell-mode, eat--eshell-visual-sentinel) (eat--eshell-exec-visual, eat-eshell-visual-command-mode) (eat-project): Move 'declare-function's and 'defvar's outside. 2022-11-29 Akib Azmain Turja Use 'vector' directly instead of backquote * eat.el (eat-term-make-keymap): Use 'vector' directly instead of backquote. 2022-11-29 Akib Azmain Turja * eat.el (eat-term-make-keymap): Use 'cl-flet' 2022-11-29 Akib Azmain Turja Use more readable '?\C-\s' instead of '?\C-\ ' * eat.el (eat-term-input-event, eat-term-make-keymap): Use more readable '?\C-\s' instead of '?\C-\ '. 2022-11-29 Akib Azmain Turja * eat.el (eat-mode): Fix char mode 'help-echo' * eat.el (eat--bell): Use 'ding' instead of 'beep' * eat.el (eat--trace-exec): Use "-*- mode: MD -*-" 2022-11-29 Akib Azmain Turja Use 'setf' + 'plist-get' instead of 'plist-put' * eat.el (eat--eshell-adjust-make-process-args): Use (setf (plist-get ...) ...) instead of 'plist-put' without taking the result, which is not guaranteed to always work. 2022-11-28 Akib Azmain Turja Prefer 'and-let*' and 'when-let*' over 'when-let' * eat.el (eat--t-set-sgr-params): Use 'and-let*' instead of 'when-let' for side-effect free 'when-let'. * eat.el (eat--flip-cursor-blink-state, eat--synchronize-scroll) (eat-exec, eat--eshell-setup-proc-and-term) (eat--eshell-synchronize-scroll, eat--eshell-visual-sentinel) (eat--trace-replay-eval-next): Use 'when-let*' instead of 'when-let' for 'when-let's with side-effect. 2022-11-28 Akib Azmain Turja Prefer 'pcase-exhaustive' over 'pcase' * eat.el (eat--t-erase-in-line, eat--t-erase-in-disp) (eat--t-set-mouse-mode, eat--t-send-device-attrs) (eat--t-handle-output, eat-term-input-event) (eat--manipulate-kill-ring, eat--trace-replay-eval): Prefer 'pcase-exhaustive' over 'pcase'. 2022-11-28 Akib Azmain Turja * eat.el (eat--t-change-charset): Assert argument 2022-11-28 Akib Azmain Turja Combine multiple setq/setf/setq-local into one * eat.el (eat--t-reset, eat--t-goto, eat--t-write) (eat--t-save-cur, eat--t-enable-alt-disp) (eat--t-change-scroll-region, eat--t-set-sgr-params) (eat--t-manipulate-selection, eat--t-handle-output) (eat--t-resize, eat--flip-slow-blink-state) (eat--flip-fast-blink-state, eat-blink-mode) (eat--cursor-blink-mode, eat--set-cursor, eat-self-input) (eat-mode, eat-exec, eat--eshell-setup-proc-and-term) (eat--eshell-cleanup, eat--eshell-local-mode) (eat--eshell-exec-visual, eat--trace-exec) (eat--eshell-trace-adjust-make-process-args) (eat--trace-replay-eval, eat-trace-replay, eat-trace--cleanup): Combine multiple setq/setf/setq-local into one wherever possible. 2022-11-28 Akib Azmain Turja Avoid copying STR to the extent possible * eat.el (eat--t-write): Take two more optional arguments BEG and END to avoid copying STR multiple times unneccessarily. 2022-11-28 Akib Azmain Turja Support multi-column characters properly * eat.el (eat--t-write): Use 'char-width' instead of the unpredictable 'string-width' to support multi-column characters properly. 2022-11-28 Akib Azmain Turja Use hash table to convert from charset * eat.el (eat--t-dec-line-drawing-chars): New constant containing the hash table. * eat.el (eat--t-write): Use hash-table instead alist while converting from DEC Line Drawing charset. 2022-11-28 Akib Azmain Turja Use as less let-bindings as possible * eat.el (eat--t-goto-bol, eat--t-goto-eol) (eat--t-repeated-insert, eat--t-cur-right, eat--t-cur-left) (eat--t-cur-horizontal-abs, eat--t-beg-of-next-line) (eat--t-beg-of-prev-line, eat--t-cur-down, eat--t-cur-up) (eat--t-cur-vertical-abs, eat--t-scroll-up, eat--t-scroll-down) (eat--t-write, eat--t-horizontal-tab) (eat--t-horizontal-backtab, eat--t-reverse-index) (eat--t-erase-in-line, eat--t-erase-in-disp) (eat--t-insert-char, eat--t-delete-char, eat--t-erase-char) (eat--t-insert-line, eat--t-delete-line) (eat--t-repeat-last-char, eat--t-change-scroll-region) (eat--t-send-device-attrs): Minimize let-binding count. * eat.el (eat--t-break-long-line, eat--t-write) (eat-trace-replay): Use replace 'propertize' call with already propertized string. 2022-11-28 Akib Azmain Turja Replace 'let*' with 'let' wherever possible * eat.el (eat--t-eol, eat--t-reset, eat--t-erase-in-disp) (eat--t-disable-alt-disp, eat--t-resize, eat-term-redisplay) (eat-term-input-event, eat--adjust-process-window-size): Use 'let' instead of 'let*' wherever possible. * eat.el (eat--eshell-exec-visual): Use (VAR nil) form instead of VAR form in 'let*' variable list. 2022-11-28 Akib Azmain Turja * README.org (NonGNU ELPA Devel): New section 2022-11-28 Akib Azmain Turja Experimentally support for multi-column characters * eat.el (eat--t-move-before-to-safe, eat--t-make-pos-safe) (eat--t-fix-partial-multi-col-char): New function. * eat.el (eat--t-write): Handle multi-column characters. * eat.el (eat--t-insert-char, eat--t-delete-char) (eat--t-erase-char): Handle multi-column characters on the display while manipulating text. 2022-11-28 Akib Azmain Turja * .elpaignore: New file. 2022-11-25 Akib Azmain Turja Use third argument of posn-col-row on Emacs >= 29 * eat.el (eat-term-input-event): Use USE-WINDOW argument of 'posn-col-row' to support text scaling properly, but only on Emacs >= 29 since on Emacs 28 'posn-col-row' takes only one argument. 2022-11-24 Akib Azmain Turja * Makefile (all): Remove "check" and "changelog." 2022-11-24 Akib Azmain Turja Add documentation file generation code to Makefile * Makefile (info, dvi, html, pdf, eat.info, eat.dvi, eat.html) (eat.pdf): New targets. * Makefile (all): New dependency "info." * Makefile (.PHONY): Add "info", "dvi", "html" and "pdf." 2022-11-24 Akib Azmain Turja Add proper dependencies to targets in Makefile * Makefile (TIC): New variable. * Makefile (eat.elc): Depend on eat.el. * Makefile (e/eat-mono, e/eat-color, eat-256color) (e/eat-truecolor): Depend on eat.ti. 2022-11-24 Akib Azmain Turja Implement the terminal This includes everything written from 2022-08-15 up to now. * .dir-locals.el: * CONTRIBUTE: * COPYING: * Makefile: * NEWS: * README.org: * eat-tests.el: * eat.el: * eat.texi: * eat.ti: * fdl.texi: * gitlog-to-changelog: * gpl.texi: * make-changelog: * texinfo.tex: New file. ;; Local Variables: ;; coding: utf-8 ;; End: Copyright (C) 2022-2023 Akib Azmain Turja. 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 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 . emacs-eat/Makefile000066400000000000000000000041111453707721100143610ustar00rootroot00000000000000# Makefile --- Build configuration # Copyright (C) 2022 Akib Azmain Turja. # This file is not part of GNU Emacs. # This file 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. # 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. # For a full copy of the GNU General Public License # see . EMACS ?= emacs TIC ?= tic TEXI2INFO ?= texi2any TEXI2DVI ?= texi2dvi TEXI2HTML ?= texi2any --no-split --html TEXI2PDF ?= texi2pdf TEXI2PS ?= texi2any --ps EMACSFLAGS ?= all: eat.elc terminfo info info: eat.info dvi: eat.dvi html: eat.html pdf: eat.pdf terminfo: eat.ti test -d terminfo || mkdir terminfo env TERMINFO=./terminfo $(TIC) -x eat.ti # We don't know which directory was created, it depends on the # case-sensitivity of the file-system. So make sure both are created. test -d terminfo/e || mkdir terminfo/e test -d terminfo/65 || mkdir terminfo/65 if test terminfo/e/eat-mono -nt terminfo/65/eat-mono ; \ then \ cp terminfo/e/eat-mono terminfo/e/eat-color \ terminfo/e/eat-256color terminfo/e/eat-truecolor \ terminfo/65 ; \ else \ cp terminfo/65/eat-mono terminfo/65/eat-color \ terminfo/65/eat-256color terminfo/65/eat-truecolor \ terminfo/e ; \ fi check: eat.el $(EMACS) -batch $(EMACSFLAGS) -l eat.el -l eat-tests.el \ -f ert-run-tests-batch-and-exit changelog: ./make-changelog .PHONY: all terminfo info dvi html pdf check changelog eat.elc: eat.el $(EMACS) -batch $(EMACSFLAGS) \ --eval '(byte-compile-file "eat.el")' eat.info: eat.texi gpl.texi fdl.texi $(TEXI2INFO) eat.texi eat.dvi: eat.texi gpl.texi fdl.texi texinfo.tex $(TEXI2DVI) eat.texi eat.html: eat.texi gpl.texi fdl.texi $(TEXI2HTML) eat.texi eat.pdf: eat.texi gpl.texi fdl.texi texinfo.tex $(TEXI2PDF) eat.texi emacs-eat/NEWS000066400000000000000000000022101453707721100134160ustar00rootroot00000000000000Eat NEWS -- History of user-visible changes Copyright (C) 2022 Akib Azmain Turja. See the end of the file for license conditions. This file is about changes in Eat. Note: +++ indicates that Eat manual have been updated. --- means no change in the manuals is needed. When you add a new item, use the appropriate mark if you are sure it applies, and please also update docstrings as needed. ---------------------------------------------------------------------- Adapted from etc/NEWS in Emacs source tree. This file is part of Eat and is not part of GNU Emacs. Eat 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. Eat 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 . emacs-eat/README.org000066400000000000000000000162061453707721100143770ustar00rootroot00000000000000#+title: Eat: Emulate A Terminal Eat's name self-explanatory, it stands for "Emulate A Terminal". Eat is a terminal emulator. It can run most (if not all) full-screen terminal programs, including Emacs. It is pretty fast, more than three times faster than Term, despite being implemented entirely in Emacs Lisp. So fast that you can comfortably run Emacs inside Eat, or even use your Emacs as a terminal multiplexer. It has many features that other Emacs terminal emulator still don't have, for example Sixel support, complete mouse support, shell integration, etc. It flickers less than other Emacs terminal emulator, so you get more performance and a smoother experience. To get the most out of Eat, you should also setup shell integration. * Usage To start Eat, run =M-x eat=. Eat has four input modes: - "semi-char" mode: This is the default input mode. Most keys are bound to send the key to the terminal, except the following keys: =C-\=, =C-c=, =C-x=, =C-g=, =C-h=, =C-M-c=, =C-u=, =C-q=, =M-x=, =M-:=, =M-!=, =M-&= and some other keys (see the user option ~eat-semi-char-non-bound-keys~ for the complete list). The following special keybinding are available: - =C-q=: Send next key to the terminal. - =C-y=: Like `yank', but send the text to the terminal. - =M-y=: Like `yank-pop', but send the text to the terminal. - =C-c C-k=: Kill process. - =C-c C-e=: Switch to "emacs" input mode. - =C-c M-d=: Switch to "char" input mode. - =C-c C-l=: Switch to "line" input mode. - "emacs" mode: No special keybinding, except the following: - =C-c C-j=: Switch to "semi-char" input mode. - =C-c M-d=: Switch to "char" input mode. - =C-c C-l=: Switch to "line" input mode. - =C-c C-k=: Kill process. - "char" mode: All supported keys are bound to send the key to the terminal, except =C-M-m= or =M-RET=, which is bound to switch to "semi-char" input mode. - "line" mode: Similar to Comint, Shell mode and Term line mode. In this input mode, terminal input is sent one line at once, and you can edit input line using the usual Emacs commands. - =C-c C-e=: Switch to "emacs" input mode - =C-c C-j=: Switch to "semi-char" input mode. - =C-c M-d=: Switch to "char" input mode. If you like Eshell, then there is a good news for you. Eat integrates with Eshell. Eat has two global minor modes for Eshell: - ~eat-eshell-visual-command-mode~: Run visual commands with Eat instead of Term. - ~eat-eshell-mode~: Run Eat inside Eshell. After enabling this, you can run full-screen terminal programs directly in Eshell. You have the above input modes here too, except line mode and that =C-c C-k= is not special (i.e. not bound by Eat) in "emacs" mode and "line" mode. You can add any of these to ~eshell-load-hook~ like the following: #+begin_src emacs-lisp ;; For `eat-eshell-mode'. (add-hook 'eshell-load-hook #'eat-eshell-mode) ;; For `eat-eshell-visual-command-mode'. (add-hook 'eshell-load-hook #'eat-eshell-visual-command-mode) #+end_src To setup shell integration for GNU Bash, put the following at the end of your ~.bashrc~: #+begin_src sh [ -n "$EAT_SHELL_INTEGRATION_DIR" ] && \ source "$EAT_SHELL_INTEGRATION_DIR/bash" #+end_src sh For Zsh, put the following in your ~.zshrc~: #+begin_src sh [ -n "$EAT_SHELL_INTEGRATION_DIR" ] && \ source "$EAT_SHELL_INTEGRATION_DIR/zsh" #+end_src sh There's a Info manual available with much more information, which can be accessed with =C-h i m Eat=, also available [[https://elpa.nongnu.org/nongnu-devel/doc/eat.html][here on the internet]]. * Installation Eat requires at least Emacs 26.1 or above. ** NonGNU ELPA Eat is available on NonGNU ELPA. So you can just do =M-x package-install RET eat RET=. If you're on Emacs 27 or earlier, you'll need to add NonGNU ELPA to your ~package-archives~ by putting the following in your ~init.el~: #+begin_src emacs-lisp (add-to-list 'package-archives '("nongnu" . "https://elpa.nongnu.org/nongnu/")) #+end_src ** Quelpa #+begin_src emacs-lisp (quelpa '(eat :fetcher git :url "https://codeberg.org/akib/emacs-eat" :files ("*.el" ("term" "term/*.el") "*.texi" "*.ti" ("terminfo/e" "terminfo/e/*") ("terminfo/65" "terminfo/65/*") ("integration" "integration/*") (:exclude ".dir-locals.el" "*-tests.el")))) #+end_src ** Straight.el #+begin_src emacs-lisp (straight-use-package '(eat :type git :host codeberg :repo "akib/emacs-eat" :files ("*.el" ("term" "term/*.el") "*.texi" "*.ti" ("terminfo/e" "terminfo/e/*") ("terminfo/65" "terminfo/65/*") ("integration" "integration/*") (:exclude ".dir-locals.el" "*-tests.el")))) #+end_src ** Manual Clone the repository and put it in your ~load-path~. * Comparison With Other Terminal Emulators ** Term Term is the Emacs built-in terminal emulator. Its terminal emulation is pretty good too. But it's slow. It is so slow that Eat can beat native-compiled Term even without byte-compilation, and when Eat is byte-compiled, Eat is more than three times fast. Also, Term flickers, just try to run =emacs -nw= in it. It doesn't support remote connections, for example over Tramp. However, it's builtin from the early days of Emacs, while Eat needs atleast Emacs 26.1. ** Vterm Vterm is powered by a C library, libvterm. For this reason, it can process huge amount of text quickly. It is about 1.5 times faster than Eat (byte-compiled or native-compiled) (and about 2.75 faster then Eat without byte-compilation). But it doesn't have a char mode (however you can make a char mode by putting some effort). And it too flickers like Term, so despite being much faster that Eat, it seems to be slow. If you need your terminal to handle huge bursts (megabytes) of data, you should use Vterm. ** Coterm + Shell Coterm adds terminal emulation to Shell mode. Although the terminal Coterm emulates is same as Term, it is much faster, about three times, just a bit slow than Eat. However, it too flickers like other terminals. Since it's an upgrade to Shell, you get all the features of Shell like "line" mode, completion using your favorite completion UI (Company, Corfu, etc), etc. Most of these features are available in Eat, and also in Eat-Eshell-Mode as Eshell is similar to Shell, however it's not Shell mode. Recommended if you like Shell. * Acknowledgements This wouldn't have been possible if the following awesome softwares didn't exist: - [[https://gnu.org][GNU Operating System]] - [[https://st.suckless.org/][St]] - [[https://sw.kovidgoyal.net/kitty/][Kitty]] - [[https://invisible-island.net/xterm/][XTerm]] - [[https://www.gnu.org/software/linux-libre/][Linux-libre]] - [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Terminal-emulator.html][Term]] - [[https://repo.or.cz/emacs-coterm.git][Coterm]] - [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Interactive-Shell.html][Shell]] - [[https://github.com/akermu/emacs-libvterm][Vterm]] - [[https://www.gnu.org/software/emacs/manual/html_node/eshell/index.html][Eshell]] - Numerous terminal programs - And obviously, [[https://www.gnu.org/software/emacs/][GNU Emacs]] emacs-eat/eat-tests.el000066400000000000000000007321261453707721100151720ustar00rootroot00000000000000;;; eat-tests.el --- Tests for Eat -*- lexical-binding: t; -*- ;; Copyright (C) 2022 Akib Azmain Turja. ;; Author: Akib Azmain Turja ;; Keywords: tests ;; This file is not part of GNU Emacs. ;; This file 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. ;; 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. ;; For a full copy of the GNU General Public License ;; see . ;;; Commentary: ;; This file contains all tests for Eat. This includes tests for the ;; terminal emulator itself. ;;; Code: (require 'ert) (require 'eat) (require 'cl-lib) ;;;; Helpers. (defun eat--tests-parse-text-properties (string) "Parse and modify the text properties of STRING. Modify the text properties of STRING so that for two strings with identical contents and similar appearance `equal-including-properties' will return t." (let ((pos 0)) (while (< pos (length string)) (let ((next-pos (or (next-single-property-change pos 'face string) (length string))) (face (get-text-property pos 'face string))) (set-text-properties pos next-pos `( :foreground ,(plist-get face :foreground) :background ,(plist-get face :background) :underline-type ,(plist-get (plist-get face :underline) :style) :underline-color ,(plist-get (plist-get face :underline) :color) :crossed ,(plist-get face :strike-through) :intensity ,(let ((list (plist-get face :inherit))) (cond ((member 'eat-term-faint list) 'faint) ((member 'eat-term-bold list) 'bold))) :italic ,(not (not (member 'eat-term-italic (plist-get face :inherit)))) :blink ,(let ((list (plist-get face :inherit))) (cond ((member 'eat-term-slow-blink list) 'slow) ((member 'eat-term-fast-blink list) 'fast))) :font ,(let ((list (plist-get face :inherit))) (cond ((member 'eat-term-font-0 list) 0) ((member 'eat-term-font-1 list) 1) ((member 'eat-term-font-2 list) 2) ((member 'eat-term-font-3 list) 3) ((member 'eat-term-font-4 list) 4) ((member 'eat-term-font-5 list) 5) ((member 'eat-term-font-6 list) 6) ((member 'eat-term-font-7 list) 7) ((member 'eat-term-font-8 list) 8) ((member 'eat-term-font-9 list) 9) (t 0)))) string) (setq pos next-pos))) string)) (defun eat--tests-sanitize-expected-text (string) "Sanitize the text properties of expected string STRING." (let ((pos 0)) (while (< pos (length string)) (let ((next-pos (or (next-property-change pos string) (length string)))) (set-text-properties pos next-pos (mapcan (lambda (prop) (list (car prop) (or (get-text-property pos (car prop) string) (cdr prop)))) '((:foreground . nil) (:background . nil) (:underline-type . nil) (:underline-color . nil) (:crossed . nil) (:intensity . nil) (:italic . nil) (:blink . nil) (:font . 0))) string) (setq pos next-pos))) string)) (defun eat--tests-compare-lines (actual expected) "Compare ACTUAL and EXPECTED." (let ((a (eat--tests-parse-text-properties actual)) (b (eat--tests-sanitize-expected-text expected))) (cl-flet ((visually-equal (a b) (if (> emacs-major-version 28) (should (equal-including-properties a b)) ;; On Emacs versions less than 29, ;; `equal-including-properties' returns t only if the ;; properties of a and b are `eq', so we compare the ;; strings ourselves. (and (should (string= a b)) (cl-every (lambda (i) (cl-flet ((plist-to-alist (plist) (let ((alist nil)) (while plist (push (cons (pop plist) (pop plist)) alist)) (sort alist (lambda (a b) (string< (symbol-name (car a)) (symbol-name (car b)))))))) (should (equal (plist-to-alist (text-properties-at i a)) (plist-to-alist (text-properties-at i b)))))) (number-sequence 0 (1- (length a)))))))) (cond ((= (length a) (length b)) (visually-equal a b)) ((< (length a) (length b)) (and (visually-equal a (substring b 0 (length a))) (let ((str (substring b (length a)))) (and (string-blank-p str) (visually-equal str (eat--tests-parse-text-properties (substring-no-properties str))))))) ((> (length a) (length b)) (and (visually-equal (substring a 0 (length b)) b) (let ((str (substring a (length b)))) (and (string-blank-p str) (visually-equal str (eat--tests-parse-text-properties (substring-no-properties str))))))))))) (defun eat--tests-compare-scrollback (terminal lines) "Compare TERMINAL's scrollback buffer with LINES." (let ((scrollback (nbutlast (split-string (buffer-substring (eat-term-beginning terminal) (eat-term-display-beginning terminal)) "\n" nil nil)))) (and (should (or (= (eat-term-display-beginning terminal) (eat-term-beginning terminal)) (= (char-before (eat-term-display-beginning terminal)) ?\n))) (should (= (length scrollback) (length lines))) (cl-every (lambda (pair) (eat--tests-compare-lines (car pair) (cdr pair))) (cl-mapcar #'cons scrollback lines))))) (defun eat--tests-compare-display (terminal lines) "Compare TERMINAL's scrollback buffer with LINES." (let ((display (split-string (buffer-substring (eat-term-display-beginning terminal) (eat-term-end terminal)) "\n" nil nil))) (and (should (<= (length display) (cdr (eat-term-size terminal)))) (cl-every (lambda (i) (let ((actual (or (nth i display) "")) (expected (or (nth i lines) ""))) (and (<= (length actual) (car (eat-term-size terminal))) (eat--tests-compare-lines actual expected)))) (number-sequence 0 (1- (cdr (eat-term-size terminal)))))))) (defun eat--tests-compare-cursor-pos (terminal cursor-pos) "Compare TERMINAL's cursor position with CURSOR-POS. CURSOR-POS should be a cons cell of form (Y . X)." (let* ((prev-lines (split-string (buffer-substring (eat-term-display-beginning terminal) (eat-term-display-cursor terminal)) "\n" nil nil)) (y (length prev-lines)) (x (1+ (length (car (last prev-lines)))))) (should (= (car cursor-pos) y)) (should (= (cdr cursor-pos) x)))) (defun eat--tests-add-properties (string &rest intervals) "Add properties to STRING according to INTERVALS. INTERVALS is a list. Each element of it is a list of form \((BEGIN . END) PLIST...). For each element in INTERVALS, add PLIST to string from BEGIN to END." (dolist (interval intervals) (set-text-properties (caar interval) (cdar interval) (cdr interval) string)) string) (defmacro eat--tests-with-term (spec &rest body) "Make a temporary terminal with SPEC and run BODY with it. SPEC is a form that should evaluate to a plist. The plist can have any of the following properties: `:width' Width of terminal. Defaults to 20. `:height' Height of terminal. Defaults to 6. The following functions are available within BODY: \(terminal) Return the terminal. \(output &rest ARGS) Output ARGS to the terminal, where ARGS is a list of string. \(input-event EVENT &optional REF-POS N) Input EVENT to the terminal. REF-POS is the starting position of the terminal, a mouse position list. N is how times to input EVENT. \(input) Return all unread input. \(should-term SCROLLBACK DISPLAY CURSOR) Match the terminal with SCROLLBACK, DISPLAY and CURSOR. SCROLLBACK is expected content of scrollback region. DISPLAY is the expected display. CURSOR is the expected cursor position, as a cons (X . Y), where X and Y are one-based. Both SCROLLBACK and DISPLAY are list of strings. They matched with the terminal for visual equivalence, not literal equivaence (i.e, properties are also compared, and \"foo\" and \"foo \" are assumed to be equivalent). \(add-props STRING &rest INTERVALS) Add text properties to STRING as specified in INTERVALS. Each argument in INTERVALS is of form ((BEGIN . END) PROPERTY VALUE PROPERTY VALUE...). Here is all PROPERTY is applied on STRING from BEGIN to END. PROPERTY should one of `:foreground', `:background' `:underline-type', `:underline-color', `:crossed', `:intensity', `:italic', `:blink' and `:font'. Any other properties are also applied but ignored by `should-term'." (declare (indent 1)) (let ((term (make-symbol "term")) (input (make-symbol "input"))) `(with-temp-buffer (let ((,term (eat-term-make (current-buffer) (point))) (,input "")) (unwind-protect (progn (cl-destructuring-bind (&key (width 20) (height 6)) ,spec (eat-term-resize ,term width height)) (setf (eat-term-parameter ,term 'input-function) (lambda (_ str) (setq ,input (concat ,input str)))) (cl-labels ((terminal () ,term) (output (&rest args) (dolist (str args) (eat-term-process-output ,term str)) (eat-term-redisplay ,term)) (input-event (event &optional ref-pos n) (eat-term-input-event ,term n event ref-pos)) (input () (prog1 ,input (setq ,input ""))) (should-term (&key scrollback display cursor) (eat--tests-compare-scrollback ,term scrollback) (eat--tests-compare-display ,term display) (eat--tests-compare-cursor-pos ,term cursor)) (add-props (str &rest intervals) (apply #'eat--tests-add-properties str intervals))) ,@body)) (eat-term-delete ,term)))))) ;;;; Tests. ;;;;; Text Writing and Insertion Tests. (ert-deftest eat-test-plain-text () "Test plain text handling. Send only plain text (i.e. no control sequences, not even newline) and compare the output with the expected output. Don't do test automatic margin." (eat--tests-with-term '() (output "some test string") (should-term :display '("some test string ") :cursor '(1 . 17)))) (ert-deftest eat-test-auto-margin () "Test automatic margin and toggling it." (eat--tests-with-term '() (should-term :cursor '(1 . 1)) ;; Default: Automatic margin enabled. (output "some test string... and some more") (should-term :display '("some test string..." "and some more") :cursor '(2 . 14)) ;; Automatic margin disabled. (output "\n\e[?7lsome test string... and some more") (should-term :display '("some test string..." "and some more" "some test string...e") :cursor '(3 . 20)) ;; Automatic margin enabled. (output "\n\e[?7hsome test string... and some more") (should-term :display '("some test string..." "and some more" "some test string...e" "some test string..." "and some more") :cursor '(5 . 14)))) (ert-deftest eat-test-insert-mode () "Test automatic margin and toggling it." (eat--tests-with-term '() ;; Default: Insert mode disabled. (output "a\bb") (should-term :display '("b") :cursor '(1 . 2)) ;; Insert mode enabled. (output "\e[4hb\ba") (should-term :display '("bab") :cursor '(1 . 3)) ;; Insert mode disabled. (output "\e[4lc\by") (should-term :display '("bay") :cursor '(1 . 4)))) ;;;;; Cursor Motion Tests. (ert-deftest eat-test-character-tabulation () "Test character tabulation control function." (eat--tests-with-term '() (output "\t") (should-term :cursor '(1 . 9)) (output "\t") (should-term :cursor '(1 . 17)) (output "\t") (should-term :cursor '(1 . 20)) (output "\n ") (should-term :cursor '(2 . 2)) (output "\t") (should-term :cursor '(2 . 9)))) (ert-deftest eat-test-cursor-backward-tabulation () "Test cursor backward tabulation control function." (eat--tests-with-term '() (output "\t") (should-term :cursor '(1 . 9)) (output "\t") (should-term :cursor '(1 . 17)) (output "\t") (should-term :cursor '(1 . 20)) (output "\n ") (should-term :cursor '(2 . 2)) (output "\t") (should-term :cursor '(2 . 9)))) (ert-deftest eat-test-line-tabulation () "Test line tabulation control function." (eat--tests-with-term '() (output "\v") (should-term :cursor '(2 . 1)) (output " ") (should-term :cursor '(2 . 3)) (output "\v") (should-term :cursor '(3 . 3)) (output "\v\v\v") (should-term :cursor '(6 . 3)) (output "\v") (should-term :scrollback '("") :cursor '(6 . 3)))) (ert-deftest eat-test-form-feed () "Test form feed." (eat--tests-with-term '() (output "\f") (should-term :cursor '(2 . 1)) (output " ") (should-term :cursor '(2 . 3)) (output "\f") (should-term :cursor '(3 . 3)) (output "\f\f\f") (should-term :cursor '(6 . 3)) (output "\f") (should-term :scrollback '("") :cursor '(6 . 3)))) (ert-deftest eat-test-line-feed () "Test line feed control function." (eat--tests-with-term '() (output "\n") (should-term :cursor '(2 . 1)) (output " \n") (should-term :cursor '(3 . 1)) (output "\eE") (should-term :cursor '(4 . 1)) (output " \eE") (should-term :cursor '(5 . 1)))) (ert-deftest eat-test-index () "Test index control function." (eat--tests-with-term '() (output "\eD") (should-term :cursor '(2 . 1)) (output " \eD") (should-term :cursor '(3 . 5)) (output "\eD") (should-term :cursor '(4 . 5)) (output " \eD") (should-term :cursor '(5 . 9)))) (ert-deftest eat-test-reverse-index () "Test reverse index control function. Use newlines to move to an initial position from where the control function is to be invoked." (eat--tests-with-term '() (output "\n\n") (should-term :cursor '(3 . 1)) (output "\eM") (should-term :cursor '(2 . 1)) (output " \eM") (should-term :cursor '(1 . 5)))) (ert-deftest eat-test-backspace () "Test backspace control function. Use spaces to move to an initial position from where the control function is to be invoked." (eat--tests-with-term '() (output " ") (should-term :cursor '(1 . 2)) (output "\b") (should-term :cursor '(1 . 1)))) (ert-deftest eat-test-carriage-return () "Test carriage return control function. Use spaces to move to an initial position from where the control function is to be invoked." (eat--tests-with-term '() (output "\r") (should-term :cursor '(1 . 1)) (output " ") (should-term :cursor '(1 . 5)) (output "\r") (should-term :cursor '(1 . 1)))) (ert-deftest eat-test-cursor-right () "Test cursor right control function." (eat--tests-with-term '() (output "\e[C") (should-term :cursor '(1 . 2)) (output "\e[0C") (should-term :cursor '(1 . 3)) (output "\e[5C") (should-term :cursor '(1 . 8)) (output "\e[20C") (should-term :cursor '(1 . 20)))) (ert-deftest eat-test-cursor-left () "Test cursor up control function. Use spaces to move to an initial position from where the control function is to be invoked." (eat--tests-with-term '() (output " ") (should-term :cursor '(1 . 17)) (output "\e[D") (should-term :cursor '(1 . 16)) (output "\e[0D") (should-term :cursor '(1 . 15)) (output "\e[7D") (should-term :cursor '(1 . 8)) (output "\e[10D") (should-term :cursor '(1 . 1)))) (ert-deftest eat-test-cursor-down () "Test cursor down control function." (eat--tests-with-term '() (output "\e[B") (should-term :cursor '(2 . 1)) (output "\e[0B") (should-term :cursor '(3 . 1)) (output " ") (should-term :cursor '(3 . 5)) (output "\e[6B") (should-term :cursor '(6 . 5)))) (ert-deftest eat-test-cursor-up () "Test cursor up control function. Use spaces and newlines to move to an initial position from where the control function is to be invoked." (eat--tests-with-term '() (output "\n\n\n\n\n") (should-term :cursor '(6 . 1)) (output "\e[A") (should-term :cursor '(5 . 1)) (output "\e[0A") (should-term :cursor '(4 . 1)) (output " ") (should-term :cursor '(4 . 5)) (output "\e[2A") (should-term :cursor '(2 . 5)) (output "\e[4A") (should-term :cursor '(1 . 5)))) (ert-deftest eat-test-cursor-next-line () "Test cursor next line control function." (eat--tests-with-term '(:height 10) (output "\e[F") (should-term :cursor '(2 . 1)) (output "\e[0F") (should-term :cursor '(3 . 1)) (output "\e[2F") (should-term :cursor '(5 . 1)) (output " \e[F") (should-term :cursor '(6 . 1)) (output " \e[0F") (should-term :cursor '(7 . 1)) (output " \e[3F") (should-term :cursor '(10 . 1)) (output " \e[F") (should-term :cursor '(10 . 1)))) (ert-deftest eat-test-cursor-previous-line () "Test cursor previous line control function. Use newlines to move to an initial position from where the control function is to be invoked." (eat--tests-with-term '(:height 10) (output "\n\n\n\n\n\n\n\n\n") (should-term :cursor '(10 . 1)) (output "\e[E") (should-term :cursor '(9 . 1)) (output "\e[0E") (should-term :cursor '(8 . 1)) (output "\e[2E") (should-term :cursor '(6 . 1)) (output " \e[E") (should-term :cursor '(5 . 1)) (output " \e[0E") (should-term :cursor '(4 . 1)) (output " \e[3E") (should-term :cursor '(1 . 1)) (output " \e[E") (should-term :cursor '(1 . 1)))) (ert-deftest eat-test-cursor-character-absolute () "Test cursor character absolute control function." (eat--tests-with-term '() (output "\e[5G") (should-term :cursor '(1 . 5)) (output "\e[0G") (should-term :cursor '(1 . 1)) (output "\e[15G") (should-term :cursor '(1 . 15)) (output "\e[G") (should-term :cursor '(1 . 1)))) (ert-deftest eat-test-character-position-absolute () "Test character position absolute control function." (eat--tests-with-term '() (output "\e[5`") (should-term :cursor '(1 . 5)) (output "\e[0`") (should-term :cursor '(1 . 1)) (output "\e[15`") (should-term :cursor '(1 . 15)) (output "\e[`") (should-term :cursor '(1 . 1)))) (ert-deftest eat-test-line-position-absolute () "Test line position absolute control function." (eat--tests-with-term '() (output "\e[2d") (should-term :cursor '(2 . 1)) (output "\e[0d") (should-term :cursor '(1 . 1)) (output "\e[5d") (should-term :cursor '(5 . 1)) (output "\e[d") (should-term :cursor '(1 . 1)))) (ert-deftest eat-test-cursor-position () "Test cursor position control function." (eat--tests-with-term '() (output "\e[2;2H") (should-term :cursor '(2 . 2)) (output "\e[;5H") (should-term :cursor '(1 . 5)) (output "\e[4;H") (should-term :cursor '(4 . 1)) (output "\e[7;H") (should-term :cursor '(6 . 1)) (output "\e[0;0H") (should-term :cursor '(1 . 1)) (output "\e[;30H") (should-term :cursor '(1 . 20)) (output "\e[10;25H") (should-term :cursor '(6 . 20)) (output "\e[H") (should-term :cursor '(1 . 1)))) (ert-deftest eat-test-character-and-line-position () "Test character and line position control function." (eat--tests-with-term '() (output "\e[2;2f") (should-term :cursor '(2 . 2)) (output "\e[;5f") (should-term :cursor '(1 . 5)) (output "\e[4;f") (should-term :cursor '(4 . 1)) (output "\e[7;f") (should-term :cursor '(6 . 1)) (output "\e[0;0f") (should-term :cursor '(1 . 1)) (output "\e[;30f") (should-term :cursor '(1 . 20)) (output "\e[10;25f") (should-term :cursor '(6 . 20)) (output "\e[f") (should-term :cursor '(1 . 1)))) ;;;;; Scrolling Tests. (ert-deftest eat-test-scroll-up () "Test scroll up control function." (eat--tests-with-term '() (output "some test string...\nmore, more...\n" "more, more, more...\nand some more") (should-term :display '("some test string..." "more, more..." "more, more, more..." "and some more") :cursor '(4 . 14)) (output "\e[S") (should-term :scrollback '("some test string...") :display '("more, more..." "more, more, more..." "and some more") :cursor '(4 . 14)) (output "\e[0S") (should-term :scrollback '("some test string...") :display '("more, more..." "more, more, more..." "and some more") :cursor '(4 . 14)) (output "\e[2S") (should-term :scrollback '("some test string..." "more, more..." "more, more, more...") :display '("and some more") :cursor '(4 . 14)) (output "\nnew line 1\nnew line 2\nnew line 3\n" "new line 4\nnew line 5\nnew line 6\e[2;4r") (should-term :scrollback '("some test string..." "more, more..." "more, more, more..." "and some more" "" "" "") :display '("new line 1" "new line 2" "new line 3" "new line 4" "new line 5" "new line 6") :cursor '(1 . 1)) (output "\e[S") (should-term :scrollback '("some test string..." "more, more..." "more, more, more..." "and some more" "" "" "") :display '("new line 1" "new line 3" "new line 4" "" "new line 5" "new line 6") :cursor '(1 . 1)) (output "\e[0S") (should-term :scrollback '("some test string..." "more, more..." "more, more, more..." "and some more" "" "" "") :display '("new line 1" "new line 3" "new line 4" "" "new line 5" "new line 6") :cursor '(1 . 1)) (output "\e[2S") (should-term :scrollback '("some test string..." "more, more..." "more, more, more..." "and some more" "" "" "") :display '("new line 1" "" "" "" "new line 5" "new line 6") :cursor '(1 . 1)))) (ert-deftest eat-test-scroll-down () "Test scroll down control function." (eat--tests-with-term '() (output "some test string...\nmore, more...\n" "more, more, more...\nand some more") (should-term :display '("some test string..." "more, more..." "more, more, more..." "and some more") :cursor '(4 . 14)) (output "\e[T") (should-term :display '("" "some test string..." "more, more..." "more, more, more..." "and some more") :cursor '(4 . 14)) (output "\e[0T") (should-term :display '("" "some test string..." "more, more..." "more, more, more..." "and some more") :cursor '(4 . 14)) (output "\e[2T") (should-term :display '("" "" "" "some test string..." "more, more..." "more, more, more...") :cursor '(4 . 14)) (output "\n\n\nnew line 1\nnew line 2\nnew line 3\n" "new line 4\nnew line 5\nnew line 6\e[2;5r") (should-term :scrollback '("" "" "" "some test string..." "more, more..." "more, more, more...") :display '("new line 1" "new line 2" "new line 3" "new line 4" "new line 5" "new line 6") :cursor '(1 . 1)) (output "\e[T") (should-term :scrollback '("" "" "" "some test string..." "more, more..." "more, more, more...") :display '("new line 1" "" "new line 2" "new line 3" "new line 4" "new line 6") :cursor '(1 . 1)) (output "\e[0T") (should-term :scrollback '("" "" "" "some test string..." "more, more..." "more, more, more...") :display '("new line 1" "" "new line 2" "new line 3" "new line 4" "new line 6") :cursor '(1 . 1)) (output "\e[2T") (should-term :scrollback '("" "" "" "some test string..." "more, more..." "more, more, more...") :display '("new line 1" "" "" "" "new line 2" "new line 6") :cursor '(1 . 1)))) (ert-deftest eat-test-auto-scrolling () "Test automatic scrolling when cursor reaches end of display. Test with every control functions and text combination that trigger automatic scrolling as a side effect." (eat--tests-with-term '() ;; Test with newlines. (output "some test string...\n\n\n\n\n\nand some more") (should-term :scrollback '("some test string...") :display '("" "" "" "" "" "and some more") :cursor '(6 . 14)) ;; Test with automatic margin. (output "...more, more, stop.") (should-term :scrollback '("some test string..." "") :display '("" "" "" "" "and some more...more" ", more, stop.") :cursor '(6 . 14)) ;; Test with reverse index. (output "\eM\eM\eM\eM\eM\eM") (should-term :scrollback '("some test string..." "") :display '("" "" "" "" "" "and some more...more") :cursor '(1 . 14)) ;; Test with newlines and scroll region. (output "\e[2;5rline 1\nline 2\nline 3\nline 4\nline 5") (should-term :scrollback '("some test string..." "") :display '("line 1" "line 2" "line 3" "line 4" "line 5" "and some more...more") :cursor '(5 . 7)) (output "\n") (should-term :scrollback '("some test string..." "") :display '("line 1" "line 3" "line 4" "line 5" "" "and some more...more") :cursor '(5 . 1)) ;; Test with automatic margin and scroll region. (output "...more content, more, stop.") (should-term :scrollback '("some test string..." "") :display '("line 1" "line 4" "line 5" "...more content, mor" "e, stop." "and some more...more") :cursor '(5 . 9)) ;; Test with reverse index and scroll region. (output "\eM\eM\eM\eM\eM") (should-term :scrollback '("some test string..." "") :display '("line 1" "" "" "line 4" "line 5" "and some more...more") :cursor '(2 . 9)))) ;;;;; SGR Tests. ;; Beware, this section is a nightware of code repetition. (ert-deftest eat-test-sgr-foreground () "Test SGR foreground color. Test 256 colors and 24-bit colors." (eat--tests-with-term '() ;; ANSI colors. (output "\e[31mred\n") (should-term :display `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t)))) :cursor '(2 . 1)) (output "\e[32mgreen\n") (should-term :display `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t)))) :cursor '(3 . 1)) (output "\e[37mwhite\n") (should-term :display `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t)))) :cursor '(4 . 1)) (output "\e[30mblack\n") (should-term :display `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t)))) :cursor '(5 . 1)) ;; ANSI bright colors. (output "\e[91mbright red\n") (should-term :display `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t)))) :cursor '(6 . 1)) (output "\e[92mbright green\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t)))) :display `(,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t)))) :cursor '(6 . 1)) (output "\e[97mbright white\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t)))) :display `(,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t)))) :cursor '(6 . 1)) (output "\e[90mbright black\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t)))) :display `(,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-8 nil t)))) :cursor '(6 . 1)) ;; ANSI colors using 256-color sequence. (output "\e[38;5;1mred\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t)))) :display `(,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t)))) :cursor '(6 . 1)) (output "\e[38;5;2mgreen\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t)))) :display `(,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t)))) :cursor '(6 . 1)) (output "\e[38;5;7mwhite\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t)))) :display `(,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t)))) :cursor '(6 . 1)) (output "\e[38;5;0mblack\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t)))) :display `(,(add-props "bright black" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t)))) :cursor '(6 . 1)) ;; ANSI bright colors using 256-color sequence. (output "\e[38;5;9mbright red\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-8 nil t)))) :display `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t)))) :cursor '(6 . 1)) (output "\e[38;5;10mbright green\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t)))) :display `(,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t)))) :cursor '(6 . 1)) (output "\e[38;5;15mbright white\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t)))) :display `(,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t)))) :cursor '(6 . 1)) (output "\e[38;5;8mbright black\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t)))) :display `(,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-8 nil t)))) :cursor '(6 . 1)) ;; 256-color. (output "\e[38;5;119mcolor-119\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t)))) :display `(,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "color-119" `((0 . 9) :foreground ,(face-foreground 'eat-term-color-119 nil t)))) :cursor '(6 . 1)) (output "\e[38;5;255mcolor-255\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t)))) :display `(,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "color-119" `((0 . 9) :foreground ,(face-foreground 'eat-term-color-119 nil t))) ,(add-props "color-255" `((0 . 9) :foreground ,(face-foreground 'eat-term-color-255 nil t)))) :cursor '(6 . 1)) ;; 24-bit color (truecolor). (output "\e[38;2;34;139;34mforest green\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t)))) :display `(,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "color-119" `((0 . 9) :foreground ,(face-foreground 'eat-term-color-119 nil t))) ,(add-props "color-255" `((0 . 9) :foreground ,(face-foreground 'eat-term-color-255 nil t))) ,(add-props "forest green" '((0 . 12) :foreground "#228b22"))) :cursor '(6 . 1)) (output "\e[38;2;160;32;240mpurple\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t)))) :display `(,(add-props "bright black" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "color-119" `((0 . 9) :foreground ,(face-foreground 'eat-term-color-119 nil t))) ,(add-props "color-255" `((0 . 9) :foreground ,(face-foreground 'eat-term-color-255 nil t))) ,(add-props "forest green" '((0 . 12) :foreground "#228b22")) ,(add-props "purple" '((0 . 6) :foreground "#a020f0"))) :cursor '(6 . 1)) (output "\e[39mdefault\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :foreground ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-8 nil t)))) :display `(,(add-props "color-119" `((0 . 9) :foreground ,(face-foreground 'eat-term-color-119 nil t))) ,(add-props "color-255" `((0 . 9) :foreground ,(face-foreground 'eat-term-color-255 nil t))) ,(add-props "forest green" '((0 . 12) :foreground "#228b22")) ,(add-props "purple" '((0 . 6) :foreground "#a020f0")) "default") :cursor '(6 . 1)))) (ert-deftest eat-test-sgr-background () "Test SGR background color. Test 256 colors and 24-bit colors." (eat--tests-with-term '() ;; ANSI colors. (output "\e[41mred\n") (should-term :display `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t)))) :cursor '(2 . 1)) (output "\e[42mgreen\n") (should-term :display `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t)))) :cursor '(3 . 1)) (output "\e[47mwhite\n") (should-term :display `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t)))) :cursor '(4 . 1)) (output "\e[40mblack\n") (should-term :display `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t)))) :cursor '(5 . 1)) ;; ANSI bright colors. (output "\e[101mbright red\n") (should-term :display `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t)))) :cursor '(6 . 1)) (output "\e[102mbright green\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t)))) :display `(,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t)))) :cursor '(6 . 1)) (output "\e[107mbright white\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t)))) :display `(,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t)))) :cursor '(6 . 1)) (output "\e[100mbright black\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t)))) :display `(,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :background ,(face-foreground 'eat-term-color-8 nil t)))) :cursor '(6 . 1)) ;; ANSI colors using 256-color sequence. (output "\e[48;5;1mred\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t)))) :display `(,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t)))) :cursor '(6 . 1)) (output "\e[48;5;2mgreen\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t)))) :display `(,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t)))) :cursor '(6 . 1)) (output "\e[48;5;7mwhite\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t)))) :display `(,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t)))) :cursor '(6 . 1)) (output "\e[48;5;0mblack\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t)))) :display `(,(add-props "bright black" `((0 . 12) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t)))) :cursor '(6 . 1)) ;; ANSI bright colors using 256-color sequence. (output "\e[48;5;9mbright red\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :background ,(face-foreground 'eat-term-color-8 nil t)))) :display `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t)))) :cursor '(6 . 1)) (output "\e[48;5;10mbright green\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t)))) :display `(,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t)))) :cursor '(6 . 1)) (output "\e[48;5;15mbright white\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t)))) :display `(,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t)))) :cursor '(6 . 1)) (output "\e[48;5;8mbright black\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t)))) :display `(,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :background ,(face-foreground 'eat-term-color-8 nil t)))) :cursor '(6 . 1)) ;; 256-color. (output "\e[48;5;119mcolor-119\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t)))) :display `(,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "color-119" `((0 . 9) :background ,(face-foreground 'eat-term-color-119 nil t)))) :cursor '(6 . 1)) (output "\e[48;5;255mcolor-255\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t)))) :display `(,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "color-119" `((0 . 9) :background ,(face-foreground 'eat-term-color-119 nil t))) ,(add-props "color-255" `((0 . 9) :background ,(face-foreground 'eat-term-color-255 nil t)))) :cursor '(6 . 1)) ;; 24-bit color (truecolor). (output "\e[48;2;34;139;34mforest green\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t)))) :display `(,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "color-119" `((0 . 9) :background ,(face-foreground 'eat-term-color-119 nil t))) ,(add-props "color-255" `((0 . 9) :background ,(face-foreground 'eat-term-color-255 nil t))) ,(add-props "forest green" '((0 . 12) :background "#228b22"))) :cursor '(6 . 1)) (output "\e[48;2;160;32;240mpurple\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t)))) :display `(,(add-props "bright black" `((0 . 12) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "color-119" `((0 . 9) :background ,(face-foreground 'eat-term-color-119 nil t))) ,(add-props "color-255" `((0 . 9) :background ,(face-foreground 'eat-term-color-255 nil t))) ,(add-props "forest green" '((0 . 12) :background "#228b22")) ,(add-props "purple" '((0 . 6) :background "#a020f0"))) :cursor '(6 . 1)) (output "\e[49mdefault\n") (should-term :scrollback `(,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "red" `((0 . 3) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "white" `((0 . 5) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props "black" `((0 . 5) :background ,(face-foreground 'eat-term-color-0 nil t))) ,(add-props "bright red" `((0 . 10) :background ,(face-foreground 'eat-term-color-9 nil t))) ,(add-props "bright green" `((0 . 12) :background ,(face-foreground 'eat-term-color-10 nil t))) ,(add-props "bright white" `((0 . 12) :background ,(face-foreground 'eat-term-color-15 nil t))) ,(add-props "bright black" `((0 . 12) :background ,(face-foreground 'eat-term-color-8 nil t)))) :display `(,(add-props "color-119" `((0 . 9) :background ,(face-foreground 'eat-term-color-119 nil t))) ,(add-props "color-255" `((0 . 9) :background ,(face-foreground 'eat-term-color-255 nil t))) ,(add-props "forest green" '((0 . 12) :background "#228b22")) ,(add-props "purple" '((0 . 6) :background "#a020f0")) "default") :cursor '(6 . 1)))) (ert-deftest eat-test-sgr-intensity () "Test SGR intensity attributes (both bold and faint)." (eat--tests-with-term '() (output "\e[1mbold\n") (should-term :display `(,(add-props "bold" `((0 . 4) :intensity bold))) :cursor '(2 . 1)) (output "\e[2mfaint\n") (should-term :display `(,(add-props "bold" `((0 . 4) :intensity bold)) ,(add-props "faint" `((0 . 5) :intensity faint))) :cursor '(3 . 1)) (output "\e[22mnormal\n") (should-term :display `(,(add-props "bold" `((0 . 4) :intensity bold)) ,(add-props "faint" `((0 . 5) :intensity faint)) "normal") :cursor '(4 . 1)))) (ert-deftest eat-test-sgr-italic () "Test SGR italic attribute." (eat--tests-with-term '() (output "\e[3mitalic\n") (should-term :display `(,(add-props "italic" `((0 . 6) :italic t))) :cursor '(2 . 1)) (output "\e[23mnormal\n") (should-term :display `(,(add-props "italic" `((0 . 6) :italic t)) "normal") :cursor '(3 . 1)))) (ert-deftest eat-test-sgr-underline () "Test SGR underline with no color, 256 colors and 24-bit colors." (eat--tests-with-term '() ;; Without colors. (output "\e[4mdefault line\n") (should-term :display `(,(add-props "default line" '((0 . 12) :underline-type line))) :cursor '(2 . 1)) (output "\e[4:0mnormal\n") (should-term :display `(,(add-props "default line" '((0 . 12) :underline-type line)) "normal") :cursor '(3 . 1)) (output "\e[4:1mdefault line\n") (should-term :display `(,(add-props "default line" '((0 . 12) :underline-type line)) "normal" ,(add-props "default line" '((0 . 12) :underline-type line))) :cursor '(4 . 1)) (output "\e[4:2mdefault line\n") (should-term :display `(,(add-props "default line" '((0 . 12) :underline-type line)) "normal" ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default line" '((0 . 12) :underline-type line))) :cursor '(5 . 1)) (output "\e[4:3mdefault wave\n") (should-term :display `(,(add-props "default line" '((0 . 12) :underline-type line)) "normal" ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default wave" '((0 . 12) :underline-type wave))) :cursor '(6 . 1)) (output "\e[4:4mdefault wave\n") (should-term :scrollback `(,(add-props "default line" '((0 . 12) :underline-type line))) :display `("normal" ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default wave" '((0 . 12) :underline-type wave))) :cursor '(6 . 1)) (output "\e[4:5mdefault wave\n") (should-term :scrollback `(,(add-props "default line" '((0 . 12) :underline-type line)) "normal") :display `(,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default wave" '((0 . 12) :underline-type wave))) :cursor '(6 . 1)) (output "\e[4;58;5;6mcyan line\n") (should-term :scrollback `(,(add-props "default line" '((0 . 12) :underline-type line)) "normal" ,(add-props "default line" '((0 . 12) :underline-type line))) :display `(,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "cyan line" `((0 . 9) :underline-type line :underline-color ,(face-foreground 'eat-term-color-6 nil t)))) :cursor '(6 . 1)) (output "\e[4:3;58;5;3myellow wave\n") (should-term :scrollback `(,(add-props "default line" '((0 . 12) :underline-type line)) "normal" ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default line" '((0 . 12) :underline-type line))) :display `(,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "cyan line" `((0 . 9) :underline-type line :underline-color ,(face-foreground 'eat-term-color-6 nil t))) ,(add-props "yellow wave" `((0 . 11) :underline-type wave :underline-color ,(face-foreground 'eat-term-color-3 nil t)))) :cursor '(6 . 1)) (output "\e[4:1;58;5;13mbright magenta line\n") (should-term :scrollback `(,(add-props "default line" '((0 . 12) :underline-type line)) "normal" ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default wave" '((0 . 12) :underline-type wave))) :display `(,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "cyan line" `((0 . 9) :underline-type line :underline-color ,(face-foreground 'eat-term-color-6 nil t))) ,(add-props "yellow wave" `((0 . 11) :underline-type wave :underline-color ,(face-foreground 'eat-term-color-3 nil t))) ,(add-props "bright magenta line" `((0 . 19) :underline-type line :underline-color ,(face-foreground 'eat-term-color-13 nil t)))) :cursor '(6 . 1)) (output "\e[4:4;58;5;133mcolor-133 wave\n") (should-term :scrollback `(,(add-props "default line" '((0 . 12) :underline-type line)) "normal" ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default wave" '((0 . 12) :underline-type wave))) :display `(,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "cyan line" `((0 . 9) :underline-type line :underline-color ,(face-foreground 'eat-term-color-6 nil t))) ,(add-props "yellow wave" `((0 . 11) :underline-type wave :underline-color ,(face-foreground 'eat-term-color-3 nil t))) ,(add-props "bright magenta line" `((0 . 19) :underline-type line :underline-color ,(face-foreground 'eat-term-color-13 nil t))) ,(add-props "color-133 wave" `((0 . 14) :underline-type wave :underline-color ,(face-foreground 'eat-term-color-133 nil t)))) :cursor '(6 . 1)) (output "\e[4:2;58;2;160;32;240mpurple line\n") (should-term :scrollback `(,(add-props "default line" '((0 . 12) :underline-type line)) "normal" ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default wave" '((0 . 12) :underline-type wave))) :display `(,(add-props "cyan line" `((0 . 9) :underline-type line :underline-color ,(face-foreground 'eat-term-color-6 nil t))) ,(add-props "yellow wave" `((0 . 11) :underline-type wave :underline-color ,(face-foreground 'eat-term-color-3 nil t))) ,(add-props "bright magenta line" `((0 . 19) :underline-type line :underline-color ,(face-foreground 'eat-term-color-13 nil t))) ,(add-props "color-133 wave" `((0 . 14) :underline-type wave :underline-color ,(face-foreground 'eat-term-color-133 nil t))) ,(add-props "purple line" '((0 . 11) :underline-type line :underline-color "#a020f0"))) :cursor '(6 . 1)) (output "\e[4:5;58;2;0;0;139mdark blue wave\n") (should-term :scrollback `(,(add-props "default line" '((0 . 12) :underline-type line)) "normal" ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "cyan line" `((0 . 9) :underline-type line :underline-color ,(face-foreground 'eat-term-color-6 nil t)))) :display `(,(add-props "yellow wave" `((0 . 11) :underline-type wave :underline-color ,(face-foreground 'eat-term-color-3 nil t))) ,(add-props "bright magenta line" `((0 . 19) :underline-type line :underline-color ,(face-foreground 'eat-term-color-13 nil t))) ,(add-props "color-133 wave" `((0 . 14) :underline-type wave :underline-color ,(face-foreground 'eat-term-color-133 nil t))) ,(add-props "purple line" '((0 . 11) :underline-type line :underline-color "#a020f0")) ,(add-props "dark blue wave" '((0 . 14) :underline-type wave :underline-color "#00008b"))) :cursor '(6 . 1)) (output "\e[59mdefault wave\n") (should-term :scrollback `(,(add-props "default line" '((0 . 12) :underline-type line)) "normal" ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "cyan line" `((0 . 9) :underline-type line :underline-color ,(face-foreground 'eat-term-color-6 nil t))) ,(add-props "yellow wave" `((0 . 11) :underline-type wave :underline-color ,(face-foreground 'eat-term-color-3 nil t)))) :display `(,(add-props "bright magenta line" `((0 . 19) :underline-type line :underline-color ,(face-foreground 'eat-term-color-13 nil t))) ,(add-props "color-133 wave" `((0 . 14) :underline-type wave :underline-color ,(face-foreground 'eat-term-color-133 nil t))) ,(add-props "purple line" '((0 . 11) :underline-type line :underline-color "#a020f0")) ,(add-props "dark blue wave" '((0 . 14) :underline-type wave :underline-color "#00008b")) ,(add-props "default wave" '((0 . 12) :underline-type wave))) :cursor '(6 . 1)) (output "\e[21mdefault line\n") (should-term :scrollback `(,(add-props "default line" '((0 . 12) :underline-type line)) "normal" ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "cyan line" `((0 . 9) :underline-type line :underline-color ,(face-foreground 'eat-term-color-6 nil t))) ,(add-props "yellow wave" `((0 . 11) :underline-type wave :underline-color ,(face-foreground 'eat-term-color-3 nil t))) ,(add-props "bright magenta line" `((0 . 19) :underline-type line :underline-color ,(face-foreground 'eat-term-color-13 nil t)))) :display `(,(add-props "color-133 wave" `((0 . 14) :underline-type wave :underline-color ,(face-foreground 'eat-term-color-133 nil t))) ,(add-props "purple line" '((0 . 11) :underline-type line :underline-color "#a020f0")) ,(add-props "dark blue wave" '((0 . 14) :underline-type wave :underline-color "#00008b")) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default line" '((0 . 12) :underline-type line))) :cursor '(6 . 1)) (output "\e[24mnormal\n") (should-term :scrollback `(,(add-props "default line" '((0 . 12) :underline-type line)) "normal" ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default line" '((0 . 12) :underline-type line)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "cyan line" `((0 . 9) :underline-type line :underline-color ,(face-foreground 'eat-term-color-6 nil t))) ,(add-props "yellow wave" `((0 . 11) :underline-type wave :underline-color ,(face-foreground 'eat-term-color-3 nil t))) ,(add-props "bright magenta line" `((0 . 19) :underline-type line :underline-color ,(face-foreground 'eat-term-color-13 nil t))) ,(add-props "color-133 wave" `((0 . 14) :underline-type wave :underline-color ,(face-foreground 'eat-term-color-133 nil t)))) :display `(,(add-props "purple line" '((0 . 11) :underline-type line :underline-color "#a020f0")) ,(add-props "dark blue wave" '((0 . 14) :underline-type wave :underline-color "#00008b")) ,(add-props "default wave" '((0 . 12) :underline-type wave)) ,(add-props "default line" '((0 . 12) :underline-type line)) "normal") :cursor '(6 . 1)))) (ert-deftest eat-test-sgr-crossed () "Test SGR crossed attribute." (eat--tests-with-term '() (output "\e[9mcrossed\n") (should-term :display `(,(add-props "crossed" `((0 . 7) :crossed t))) :cursor '(2 . 1)) (output "\e[29mnormal\n") (should-term :display `(,(add-props "crossed" `((0 . 7) :crossed t)) "normal") :cursor '(3 . 1)))) (ert-deftest eat-test-sgr-inverse () "Test SGR inverse attributes." (eat--tests-with-term '() (output "\e[7mdefault\n") (should-term :display `(,(add-props "default" `((0 . 7) :foreground ,(face-background 'default nil t) :background ,(face-foreground 'default nil t)))) :cursor '(2 . 1)) (output "\e[31mred fg\n") (should-term :display `(,(add-props "default" `((0 . 7) :foreground ,(face-background 'default nil t) :background ,(face-foreground 'default nil t))) ,(add-props "red fg" `((0 . 6) :foreground ,(face-background 'default nil t) :background ,(face-foreground 'eat-term-color-1 nil t)))) :cursor '(3 . 1)) (output "\e[42mred fg green bg\n") (should-term :display `(,(add-props "default" `((0 . 7) :foreground ,(face-background 'default nil t) :background ,(face-foreground 'default nil t))) ,(add-props "red fg" `((0 . 6) :foreground ,(face-background 'default nil t) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "red fg green bg" `((0 . 15) :foreground ,(face-foreground 'eat-term-color-2 nil t) :background ,(face-foreground 'eat-term-color-1 nil t)))) :cursor '(4 . 1)) (output "\e[39mgreen bg\n") (should-term :display `(,(add-props "default" `((0 . 7) :foreground ,(face-background 'default nil t) :background ,(face-foreground 'default nil t))) ,(add-props "red fg" `((0 . 6) :foreground ,(face-background 'default nil t) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "red fg green bg" `((0 . 15) :foreground ,(face-foreground 'eat-term-color-2 nil t) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green bg" `((0 . 8) :foreground ,(face-foreground 'eat-term-color-2 nil t) :background ,(face-foreground 'default nil t)))) :cursor '(5 . 1)) (output "\e[27;49mnormal\n") (should-term :display `(,(add-props "default" `((0 . 7) :foreground ,(face-background 'default nil t) :background ,(face-foreground 'default nil t))) ,(add-props "red fg" `((0 . 6) :foreground ,(face-background 'default nil t) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "red fg green bg" `((0 . 15) :foreground ,(face-foreground 'eat-term-color-2 nil t) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green bg" `((0 . 8) :foreground ,(face-foreground 'eat-term-color-2 nil t) :background ,(face-foreground 'default nil t))) "normal") :cursor '(6 . 1)))) (ert-deftest eat-test-sgr-blink () "Test SGR blink attributes (both slow and fast blink)." (eat--tests-with-term '() (output "\e[5mslow\n") (should-term :display `(,(add-props "slow" `((0 . 4) :blink slow))) :cursor '(2 . 1)) (output "\e[6mfast\n") (should-term :display `(,(add-props "slow" `((0 . 4) :blink slow)) ,(add-props "fast" `((0 . 4) :blink fast))) :cursor '(3 . 1)) (output "\e[25mnormal\n") (should-term :display `(,(add-props "slow" `((0 . 4) :blink slow)) ,(add-props "fast" `((0 . 4) :blink fast)) "normal") :cursor '(4 . 1)))) (ert-deftest eat-test-sgr-conceal () (eat--tests-with-term '() (output "\e[8mdefault\n") (should-term :display `(,(add-props "default" `((0 . 7) :foreground ,(face-background 'default nil t)))) :cursor '(2 . 1)) (output "\e[31mdefault with fg\n") (should-term :display `(,(add-props "default" `((0 . 7) :foreground ,(face-background 'default nil t))) ,(add-props "default with fg" `((0 . 15) :foreground ,(face-background 'default nil t)))) :cursor '(3 . 1)) (output "\e[41mred\n") (should-term :display `(,(add-props "default" `((0 . 7) :foreground ,(face-background 'default nil t))) ,(add-props "default with fg" `((0 . 15) :foreground ,(face-background 'default nil t))) ,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t) :background ,(face-foreground 'eat-term-color-1 nil t)))) :cursor '(4 . 1)) (output "\e[31;42mgreen\n") (should-term :display `(,(add-props "default" `((0 . 7) :foreground ,(face-background 'default nil t))) ,(add-props "default with fg" `((0 . 15) :foreground ,(face-background 'default nil t))) ,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t) :background ,(face-foreground 'eat-term-color-2 nil t)))) :cursor '(5 . 1)) (output "\e[28mred on green\n") (should-term :display `(,(add-props "default" `((0 . 7) :foreground ,(face-background 'default nil t))) ,(add-props "default with fg" `((0 . 15) :foreground ,(face-background 'default nil t))) ,(add-props "red" `((0 . 3) :foreground ,(face-foreground 'eat-term-color-1 nil t) :background ,(face-foreground 'eat-term-color-1 nil t))) ,(add-props "green" `((0 . 5) :foreground ,(face-foreground 'eat-term-color-2 nil t) :background ,(face-foreground 'eat-term-color-2 nil t))) ,(add-props "red on green" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-1 nil t) :background ,(face-foreground 'eat-term-color-2 nil t)))) :cursor '(6 . 1)))) (ert-deftest eat-test-sgr-font () "Test SGR font attributes." (eat--tests-with-term '() (output "font 0\n") (should-term :display '("font 0") :cursor '(2 . 1)) (should-term :display `(,(add-props "font 0" `((0 . 6) :font 0))) :cursor '(2 . 1)) (output "\e[13mfont 3\n") (should-term :display `("font 0" ,(add-props "font 3" `((0 . 6) :font 3))) :cursor '(3 . 1)) (output "\e[19mfont 9\n") (should-term :display `("font 0" ,(add-props "font 3" `((0 . 6) :font 3)) ,(add-props "font 9" `((0 . 6) :font 9))) :cursor '(4 . 1)) (output "\e[12mfont 2\n") (should-term :display `("font 0" ,(add-props "font 3" `((0 . 6) :font 3)) ,(add-props "font 9" `((0 . 6) :font 9)) ,(add-props "font 2" `((0 . 6) :font 2))) :cursor '(5 . 1)) (output "\e[10mfont 0, normal\n") (should-term :display `("font 0" ,(add-props "font 3" `((0 . 6) :font 3)) ,(add-props "font 9" `((0 . 6) :font 9)) ,(add-props "font 2" `((0 . 6) :font 2)) "font 0, normal") :cursor '(6 . 1)))) (ert-deftest eat-test-sgr-reset () "Test SGR attributes reset sequence." (eat--tests-with-term '(:width 30 :height 10) (output "\e[1;3;4:3;6;7;9;15;33;105;" "58;5;10mcrazy text 1\n") (should-term :display `(,(add-props "crazy text 1" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-13 nil t) :background ,(face-foreground 'eat-term-color-3 nil t) :intensity bold :italic t :underline-type wave :underline-color ,(face-foreground 'eat-term-color-10 nil t) :blink fast :crossed t :font 5))) :cursor '(2 . 1)) (output "\e[0mnormal text 1\r\n") (should-term :display `(,(add-props "crazy text 1" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-13 nil t) :background ,(face-foreground 'eat-term-color-3 nil t) :intensity bold :italic t :underline-type wave :underline-color ,(face-foreground 'eat-term-color-10 nil t) :blink fast :crossed t :font 5)) "normal text 1") :cursor '(3 . 1)) (output "\e[2;3;4:1;5;7;8;9;18;" "38;2;50;90;100;48;2;100;50;9;" "58;2;10;90;45mcrazy text 2\n") (should-term :display `(,(add-props "crazy text 1" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-13 nil t) :background ,(face-foreground 'eat-term-color-3 nil t) :intensity bold :italic t :underline-type wave :underline-color ,(face-foreground 'eat-term-color-10 nil t) :blink fast :crossed t :font 5)) "normal text 1" ,(add-props "crazy text 2" '((0 . 12) :foreground "#643209" :background "#643209" :intensity faint :italic t :underline-type line :underline-color "#0a5a2d" :blink slow :crossed t :font 8))) :cursor '(4 . 1)) (output "\e[mnormal text 2\n") (should-term :display `(,(add-props "crazy text 1" `((0 . 12) :foreground ,(face-foreground 'eat-term-color-13 nil t) :background ,(face-foreground 'eat-term-color-3 nil t) :intensity bold :italic t :underline-type wave :underline-color ,(face-foreground 'eat-term-color-10 nil t) :blink fast :crossed t :font 5)) "normal text 1" ,(add-props "crazy text 2" '((0 . 12) :foreground "#643209" :background "#643209" :intensity faint :italic t :underline-type line :underline-color "#0a5a2d" :blink slow :crossed t :font 8)) "normal text 2") :cursor '(5 . 1)))) ;;;;; Text Manipulation Tests. (ert-deftest eat-test-insert-character () "Test insert character control function." (eat--tests-with-term '() ;; Without background. (output "in the universe,\nthere are\nbugs and antibugs\n" "iwritebothoftheme") (should-term :display '("in the universe," "there are" "bugs and antibugs" "iwritebothoftheme") :cursor '(4 . 18)) (output "\e[2G\e[@") (should-term :display '("in the universe," "there are" "bugs and antibugs" "i writebothoftheme") :cursor '(4 . 2)) (output "\e[6C\e[1@") (should-term :display '("in the universe," "there are" "bugs and antibugs" "i write bothoftheme") :cursor '(4 . 8)) (output "\e[5C\e[@") (should-term :display '("in the universe," "there are" "bugs and antibugs" "i write both oftheme") :cursor '(4 . 13)) (output "\e[3C\e[0@") (should-term :display '("in the universe," "there are" "bugs and antibugs" "i write both of them") :cursor '(4 . 16)) ;; With background. (output "\nfoobar\e[3D") (should-term :display '("in the universe," "there are" "bugs and antibugs" "i write both of them" "foobar") :cursor '(5 . 4)) (output "\e[40m\e[@") (should-term :display `("in the universe," "there are" "bugs and antibugs" "i write both of them" ,(add-props "foo bar" `((3 . 4) :background ,(face-foreground 'eat-term-color-0 nil t)))) :cursor '(5 . 4)) (output "\e[107m\e[0@") (should-term :display `("in the universe," "there are" "bugs and antibugs" "i write both of them" ,(add-props "foo bar" `((3 . 4) :background ,(face-foreground 'eat-term-color-15 nil t)) `((4 . 5) :background ,(face-foreground 'eat-term-color-0 nil t)))) :cursor '(5 . 4)) (output "\e[48;5;133m\e[1@") (should-term :display `("in the universe," "there are" "bugs and antibugs" "i write both of them" ,(add-props "foo bar" `((3 . 4) :background ,(face-foreground 'eat-term-color-133 nil t)) `((4 . 5) :background ,(face-foreground 'eat-term-color-15 nil t)) `((5 . 6) :background ,(face-foreground 'eat-term-color-0 nil t)))) :cursor '(5 . 4)) (output "\e[48;2;50;255;62m\e[5@") (should-term :display `("in the universe," "there are" "bugs and antibugs" "i write both of them" ,(add-props "foo bar" '((3 . 8) :background "#32ff3e") `((8 . 9) :background ,(face-foreground 'eat-term-color-133 nil t)) `((9 . 10) :background ,(face-foreground 'eat-term-color-15 nil t)) `((10 . 11) :background ,(face-foreground 'eat-term-color-0 nil t)))) :cursor '(5 . 4)) (output "\e[49m\e[3@") (should-term :display `("in the universe," "there are" "bugs and antibugs" "i write both of them" ,(add-props "foo bar" '((6 . 11) :background "#32ff3e") `((11 . 12) :background ,(face-foreground 'eat-term-color-133 nil t)) `((12 . 13) :background ,(face-foreground 'eat-term-color-15 nil t)) `((13 . 14) :background ,(face-foreground 'eat-term-color-0 nil t)))) :cursor '(5 . 4)))) (ert-deftest eat-test-delete-character () "Test delete character control function." (eat--tests-with-term '() ;; Without background. (output "sun is an star") (should-term :display '("sun is an star") :cursor '(1 . 15)) (output "\e[6D\e[P") (should-term :display '("sun is a star") :cursor '(1 . 9)) (output "\nmars is an exoplanet") (should-term :display '("sun is a star" "mars is an exoplanet") :cursor '(2 . 20)) (output "\e[9D\e[3P") (should-term :display '("sun is a star" "mars is an planet") :cursor '(2 . 12)) (output "\e[2D\e[0P") (should-term :display '("sun is a star" "mars is a planet") :cursor '(2 . 10)) ;; With background. (output "\nnill isn't false") (should-term :display '("sun is a star" "mars is a planet" "nill isn't false") :cursor '(3 . 17)) (output "\e[3G\e[46m\e[P\e[49m") (should-term :display `("sun is a star" "mars is a planet" ,(add-props "nil isn't false " `((19 . 20) :background ,(face-foreground 'eat-term-color-6 nil t)))) :cursor '(3 . 3)) (output "\e[7G\e[48;5;14m\e[3P\e[49m") (should-term :display `("sun is a star" "mars is a planet" ,(add-props "nil is false " `((16 . 17) :background ,(face-foreground 'eat-term-color-6 nil t)) `((17 . 20) :background ,(face-foreground 'eat-term-color-14 nil t)))) :cursor '(3 . 7)) (output "\nemacs is awesomes") (should-term :display `("sun is a star" "mars is a planet" ,(add-props "nil is false " `((16 . 17) :background ,(face-foreground 'eat-term-color-6 nil t)) `((17 . 20) :background ,(face-foreground 'eat-term-color-14 nil t))) "emacs is awesomes") :cursor '(4 . 18)) (output "\e[D\e[48;2;226;43;93m\e[0P\e[49m") (should-term :display `("sun is a star" "mars is a planet" ,(add-props "nil is false " `((16 . 17) :background ,(face-foreground 'eat-term-color-6 nil t)) `((17 . 20) :background ,(face-foreground 'eat-term-color-14 nil t))) ,(add-props "emacs is awesome " '((19 . 20) :background "#e22b5d"))) :cursor '(4 . 17)))) (ert-deftest eat-test-erase-character () "Test erase character control function." (eat--tests-with-term '() ;; Without background. (output "abbcccddddee") (should-term :display '("abbcccddddee") :cursor '(1 . 13)) (output "\e[2`\e[X") (should-term :display '("a bcccddddee") :cursor '(1 . 2)) (output "\e[2C\e[1X") (should-term :display '("a b ccddddee") :cursor '(1 . 4)) (output "\e[2C\e[2X") (should-term :display '("a b c dddee") :cursor '(1 . 6)) (output "\e[3C\e[2X") (should-term :display '("a b c d ee") :cursor '(1 . 9)) (output "\e[3C\e[0X") (should-term :display '("a b c d e") :cursor '(1 . 12)) ;; With background. (output "\nabbcccddddee") (should-term :display '("a b c d e" "abbcccddddee") :cursor '(2 . 13)) (output "\e[2`\e[42m\e[X") (should-term :display `("a b c d e" ,(add-props "a bcccddddee" `((1 . 2) :background ,(face-foreground 'eat-term-color-2 nil t)))) :cursor '(2 . 2)) (output "\e[2C\e[48;5;42m\e[1X") (should-term :display `("a b c d e" ,(add-props "a b ccddddee" `((1 . 2) :background ,(face-foreground 'eat-term-color-2 nil t)) `((3 . 4) :background ,(face-foreground 'eat-term-color-42 nil t)))) :cursor '(2 . 4)) (output "\e[2C\e[48;2;0;46;160m\e[2X") (should-term :display `("a b c d e" ,(add-props "a b c dddee" `((1 . 2) :background ,(face-foreground 'eat-term-color-2 nil t)) `((3 . 4) :background ,(face-foreground 'eat-term-color-42 nil t)) '((5 . 7) :background "#002ea0"))) :cursor '(2 . 6)) (output "\e[3C\e[103m\e[2X") (should-term :display `("a b c d e" ,(add-props "a b c d ee" `((1 . 2) :background ,(face-foreground 'eat-term-color-2 nil t)) `((3 . 4) :background ,(face-foreground 'eat-term-color-42 nil t)) '((5 . 7) :background "#002ea0") `((8 . 10) :background ,(face-foreground 'eat-term-color-11 nil t)))) :cursor '(2 . 9)) (output "\e[3C\e[48;2;162;96;198m\e[0X") (should-term :display `("a b c d e" ,(add-props "a b c d e " `((1 . 2) :background ,(face-foreground 'eat-term-color-2 nil t)) `((3 . 4) :background ,(face-foreground 'eat-term-color-42 nil t)) '((5 . 7) :background "#002ea0") `((8 . 10) :background ,(face-foreground 'eat-term-color-11 nil t)) '((11 . 12) :background "#a260c6"))) :cursor '(2 . 12)))) (ert-deftest eat-test-repeat () "Test repeat control function." (eat--tests-with-term '() ;; Without SGR attributes. (output "a\e[b") (should-term :display '("aa") :cursor '(1 . 3)) (output "\nb\e[2b") (should-term :display '("aa" "bbb") :cursor '(2 . 4)) (output "\nc\e[0b") (should-term :display '("aa" "bbb" "cc") :cursor '(3 . 3)) ;; With SGR attributes. (output "\n\e[34;43md\e[b") (should-term :display `("aa" "bbb" "cc" ,(add-props "dd" `((0 . 2) :foreground ,(face-foreground 'eat-term-color-4 nil t) :background ,(face-foreground 'eat-term-color-3 nil t)))) :cursor '(4 . 3)) (output "\n\e[;1;11me\e[5b") (should-term :display `("aa" "bbb" "cc" ,(add-props "dd" `((0 . 2) :foreground ,(face-foreground 'eat-term-color-4 nil t) :background ,(face-foreground 'eat-term-color-3 nil t))) ,(add-props "eeeeee" '((0 . 6) :intensity bold :font 1))) :cursor '(5 . 7)) (output "\n\e[;2;5mf\e[0b") (should-term :display `("aa" "bbb" "cc" ,(add-props "dd" `((0 . 2) :foreground ,(face-foreground 'eat-term-color-4 nil t) :background ,(face-foreground 'eat-term-color-3 nil t))) ,(add-props "eeeeee" '((0 . 6) :intensity bold :font 1)) ,(add-props "ff" '((0 . 2) :intensity faint :blink slow))) :cursor '(6 . 3)))) (ert-deftest eat-test-insert-line () "Test insert line control function." (eat--tests-with-term '() ;; Without background. (output "early to bed and\nearly to rise,\nmakes a man\n" "healthy, wealthy,\nand wise") (should-term :display '("early to bed and" "early to rise," "makes a man" "healthy, wealthy," "and wise") :cursor '(5 . 9)) (output "\e[L") (should-term :display '("early to bed and" "early to rise," "makes a man" "healthy, wealthy," "" "and wise") :cursor '(5 . 9)) (output "\e[2;15H\e[3L") (should-term :display '("early to bed and" "" "" "" "early to rise," "makes a man") :cursor '(2 . 15)) (output "\e[0L") (should-term :display '("early to bed and" "" "" "" "" "early to rise,") :cursor '(2 . 15)) ;; With background. (output "\e[2Jearly to bed and\nearly to rise,\nmakes a man\n" "healthy, wealthy,\nand wise") (should-term :display '("early to bed and" "early to rise," "makes a man" "healthy, wealthy," "and wise") :cursor '(5 . 9)) (output "\e[44m\e[L") (should-term :display `("early to bed and" "early to rise," "makes a man" "healthy, wealthy," ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-4 nil t))) "and wise") :cursor '(5 . 9)) (output "\e[2;15H\e[100m\e[3L") (should-term :display `("early to bed and" ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-8 nil t))) "early to rise," "makes a man") :cursor '(2 . 15)) (output "\e[48;2;100;100;50m\e[0L") (should-term :display `("early to bed and" ,(add-props " " '((0 . 20) :background "#646432")) ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-8 nil t))) "early to rise,") :cursor '(2 . 15)) (output "\e[49m\e[1L") (should-term :display `("early to bed and" "" ,(add-props " " '((0 . 20) :background "#646432")) ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-8 nil t)))) :cursor '(2 . 15)))) (ert-deftest eat-test-delete-line () "Test insert line control function." (eat--tests-with-term '() ;; Without background. (output "early to bed and\nearly to rise,\nmakes a man\n" "healthy, wealthy,\nand wise") (should-term :display '("early to bed and" "early to rise," "makes a man" "healthy, wealthy," "and wise") :cursor '(5 . 9)) (output "\e[M") (should-term :display '("early to bed and" "early to rise," "makes a man" "healthy, wealthy,") :cursor '(5 . 9)) (output "\e[2;15H\e[3M") (should-term :display '("early to bed and") :cursor '(2 . 15)) (output "\e[;5H\e[0M") (should-term :cursor '(1 . 5)) ;; With background. (output "\e[Hearly to bed and\nearly to rise,\nmakes a man\n" "healthy, wealthy,\nand wise") (should-term :display '("early to bed and" "early to rise," "makes a man" "healthy, wealthy," "and wise") :cursor '(5 . 9)) (output "\e[44m\e[M") (should-term :display `("early to bed and" "early to rise," "makes a man" "healthy, wealthy," "" ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-4 nil t)))) :cursor '(5 . 9)) (output "\e[2;15H\e[100m\e[3M") (should-term :display `("early to bed and" "" ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-4 nil t))) ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-8 nil t)))) :cursor '(2 . 15)) (output "\e[H\e[48;2;100;100;50m\e[0M") (should-term :display `("" ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-4 nil t))) ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props " " '((0 . 20) :background "#646432"))) :cursor '(1 . 1)) (output "\e[49m\e[1M") (should-term :display `(,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-4 nil t))) ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props " " `((0 . 20) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props " " '((0 . 20) :background "#646432"))) :cursor '(1 . 1)))) (ert-deftest eat-test-erase-in-line () "Test erase in line control function." (eat--tests-with-term '() ;; Without background. (output "foo bar baz\e[6G") (should-term :display '("foo bar baz") :cursor '(1 . 6)) (output "\e[K") (should-term :display '("foo b") :cursor '(1 . 6)) (output "\nbar baz foo\e[6G") (should-term :display '("foo b" "bar baz foo") :cursor '(2 . 6)) (output "\e[1K") (should-term :display '("foo b" " z foo") :cursor '(2 . 6)) (output "\nbaz foo bar\e[6G") (should-term :display '("foo b" " z foo" "baz foo bar") :cursor '(3 . 6)) (output "\e[0K") (should-term :display '("foo b" " z foo" "baz f") :cursor '(3 . 6)) (output "\nfoo bar baz\e[6G") (should-term :display '("foo b" " z foo" "baz f" "foo bar baz") :cursor '(4 . 6)) (output "\e[2K") (should-term :display '("foo b" " z foo" "baz f") :cursor '(4 . 6)) ;; With background. (output "\nfoo bar baz\e[6G") (should-term :display '("foo b" " z foo" "baz f" "" "foo bar baz") :cursor '(5 . 6)) (output "\e[47m\e[K\e[m") (should-term :display `("foo b" " z foo" "baz f" "" ,(add-props "foo b " `((5 . 20) :background ,(face-foreground 'eat-term-color-7 nil t)))) :cursor '(5 . 6)) (output "\nbar baz foo\e[6G") (should-term :display `("foo b" " z foo" "baz f" "" ,(add-props "foo b " `((5 . 20) :background ,(face-foreground 'eat-term-color-7 nil t))) "bar baz foo") :cursor '(6 . 6)) (output "\e[100m\e[1K\e[m") (should-term :display `("foo b" " z foo" "baz f" "" ,(add-props "foo b " `((5 . 20) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props " z foo" `((0 . 6) :background ,(face-foreground 'eat-term-color-8 nil t)))) :cursor '(6 . 6)) (output "\nbaz foo bar\e[6G") (should-term :scrollback '("foo b") :display `(" z foo" "baz f" "" ,(add-props "foo b " `((5 . 20) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props " z foo" `((0 . 6) :background ,(face-foreground 'eat-term-color-8 nil t))) "baz foo bar") :cursor '(6 . 6)) (output "\e[48;5;55m\e[0K\e[m") (should-term :scrollback '("foo b") :display `(" z foo" "baz f" "" ,(add-props "foo b " `((5 . 20) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props " z foo" `((0 . 6) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "baz f " `((5 . 20) :background ,(face-foreground 'eat-term-color-55 nil t)))) :cursor '(6 . 6)) (output "\nfoo bar baz\e[6G") (should-term :scrollback '("foo b" " z foo") :display `("baz f" "" ,(add-props "foo b " `((5 . 20) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props " z foo" `((0 . 6) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "baz f " `((5 . 20) :background ,(face-foreground 'eat-term-color-55 nil t))) "foo bar baz") :cursor '(6 . 6)) (output "\e[48;2;255;255;255m\e[2K") (should-term :scrollback '("foo b" " z foo") :display `("baz f" "" ,(add-props "foo b " `((5 . 20) :background ,(face-foreground 'eat-term-color-7 nil t))) ,(add-props " z foo" `((0 . 6) :background ,(face-foreground 'eat-term-color-8 nil t))) ,(add-props "baz f " `((5 . 20) :background ,(face-foreground 'eat-term-color-55 nil t))) ,(add-props " " '((0 . 20) :background "#ffffff"))) :cursor '(6 . 6)))) (ert-deftest eat-test-erase-in-display () "Test erase in display control function." (eat--tests-with-term '() ;; Without background. (output "foo bar baz\nbar baz foo\nbaz foo bar\e[2;6H") (should-term :display '("foo bar baz" "bar baz foo" "baz foo bar") :cursor '(2 . 6)) (output "\e[J") (should-term :display '("foo bar baz" "bar b") :cursor '(2 . 6)) (output "\e[Hfoo bar baz\nbar baz foo\nbaz foo bar\e[2;6H") (should-term :display '("foo bar baz" "bar baz foo" "baz foo bar") :cursor '(2 . 6)) (output "\e[1J") (should-term :display '("" " z foo" "baz foo bar") :cursor '(2 . 6)) (output "\e[Hfoo bar baz\nbar baz foo\nbaz foo bar\e[2;6H") (should-term :display '("foo bar baz" "bar baz foo" "baz foo bar") :cursor '(2 . 6)) (output "\e[0J") (should-term :display '("foo bar baz" "bar b") :cursor '(2 . 6)) (output "\e[Hfoo bar baz\nbar baz foo\nbaz foo bar\e[2;6H") (should-term :display '("foo bar baz" "bar baz foo" "baz foo bar") :cursor '(2 . 6)) (output "\e[2J") (should-term :cursor '(1 . 1)) (output "foo bar baz\nbar baz foo\nbaz foo bar\n" "foo bar baz\nbar baz foo\nbaz foo bar\n") (should-term :scrollback '("foo bar baz") :display '("bar baz foo" "baz foo bar" "foo bar baz" "bar baz foo" "baz foo bar") :cursor '(6 . 1)) (output "\e[3J") (should-term :cursor '(1 . 1)) ;; With background. (output "foo bar baz\nbar baz foo\nbaz foo bar\e[2;6H") (should-term :display '("foo bar baz" "bar baz foo" "baz foo bar") :cursor '(2 . 6)) (output "\e[11;33;44m\e[J\e[m") (should-term :display `("foo bar baz" ,(add-props "bar b " `((5 . 20) :foreground ,(face-foreground 'eat-term-color-3 nil t) :background ,(face-foreground 'eat-term-color-4 nil t) :font 1)) ,(add-props " " `((0 . 20) :foreground ,(face-foreground 'eat-term-color-3 nil t) :background ,(face-foreground 'eat-term-color-4 nil t) :font 1)) ,(add-props " " `((0 . 20) :foreground ,(face-foreground 'eat-term-color-3 nil t) :background ,(face-foreground 'eat-term-color-4 nil t) :font 1)) ,(add-props " " `((0 . 20) :foreground ,(face-foreground 'eat-term-color-3 nil t) :background ,(face-foreground 'eat-term-color-4 nil t) :font 1)) ,(add-props " " `((0 . 20) :foreground ,(face-foreground 'eat-term-color-3 nil t) :background ,(face-foreground 'eat-term-color-4 nil t) :font 1))) :cursor '(2 . 6)) (output "\e[2Jfoo bar baz\nbar baz foo\nbaz foo bar\e[2;6H") (should-term :display '("foo bar baz" "bar baz foo" "baz foo bar") :cursor '(2 . 6)) (output "\e[1;97;103m\e[1J\e[m") (should-term :display `(,(add-props " " `((0 . 20) :foreground ,(face-foreground 'eat-term-color-15 nil t) :background ,(face-foreground 'eat-term-color-11 nil t) :intensity bold)) ,(add-props " z foo" `((0 . 6) :foreground ,(face-foreground 'eat-term-color-15 nil t) :background ,(face-foreground 'eat-term-color-11 nil t) :intensity bold)) "baz foo bar") :cursor '(2 . 6)) (output "\e[2Jfoo bar baz\nbar baz foo\nbaz foo bar\e[2;6H") (should-term :display '("foo bar baz" "bar baz foo" "baz foo bar") :cursor '(2 . 6)) (output "\e[11;34;43m\e[0J\e[m") (should-term :display `("foo bar baz" ,(add-props "bar b " `((5 . 20) :foreground ,(face-foreground 'eat-term-color-4 nil t) :background ,(face-foreground 'eat-term-color-3 nil t) :font 1)) ,(add-props " " `((0 . 20) :foreground ,(face-foreground 'eat-term-color-4 nil t) :background ,(face-foreground 'eat-term-color-3 nil t) :font 1)) ,(add-props " " `((0 . 20) :foreground ,(face-foreground 'eat-term-color-4 nil t) :background ,(face-foreground 'eat-term-color-3 nil t) :font 1)) ,(add-props " " `((0 . 20) :foreground ,(face-foreground 'eat-term-color-4 nil t) :background ,(face-foreground 'eat-term-color-3 nil t) :font 1)) ,(add-props " " `((0 . 20) :foreground ,(face-foreground 'eat-term-color-4 nil t) :background ,(face-foreground 'eat-term-color-3 nil t) :font 1))) :cursor '(2 . 6)) (output "\e[2Jfoo bar baz\nbar baz foo\nbaz foo bar\e[2;6H") (should-term :display '("foo bar baz" "bar baz foo" "baz foo bar") :cursor '(2 . 6)) (output "\e[48;2;50;200;100m\e[2J\e[m") (should-term :display `(,(add-props " " '((0 . 20) :background "#32c864")) ,(add-props " " '((0 . 20) :background "#32c864")) ,(add-props " " '((0 . 20) :background "#32c864")) ,(add-props " " '((0 . 20) :background "#32c864")) ,(add-props " " '((0 . 20) :background "#32c864")) ,(add-props " " '((0 . 20) :background "#32c864"))) :cursor '(1 . 1)) (output "\e[2Jfoo bar baz\nbar baz foo\nbaz foo bar\n" "foo bar baz\nbar baz foo\nbaz foo bar\n") (should-term :scrollback '("foo bar baz") :display '("bar baz foo" "baz foo bar" "foo bar baz" "bar baz foo" "baz foo bar") :cursor '(6 . 1)) (output "\e[48;2;20;5;200m\e[3J\e[m") (should-term :display `(,(add-props " " '((0 . 20) :background "#1405c8")) ,(add-props " " '((0 . 20) :background "#1405c8")) ,(add-props " " '((0 . 20) :background "#1405c8")) ,(add-props " " '((0 . 20) :background "#1405c8")) ,(add-props " " '((0 . 20) :background "#1405c8")) ,(add-props " " '((0 . 20) :background "#1405c8"))) :cursor '(1 . 1)))) ;;;;; Resizing Tests. (ert-deftest eat-test-resize-when-at-beginning-of-first-line () "Test resize when on the beginning of the first line of display." (eat--tests-with-term '() (output "1\n2\n3\n4\n5\n6\e[H") (should-term :display '("1" "2" "3" "4" "5" "6") :cursor '(1 . 1)) (eat-term-resize (terminal) 20 5) (should-term :scrollback '("1") :display '("2" "3" "4" "5" "6") :cursor '(1 . 1)) (output "7\n") (should-term :scrollback '("1") :display '("7" "3" "4" "5" "6") :cursor '(2 . 1)))) (ert-deftest eat-test-resize-when-at-beginning-of-last-line () "Test resize when on the beginning of the last line of display." (eat--tests-with-term '() (output "1\n2\n3\n4\n5\n") (should-term :display '("1" "2" "3" "4" "5") :cursor '(6 . 1)) (eat-term-resize (terminal) 20 5) (should-term :scrollback '("1") :display '("2" "3" "4" "5") :cursor '(5 . 1)) (output "6\n") (should-term :scrollback '("1" "2") :display '("3" "4" "5" "6") :cursor '(5 . 1)))) (ert-deftest eat-test-resize-alternative-display () "Test resize when the beginning of the first line on display." (eat--tests-with-term '() (output "\e[?1049htoo looooooooooooong\nl\no\no\nn\ng") (should-term :display '("too looooooooooooong" "l" "o" "o" "n" "g") :cursor '(6 . 2)) (eat-term-resize (terminal) 18 4) (should-term :display '("too looooooooooooo" "l" "o" "o") :cursor '(4 . 2)))) ;;;;; Miscellaneous Tests. (ert-deftest eat-test-bell () "Test bell control function." (eat--tests-with-term '() (let ((bell-rang nil)) (setf (eat-term-parameter (terminal) 'ring-bell-function) (lambda (term) (should (eq term (terminal))) (setq bell-rang t))) (output "\a") (should bell-rang) (should-term :cursor '(1 . 1))))) (ert-deftest eat-test-character-sets () "Test character sets." (eat--tests-with-term '() (output "some text") (should-term :display '("some text") :cursor '(1 . 10)) (output "\n\e(0some text") (should-term :display '("some text" "⎽⎺└⊠├âŠâ”‚├") :cursor '(2 . 10)) (output "\n\e(Bsome text") (should-term :display '("some text" "⎽⎺└⊠├âŠâ”‚├" "some text") :cursor '(3 . 10)) (output "\n\C-nsome text") (should-term :display '("some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text") :cursor '(4 . 10)) (output "\n\e)0some text") (should-term :display '("some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├") :cursor '(5 . 10)) (output "\n\e)Bsome text") (should-term :display '("some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├" "some text") :cursor '(6 . 10)) (output "\n\ensome text") (should-term :scrollback '("some text") :display '("⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text") :cursor '(6 . 10)) (output "\n\e*0some text") (should-term :scrollback '("some text" "⎽⎺└⊠├âŠâ”‚├") :display '("some text" "some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├") :cursor '(6 . 10)) (output "\n\e*Bsome text") (should-term :scrollback '("some text" "⎽⎺└⊠├âŠâ”‚├" "some text") :display '("some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├" "some text") :cursor '(6 . 10)) (output "\n\eosome text") (should-term :scrollback '("some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text") :display '("⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text") :cursor '(6 . 10)) (output "\n\e+0some text") (should-term :scrollback '("some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├") :display '("some text" "some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├") :cursor '(6 . 10)) (output "\n\e+Bsome text") (should-term :scrollback '("some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├" "some text") :display '("some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├" "some text") :cursor '(6 . 10)) (output "\n\C-osome text") (should-term :scrollback '("some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text") :display '("⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text") :cursor '(6 . 10)) (output "\n\e(0some text") (should-term :scrollback '("some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├") :display '("some text" "some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├") :cursor '(6 . 10)) (output "\n\e(Bsome text") (should-term :scrollback '("some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├" "some text") :display '("some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├" "some text") :cursor '(6 . 10)) (output "\n\e(0+,-.0`abcdefghijklmnopqrstuvwxyz{|}~") (should-term :scrollback '("some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "some text" "⎽⎺└⊠├âŠâ”‚├") :display '("some text" "some text" "⎽⎺└⊠├âŠâ”‚├" "some text" "→â†â†‘↓█�▒â‰âŒââŠÂ°Â±â–‘#┘â”┌└┼" "⎺⎻─⎼⎽├┤┴┬│≤≥π≠£•") :cursor '(6 . 17)))) (ert-deftest eat-test-save-and-restore-cursor () "Test saving and restoring cursor position. Write plain text and newline to move cursor." (eat--tests-with-term '() (output "foo") (should-term :display '("foo") :cursor '(1 . 4)) (output "\e7") (should-term :display '("foo") :cursor '(1 . 4)) (output "bar\nfrob") (should-term :display '("foobar" "frob") :cursor '(2 . 5)) (output "\e8") (should-term :display '("foobar" "frob") :cursor '(1 . 4)))) (ert-deftest eat-test-set-cwd () "Test setting current working directory." (eat--tests-with-term '() (let ((cwd "nowhere") (host "ghost")) (setf (eat-term-parameter (terminal) 'set-cwd-function) (lambda (term hostname dir) (should (eq term (terminal))) (setq cwd dir) (setq host hostname))) ;; file://HOST/PATH/. (output "\e]7;file://frob/foo/bar/\e\\") (should (string= host "frob")) (should (string= cwd "/foo/bar/")) (should-term :cursor '(1 . 1)) ;; file://HOST/PATH (note the missing trailing slash). (output "\e]7;file://foo/bar/baz\e\\") (should (string= host "foo")) (should (string= cwd "/bar/baz/")) (should-term :cursor '(1 . 1))))) ;;;;; Input Event Tests. (ert-deftest eat-test-input-character () "Test character input events. This includes all events to which `self-insert-command' is bound to by default." (eat--tests-with-term '() (dolist (c '(?a ?E ?b ?D ?অ ?আ ?ক ?খ ?ম ?া ?Ï€ ?θ ?μ ?Δ)) (input-event c) (should (string= (input) (string c)))))) (ert-deftest eat-test-input-character-with-modifier () "Test character input events with modifiers (`control' and `meta'). This includes all events to which `self-insert-command' is bound to by default." (eat--tests-with-term '() (dolist (p '((?\C-\ . "\C-@") (?\C-a . "\C-a") (?\C-E . "\C-e") (?\C-b . "\C-b") (?\C-D . "\C-d") (?\M-x . "\ex") (?\M-O . "\e") (?\M-\[ . "\e") (?\M-C . "\eC") (?\C-\M-U . "\e\C-u") (?\M-ম . "\eম") (?\M-া . "\eা") (?\M-Ï€ . "\eÏ€") (?\M-θ . "\eθ"))) (input-event (car p)) (should (string= (input) (cdr p)))))) (ert-deftest eat-test-input-special-keys () "Test special key input events like arrow keys, backspace, etc." (eat--tests-with-term '() (cl-labels ((check (alist) (dolist (p alist) (input-event (car p)) (should (string= (input) (cdr p)))))) (check '((backspace . "\C-?") (C-backspace . "\C-h") (left . "\e[D") (C-right . "\e[1;5C") (M-up . "\e[1;3A") (S-down . "\e[1;2B") (C-M-insert . "\e[2;7~") (C-S-delete . "\e[3;6~") (M-S-deletechar . "\e[3;4~") (C-M-S-home . "\e[1;8H") (C-S-end . "\e[1;6F") (M-S-prior . "\e[5;4~") (next . "\e[6~"))) (output "\e[?1h") (check '((left . "\eOD") (up . "\eOA") (C-right . "\e[1;5C") (M-up . "\e[1;3A") (S-down . "\e[1;2B"))) (output "\e[?1l") (check '((up . "\e[A")))))) (ert-deftest eat-test-input-focus-events () "Test focus events." (eat--tests-with-term '() (let ((focus-event-enabled nil)) (setf (eat-term-parameter (terminal) 'grab-focus-events-function) (lambda (term value) (should (eq term (terminal))) (setq focus-event-enabled value))) (cl-labels ((check (alist) (dolist (p alist) (input-event (car p)) (should (string= (input) (cdr p)))))) (check '(((eat-focus-in) . "") ((eat-focus-out) . ""))) (output "\e[?1004h") ;; The mode is MUST BE t, not non-nil. (should (eq focus-event-enabled t)) (check '(((eat-focus-in) . "\e[I") ((eat-focus-out) . "\e[O"))) (output "\e[?1004l") (should (eq focus-event-enabled nil)) (check '(((eat-focus-in) . "") ((eat-focus-out) . ""))))))) (ert-deftest eat-test-input-mouse-events () "Test focus events." (eat--tests-with-term '(:width 200 :height 200) (cl-letf* ((mouse-mode nil) ((eat-term-parameter (terminal) 'grab-mouse-function) (lambda (term mouse) (should (eq term (terminal))) (setq mouse-mode mouse))) ((symbol-function #'posn-col-row) (lambda (posn &optional _use-window) (nth 6 posn)))) (cl-labels ((check (alist) (dolist (p alist) (input-event (car p) (cadr p)) (should (string= (input) (caddr p))))) (make-posn (&key (window (selected-window)) pos-or-area (timestamp 0) (object (point-min)) text-pos (col 0) (row 0) (x col) (y row) image (dx x) (dy y) (width 1) (height 1)) `( ,window ,pos-or-area (,x . ,y) ,timestamp ,object ,text-pos (,col . ,row) ,image (,dx . ,dy) (,width . ,height)))) ;; Default mouse event encoding. ;; Mouse mode disabled. (check `(((mouse-1 ,(make-posn)) nil "") ((mouse-2 ,(make-posn)) ,(make-posn) "") ((mouse-3 ,(make-posn :col 8 :row 3)) ,(make-posn :col 1 :row 1) "") ((mouse-4 ,(make-posn :col 13 :row 4)) ,(make-posn :col 5 :row 1) "") ((mouse-movement ,(make-posn :col 90 :row 40)) ,(make-posn :col 82 :row 33) ""))) ;; X10 mouse mode. (output "\e[?9h") (should (eq mouse-mode :click)) (check `(((mouse-1 ,(make-posn)) nil "\e[M !!") ((mouse-2 ,(make-posn)) ,(make-posn) "\e[M!!!") ((mouse-3 ,(make-posn :col 10 :row 5)) ,(make-posn :col 6 :row 3) "\e[M\"%#") ;; Position out of range of default mouse event encoding. ((mouse-1 ,(make-posn :col 95 :row 5)) nil "") ;; Position not inside terminal. ((mouse-2 ,(make-posn :col 94 :row 5)) ,(make-posn :col 96 :row 5) "") ((mouse-2 ,(make-posn :col 95 :row 4)) ,(make-posn :col 96 :row 8) "") ((mouse-4 ,(make-posn :col 24 :row 9)) ,(make-posn :col 8 :row 4) "") ((mouse-movement ,(make-posn :col 30 :row 45)) ,(make-posn :col 28 :row 42) ""))) ;; Normal mouse mode. (output "\e[?1000h") (should (eq mouse-mode :modifier-click)) (check `(((down-mouse-1 ,(make-posn)) nil "\e[M !!") ((mouse-1 ,(make-posn)) nil "\e[M#!!") ((down-mouse-2 ,(make-posn)) ,(make-posn) "\e[M!!!") ((mouse-2 ,(make-posn)) ,(make-posn) "\e[M#!!") ((down-mouse-3 ,(make-posn :col 10 :row 5)) ,(make-posn) "\e[M\"+&") ;; Note that, although the mouse position has changed, it's ;; still unmoved relative to the reference position, so the ;; mouse position inputted should be same. ((drag-mouse-3 ,(make-posn :col 10 :row 5) ,(make-posn :col 20 :row 50)) ,(make-posn :col 10 :row 45) "\e[M#+&") ((mouse-4 ,(make-posn :col 33 :row 10)) ,(make-posn :col 30 :row 5) "\e[M\`$&") ((C-mouse-5 ,(make-posn :col 4250 :row 3145)) ,(make-posn :col 4242 :row 3141) "\e[Mq)%") ((M-mouse-6 ,(make-posn :col 425 :row 314)) ,(make-posn :col 424 :row 314) "\e[Mj\"!") ((S-mouse-7 ,(make-posn :col 85 :row 20)) ,(make-posn :col 32 :row 16) "\e[MgV%") ((C-M-down-mouse-1 ,(make-posn :col 58 :row 32)) ,(make-posn :col 23 :row 31) "\e[M8D\"") ((C-M-mouse-1 ,(make-posn :col 58 :row 32)) ,(make-posn :col 23 :row 31) "\e[M;D\"") ((C-S-down-mouse-2 ,(make-posn :col 40 :row 39)) ,(make-posn :col 33 :row 13) "\e[M5(;") ;; Modifier changed! ((M-S-mouse-2 ,(make-posn :col 40 :row 39)) ,(make-posn :col 33 :row 13) "\e[M/(;") ((C-M-S-down-mouse-3 ,(make-posn :col 48 :row 29)) ,(make-posn :col 39 :row 23) "\e[M>*'") ;; Everything changed! ((mouse-3 ,(make-posn :col 92 :row 83)) ,(make-posn :col 10 :row 8) "\e[M#sl") ;; Button out of range of default mouse event encoding. ((mouse-8 ,(make-posn :col 1 :row 1)) ,(make-posn :col 1 :row 1) "") ((mouse-movement ,(make-posn :col 49 :row 85)) ,(make-posn :col 45 :row 82) ""))) ;; Button event mouse mode. (output "\e[?1002h") (should (eq mouse-mode :drag)) (check `(((mouse-movement ,(make-posn :col 84 :row 10)) ,(make-posn :col 48 :row 8) "") ((down-mouse-1 ,(make-posn :col 44 :row 88)) ,(make-posn :col 44 :row 88) "\e[M !!") ((mouse-movement ,(make-posn :col 48 :row 1)) ,(make-posn :col 19 :row 0) "\e[M@>\"") ((down-mouse-2 ,(make-posn :col 29 :row 21)) ,(make-posn :col 18 :row 8) "\e[M!,.") ((mouse-movement ,(make-posn :col 93 :row 54)) ,(make-posn :col 29 :row 38) "\e[M@a1") ((down-mouse-3 ,(make-posn :col 92 :row 63)) ,(make-posn :col 32 :row 38) "\e[M\"]:") ((mouse-movement ,(make-posn :col 8 :row 92)) ,(make-posn :col 3 :row 34) "\e[M@&[") ((mouse-1 ,(make-posn :col 93 :row 21)) ,(make-posn :col 0 :row 0) "\e[M#~6") ((mouse-movement ,(make-posn :col 29 :row 74)) ,(make-posn :col 7 :row 64) "\e[MA7+") ((mouse-2 ,(make-posn :col 28 :row 92)) ,(make-posn :col 23 :row 29) "\e[M#&`") ((mouse-movement ,(make-posn :col 75 :row 36)) ,(make-posn :col 75 :row 19) "\e[MB!2") ((mouse-3 ,(make-posn :col 36 :row 76)) ,(make-posn :col 17 :row 67) "\e[M#4*") ((mouse-movement ,(make-posn :col 94 :row 58)) ,(make-posn :col 54 :row 28) ""))) ;; All event mouse mode. (output "\e[?1003h") (should (eq mouse-mode :all)) (check `(((mouse-movement ,(make-posn :col 28 :row 38)) ,(make-posn :col 4 :row 2) "\e[MC9E") ((mouse-movement ,(make-posn :col 49 :row 85)) ,(make-posn :col 45 :row 82) "\e[MC%$") ((mouse-movement ,(make-posn :col 84 :row 10)) ,(make-posn :col 48 :row 8) "\e[MCE#") ((down-mouse-1 ,(make-posn :col 44 :row 88)) ,(make-posn :col 44 :row 88) "\e[M !!") ((mouse-movement ,(make-posn :col 48 :row 1)) ,(make-posn :col 19 :row 0) "\e[M@>\"") ((down-mouse-2 ,(make-posn :col 29 :row 21)) ,(make-posn :col 18 :row 8) "\e[M!,.") ((mouse-movement ,(make-posn :col 93 :row 54)) ,(make-posn :col 29 :row 38) "\e[M@a1") ((down-mouse-3 ,(make-posn :col 92 :row 63)) ,(make-posn :col 32 :row 38) "\e[M\"]:") ((mouse-movement ,(make-posn :col 8 :row 92)) ,(make-posn :col 3 :row 34) "\e[M@&[") ((mouse-1 ,(make-posn :col 93 :row 21)) ,(make-posn :col 0 :row 0) "\e[M#~6") ((mouse-movement ,(make-posn :col 29 :row 74)) ,(make-posn :col 7 :row 64) "\e[MA7+") ((mouse-2 ,(make-posn :col 28 :row 92)) ,(make-posn :col 23 :row 29) "\e[M#&`") ((mouse-movement ,(make-posn :col 75 :row 36)) ,(make-posn :col 75 :row 19) "\e[MB!2") ((mouse-3 ,(make-posn :col 36 :row 76)) ,(make-posn :col 17 :row 67) "\e[M#4*") ((mouse-movement ,(make-posn :col 94 :row 58)) ,(make-posn :col 54 :row 28) "\e[MCI?") ((mouse-movement ,(make-posn :col 97 :row 79)) ,(make-posn :col 46 :row 69) "\e[MCT+") ((mouse-movement ,(make-posn :col 34 :row 76)) ,(make-posn :col 28 :row 29) "\e[MC'P"))) ;; Mouse mode disabled. (output "\e[?1003l") (should (eq mouse-mode nil)) (check `(((mouse-1 ,(make-posn)) nil "") ((mouse-2 ,(make-posn)) ,(make-posn) "") ((mouse-3 ,(make-posn :col 8 :row 3)) ,(make-posn :col 1 :row 1) "") ((mouse-4 ,(make-posn :col 13 :row 4)) ,(make-posn :col 5 :row 1) "") ((mouse-movement ,(make-posn :col 90 :row 40)) ,(make-posn :col 82 :row 33) ""))) ;; SGR mouse event encoding. (output "\e[?1006h") ;; Mouse mode disabled. (check `(((mouse-1 ,(make-posn)) nil "") ((mouse-2 ,(make-posn)) ,(make-posn) "") ((mouse-3 ,(make-posn :col 8 :row 3)) ,(make-posn :col 1 :row 1) "") ((mouse-4 ,(make-posn :col 13 :row 4)) ,(make-posn :col 5 :row 1) "") ((mouse-movement ,(make-posn :col 90 :row 40)) ,(make-posn :col 82 :row 33) ""))) ;; X10 mouse mode. (output "\e[?9h") (should (eq mouse-mode :click)) (check `(((mouse-1 ,(make-posn)) nil "\e[<0;1;1M") ((mouse-2 ,(make-posn)) ,(make-posn) "\e[<1;1;1M") ((mouse-3 ,(make-posn :col 10 :row 5)) ,(make-posn :col 6 :row 3) "\e[<2;5;3M") ((mouse-1 ,(make-posn :col 95 :row 5)) nil "\e[<0;96;6M") ;; Position not inside terminal. ((mouse-2 ,(make-posn :col 94 :row 5)) ,(make-posn :col 96 :row 5) "") ((mouse-2 ,(make-posn :col 95 :row 4)) ,(make-posn :col 96 :row 8) "") ((mouse-4 ,(make-posn :col 24 :row 9)) ,(make-posn :col 8 :row 4) "") ((mouse-movement ,(make-posn :col 30 :row 45)) ,(make-posn :col 28 :row 42) ""))) ;; Normal mouse mode. (output "\e[?1000h") (should (eq mouse-mode :modifier-click)) (check `(((down-mouse-1 ,(make-posn)) nil "\e[<0;1;1M") ((mouse-1 ,(make-posn)) nil "\e[<0;1;1m") ((down-mouse-2 ,(make-posn)) ,(make-posn) "\e[<1;1;1M") ((mouse-2 ,(make-posn)) ,(make-posn) "\e[<1;1;1m") ((down-mouse-3 ,(make-posn :col 10 :row 5)) ,(make-posn) "\e[<2;11;6M") ;; Note that, although the mouse position has changed, it's ;; still unmoved relative to the reference position, so the ;; mouse position inputted should be same. ((drag-mouse-3 ,(make-posn :col 10 :row 5) ,(make-posn :col 20 :row 50)) ,(make-posn :col 10 :row 45) "\e[<2;11;6m") ((mouse-4 ,(make-posn :col 33 :row 10)) ,(make-posn :col 30 :row 5) "\e[<64;4;6M") ((C-mouse-5 ,(make-posn :col 4250 :row 3145)) ,(make-posn :col 4242 :row 3141) "\e[<81;9;5M") ((M-mouse-6 ,(make-posn :col 425 :row 314)) ,(make-posn :col 424 :row 314) "\e[<74;2;1M") ((S-mouse-7 ,(make-posn :col 85 :row 20)) ,(make-posn :col 32 :row 16) "\e[<71;54;5M") ((mouse-8 ,(make-posn :col 91 :row 92)) ,(make-posn :col 75 :row 18) "\e[<128;17;75M") ((mouse-9 ,(make-posn :col 71 :row 81)) ,(make-posn :col 37 :row 72) "\e[<129;35;10M") ((mouse-10 ,(make-posn :col 38 :row 92)) ,(make-posn :col 29 :row 90) "\e[<130;10;3M") ((mouse-11 ,(make-posn :col 29 :row 14)) ,(make-posn :col 28 :row 8) "\e[<131;2;7M") ((C-M-down-mouse-1 ,(make-posn :col 58 :row 32)) ,(make-posn :col 23 :row 31) "\e[<24;36;2M") ((C-M-mouse-1 ,(make-posn :col 58 :row 32)) ,(make-posn :col 23 :row 31) "\e[<24;36;2m") ((C-S-down-mouse-2 ,(make-posn :col 40 :row 39)) ,(make-posn :col 33 :row 13) "\e[<21;8;27M") ;; Modifier changed! ((M-S-mouse-2 ,(make-posn :col 40 :row 39)) ,(make-posn :col 33 :row 13) "\e[<13;8;27m") ((C-M-S-down-mouse-3 ,(make-posn :col 48 :row 29)) ,(make-posn :col 39 :row 23) "\e[<30;10;7M") ;; Everything changed! ((mouse-3 ,(make-posn :col 92 :row 83)) ,(make-posn :col 10 :row 8) "\e[<2;83;76m") ((mouse-movement ,(make-posn :col 49 :row 85)) ,(make-posn :col 45 :row 82) ""))) ;; Button event mouse mode. (output "\e[?1002h") (should (eq mouse-mode :drag)) (check `(((mouse-movement ,(make-posn :col 84 :row 10)) ,(make-posn :col 48 :row 8) "") ((down-mouse-1 ,(make-posn :col 44 :row 88)) ,(make-posn :col 44 :row 88) "\e[<0;1;1M") ((mouse-movement ,(make-posn :col 48 :row 1)) ,(make-posn :col 19 :row 0) "\e[<32;30;2M") ((down-mouse-2 ,(make-posn :col 29 :row 21)) ,(make-posn :col 18 :row 8) "\e[<1;12;14M") ((mouse-movement ,(make-posn :col 93 :row 54)) ,(make-posn :col 29 :row 38) "\e[<32;65;17M") ((down-mouse-3 ,(make-posn :col 92 :row 63)) ,(make-posn :col 32 :row 38) "\e[<2;61;26M") ((mouse-movement ,(make-posn :col 8 :row 92)) ,(make-posn :col 3 :row 34) "\e[<32;6;59M") ((mouse-1 ,(make-posn :col 93 :row 21)) ,(make-posn :col 0 :row 0) "\e[<0;94;22m") ((mouse-movement ,(make-posn :col 29 :row 74)) ,(make-posn :col 7 :row 64) "\e[<33;23;11M") ((mouse-2 ,(make-posn :col 28 :row 92)) ,(make-posn :col 23 :row 29) "\e[<1;6;64m") ((mouse-movement ,(make-posn :col 75 :row 36)) ,(make-posn :col 75 :row 19) "\e[<34;1;18M") ((mouse-3 ,(make-posn :col 36 :row 76)) ,(make-posn :col 17 :row 67) "\e[<2;20;10m") ((mouse-movement ,(make-posn :col 94 :row 58)) ,(make-posn :col 54 :row 28) ""))) ;; All event mouse mode. (output "\e[?1003h") (should (eq mouse-mode :all)) (check `(((mouse-movement ,(make-posn :col 28 :row 38)) ,(make-posn :col 4 :row 2) "\e[<35;25;37M") ((mouse-movement ,(make-posn :col 49 :row 85)) ,(make-posn :col 45 :row 82) "\e[<35;5;4M") ((mouse-movement ,(make-posn :col 84 :row 10)) ,(make-posn :col 48 :row 8) "\e[<35;37;3M") ((down-mouse-1 ,(make-posn :col 44 :row 88)) ,(make-posn :col 44 :row 88) "\e[<0;1;1M") ((mouse-movement ,(make-posn :col 48 :row 1)) ,(make-posn :col 19 :row 0) "\e[<32;30;2M") ((down-mouse-2 ,(make-posn :col 29 :row 21)) ,(make-posn :col 18 :row 8) "\e[<1;12;14M") ((mouse-movement ,(make-posn :col 93 :row 54)) ,(make-posn :col 29 :row 38) "\e[<32;65;17M") ((down-mouse-3 ,(make-posn :col 92 :row 63)) ,(make-posn :col 32 :row 38) "\e[<2;61;26M") ((mouse-movement ,(make-posn :col 8 :row 92)) ,(make-posn :col 3 :row 34) "\e[<32;6;59M") ((mouse-1 ,(make-posn :col 93 :row 21)) ,(make-posn :col 0 :row 0) "\e[<0;94;22m") ((mouse-movement ,(make-posn :col 29 :row 74)) ,(make-posn :col 7 :row 64) "\e[<33;23;11M") ((mouse-2 ,(make-posn :col 28 :row 92)) ,(make-posn :col 23 :row 29) "\e[<1;6;64m") ((mouse-movement ,(make-posn :col 75 :row 36)) ,(make-posn :col 75 :row 19) "\e[<34;1;18M") ((mouse-3 ,(make-posn :col 36 :row 76)) ,(make-posn :col 17 :row 67) "\e[<2;20;10m") ((mouse-movement ,(make-posn :col 94 :row 58)) ,(make-posn :col 54 :row 28) "\e[<35;41;31M") ((mouse-movement ,(make-posn :col 97 :row 79)) ,(make-posn :col 46 :row 69) "\e[<35;52;11M") ((mouse-movement ,(make-posn :col 34 :row 76)) ,(make-posn :col 28 :row 29) "\e[<35;7;48M"))) ;; Default mouse encoding. (output "\e[?1006l") (check `(((mouse-movement ,(make-posn :col 28 :row 38)) ,(make-posn :col 4 :row 2) "\e[MC9E") ((mouse-movement ,(make-posn :col 49 :row 85)) ,(make-posn :col 45 :row 82) "\e[MC%$") ((mouse-movement ,(make-posn :col 84 :row 10)) ,(make-posn :col 48 :row 8) "\e[MCE#") ((down-mouse-1 ,(make-posn :col 44 :row 88)) ,(make-posn :col 44 :row 88) "\e[M !!") ((mouse-movement ,(make-posn :col 48 :row 1)) ,(make-posn :col 19 :row 0) "\e[M@>\"") ((down-mouse-2 ,(make-posn :col 29 :row 21)) ,(make-posn :col 18 :row 8) "\e[M!,.") ((mouse-movement ,(make-posn :col 93 :row 54)) ,(make-posn :col 29 :row 38) "\e[M@a1") ((down-mouse-3 ,(make-posn :col 92 :row 63)) ,(make-posn :col 32 :row 38) "\e[M\"]:") ((mouse-movement ,(make-posn :col 8 :row 92)) ,(make-posn :col 3 :row 34) "\e[M@&[") ((mouse-1 ,(make-posn :col 93 :row 21)) ,(make-posn :col 0 :row 0) "\e[M#~6") ((mouse-movement ,(make-posn :col 29 :row 74)) ,(make-posn :col 7 :row 64) "\e[MA7+") ((mouse-2 ,(make-posn :col 28 :row 92)) ,(make-posn :col 23 :row 29) "\e[M#&`") ((mouse-movement ,(make-posn :col 75 :row 36)) ,(make-posn :col 75 :row 19) "\e[MB!2") ((mouse-3 ,(make-posn :col 36 :row 76)) ,(make-posn :col 17 :row 67) "\e[M#4*") ((mouse-movement ,(make-posn :col 94 :row 58)) ,(make-posn :col 54 :row 28) "\e[MCI?") ((mouse-movement ,(make-posn :col 97 :row 79)) ,(make-posn :col 46 :row 69) "\e[MCT+") ((mouse-movement ,(make-posn :col 34 :row 76)) ,(make-posn :col 28 :row 29) "\e[MC'P"))) ;; Mouse mode disabled. ;; Disabling any mouse mode, either enabled or not, should ;; disable mouse. (output "\e[?9l") (should (eq mouse-mode nil)) (check `(((mouse-1 ,(make-posn)) nil "") ((mouse-2 ,(make-posn)) ,(make-posn) "") ((mouse-3 ,(make-posn :col 8 :row 3)) ,(make-posn :col 1 :row 1) "") ((mouse-4 ,(make-posn :col 13 :row 4)) ,(make-posn :col 5 :row 1) "") ((mouse-movement ,(make-posn :col 90 :row 40)) ,(make-posn :col 82 :row 33) ""))))))) (provide 'eat-tests) ;;; eat-tests.el ends here emacs-eat/eat.el000066400000000000000000012162351453707721100140310ustar00rootroot00000000000000;;; eat.el --- Emulate A Terminal, in a region, in a buffer and in Eshell -*- lexical-binding: t; -*- ;; Copyright (C) 2022, 2023 Akib Azmain Turja. ;; Author: Akib Azmain Turja ;; Created: 2022-08-15 ;; Version: 0.9.4 ;; Package-Requires: ((emacs "26.1") (compat "29.1")) ;; Keywords: terminals processes ;; Homepage: https://codeberg.org/akib/emacs-eat ;; This file is not part of GNU Emacs. ;; This file 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. ;; 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. ;; For a full copy of the GNU General Public License ;; see . ;;; Commentary: ;; Eat's name self-explanatory, it stands for "Emulate A Terminal". ;; Eat is a terminal emulator. It can run most (if not all) ;; full-screen terminal programs, including Emacs. ;; It is pretty fast, more than three times faster than Term, despite ;; being implemented entirely in Emacs Lisp. So fast that you can ;; comfortably run Emacs inside Eat, or even use your Emacs as a ;; terminal multiplexer. ;; It has many feature that other Emacs terminal emulator still don't ;; have, for example complete mouse support. ;; It flickers less than other Emacs terminal emulator, so you get ;; more performance and a smooth experience. ;; To start Eat, run M-x eat. Eat has three keybinding modes: ;; * "semi-char" mode: This is the default keybinding mode. Most ;; keys are bound to send the key to the terminal, except the ;; following keys: `C-\', `C-c', `C-x', `C-g', `C-h', `C-M-c', ;; `C-u', `M-x', `M-:', `M-!', `M-&' and some other keys (see the ;; user option `eat-semi-char-non-bound-keys' for the complete ;; list). The following special keybinding are available: ;; * `C-q': Send next key to the terminal. ;; * `C-y': Like `yank', but send the text to the terminal. ;; * `M-y': Like `yank-pop', but send the text to the terminal. ;; * `C-c' `C-k': Kill process. ;; * `C-c' `C-e': Switch to "emacs" keybinding mode. ;; * `C-c' `M-d': Switch to "char" keybinding mode. ;; * "emacs" mode: No special keybinding, except the following: ;; * `C-c' `C-j': Switch to "semi-char" keybinding mode. ;; * `C-c' `M-d': Switch to "char" keybinding mode. ;; * `C-c' `C-k': Kill process. ;; * "char" mode: All supported keys are bound to send the key to ;; the terminal, except `C-M-m' or `M-RET', which is bound to ;; switch to "semi-char" keybinding mode. ;; If you like Eshell, then there is a good news for you. Eat ;; integrates with Eshell. Eat has two global minor modes for Eshell: ;; * `eat-eshell-visual-command-mode': Run visual commands with Eat ;; instead of Term. ;; * `eat-eshell-mode': Run Eat inside Eshell. After enabling this, ;; you can run full-screen terminal programs directly in Eshell. ;; You have three keybinding modes here too, except that `C-c' ;; `C-k' is not special (i.e. not bound by Eat) in "emacs" mode ;; and "line" mode. ;;; Code: (require 'compat) (require 'subr-x) (require 'cl-lib) (require 'ansi-color) (require 'color) (require 'shell) (require 'term) (require 'url) ;; Needed by `eat-reload'. (defvar eat--being-loaded nil "Non-nil means Eat is being loaded.") (setq eat--being-loaded t) ;;;; User Options. (defgroup eat nil "Emulate A Terminal." :group 'processes :group 'terminals :link '(url-link "https://codeberg.org/akib/emacs-eat")) (defgroup eat-term nil "Eat terminal emulator." :group 'eat) (defgroup eat-ui nil "Eat user interface." :group 'eat) (defgroup eat-eshell nil "Eat Eshell integration." :group 'eat) (defcustom eat-buffer-name "*eat*" "The basename used for Eat buffers. This is the default name used when running Eat." :type 'string :group 'eat-ui) (defcustom eat-kill-buffer-on-exit nil "Non-nil means automatically kill Eat buffer when process exits." :type 'boolean :group 'eat-ui) (defcustom eat-term-scrollback-size 131072 ; 128 K "Size of scrollback area in characters. nil means unlimited." :type '(choice natnum (const nil)) :group 'eat-term :group 'eat-ui) (defcustom eat-enable-kill-from-terminal t "Non-nil means allow terminal program to add text to `kill-ring'. When non-nil, terminal program can send special escape sequence to add some text to `kill-ring'." :type 'boolean :group 'eat-ui :group 'eat-eshell) (defcustom eat-enable-yank-to-terminal nil "Non-nil means allow terminal program to get text from `kill-ring'. When non-nil, terminal program can get killed text from `kill-ring'. This is left disabled for security reasons." :type 'boolean :group 'eat-ui :group 'eat-eshell) (defcustom eat-query-before-killing-running-terminal 'auto "Whether to query before killing running terminal. If the value is t, always query. If the value is nil, never query. If the value is `auto', query if a shell command is running (shell integration needs to be enabled to use this properly)." :type '(choice (const :tag "Yes" t) (const :tag "No" nil) (const :tag "If a shell command is running" auto)) :group 'eat-ui) (defcustom eat-eshell-fallback-if-stty-not-available 'ask "What to do if `stty' is unavailable. `stty' is a dependency to setup terminal. If `stty' is unavailable, Eat won't be able to setup terminal, so any input won't be visible. The value should be any of the following: nil Do nothing. t Fallback to plain Eshell if `stty' is not available. `ask' Ask what to do. FUNCTION Call FUNCTION with the command and arguments (using `apply') and fallback to plain Eshell if it returns nil." :type '(radio (const :tag "Do nothing" nil) (const :tag "Fallback to plain Eshell" t) (const :tag "Ask interactively" ask) (function :tag "Function")) :group 'eat-eshell) (defcustom eat-sixel-scale 1.0 "Scale Sixel images by this amount." :type 'number :group 'eat-ui :group 'eat-eshell) (defcustom eat-sixel-aspect-ratio 1.0 "Aspect ratio of Sixel images. The value is a positive number specifying the ratio of the width and height of a Sixel pixel. For example, the value of 1.5 means the aspect ratio of 3:2." :type 'number :group 'eat-ui :group 'eat-eshell) (defcustom eat-sixel-render-formats '(xpm svg half-block background none) "List of formats to render Sixel, in order of preference." :type '(repeat (choice (const :tag "XPM Image" xpm) (const :tag "SVG Image" svg) (const :tag "UTF-8 half block" half-block) (const :tag "Background color" background) (const :tag "None" none))) :group 'eat-ui :group 'eat-eshell) (defcustom eat-line-input-ring-size 1000 "Number of input history items to keep." :type 'natnum :group 'eat-ui) (defcustom eat-line-auto-move-to-input t "Non-nil means move to input line when inserting characters." :type 'boolean :group 'eat-ui) (defcustom eat-line-move-point-for-matching-input 'after-input "Controls where to place point after matching input. \\This influences the commands \ \\[eat-line-previous-matching-input-from-input] and \ \\[eat-line-next-matching-input-from-input]. If `after-input', point will be positioned after the input typed by the user, but before the rest of the history entry that has been inserted. If `end-of-line', point will be positioned at the end of the current logical (not visual) line after insertion." :type '(radio (const :tag "Stay after input" after-input) (const :tag "Move to end of line" end-of-line)) :group 'eat-ui) (defcustom eat-line-input-history-isearch nil "Non-nil to Isearch in input history only, not in the terminal. If t, usual Isearch keys like \\[isearch-backward] and \ \\[isearch-backward-regexp] in Eat buffer search in the input history. If `dwim', Isearch keys search in the input history only when initial point position is on input line. When starting Isearch from other parts of the Eat buffer, they search in the Eat buffer. If nil, Isearch operates on the whole Eat buffer." :type '(choice (const :tag "Don't search in input history" nil) (const :tag "When point is on input line initially, \ search history" dwim) (const :tag "Always search in input history" t)) :group 'eat-ui) (defcustom eat-line-input-send-function #'eat-line-send-default "Function to send the shell prompt input. The function is called without any argument. The buffer is narrowed to the input. The function may modify the input but mustn't modify the buffer restrictions. It should call `eat-line-send-default' to send the final output." :type 'function :group 'eat-ui) (defcustom eat-semi-char-non-bound-keys '([?\C-x] [?\C-\\] [?\C-q] [?\C-g] [?\C-h] [?\e ?\C-c] [?\C-u] [?\e ?x] [?\e ?:] [?\e ?!] [?\e ?&] [C-insert] [M-insert] [S-insert] [C-M-insert] [C-S-insert] [M-S-insert] [C-M-S-insert] [C-delete] [M-delete] [S-delete] [C-M-delete] [C-S-delete] [M-S-delete] [C-M-S-delete] [C-deletechar] [M-deletechar] [S-deletechar] [C-M-deletechar] [C-S-deletechar] [M-S-deletechar] [C-M-S-deletechar] [C-up] [C-down] [C-right] [C-left] [M-up] [M-down] [M-right] [M-left] [S-up] [S-down] [S-right] [S-left] [C-M-up] [C-M-down] [C-M-right] [C-M-left] [C-S-up] [C-S-down] [C-S-right] [C-S-left] [M-S-up] [M-S-down] [M-S-right] [M-S-left] [C-M-S-up] [C-M-S-down] [C-M-S-right] [C-M-S-left] [C-home] [M-home] [S-home] [C-M-home] [C-S-home] [M-S-home] [C-M-S-home] [C-end] [M-end] [S-end] [C-M-end] [C-S-end] [M-S-end] [C-M-S-end] [C-prior] [M-prior] [S-prior] [C-M-prior] [C-S-prior] [M-S-prior] [C-M-S-prior] [C-next] [M-next] [S-next] [C-M-next] [C-S-next] [M-S-next] [C-M-S-next]) "List of keys not bound in Eat \"semi-char\" mode. Keys appearing in this list are not bound to send the key to terminal. Eat might still bound them to do something else (for example, changing keybinding mode). Each element is a vector of form [KEY] or [?\\e KEY], meaning KEY or M-KEY shouldn't be bound. KEY shouldn't contain meta (Alt) modifier. When changing this from Lisp, make sure to call `eat-update-semi-char-mode-map' to update the keymap and reload Eat to make the changes effective." :type '(repeat sexp) :set (lambda (sym val) (set-default-toplevel-value sym val) (when (and (not eat--being-loaded) (boundp 'eat-semi-char-mode-map)) (eat-update-semi-char-mode-map) (let ((after-load-alist nil) (after-load-functions nil)) (eat-reload)))) :group 'eat-ui) (defcustom eat-eshell-semi-char-non-bound-keys '([?\C-\\] [?\C-x] [?\C-g] [?\C-h] [?\e ?\C-c] [?\C-u] [?\C-q] [?\e ?x] [?\e ?:] [?\e ?!] [?\e ?&] [C-insert] [M-insert] [S-insert] [C-M-insert] [C-S-insert] [M-S-insert] [C-M-S-insert] [C-delete] [M-delete] [S-delete] [C-M-delete] [C-S-delete] [M-S-delete] [C-M-S-delete] [C-deletechar] [M-deletechar] [S-deletechar] [C-M-deletechar] [C-S-deletechar] [M-S-deletechar] [C-M-S-deletechar] [C-up] [C-down] [C-right] [C-left] [M-up] [M-down] [M-right] [M-left] [S-up] [S-down] [S-right] [S-left] [C-M-up] [C-M-down] [C-M-right] [C-M-left] [C-S-up] [C-S-down] [C-S-right] [C-S-left] [M-S-up] [M-S-down] [M-S-right] [M-S-left] [C-M-S-up] [C-M-S-down] [C-M-S-right] [C-M-S-left] [C-home] [M-home] [S-home] [C-M-home] [C-S-home] [M-S-home] [C-M-S-home] [C-end] [M-end] [S-end] [C-M-end] [C-S-end] [M-S-end] [C-M-S-end] [C-prior] [M-prior] [S-prior] [C-M-prior] [C-S-prior] [M-S-prior] [C-M-S-prior] [C-next] [M-next] [S-next] [C-M-next] [C-S-next] [M-S-next] [C-M-S-next]) "List of keys not bound in Eat-Eshell \"semi-char\" mode. Keys appearing in this list are not bound to send the key to terminal. Eat might still bound them to do something else (for example, changing keybinding mode). Each element is a vector of form [KEY] or [?\\e KEY], meaning KEY or M-KEY shouldn't be bound. KEY shouldn't contain meta (Alt) modifier. When changing this from Lisp, make sure to call `eat-eshell-update-semi-char-mode-map' to update the keymap and reload Eat to make the changes effective." :type '(repeat sexp) :set (lambda (sym val) (set-default-toplevel-value sym val) (when (and (not eat--being-loaded) (boundp 'eat-eshell-semi-char-mode-map)) (eat-eshell-update-semi-char-mode-map) (let ((after-load-alist nil) (after-load-functions nil)) (eat-reload)))) :group 'eat-eshell) (defcustom eat-enable-directory-tracking t "Non-nil means do directory tracking. When non-nil, Eat will track the working directory of program. You need to configure the program to send current working directory information. See Info node `(eat)Directory Tracking' for instructions to setup your shell." :type 'boolean :group 'eat-ui :group 'eat-eshell) (defcustom eat-enable-shell-command-history t "Non-nil means add shell commands to Emacs history. When non-nil, any command you run in your shell will also appear in the history of commands like `eat', `shell-command' and `async-shell-command'." :type 'boolean :group 'eat-ui :group 'eat-eshell) (defcustom eat-message-handler-alist nil "Alist of message handler name and its handler function. The keys are the names of message handlers, and the values are their respective handler functions. Shells can send Eat messages, as defined in this user option. If an appropiate message handler is defined, it's called with the other arguments, otherwise it's ignored." :type '(alist :key-type string :value-type function) :group 'eat-ui :group 'eat-eshell) (defcustom eat-enable-auto-line-mode nil "Non-nil means switch to line mode automatically on shell prompt." :type 'boolean :group 'eat-ui) (defcustom eat-enable-shell-prompt-annotation t "Non-nil means annotate shell prompt with the status of command. When non-nil, display a mark in front of shell prompt describing the status of the command executed in that prompt." :type 'boolean :group 'eat-ui) (defcustom eat-shell-prompt-annotation-position 'left-margin "The position where to display shell prompt annotation. The value can be one of the following: `left-margin' Use the left margin. `right-margin' Use the right margin." :type '(choice (const :tag "Left margin" left-margin) (const :tag "Right margin" right-margin)) :group 'eat-ui) (defcustom eat-shell-prompt-annotation-running-margin-indicator "-" "String in margin annotation to indicate the command is running." :type 'string :group 'eat-ui) (defface eat-shell-prompt-annotation-running '((t :inherit compilation-info)) "Face used in annotation to indicate the command is running." :group 'eat-ui) (defcustom eat-shell-prompt-annotation-success-margin-indicator "0" "String in margin annotation to indicate the command has succeeded." :type 'string :group 'eat-ui) (defface eat-shell-prompt-annotation-success '((t :inherit success)) "Face used in annotation to indicate the command has succeeded." :group 'eat-ui) (defcustom eat-shell-prompt-annotation-failure-margin-indicator "X" "String in margin annotation to indicate the command has failed." :type 'string :group 'eat-ui) (defface eat-shell-prompt-annotation-failure '((t :inherit error)) "Face used in annotation to indicate the command has failed." :group 'eat-ui) (defcustom eat-shell-prompt-annotation-correction-delay 0.1 "Seconds to wait before correcting shell prompt annotations. Wait this many second after terminal update before correcting shell prompt annotation." :type 'number :group 'eat-ui) (defcustom eat-exec-hook nil "Hook run after `eat' executes a commamnd. The hook is run with the process run in the terminal as the only argument." :type 'hook :group 'eat-ui) (defcustom eat-update-hook nil "Hook run after the terminal in a Eat buffer is updated." :type 'hook :group 'eat-ui) (defcustom eat-exit-hook nil "Hook run after the command executed by `eat' exits. The hook is run with the process that just exited as the only argument." :type 'hook :group 'eat-ui) (defcustom eat-eshell-exec-hook nil "Hook run after a terminal is created in Eshell." :type 'hook :group 'eat-eshell) (defcustom eat-eshell-update-hook nil "Hook run after the terminal in a Eshell buffer is updated." :type 'hook :group 'eat-eshell) (defcustom eat-eshell-exit-hook nil "Hook run after the terminal in Eshell is deleted." :type 'hook :group 'eat-eshell) (defconst eat--cursor-type-value-type (let ((cur-type '(choice (const :tag "Frame default" t) (const :tag "Filled box" box) (cons :tag "Box with specified size" (const box) integer) (const :tag "Hollow cursor" hollow) (const :tag "Vertical bar" bar) (cons :tag "Vertical bar with specified height" (const bar) integer) (const :tag "Horizontal bar" hbar) (cons :tag "Horizontal bar with specified width" (const hbar) integer) (const :tag "None " nil)))) `(list ,cur-type (choice (const :tag "No blinking" nil) (number :tag "Blinking frequency")) ,cur-type)) "Custom type specification for Eat's cursor type variables.") (defcustom eat-invisible-cursor-type '(nil nil nil) "Type of cursor to use as invisible cursor in Eat buffer. The value is a list of form (CURSOR-ON BLINKING-FREQUENCY CURSOR-OFF). When the cursor is on, CURSOR-ON is used as `cursor-type', which see. BLINKING-FREQUENCY is the blinking frequency of cursor's blinking. When the cursor is off, CURSOR-OFF is used as `cursor-type'. This should be nil when cursor is not blinking." :type eat--cursor-type-value-type :group 'eat-ui :group 'eat-eshell) (defcustom eat-default-cursor-type `(,(default-value 'cursor-type) nil nil) "Cursor to use in Eat buffer. The value is a list of form (CURSOR-ON BLINKING-FREQUENCY CURSOR-OFF). When the cursor is on, CURSOR-ON is used as `cursor-type', which see. BLINKING-FREQUENCY is the blinking frequency of cursor's blinking. When the cursor is off, CURSOR-OFF is used as `cursor-type'. This should be nil when cursor is not blinking." :type eat--cursor-type-value-type :group 'eat-ui :group 'eat-eshell) (defcustom eat-very-visible-cursor-type `(,(default-value 'cursor-type) 2 hollow) "Very visible cursor to use in Eat buffer. The value is a list of form (CURSOR-ON BLINKING-FREQUENCY CURSOR-OFF). When the cursor is on, CURSOR-ON is used as `cursor-type', which see. BLINKING-FREQUENCY is the blinking frequency of cursor's blinking. When the cursor is off, CURSOR-OFF is used as `cursor-type'. This should be nil when cursor is not blinking." :type eat--cursor-type-value-type :group 'eat-ui :group 'eat-eshell) (defcustom eat-vertical-bar-cursor-type '(bar nil nil) "Vertical bar cursor to use in Eat buffer. The value is a list of form (CURSOR-ON BLINKING-FREQUENCY CURSOR-OFF). When the cursor is on, CURSOR-ON is used as `cursor-type', which see. BLINKING-FREQUENCY is the blinking frequency of cursor's blinking. When the cursor is off, CURSOR-OFF is used as `cursor-type'. This should be nil when cursor is not blinking." :type eat--cursor-type-value-type :group 'eat-ui :group 'eat-eshell) (defcustom eat-very-visible-vertical-bar-cursor-type '(bar 2 nil) "Very visible vertical bar cursor to use in Eat buffer. The value is a list of form (CURSOR-ON BLINKING-FREQUENCY CURSOR-OFF). When the cursor is on, CURSOR-ON is used as `cursor-type', which see. BLINKING-FREQUENCY is the blinking frequency of cursor's blinking. When the cursor is off, CURSOR-OFF is used as `cursor-type'. This should be nil when cursor is not blinking." :type eat--cursor-type-value-type :group 'eat-ui :group 'eat-eshell) (defcustom eat-horizontal-bar-cursor-type '(hbar nil nil) "Horizontal bar cursor to use in Eat buffer. The value is a list of form (CURSOR-ON BLINKING-FREQUENCY CURSOR-OFF). When the cursor is on, CURSOR-ON is used as `cursor-type', which see. BLINKING-FREQUENCY is the blinking frequency of cursor's blinking. When the cursor is off, CURSOR-OFF is used as `cursor-type'. This should be nil when cursor is not blinking." :type eat--cursor-type-value-type :group 'eat-ui :group 'eat-eshell) (defcustom eat-very-visible-horizontal-bar-cursor-type '(hbar 2 nil) "Very visible horizontal bar cursor to use in Eat buffer. The value is a list of form (CURSOR-ON BLINKING-FREQUENCY CURSOR-OFF). When the cursor is on, CURSOR-ON is used as `cursor-type', which see. BLINKING-FREQUENCY is the blinking frequency of cursor's blinking. When the cursor is off, CURSOR-OFF is used as `cursor-type'. This should be nil when cursor is not blinking." :type eat--cursor-type-value-type :group 'eat-ui :group 'eat-eshell) (defcustom eat-minimum-latency 0.008 "Minimum display latency in seconds. Lowering it too much may cause (or increase) flickering and decrease performance due to too many redisplays. Increasing it too much will cause the terminal to feel less responsive. Try to increase this value if the terminal flickers." :type 'number :group 'eat-ui :group 'eat-eshell) (defcustom eat-maximum-latency 0.033 "Minimum display latency in seconds. Increasing it too much may make the terminal feel less responsive in case of huge burst of output. Try to increase this value if the terminal flickers. Try to lower the value if the terminal feels less responsive." :type 'number :group 'eat-ui :group 'eat-eshell) (defcustom eat-term-name #'eat-term-get-suitable-term-name "Value for the `TERM' environment variable. The value can also be a function. In that case, the function is called without any argument and the return value is used as the value. For example, this can set to `eat-term-get-suitable-term-name' to set the value according to the number of colors supported by the current display. This value is used by terminal programs to identify the terminal." :type '(choice (string :tag "Value") (const :tag "Automatic" eat-term-get-suitable-term-name) (function :tag "Function")) :group 'eat-term) ;; Upgrading Eat causes `eat-term-terminfo-directory' and ;; `eat-term-shell-integration-directory' to be outdated, so update it ;; if not modified by user (or something else). (defvar eat--load-file-path nil "Path to currently loaded Eat.") (defvar eat--install-path nil "Path to directory where Eat is installed.") (defvar eat--terminfo-path nil "Path to directory where Terminfo databases are installed.") (defvar eat--shell-integration-path nil "Path to directory where shell integration scripts are installed.") (setq eat--load-file-path (or load-file-name buffer-file-name)) (setq eat--install-path (copy-sequence (file-name-directory eat--load-file-path))) (defvar eat-term-terminfo-directory) (defvar eat-term-shell-integration-directory) (let ((old-terminfo-path eat--terminfo-path) (old-shell-integration-path eat--shell-integration-path)) (setq eat--terminfo-path (expand-file-name "terminfo" eat--install-path)) (setq eat--shell-integration-path (expand-file-name "integration" eat--install-path)) (defcustom eat-term-terminfo-directory eat--terminfo-path "Directory where required terminfo databases can be found. This value is used by terminal programs to find the terminfo databases that describe the capabilities of the terminal." :type 'directory :group 'eat-term) (defcustom eat-term-shell-integration-directory eat--shell-integration-path "Directory where Eat shell integration scripts can be found. This value is exposed to terminal programs as `EAT_SHELL_INTEGRATION_DIR' environment variable." :type 'directory :group 'eat-ui :group 'eat-eshell) (when (eq eat-term-terminfo-directory old-terminfo-path) (setq eat-term-terminfo-directory eat--terminfo-path)) (when (eq eat-term-shell-integration-directory old-shell-integration-path) (setq eat-term-shell-integration-directory eat--shell-integration-path))) (defcustom eat-term-inside-emacs (format "%s,eat" emacs-version) "Value for the `INSIDE_EMACS' environment variable." :type 'string :group 'eat-term) (defcustom eat-enable-blinking-text nil "Non-nil means enable blinking of text with blink attribute. When non-nil, enable `eat-blink-mode' to enable blinking of text with blink attribute by default. You manually toggle `eat-blink-mode' to toggle this behavior buffer-locally." :type 'boolean :group 'eat-ui :group 'eat-eshell) (defcustom eat-slow-blink-frequency 2 "Frequency of blinking of slowly text. This has an effect only if `eat-blink-mode' is enabled." :type 'number :group 'eat-ui) (defcustom eat-fast-blink-frequency 3 "Frequency of blinking of rapidly text. This has an effect only if `eat-blink-mode' is enabled." :type 'number :group 'eat-ui) (defcustom eat-enable-alternative-display t "Non-nil means enable alternative display. Full screen programs often use alternative display to keep old contents on display unaltered." :type 'boolean :group 'eat-term) (make-obsolete-variable 'eat-enable-alternative-display "don't use it." "0.9" 'set) (defcustom eat-enable-mouse t "Non-nil means enable mouse support. When non-nil, terminal programs can receive mouse events from Emacs." :type 'boolean :group 'eat-ui) (defcustom eat-input-chunk-size 1024 "Maximum size of chunk of data send at once. Long inputs send to Eat processes are broken up into chunks of this size. If your process is choking on big inputs, try lowering the value." :type 'integer :group 'eat-ui) (defface eat-term-bold '((t :inherit bold)) "Face used to render bold text." :group 'eat-term) (defface eat-term-faint '((t :weight light)) "Face used to render faint text." :group 'eat-term) (defface eat-term-italic '((t :inherit italic)) "Face used to render italic text." :group 'eat-term) (defface eat-term-slow-blink '((t :inverse-video t)) "Face used to render slowly blinking text." :group 'eat-term) (defface eat-term-fast-blink '((t :inverse-video t)) "Face used to render rapidly blinking text." :group 'eat-term) ;; Define color faces. (let ((face-counter 0)) (let ((colors '("black" "red" "green" "yellow" "blue" "magenta" "cyan" "white"))) ;; Basic colors. (dolist (color colors) (let ((face (intern (format "eat-term-color-%i" face-counter)))) (custom-declare-face face `((t :inherit ,(intern (format (if (eval-when-compile (>= emacs-major-version 28)) "ansi-color-%s" "term-color-%s") color)))) (format "Face used to render %s color text." color) :group 'eat-term) (put (intern (format "eat-term-color-%s" color)) 'face-alias face)) (cl-incf face-counter)) ;; Bright colors. (dolist (color colors) (let ((face (intern (format "eat-term-color-%i" face-counter)))) (custom-declare-face face `((t :inherit ,(intern (format (if (eval-when-compile (>= emacs-major-version 28)) "ansi-color-bright-%s" "term-color-%s") color)))) (format "Face used to render bright %s color text." color) :group 'eat-term) (put (intern (format "eat-term-color-bright-%s" color)) 'face-alias face)) (cl-incf face-counter))) ;; 256-colors. (while (< face-counter 256) (let ((color (if (>= face-counter 232) (format "#%06X" (* #x010101 (+ 8 (* 10 (- face-counter 232))))) (let ((col (- face-counter 16)) (res 0) (frac (* 6 6))) (while (<= 1 frac) (setq res (* res #x000100)) (let ((color-num (mod (/ col frac) 6))) (unless (zerop color-num) (setq res (+ res #x37 (* #x28 color-num))))) (setq frac (/ frac 6))) (format "#%06X" res))))) (custom-declare-face (intern (format "eat-term-color-%i" face-counter)) `((t :foreground ,color :background ,color)) (format "Face used to render text with %i%s color of 256 color\ palette." face-counter (or (and (not (<= 11 (% face-counter 100) 13)) (nth (% face-counter 10) '(nil "st" "nd" "rd"))) "th")) :group 'eat-term)) (cl-incf face-counter))) (defface eat-term-font-0 '((t)) "Default font." :group 'eat-term) (put 'eat-term-font-default 'face-alias 'eat-term-font-0) ;; Font faces, 1 to 9 (inclusive). (cl-loop for counter from 1 to 9 do (custom-declare-face (intern (format "eat-term-font-%i" counter)) '((t)) (format "Alternative font %i." counter) :group 'eat-term)) ;;;; Utility Functions. (defun eat--t-goto-bol (&optional n) "Go to the beginning of current line. With optional argument N, go to the beginning of Nth next line if N is positive, otherwise go to the beginning of -Nth previous line. If the specified position is before `point-min' or after `point-max', go to that point. Return the number of lines moved. Treat LINE FEED (?\\n) as the line delimiter." ;; TODO: Comment. (setq n (or n 0)) (cond ((> n 0) (let ((moved 0)) (while (and (< (point) (point-max)) (< moved n)) (and (search-forward "\n" nil 'move) (cl-incf moved))) moved)) ((<= n 0) (let ((moved 1)) (while (and (or (= moved 1) (< (point-min) (point))) (< n moved)) (cl-decf moved) (and (search-backward "\n" nil 'move) (= moved n) (goto-char (match-end 0)))) moved)))) (defun eat--t-goto-eol (&optional n) "Go to the end of current line. With optional argument N, go to the end of Nth next line if N is positive, otherwise go to the end of -Nth previous line. If the specified position is before `point-min' or after `point-max', go to that point. Return the number of lines moved. Treat LINE FEED (?\\n) as the line delimiter." ;; TODO: Comment. (setq n (or n 0)) (cond ((>= n 0) (let ((moved -1)) (while (and (or (= moved -1) (< (point) (point-max))) (< moved n)) (cl-incf moved) (and (search-forward "\n" nil 'move) (= moved n) (goto-char (match-beginning 0)))) moved)) ((< n 0) (let ((moved 0)) (while (and (< (point-min) (point)) (< n moved)) (and (search-backward "\n" nil 'move) (cl-decf moved))) moved)))) (defun eat--t-bol (&optional n) "Return the beginning of current line. With optional argument N, return a cons cell whose car is the beginning of Nth next line and cdr is N, if N is positive, otherwise return a cons cell whose car is the beginning of -Nth previous line and cdr is N. If the specified position is before `point-min' or after `point-max', return a cons cell whose car is that point and cdr is number of lines that point is away from current line. Treat LINE FEED (?\\n) as the line delimiter." ;; Move to the beginning of line, record the point, and return that ;; point and the distance of that point from current line in lines. (save-excursion ;; `let' is neccessary, we need to evaluate (point) after going to ;; `(eat--t-goto-bol N)'. (let ((moved (eat--t-goto-bol n))) (cons (point) moved)))) (defun eat--t-eol (&optional n) "Return the end of current line. With optional argument N, return a cons cell whose car the end of Nth next line and cdr is N, if N is positive, otherwise return a cons cell whose car is the end of -Nth previous line and cdr in N. If the specified position is before `point-min' or after `point-max', return a cons cell whose car is that point and cdr is number of lines that point is away from current line. Treat LINE FEED (?\\n) as the line delimiter." ;; Move to the beginning of line, record the point, and return that ;; point and the distance of that point from current line in lines. (save-excursion ;; `let' is neccessary, we need to evaluate (point) after going to ;; (eat--t-goto-eol N). (let ((moved (eat--t-goto-eol n))) (cons (point) moved)))) (defun eat--t-col-motion (n) "Move to Nth next column. Go to Nth next column if N is positive, otherwise go to -Nth previous column. If the specified position is before `point-min' or after `point-max', go to that point. Return the number of columns moved. Assume all characters occupy a single column." ;; Record the current position. (let ((start-pos (point))) ;; Move to the new position. (cond ((> n 0) (let ((eol (car (eat--t-eol))) (pos (+ (point) n))) (goto-char (min pos eol)))) ((< n 0) (let ((bol (car (eat--t-bol))) (pos (+ (point) n))) (goto-char (max pos bol))))) ;; Return the distance from the previous position. (- (point) start-pos))) (defun eat--t-current-col () "Return the current column. Assume all characters occupy a single column." ;; We assume that that all characters occupy a single column, so a ;; subtraction should work. For multi-column characters, we add ;; extra invisible spaces before the character to make it occupy as ;; many character is its width. (- (point) (car (eat--t-bol)))) (defun eat--t-goto-col (n) "Go to column N. Return the current column after moving point. Assume all characters occupy a single column." ;; Move to column 0. (eat--t-goto-bol) ;; Now the target column is N characters away. (eat--t-col-motion n)) (defun eat--t-repeated-insert (c n &optional face) "Insert character C, N times, using face FACE, if given." (insert (if face (let ((str (make-string n c))) (put-text-property 0 n 'face face str) (put-text-property 0 n 'font-lock-face face str) str) ;; Avoid the `let'. (make-string n c)))) (defun eat--t-join-long-line (&optional limit) "Join long line once, but don't try to go beyond LIMIT. For example: \"*foo\\nbar\\nbaz\" is converted to \"foo*bar\\nbaz\", where `*' indicates point." ;; Are we already at the end a part of a long line? (unless (get-text-property (point) 'eat--t-wrap-line) ;; Find the next end of a part of a long line. (goto-char (or (next-single-property-change (point) 'eat--t-wrap-line nil limit) limit (point-max)))) ;; Remove the newline. (when (< (point) (or limit (point-max))) (1value (cl-assert (1value (= (1value (char-after)) ?\n)))) (delete-char 1))) (defun eat--t-break-long-line (threshold) "Break a line longer than THRESHOLD once. For example: when THRESHOLD is 3, \"*foobarbaz\" is converted to \"foo\\n*barbaz\", where `*' indicates point." (let ((loop t)) ;; Find a too long line. (while (and loop (< (point) (point-max))) ;; Go to the threshold column. (eat--t-goto-col threshold) ;; Are we at the end of line? (if (eq (char-after) ?\n) ;; We are already at the end of line, so move to the next ;; line and start from the beginning. (forward-char) ;; The next character is not a newline, so we must be at a ;; long line, or we are the end of the accessible part of the ;; buffer. Whatever the case, we break the loop, and if it is ;; a long line, we break the line. (setq loop nil) (unless (= (point) (point-max)) (insert-before-markers #("\n" 0 1 (eat--t-wrap-line t)))))))) ;;;; Emulator. (cl-defstruct (eat--t-cur (:constructor eat--t-make-cur) (:copier eat--t-copy-cur)) "Structure describing cursor position." (position nil :documentation "Position of cursor.") (y 1 :documentation "Y coordinate of cursor.") (x 1 :documentation "X coordinate of cursor.") (sixel-x 0 :documentation "X coordinate of Sixel cursor.") (sixel-y 0 :documentation "Y coordinate of Sixel cursor.") (sixel-beg nil :documentation "Cons cell of current sixel line.")) (cl-defstruct (eat--t-disp (:constructor eat--t-make-disp) (:copier eat--t-copy-disp)) "Structure describing the display." (begin nil :documentation "Beginning of visible display.") (width 80 :documentation "Width of display.") (height 24 :documentation "Height of display.") (cursor nil :documentation "Cursor.") (saved-cursor (1value (eat--t-make-cur)) :documentation "Saved cursor.") (old-begin nil :documentation "Beginning of visible display during last Eat redisplay.")) (cl-defstruct (eat--t-face (:constructor eat--t-make-face) (:copier eat--t-copy-face)) "Structure describing the display attributes to use." (face nil :documentation "Face to use.") (fg nil :documentation "Foreground color.") (bg nil :documentation "Background color.") (intensity nil :documentation "Intensity face, or nil.") (italic nil :documentation "Non-nil means use italic face.") (underline nil :documentation "Non-nil means underline text.") (underline-color nil :documentation "Underline color.") (crossed nil :documentation "Non-nil means strike-through text.") (conceal nil :documentation "Non-nil means invisible text.") (inverse nil :documentation "Non-nil means inverse colors.") (blink nil :documentation "Blink face, or nil.") (font 'eat-term-font-0 :documentation "Current font face.")) (cl-defstruct (eat--t-term (:constructor eat--t-make-term) (:copier eat--t-copy-term)) "Structure describing a terminal." (buffer nil :documentation "The buffer of terminal.") (begin nil :documentation "Beginning of terminal.") (end nil :documentation "End of terminal area.") (title "" :documentation "The title of the terminal.") (bell-fn (1value #'ignore) :documentation "Function to ring the bell.") (input-fn (1value #'ignore) :documentation "Function to send input.") (set-cursor-fn (1value #'ignore) :documentation "Function to set cursor.") (manipulate-selection-fn (1value #'ignore) :documentation "Function to manipulate selection.") (grab-mouse-fn (1value #'ignore) :documentation "Function to grab mouse.") (set-focus-ev-mode-fn (1value #'ignore) :documentation "Function to set focus event mode.") (set-title-fn (1value #'ignore) :documentation "Function to set the title.") (set-cwd-fn (1value #'ignore) :documentation "Function to set the current working directory.") (ui-cmd-fn (1value #'ignore) :documentation "Function to handle UI command sequence.") (parser-state nil :documentation "State of parser.") (scroll-begin 1 :documentation "First line of scroll region.") (scroll-end 24 :documentation "Last line of scroll region.") (display nil :documentation "The display.") (main-display nil :documentation "Main display. Nil when not in alternative display mode.") (face (1value (eat--t-make-face)) :documentation "Display attributes.") (auto-margin t :documentation "State of auto margin mode.") (ins-mode nil :documentation "State of insert mode.") (charset (copy-tree '(g0 . ((g0 . us-ascii) (g1 . us-ascii) (g2 . us-ascii) (g3 . us-ascii)))) :documentation "Current character set.") (cur-state :block :documentation "Current state of cursor.") (cur-visible-p t :documentation "Is the cursor visible?") (saved-face (1value (eat--t-make-face)) :documentation "Saved SGR attributes.") (bracketed-yank nil :documentation "State of bracketed yank mode.") (keypad-mode nil :documentation "State of keypad mode.") (mouse-mode nil :documentation "Current mouse mode.") (mouse-pressed nil :documentation "Pressed mouse buttons.") (mouse-encoding nil :documentation "Current mouse event encoding.") (focus-event-mode nil :documentation "Whether to send focus event.") (cut-buffers (1value (make-vector 8 nil)) :documentation "Cut buffers.") (sixel-buffer (let ((pair (cons (cons 0 (make-vector 1000 nil)) nil))) (setf (cdr pair) (cons pair pair)) pair) :documentation "Buffer to hold Sixel data.") (sixel-buffer-size 1 :documentation "Line count in Sixel buffer.") (sixel-palette (copy-sequence (make-vector 256 nil)) :documentation "Sixel color registers.") (sixel-color 0 :documentation "Current Sixel color register.") (sixel-render-format 'background :documentation "Format to render Sixel images in.") (sixel-image-extra-props nil :documentation "Extra properties of images used to display Sixel.") (sixel-scroll-mode t :documentation "Whether to auto-scroll.") (sixel-initial-cursor-pos '(1 . 1) :documentation "Initial position of cursor before entering Sixel.") (char-width 1 :documentation "Width of each character in pixel.") (char-height 1 :documentation "Height of each character in pixel.") ;; NOTE: Change the default value of parameters when changing this. (bold-face 'eat-term-bold :documentation "Face for bold text.") (faint-face 'eat-term-faint :documentation "Face for faint text.") (italic-face 'eat-term-italic :documentation "Face for slant text.") (slow-blink-face 'eat-term-slow-blink :documentation "Slow blink.") (fast-blink-face 'eat-term-fast-blink :documentation "Fast blink.") (color-faces (copy-sequence (eval-when-compile (vconcat (cl-loop for i from 0 to 255 collect (intern (format "eat-term-color-%i" i)))))) :documentation "Faces for colors.") (font-faces (copy-sequence (eval-when-compile (vconcat (cl-loop for i from 0 to 9 collect (intern (format "eat-term-font-%i" i)))))) :documentation "Faces for fonts.") (params (copy-hash-table (eval-when-compile (let ((tbl (make-hash-table :test 'eq))) (puthash 'input-function #'ignore tbl) (puthash 'ring-bell-function #'ignore tbl) (puthash 'set-cursor-function #'ignore tbl) (puthash 'grab-mouse-function #'ignore tbl) (puthash 'grab-focus-events-function #'ignore tbl) (puthash 'manipulate-selection-function #'ignore tbl) (puthash 'set-title-function #'ignore tbl) (puthash 'set-cwd-function #'ignore tbl) (puthash 'ui-command-function #'ignore tbl) (puthash 'char-dimensions '(1 . 1) tbl) (puthash 'sixel-render-format 'background tbl) (puthash 'bold-face 'eat-term-bold tbl) (puthash 'faint-face 'eat-term-faint tbl) (puthash 'italic-face 'eat-term-italic tbl) (puthash 'slow-blink-face 'eat-term-slow-blink tbl) (puthash 'fast-blink-face 'eat-term-fast-blink tbl) (cl-loop for i from 0 to 255 do (puthash (intern (format "color-%i-face" i)) (intern (format "eat-term-color-%i" i)) tbl)) (cl-loop for i from 0 to 9 do (puthash (intern (format "font-%i-face" i)) (intern (format "eat-term-font-%i" i)) tbl)) tbl))) :documentation "Alist of terminal parameters.")) (defvar eat--t-term nil "The current terminal. Don't `set' it, bind it to a value with `let'.") (defun eat--t-reset () "Reset terminal." (let ((disp (eat--t-term-display eat--t-term))) ;; Reset most of the things to their respective default values. (setf (eat--t-term-parser-state eat--t-term) nil) (setf (eat--t-disp-begin disp) (point-min-marker)) (setf (eat--t-disp-old-begin disp) (point-min-marker)) (setf (eat--t-disp-cursor disp) (eat--t-make-cur :position (point-min-marker))) (setf (eat--t-disp-saved-cursor disp) (eat--t-make-cur)) (setf (eat--t-term-scroll-begin eat--t-term) 1) (setf (eat--t-term-scroll-end eat--t-term) (eat--t-disp-height disp)) (setf (eat--t-term-main-display eat--t-term) nil) (setf (eat--t-term-face eat--t-term) (eat--t-make-face)) (setf (eat--t-term-auto-margin eat--t-term) t) (setf (eat--t-term-ins-mode eat--t-term) nil) (setf (eat--t-term-charset eat--t-term) '(g0 (g0 . us-ascii) (g1 . dec-line-drawing) (g2 . dec-line-drawing) (g3 . dec-line-drawing))) (setf (eat--t-term-saved-face eat--t-term) (eat--t-make-face)) (setf (eat--t-term-bracketed-yank eat--t-term) nil) (setf (eat--t-term-cur-state eat--t-term) :block) (setf (eat--t-term-cur-visible-p eat--t-term) t) (setf (eat--t-term-title eat--t-term) "") (setf (eat--t-term-keypad-mode eat--t-term) nil) (setf (eat--t-term-mouse-mode eat--t-term) nil) (setf (eat--t-term-mouse-encoding eat--t-term) nil) (setf (eat--t-term-focus-event-mode eat--t-term) nil) (setf (eat--t-term-sixel-scroll-mode eat--t-term) t) ;; Clear everything. (delete-region (point-min) (point-max)) ;; Inform the UI about our new state. (funcall (eat--t-term-grab-mouse-fn eat--t-term) eat--t-term nil) (funcall (eat--t-term-set-focus-ev-mode-fn eat--t-term) eat--t-term nil) (funcall (eat--t-term-set-title-fn eat--t-term) eat--t-term "") (funcall (eat--t-term-set-cursor-fn eat--t-term) eat--t-term :block))) (defun eat--t-cur-right (&optional n) "Move cursor N columns right. N default to 1. If N is out of range, place cursor at the edge of display." (let* ((disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp))) ;; If N is less than 1, set N to 1. If N is more than the number ;; of available columns on the right side, set N to the maximum ;; possible value. (setq n (min (- (eat--t-disp-width disp) (eat--t-cur-x cursor)) (max (or n 1) 1))) ;; N is non-zero in most cases, except at the edge of display. (unless (zerop n) ;; Move to the Nth next column, use spaces to reach that column ;; if needed. (eat--t-repeated-insert ?\s (- n (eat--t-col-motion n))) (cl-incf (eat--t-cur-x cursor) n)))) (defun eat--t-cur-left (&optional n) "Move cursor N columns left. N default to 1. If N is out of range, place cursor at the edge of display." (let* ((disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp))) ;; If N is less than 1, set N to 1. If N is more than the number ;; of available columns on the left side, set N to the maximum ;; possible value. (setq n (min (1- (eat--t-cur-x cursor)) (max (or n 1) 1))) ;; N is non-zero in most cases, except at the edge of display. (unless (zerop n) ;; Move to the Nth previous column. (cl-assert (1value (>= (eat--t-current-col) n))) (backward-char n) (cl-decf (eat--t-cur-x cursor) n)))) (defun eat--t-cur-horizontal-abs (&optional n) "Move cursor to Nth column on current line. N default to 1. If N is out of range, place cursor at the edge of display." (let* ((disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp))) ;; If N is out of range, bring it within the bounds of range. (setq n (min (max (or n 1) 1) (eat--t-disp-width disp))) ;; Depending on the current position of cursor, move right or ;; left. (cond ((< (eat--t-cur-x cursor) n) (eat--t-cur-right (- n (eat--t-cur-x cursor)))) ((< n (eat--t-cur-x cursor)) (eat--t-cur-left (- (eat--t-cur-x cursor) n)))))) (defun eat--t-beg-of-next-line (n) "Move to beginning of Nth next line." (let* ((disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp))) ;; If N is less than 1, set N to 1. If N is more than the number ;; of available lines below, set N to the maximum possible value. (setq n (min (- (eat--t-disp-height disp) (eat--t-cur-y cursor)) (max (or n 1) 1))) ;; N is non-zero in most cases, except at the edge of display. ;; Whatever the case, we move to the beginning of line. (if (zerop n) (1value (eat--t-goto-bol)) ;; Move to the Nth next line, use newlines to reach that line if ;; needed. (eat--t-repeated-insert ?\n (- n (eat--t-goto-bol n))) (cl-incf (eat--t-cur-y cursor) n)) (1value (setf (eat--t-cur-x cursor) 1)))) (defun eat--t-beg-of-prev-line (n) "Move to beginning of Nth previous line." (let* ((disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp))) ;; If N is less than 1, set N to 1. If N is more than the number ;; of available lines above, set N to the maximum possible value. (setq n (min (1- (eat--t-cur-y cursor)) (max (or n 1) 1))) ;; Move to the beginning Nth previous line. Even if there are no ;; line above, move to the beginning of the line. (eat--t-goto-bol (- n)) (cl-decf (eat--t-cur-y cursor) n) (1value (setf (eat--t-cur-x cursor) 1)))) (defun eat--t-cur-down (&optional n) "Move cursor N lines down. N default to 1. If N is out of range, place cursor at the edge of display." (let ((x (eat--t-cur-x (eat--t-disp-cursor (eat--t-term-display eat--t-term))))) ;; Move to the beginning of target line. (eat--t-beg-of-next-line n) ;; If the cursor wasn't at column one, move the cursor to the ;; cursor to that column. (unless (= x 1) (eat--t-cur-right (1- x))))) (defun eat--t-cur-up (&optional n) "Move cursor N lines up. N default to 1. If N is out of range, place cursor at the edge of display." (let ((x (eat--t-cur-x (eat--t-disp-cursor (eat--t-term-display eat--t-term))))) ;; Move to the beginning of target line. (eat--t-beg-of-prev-line n) ;; If the cursor wasn't at column one, move the cursor to the ;; cursor to that column. (unless (= x 1) (eat--t-cur-right (1- x))))) (defun eat--t-cur-vertical-abs (&optional n) "Move cursor to Nth line on display. N default to 1. If N is out of range, place cursor at the edge of display." (let* ((disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp))) ;; If N is out of range, bring it within the bounds of range. (setq n (min (max (or n 1) 1) (eat--t-disp-height disp))) ;; Depending on the current position of cursor, move downward or ;; upward. (cond ((< (eat--t-cur-y cursor) n) (eat--t-cur-down (- n (eat--t-cur-y cursor)))) ((< n (eat--t-cur-y cursor)) (eat--t-cur-up (- (eat--t-cur-y cursor) n)))))) (defun eat--t-scroll-up (&optional n as-side-effect) "Scroll up N lines, preserving cursor position. N default to 1. By default, don't change current line and current column, but if AS-SIDE-EFFECT is given and non-nil, assume that scrolling is triggered as a side effect of some other control function and don't move the point relative to the text and change current line accordingly." (let ((disp (eat--t-term-display eat--t-term)) (scroll-begin (eat--t-term-scroll-begin eat--t-term)) (scroll-end (eat--t-term-scroll-end eat--t-term))) ;; N shouldn't be more more than the number of lines in scroll ;; region. (setq n (min (max (or n 1) 0) (1+ (- scroll-end scroll-begin)))) ;; Make sure that N is positive. (unless (zerop n) ;; Try to not point relative to the text. (save-excursion (goto-char (eat--t-disp-begin disp)) ;; Move to the beginning of scroll region. (eat--t-goto-bol (1- scroll-begin)) ;; If the first line on display isn't in scroll region or ;; if this is the alternative display, delete text. (if (or (eat--t-term-main-display eat--t-term) (> scroll-begin 1)) (delete-region (point) (car (eat--t-bol n))) ;; Otherwise, send the text to the scrollback area by ;; advancing the display beginning marker. (eat--t-goto-bol n) ;; Make sure we're at the beginning of a line, because we ;; might be at `point-max'. (unless (or (= (point) (point-min)) (= (char-before) ?\n)) (insert ?\n)) (set-marker (eat--t-disp-begin disp) (point))) ;; Is the last line on display in scroll region? (when (< scroll-end (eat--t-disp-width disp)) ;; No, it isn't. ;; Go to the end of scroll region (before deleting or moving ;; texts). (eat--t-goto-bol (- (1+ (- scroll-end scroll-begin)) n)) ;; If there is anything after the scroll region, insert ;; newlines to keep that text unmoved. (when (< (point) (point-max)) (eat--t-repeated-insert ?\n n)))) ;; Recalculate point if needed. (let* ((cursor (eat--t-disp-cursor disp)) (recalc-point (<= scroll-begin (eat--t-cur-y cursor) scroll-end))) ;; If recalc-point is non-nil, and AS-SIDE-EFFECT is non-nil, ;; update cursor position so that it is unmoved relative to ;; surrounding text and reconsider point recalculation. (when (and recalc-point as-side-effect) (setq recalc-point (< (- (eat--t-cur-y cursor) n) scroll-begin)) (setf (eat--t-cur-y cursor) (max (- (eat--t-cur-y cursor) n) scroll-begin))) (when recalc-point ;; Recalculate point. (let ((y (eat--t-cur-y cursor)) (x (eat--t-cur-x cursor))) (eat--t-goto 1 1) (eat--t-goto y x))))))) (defun eat--t-scroll-down (&optional n) "Scroll down N lines, preserving cursor position. N defaults to 1." (let ((disp (eat--t-term-display eat--t-term)) (scroll-begin (eat--t-term-scroll-begin eat--t-term)) (scroll-end (eat--t-term-scroll-end eat--t-term))) ;; N shouldn't be more more than the number of lines in scroll ;; region. (setq n (min (max (or n 1) 0) (1+ (- scroll-end scroll-begin)))) ;; Make sure that N is positive. (unless (zerop n) ;; Move to the beginning of scroll region. (goto-char (eat--t-disp-begin disp)) (eat--t-goto-bol (1- scroll-begin)) ;; Insert newlines to push text downwards. (eat--t-repeated-insert ?\n n) ;; Go to the end scroll region (after inserting newlines). (eat--t-goto-eol (- (1+ (- scroll-end scroll-begin)) (1+ n))) ;; Delete the text that was pushed out of scroll region. (when (< (point) (point-max)) (delete-region (point) (car (eat--t-eol n)))) ;; The cursor mustn't move, so we have to recalculate point. (let* ((cursor (eat--t-disp-cursor disp)) (y (eat--t-cur-y cursor)) (x (eat--t-cur-x cursor))) (eat--t-goto 1 1) (eat--t-goto y x))))) (defun eat--t-goto (&optional y x) "Go to Xth column of Yth line of display. Y and X default to 1. Y and X are one-based. If Y and/or X are out of range, place cursor at the edge of display." ;; Important special case: if Y and X are both one, move to the ;; display beginning. (if (and (or (not y) (eql y 1)) (or (not x) (eql x 1))) (let* ((disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp))) (goto-char (eat--t-disp-begin disp)) (1value (setf (eat--t-cur-y cursor) 1 (eat--t-cur-x cursor) 1))) ;; Move to column one, go to Yth line and move to Xth column. ;; REVIEW: We move relative to cursor position, which faster for ;; positions near the point (usually the case), but slower for ;; positions far away from the point. There are only two cursor ;; positions whose exact position is known beforehand, the cursor ;; (whose position is (point)) and (1, 1) (the display beginning). ;; There are almost always some points which are at more distance ;; from current position than from the display beginning (the only ;; exception is when the cursor is at the display beginning). So ;; first moving to the display beginning and then moving to those ;; point will be faster than moving from cursor (except a tiny ;; (perhaps negligible) overhead of `goto-char'). What we don't ;; have is a formula the calculate the distance between two ;; positions. (eat--t-cur-horizontal-abs 1) (eat--t-cur-vertical-abs y) (eat--t-cur-horizontal-abs x))) (defun eat--t-enable-auto-margin () "Enable automatic margin." ;; Set the automatic margin flag to t, the rest of code will take ;; care of the effects. (1value (setf (eat--t-term-auto-margin eat--t-term) t))) (defun eat--t-disable-auto-margin () "Disable automatic margin." ;; Set the automatic margin flag to nil, the rest of code will take ;; care of the effects. (1value (setf (eat--t-term-auto-margin eat--t-term) nil))) (defun eat--t-set-charset (slot charset) "SLOT's character set to CHARSET." (setf (alist-get slot (cdr (eat--t-term-charset eat--t-term))) charset)) (defun eat--t-change-charset (charset) "Change character set to CHARSET. CHARSET should be one of `g0', `g1', `g2' and `g3'." (cl-assert (memq charset '(g0 g1 g2 g3))) (setf (car (eat--t-term-charset eat--t-term)) charset)) (defun eat--t-move-before-to-safe () "Move to a safe position before point. Return how much moved. If the current position is safe, do nothing and return 0. Safe position is the position that's not on a multi-column wide character or its the internal invisible spaces." (if (and (not (bobp)) ;; Is the current position unsafe? (get-text-property (1- (point)) 'eat--t-invisible-space)) (let ((start-pos (point))) ;; Move to the safe position. (goto-char (or (previous-single-property-change (point) 'eat--t-invisible-space) (point-min))) (cl-assert (1value (or (bobp) (null (get-text-property (1- (point)) 'eat--t-invisible-space))))) (- start-pos (point))) 0)) (defun eat--t-make-pos-safe () "If the position isn't safe, make it safe by replacing with spaces." (let ((moved (eat--t-move-before-to-safe))) (unless (zerop moved) (let ((width (get-text-property (point) 'eat--t-char-width))) (cl-assert width) (delete-region (point) (+ (point) width)) (eat--t-repeated-insert ?\s width (eat--t-face-face (eat--t-term-face eat--t-term))) (backward-char (- width moved)))))) (defun eat--t-fix-partial-multi-col-char (&optional preserve-face) "Replace any partial multi-column character with spaces. If PRESERVE-FACE is non-nil, preserve original face." (let ((face (if preserve-face (get-char-property (point) 'face) (eat--t-face-face (eat--t-term-face eat--t-term))))) (if (get-text-property (point) 'eat--t-invisible-space) (let ((start-pos (point)) (count nil)) (goto-char (or (next-single-property-change (point) 'eat--t-invisible-space) (point-max))) (setq count (- (1+ (point)) start-pos)) ;; Make sure we really overwrote the character ;; partially. (when (< count (get-text-property (point) 'eat--t-char-width)) (delete-region start-pos (1+ (point))) (eat--t-repeated-insert ?\s count face)) (goto-char start-pos)) ;; Detect the case where we have deleted all the invisible ;; spaces before, but not the multi-column character itself. (when-let* (((not (eobp))) (w (get-text-property (point) 'eat--t-char-width)) ((> w 1))) ;; `delete-char' also works, but it does more checks, so ;; hopefully this will be faster. (delete-region (point) (1+ (point))) (insert (propertize " " 'face face 'font-lock-face face)) (backward-char))))) (defconst eat--t-dec-line-drawing-chars (eval-and-compile (let ((alist '((?+ . ?→) (?, . ?â†) (?- . ?↑) (?. . ?↓) (?0 . ?â–ˆ) (?\` . ?�) (?a . ?â–’) (?b . ?â‰) (?c . ?âŒ) (?d . ?â) (?e . ?âŠ) (?f . ?°) (?g . ?±) (?h . ?â–‘) (?i . ?#) (?j . ?┘) (?k . ?â”) (?l . ?┌) (?m . ?â””) (?n . ?┼) (?o . ?⎺) (?p . ?⎻) (?q . ?─) (?r . ?⎼) (?s . ?⎽) (?t . ?├) (?u . ?┤) (?v . ?â”´) (?w . ?┬) (?x . ?│) (?y . ?≤) (?z . ?≥) (?{ . ?Ï€) (?| . ?≠) (?} . ?£) (?~ . ?•))) (table (make-hash-table :purecopy t))) (dolist (pair alist) (puthash (car pair) (cdr pair) table)) table)) "Hash table for DEC Line Drawing charset. The key is the output character from client, and value of the character to actually show.") (defun eat--t-write (str &optional beg end) "Write STR from BEG to END on display." (setq beg (or beg 0)) (setq end (or end (length str))) (let* ((disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp)) (scroll-end (eat--t-term-scroll-end eat--t-term)) (charset (alist-get (car (eat--t-term-charset eat--t-term)) (cdr (eat--t-term-charset eat--t-term)))) (face (eat--t-face-face (eat--t-term-face eat--t-term))) ;; Alist of indices and width of multi-column characters. (multi-col-char-indices nil) (inserted-till beg)) (cl-assert charset) ;; Find all the multi-column wide characters in ST; hopefully it ;; won't slow down showing plain ASCII. (setq multi-col-char-indices (cl-loop for i from beg to (1- end) when (/= (char-width (aref str i)) 1) collect (cons i (char-width (aref str i))))) ;; If the position isn't safe, replace the multi-column ;; character with spaces to make it safe. (eat--t-make-pos-safe) ;; TODO: Comment. ;; REVIEW: This probably needs to be updated. (while (< inserted-till end) ;; Insert STR, and record the width of STR inserted ;; successfully. (let ((ins-count (named-let write ((max (min (- (eat--t-disp-width disp) (1- (eat--t-cur-x cursor))) (+ (- end inserted-till) (cl-loop for p in multi-col-char-indices sum (1- (cdr p)))))) (written 0)) (let* ((next-multi-col (car multi-col-char-indices)) (end (+ inserted-till max)) (e (if next-multi-col ;; Exclude the multi-column character. (min (car next-multi-col) end) end)) (wrote (- e inserted-till))) (cl-assert (>= wrote 0)) (let ((s (substring str inserted-till e))) ;; Convert STR to Unicode according to the ;; current character set. (pcase-exhaustive charset ;; For `us-ascii', the default, no conversion ;; is necessary. ('us-ascii) ;; `dec-line-drawing' contains various ;; characters useful for drawing line diagram, ;; so it is a must. This is also possible ;; with `us-ascii', thanks to Unicode, but the ;; character set `dec-line-drawing' is usually ;; less expensive in terms of bytes needed to ;; transfer than `us-ascii'. ('dec-line-drawing (dotimes (i (length s)) (when-let* ((r (gethash (aref s i) eat--t-dec-line-drawing-chars))) (aset s i r))))) ;; Add face. (put-text-property 0 (length s) 'face face s) (put-text-property 0 (length s) 'font-lock-face face s) (insert s)) (setq inserted-till e) (if (or (null next-multi-col) (< (- end e) (cdr next-multi-col))) ;; Either everything is done, or we reached ;; the limit. (+ written wrote) ;; There are many characters which are too ;; narrow for `char-width' to return 1. XTerm, ;; Kitty and St seems to ignore them, so we too. (if (zerop (cdr next-multi-col)) (cl-incf inserted-till) (insert ;; Make sure the multi-column character ;; occupies the same number of characters as ;; its width. (propertize (make-string (1- (cdr next-multi-col)) ?\s) 'invisible t 'face face 'font-lock-face face 'eat--t-invisible-space t 'eat--t-char-width (cdr next-multi-col)) ;; Now insert the multi-column character. (propertize (substring str inserted-till (cl-incf inserted-till)) 'face face 'font-lock-face face 'eat--t-char-width (cdr next-multi-col)))) (setf multi-col-char-indices (cdr multi-col-char-indices)) (write (- max wrote (cdr next-multi-col)) (+ written wrote (cdr next-multi-col)))))))) (cl-incf (eat--t-cur-x cursor) ins-count) (if (eat--t-term-ins-mode eat--t-term) (delete-region (save-excursion (eat--t-col-motion (- (eat--t-disp-width disp) (1- (eat--t-cur-x cursor)))) ;; Make sure the point is safe. (eat--t-move-before-to-safe) (point)) (car (eat--t-eol))) (delete-region (point) (min (+ ins-count (point)) (car (eat--t-eol)))) ;; Replace any partially-overwritten character with ;; spaces. (eat--t-fix-partial-multi-col-char)) (when (> (eat--t-cur-x cursor) (eat--t-disp-width disp)) (if (not (eat--t-term-auto-margin eat--t-term)) (eat--t-cur-left 1) (when (< inserted-till end) (when (= (eat--t-cur-y cursor) scroll-end) (eat--t-scroll-up 1 'as-side-effect)) (if (= (eat--t-cur-y cursor) scroll-end) (eat--t-carriage-return) (if (= (point) (point-max)) (insert #("\n" 0 1 (eat--t-wrap-line t))) (put-text-property (point) (1+ (point)) 'eat--t-wrap-line t) (forward-char)) (1value (setf (eat--t-cur-x cursor) 1)) (cl-incf (eat--t-cur-y cursor)))))))))) (defun eat--t-horizontal-tab (&optional n) "Go to the Nth next tabulation stop. N default to 1." ;; N must be positive. (setq n (max (or n 1) 1)) (let* ((disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp))) ;; Do some math calculate the distance of the Nth next tabulation ;; stop from cursor, and go there. (eat--t-cur-right (+ (- 8 (mod (1- (eat--t-cur-x cursor)) 8)) (* (1- n) 8))))) (defun eat--t-horizontal-backtab (&optional n) "Go to the Nth previous tabulation stop. N default to 1." ;; N must be positive. (setq n (max (or n 1) 1)) (let* ((disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp))) ;; Do some math calculate the distance of the Nth next tabulation ;; stop from cursor, and go there. (eat--t-cur-left (+ (1+ (mod (- (eat--t-cur-x cursor) 2) 8)) (* (1- n) 8))))) (defun eat--t-index () "Go to the next line preserving column, scrolling if necessary." (let* ((disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp)) (scroll-end (eat--t-term-scroll-end eat--t-term)) ;; Are we inside scroll region? (in-scroll-region (<= (eat--t-cur-y cursor) scroll-end))) ;; If this is the last line (of the scroll region or the display), ;; scroll up, otherwise move cursor downward. (if (= (if in-scroll-region scroll-end (eat--t-disp-height disp)) (eat--t-cur-y cursor)) (eat--t-scroll-up 1) (eat--t-cur-down 1)))) (defun eat--t-carriage-return () "Go to column one." (eat--t-cur-horizontal-abs 1)) (defun eat--t-line-feed () "Go to the first column of the next line, scrolling if necessary." (let* ((disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp)) (scroll-end (eat--t-term-scroll-end eat--t-term)) ;; Are we inside scroll region? (in-scroll-region (<= (eat--t-cur-y cursor) scroll-end))) ;; If we are at the very end of the terminal, we might have some ;; optimizations. (if (= (point) (point-max)) ;; If the cursor is above the last line of the scroll region ;; (or the display, if we are outside the scroll region), we ;; can simply insert a newline and update the cursor position. (if (/= (if in-scroll-region scroll-end (eat--t-disp-height disp)) (eat--t-cur-y cursor)) (progn (insert ?\n) (setf (eat--t-cur-x cursor) 1) (cl-incf (eat--t-cur-y cursor))) ;; This is the last line. We need to scroll up. (eat--t-scroll-up 1 'as-side-effect) ;; If we're still at the last line (only happens when the ;; display has only a single line), go to column one of it. (if (= (if in-scroll-region scroll-end (eat--t-disp-height disp)) (eat--t-cur-y cursor)) (eat--t-carriage-return) ;; If we are somehow moved from the end of terminal, ;; `eat--t-beg-of-next-line' is the best option. (if (/= (point) (point-max)) (eat--t-beg-of-next-line 1) ;; We are still at the end! We can can simply insert a ;; newline and update the cursor position. (insert ?\n) (setf (eat--t-cur-x cursor) 1) (cl-incf (eat--t-cur-y cursor))))) ;; We are not at the end of terminal. But we still have a last ;; chance. `eat--t-beg-of-next-line' is usually faster than ;; `eat--t-carriage-return' followed by `eat--t-index', so if ;; there is at least a single line (in the scroll region, if the ;; cursor in the scroll region, otherwise in the display) ;; underneath the cursor, we can use `eat--t-beg-of-next-line'. (if (/= (if in-scroll-region scroll-end (eat--t-disp-height disp)) (eat--t-cur-y cursor)) (eat--t-beg-of-next-line 1) ;; We don't have any other option, so we must use the most ;; time-expensive option. (eat--t-carriage-return) (eat--t-index))))) (defun eat--t-reverse-index () "Go to the previous line preserving column, scrolling if needed." (let* ((cursor (eat--t-disp-cursor (eat--t-term-display eat--t-term))) (scroll-begin (eat--t-term-scroll-begin eat--t-term)) ;; Are we in the scroll region? (in-scroll-region (<= scroll-begin (eat--t-cur-y cursor)))) ;; If this is the first line (of the scroll region or the ;; display), scroll down, otherwise move cursor upward. (if (= (if in-scroll-region scroll-begin 1) (eat--t-cur-y cursor)) (eat--t-scroll-down 1) (eat--t-cur-up 1)))) (defun eat--t-bell () "Ring the bell." ;; Call the UI's bell handler. (funcall (eat--t-term-bell-fn eat--t-term) eat--t-term)) (defun eat--t-form-feed () "Insert a vertical tab." ;; Form feed is same as `eat--t-index'. (eat--t-index)) (defun eat--t-save-cur () "Save current cursor position." (let ((disp (eat--t-term-display eat--t-term)) (saved-face (eat--t-copy-face (eat--t-term-face eat--t-term)))) ;; Save cursor position. (setf (eat--t-disp-saved-cursor disp) (eat--t-copy-cur (eat--t-disp-cursor disp))) ;; Save SGR attributes. (setf (eat--t-term-saved-face eat--t-term) saved-face) ;; We use side-effects, so make sure the saved face doesn't share ;; structure with the current face. (setf (eat--t-face-face saved-face) (copy-tree (eat--t-face-face saved-face))) (setf (eat--t-face-underline-color saved-face) (copy-tree (eat--t-face-underline-color saved-face))))) (defun eat--t-restore-cur () "Restore previously save cursor position." (let ((saved (eat--t-disp-saved-cursor (eat--t-term-display eat--t-term)))) ;; Restore cursor position. (eat--t-goto (eat--t-cur-y saved) (eat--t-cur-x saved)) ;; Restore SGR attributes. (setf (eat--t-term-face eat--t-term) (copy-tree (eat--t-term-saved-face eat--t-term))) (setf (eat--t-face-underline-color (eat--t-term-face eat--t-term)) (copy-tree (eat--t-face-underline-color (eat--t-term-face eat--t-term)))))) (defun eat--t-erase-in-line (&optional n) "Erase part of current line, but don't move cursor. N defaults to 0. When N is 0, erase cursor to end of line. When N is 1, erase beginning of line to cursor. When N is 2, erase whole line." (let ((face (eat--t-term-face eat--t-term))) (pcase-exhaustive n ((or 0 'nil (pred (< 2))) ;; Delete cursor position (inclusive) to end of line. (delete-region (point) (car (eat--t-eol))) ;; If the SGR background attribute is set, we need to fill the ;; erased area with that background. (when (eat--t-face-bg face) (save-excursion (let* ((disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp))) (eat--t-repeated-insert ?\s (1+ (- (eat--t-disp-width disp) (eat--t-cur-x cursor))) (and (eat--t-face-bg face) (eat--t-face-face face))))))) (1 ;; Delete beginning of line to cursor position (inclusive). (delete-region (car (eat--t-bol)) (if (or (= (point) (point-max)) (= (char-after) ?\n)) (point) (1+ (point)))) ;; Fill the region with spaces, use SGR background attribute ;; if set. (let ((cursor (eat--t-disp-cursor (eat--t-term-display eat--t-term)))) (eat--t-repeated-insert ?\s (eat--t-cur-x cursor) (and (eat--t-face-bg face) (eat--t-face-face face)))) ;; We erased the character at the cursor position, so after ;; fill with spaces we are still off by one column; so move a ;; column backward. (backward-char)) (2 ;; Delete whole line. (delete-region (car (eat--t-bol)) (car (eat--t-eol))) (let* ((disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp))) ;; Fill the region before cursor position with spaces, use ;; SGR background attribute if set. (eat--t-repeated-insert ?\s (1- (eat--t-cur-x cursor)) (and (eat--t-face-bg face) (eat--t-face-face face))) ;; If the SGR background attribute is set, we need to fill ;; the erased area including and after cursor position with ;; that background. (when (eat--t-face-bg face) (save-excursion (eat--t-repeated-insert ?\s (1+ (- (eat--t-disp-width disp) (eat--t-cur-x cursor))) (and (eat--t-face-bg face) (eat--t-face-face face)))))))))) (defun eat--t-erase-in-disp (&optional n) "Erase part of display. N defaults to 0. When N is 0, erase cursor to end of display. When N is 1, erase beginning of display to cursor. In both on the previous cases, don't move cursor. When N is 2, erase display and reset cursor to (1, 1). When N is 3, also erase the scrollback." (let ((face (eat--t-term-face eat--t-term))) (pcase-exhaustive n ((or 0 'nil (pred (< 3))) ;; Delete from cursor position (inclusive) to end of terminal. (delete-region (point) (point-max)) ;; If the SGR background attribute is set, we need to fill the ;; erased area with that background. (when (eat--t-face-bg face) ;; `save-excursion' probably uses marker to save point, which ;; doesn't work in this case. So we the store the point as a ;; integer. (let* ((pos (point)) (disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp))) ;; Fill current line. (eat--t-repeated-insert ?\s (1+ (- (eat--t-disp-width disp) (eat--t-cur-x cursor))) (eat--t-face-face face)) ;; Fill the following lines. (dotimes (_ (- (eat--t-disp-height disp) (eat--t-cur-y cursor))) (insert ?\n) (eat--t-repeated-insert ?\s (eat--t-disp-width disp) (eat--t-face-face face))) ;; Restore position. (goto-char pos)))) (1 (let* ((disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp)) (y (eat--t-cur-y cursor)) (x (eat--t-cur-x cursor)) ;; Should we erase including the cursor position? (incl-point (/= (point) (point-max)))) ;; Delete the region to be erased. (delete-region (eat--t-disp-begin disp) (if incl-point (1+ (point)) (point))) ;; If the SGR background attribute isn't set, insert ;; newlines, otherwise fill the erased area above the current ;; line with background color. (if (not (eat--t-face-bg face)) (eat--t-repeated-insert ?\n (1- y)) (dotimes (_ (1- y)) (eat--t-repeated-insert ?\s (eat--t-disp-width disp) (eat--t-face-face face)) (insert ?\n))) ;; Fill the current line to keep the cursor unmoved. Use ;; background if the corresponding SGR attribute is set. (eat--t-repeated-insert ?\s x (and (eat--t-face-bg face) (eat--t-face-face face))) ;; We are off by one column; so move a column backward. (when incl-point (backward-char)))) ((or 2 3) ;; Move to the display beginning. (eat--t-goto 1 1) ;; Delete everything in the display, and if N is 3, also delete ;; everything in the scrollback area. (delete-region (if (= n 2) (point) (point-min)) (point-max)) ;; If the SGR background attribute is set, fill the display ;; with that background. (when (eat--t-face-bg face) ;; `save-excursion' probably uses marker to save point, which ;; doesn't work in this case. So we the store the point as a ;; integer. (let ((pos (point)) (disp (eat--t-term-display eat--t-term))) (dotimes (i (eat--t-disp-height disp)) (unless (zerop i) (insert ?\n)) (eat--t-repeated-insert ?\s (eat--t-disp-width disp) (eat--t-face-face face))) ;; Restore point. (goto-char pos))))))) (defun eat--t-device-status-report (n) "Report device (terminal) status. If N is 5, send OK sequence. If N is 6, send the current Y and X coordinate to client." (pcase n (5 (funcall (eat--t-term-input-fn eat--t-term) eat--t-term "\e[0n")) (6 (let ((cursor (eat--t-disp-cursor (eat--t-term-display eat--t-term)))) (funcall (eat--t-term-input-fn eat--t-term) eat--t-term (format "\e[%i;%iR" (eat--t-cur-y cursor) (eat--t-cur-x cursor))))))) (defun eat--t-set-cursor-state (state) "Set cursor state to STATE. STATE one of the `:invisible', `:block', `:blinking-block', `:underline', `:blinking-underline', `:bar', `:blinking-bar'." (if (eq state :invisible) (when (eat--t-term-cur-visible-p eat--t-term) (setf (eat--t-term-cur-visible-p eat--t-term) nil) (funcall (eat--t-term-set-cursor-fn eat--t-term) eat--t-term :invisible)) (unless (and (eat--t-term-cur-visible-p eat--t-term) (eq (eat--t-term-cur-state eat--t-term) state)) ;; Update state. (setf (eat--t-term-cur-state eat--t-term) state) (setf (eat--t-term-cur-visible-p eat--t-term) t) ;; Inform the UI. (funcall (eat--t-term-set-cursor-fn eat--t-term) eat--t-term state)))) (defun eat--t-set-cursor-style (style) "Set cursor state as described by STYLE." (when (<= 0 style 6) (let ((state (aref [ :blinking-block :blinking-block :block :blinking-underline :underline :blinking-bar :bar] style))) (if (eat--t-term-cur-visible-p eat--t-term) (eat--t-set-cursor-state state) (setf (eat--t-term-cur-state eat--t-term) state))))) (defun eat--t-show-cursor () "Make the cursor visible." (when (not (eat--t-term-cur-visible-p eat--t-term)) (eat--t-set-cursor-state (eat--t-term-cur-state eat--t-term)))) (defun eat--t-hide-cursor () "Make the cursor invisible." (when (eat--t-term-cur-visible-p eat--t-term) (eat--t-set-cursor-state :invisible))) (defun eat--t-blinking-cursor () "Make the cursor blink." (let ((state (pcase (eat--t-term-cur-state eat--t-term) (:block :blinking-block) (:underline :blinking-underline) (:bar :blinking-bar) (state state)))) (if (eat--t-term-cur-visible-p eat--t-term) (eat--t-set-cursor-state state) (setf (eat--t-term-cur-state eat--t-term) state)))) (defun eat--t-non-blinking-cursor () "Make the cursor not blink." (let ((state (pcase (eat--t-term-cur-state eat--t-term) (:blinking-block :block) (:blinking-underline :underline) (:blinking-bar :bar) (state state)))) (if (eat--t-term-cur-visible-p eat--t-term) (eat--t-set-cursor-state state) (setf (eat--t-term-cur-state eat--t-term) state)))) (defun eat--t-enable-bracketed-yank () "Enable bracketed yank mode." (setf (eat--t-term-bracketed-yank eat--t-term) t)) (defun eat--t-disable-bracketed-yank () "Disable bracketed yank mode." (setf (eat--t-term-bracketed-yank eat--t-term) nil)) (defun eat--t-enable-alt-disp () "Enable alternative display." ;; Effective only when alternative display is enabled by user. (when eat-enable-alternative-display ;; Make sure we not already in the alternative display. (unless (eat--t-term-main-display eat--t-term) ;; Store the current display, including scrollback. (let ((main-disp (eat--t-copy-disp (eat--t-term-display eat--t-term)))) (setf (eat--t-disp-begin main-disp) (- (eat--t-disp-begin main-disp) (point-min))) (setf (eat--t-disp-old-begin main-disp) (- (eat--t-disp-old-begin main-disp) (point-min))) (setf (eat--t-disp-cursor main-disp) (eat--t-copy-cur (eat--t-disp-cursor main-disp))) (setf (eat--t-disp-saved-cursor main-disp) (eat--t-copy-cur (eat--t-disp-saved-cursor main-disp))) (setf (eat--t-cur-position (eat--t-disp-cursor main-disp)) (- (point) (point-min))) (setf (eat--t-term-main-display eat--t-term) (cons main-disp (buffer-string))) ;; Delete everything, and move to the beginning of terminal. (delete-region (point-min) (point-max)) (eat--t-goto 1 1))))) (defun eat--t-disable-alt-disp (&optional dont-move-cursor) "Disable alternative display. If DONT-MOVE-CURSOR is non-nil, don't move cursor from current position." ;; Make sure we in the alternative display. (when (eat--t-term-main-display eat--t-term) (let ((main-disp (eat--t-term-main-display eat--t-term)) (old-y (eat--t-cur-y (eat--t-disp-cursor (eat--t-term-display eat--t-term)))) (old-x (eat--t-cur-x (eat--t-disp-cursor (eat--t-term-display eat--t-term)))) (width (eat--t-disp-width (eat--t-term-display eat--t-term))) (height (eat--t-disp-height (eat--t-term-display eat--t-term)))) ;; Delete everything. (delete-region (point-min) (point-max)) ;; Restore the main display. (insert (cdr main-disp)) (setf (eat--t-cur-position (eat--t-disp-cursor (car main-disp))) (copy-marker (+ (point-min) (eat--t-cur-position (eat--t-disp-cursor (car main-disp)))))) (setf (eat--t-disp-old-begin (car main-disp)) (copy-marker (+ (point-min) (eat--t-disp-old-begin (car main-disp))))) (setf (eat--t-disp-begin (car main-disp)) (copy-marker (+ (point-min) (eat--t-disp-begin (car main-disp))))) (setf (eat--t-term-display eat--t-term) (car main-disp) (eat--t-term-main-display eat--t-term) nil) (goto-char (eat--t-cur-position (eat--t-disp-cursor (eat--t-term-display eat--t-term)))) ;; Maybe the terminal was resized after enabling alternative ;; display, so we have to resize again. (eat--t-resize width height) ;; Restore cursor position if DONT-MOVE-CURSOR is non-nil. (when dont-move-cursor (eat--t-goto old-y old-x))))) (defun eat--t-insert-char (n) "Insert N empty (space) characters, preserving cursor." (let* ((disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp))) ;; Make sure N is positive. If N is more than the number of ;; available columns available, set N to the maximum possible ;; value. (setq n (min (- (eat--t-disp-width disp) (1- (eat--t-cur-x cursor))) (max (or n 1) 1))) ;; Return if N is zero. (unless (zerop n) ;; If the position isn't safe, replace the multi-column ;; character with spaces to make it safe. (eat--t-make-pos-safe) (save-excursion (let ((face (eat--t-term-face eat--t-term))) ;; Insert N spaces, with SGR background if that attribute is ;; set. (eat--t-repeated-insert ?\s n (and (eat--t-face-bg face) (eat--t-face-face face)))) ;; Remove the characters that went beyond the edge of ;; display. (eat--t-col-motion (- (eat--t-disp-width disp) (+ (1- (eat--t-cur-x cursor)) n))) ;; Make sure we delete any multi-column character ;; completely. (eat--t-move-before-to-safe) (delete-region (point) (car (eat--t-eol))))))) (defun eat--t-delete-char (n) "Delete N characters, preserving cursor." (let* ((disp (eat--t-term-display eat--t-term)) (face (eat--t-term-face eat--t-term)) (cursor (eat--t-disp-cursor disp))) ;; Make sure N is positive. If N is more than the number of ;; available columns available, set N to the maximum possible ;; value. (setq n (min (- (eat--t-disp-width disp) (1- (eat--t-cur-x cursor))) (max (or n 1) 1))) ;; Return if N is zero. (unless (zerop n) ;; If the position isn't safe, replace the multi-column ;; character with spaces to make it safe. (eat--t-make-pos-safe) (save-excursion (let ((m (point))) ;; Delete N character on current line. (eat--t-col-motion n) (delete-region m (point)) ;; Replace any partially-overwritten character with spaces. (eat--t-fix-partial-multi-col-char) ;; If SGR background attribute is set, fill N characters at ;; the right edge of display with that background. (when (eat--t-face-bg face) (save-excursion (eat--t-goto-eol) (let ((empty (1+ (- (eat--t-disp-width disp) (eat--t-cur-x cursor) (- (point) m))))) ;; Reach the position from where to start filling. ;; Use spaces if needed. (when (> empty n) (eat--t-repeated-insert ?\s (- empty n))) ;; Fill with background. (eat--t-repeated-insert ?\s (min empty n) (eat--t-face-face face)))))))))) (defun eat--t-erase-char (n) "Make next N character cells empty, preserving cursor." (let* ((disp (eat--t-term-display eat--t-term)) (face (eat--t-term-face eat--t-term)) (cursor (eat--t-disp-cursor disp))) ;; Make sure N is positive. If N is more than the number of ;; available columns available, set N to the maximum possible ;; value. (setq n (min (- (eat--t-disp-width disp) (1- (eat--t-cur-x cursor))) (max (or n 1) 1))) ;; Return if N is zero. (unless (zerop n) ;; If the position isn't safe, replace the multi-column ;; character with spaces to make it safe. (eat--t-make-pos-safe) (save-excursion (let ((m (point))) ;; Delete N character on current line. (eat--t-col-motion n) (delete-region m (point)) ;; Replace any partially-overwritten character with spaces. (eat--t-fix-partial-multi-col-char) ;; Insert N spaces, with background if SGR background ;; attribute is set. (eat--t-repeated-insert ?\s n (and (eat--t-face-bg face) (eat--t-face-face face)))))))) (defun eat--t-insert-line (n) "Insert N empty lines, preserving cursor." (let* ((disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp)) (scroll-begin (eat--t-term-scroll-begin eat--t-term)) (scroll-end (eat--t-term-scroll-end eat--t-term))) ;; N should be positive and shouldn't exceed the number of lines ;; below cursor position and inside current scroll region. (setq n (min (- (1+ (- scroll-end scroll-begin)) (1- (eat--t-cur-y cursor))) (max (or n 1) 1))) ;; Make sure we are in the scroll region and N is positive, return ;; on failure. (when (and (<= scroll-begin (eat--t-cur-y cursor) scroll-end) (not (zerop n))) ;; This function doesn't move the cursor, but pushes all the ;; line below and including current line. So to keep the cursor ;; unmoved, go to the beginning of line and insert enough spaces ;; to not move the cursor. (eat--t-goto-bol) (let ((face (eat--t-term-face eat--t-term))) (eat--t-repeated-insert ?\s (1- (eat--t-cur-x cursor)) (and (eat--t-face-bg face) (eat--t-face-face face))) (goto-char (prog1 (point) ;; Insert N lines. (if (not (eat--t-face-bg face)) (eat--t-repeated-insert ?\n n) ;; SGR background attribute set, so fill the inserted ;; lines with background. (dotimes (i n) ;; Fill a line. (eat--t-repeated-insert ?\s (if (not (zerop i)) (eat--t-disp-width disp) ;; The first inserted line is already filled ;; partially, so calculate the number columns ;; left to fill. (1+ (- (eat--t-disp-width disp) (eat--t-cur-x cursor)))) (eat--t-face-face face)) ;; New line. (insert ?\n))) ;; Delete the lines that were just pushed beyond the end of ;; scroll region. (eat--t-goto-eol (- (1+ (- scroll-end scroll-begin)) (+ (- (eat--t-cur-y cursor) (1- scroll-begin)) n))) (delete-region (point) (car (eat--t-eol n))))))))) (defun eat--t-delete-line (n) "Delete N lines, preserving cursor." (let* ((disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp)) (x (eat--t-cur-x cursor)) (scroll-begin (eat--t-term-scroll-begin eat--t-term)) (scroll-end (eat--t-term-scroll-end eat--t-term))) ;; N should be positive and shouldn't exceed the number of ;; lines below cursor position and inside current scroll ;; region. (setq n (min (- (1+ (- scroll-end scroll-begin)) (1- (eat--t-cur-y cursor))) (max (or n 1) 1))) ;; Make sure we are in the scroll region and N is positive, return ;; on failure. (when (and (<= scroll-begin (eat--t-cur-y cursor) scroll-end) (not (zerop n))) ;; Delete N lines (including the current one). (eat--t-goto-bol) (save-excursion (let ((m (point))) (eat--t-goto-bol n) (delete-region m (point)))) (let ((face (eat--t-term-face eat--t-term))) ;; Keep the lines beyond end of scroll region unmoved. (when (or (< scroll-end (eat--t-disp-height disp)) (eat--t-face-bg face)) (let* ((pos (point)) (move (- (1+ (- scroll-end scroll-begin)) (- (+ (eat--t-cur-y cursor) n) (1- scroll-begin)))) (moved (eat--t-goto-eol move))) (when (or (/= (point) (point-max)) (eat--t-face-bg face)) ;; Move to the end of scroll region. (eat--t-repeated-insert ?\n (- move moved)) ;; Insert enough new lines, fill them when SGR ;; background attribute is set. (if (not (eat--t-face-bg face)) (eat--t-repeated-insert ?\n n) (dotimes (_ n) (insert ?\n) (eat--t-repeated-insert ?\s (eat--t-disp-width disp) (eat--t-face-face face))))) (goto-char pos)))) ;; Go to column where cursor is to preserve cursor position, use ;; spaces if needed to reach the position. (eat--t-repeated-insert ?\s (- (1- x) (eat--t-col-motion (1- x))))))) (defun eat--t-repeat-last-char (&optional n) "Repeat last character N times." ;; N must be at least one. (setq n (max (or n 1) 1)) (let* ((disp (eat--t-term-display eat--t-term)) (char ;; Get the character before cursor. (when (< (eat--t-disp-begin disp) (point)) (if (get-text-property (1- (point)) 'eat--t-wrap-line) ;; The character before cursor is a newline to break ;; a long line, so use the character before that. (when (< (eat--t-disp-begin disp) (1- (point))) (char-before (1- (point)))) (char-before))))) ;; Insert `char' N times. Make sure `char' is a non-nil and not ;; a newline. (when (and char (/= char ?\n)) (eat--t-write (make-string n char))))) (defun eat--t-change-scroll-region (&optional top bottom) "Change the scroll region from lines TOP to BOTTOM (inclusive). TOP defaults to 1 and BOTTOM defaults to the height of the display." (let ((disp (eat--t-term-display eat--t-term))) (setq top (or top 1)) (setq bottom (or bottom (eat--t-disp-height disp))) ;; According to DEC's documentation (found somewhere on the ;; internet, but can't remember where), TOP and BOTTOM must be ;; within display, and BOTTOM must be below TOP. Otherwise the ;; control function is a nop. (when (< 0 top bottom (1+ (eat--t-disp-height disp))) (setf (eat--t-term-scroll-begin eat--t-term) top (eat--t-term-scroll-end eat--t-term) bottom) (eat--t-goto 1 1)))) (defun eat--t-insert-mode () "Enable insert mode and disable replace mode." (setf (eat--t-term-ins-mode eat--t-term) t)) (defun eat--t-replace-mode () "Enable replace mode and disable insert mode." (setf (eat--t-term-ins-mode eat--t-term) nil)) (defun eat--t-set-sgr-params (params) "Set SGR parameters PARAMS." (let ((face (eat--t-term-face eat--t-term))) ;; Set attributes. (while params (pcase (pop params) (`(,(or 0 'nil)) (1value (setf (eat--t-face-fg face) nil)) (1value (setf (eat--t-face-bg face) nil)) (1value (setf (eat--t-face-intensity face) nil)) (1value (setf (eat--t-face-italic face) nil)) (1value (setf (eat--t-face-underline face) nil)) (1value (setf (eat--t-face-underline-color face) nil)) (1value (setf (eat--t-face-crossed face) nil)) (1value (setf (eat--t-face-conceal face) nil)) (1value (setf (eat--t-face-inverse face) nil)) (1value (setf (eat--t-face-blink face) nil)) (setf (eat--t-face-font face) (aref (eat--t-term-font-faces eat--t-term) 0))) ('(1) (setf (eat--t-face-intensity face) (eat--t-term-bold-face eat--t-term))) ('(2) (setf (eat--t-face-intensity face) (eat--t-term-faint-face eat--t-term))) ('(3) (setf (eat--t-face-italic face) (eat--t-term-italic-face eat--t-term))) ('(4) (1value (setf (eat--t-face-underline face) 'line))) ('(4 0) (1value (setf (eat--t-face-underline face) nil))) ('(4 1) (1value (setf (eat--t-face-underline face) 'line))) ('(4 2) (1value (setf (eat--t-face-underline face) 'line))) ('(4 3) (1value (setf (eat--t-face-underline face) 'wave))) ('(4 4) (1value (setf (eat--t-face-underline face) 'wave))) ('(4 5) (1value (setf (eat--t-face-underline face) 'wave))) ('(5) (setf (eat--t-face-blink face) (eat--t-term-slow-blink-face eat--t-term))) ('(6) (setf (eat--t-face-blink face) (eat--t-term-fast-blink-face eat--t-term))) ('(7) (1value (setf (eat--t-face-inverse face) t))) ('(8) (1value (setf (eat--t-face-conceal face) t))) ('(9) (1value (setf (eat--t-face-crossed face) t))) (`(,(and (pred (lambda (font) (<= 10 font 19))) font)) (setf (eat--t-face-font face) (aref (eat--t-term-font-faces eat--t-term) (- font 10)))) ('(21) (1value (setf (eat--t-face-underline face) 'line))) ('(22) (1value (setf (eat--t-face-intensity face) nil))) ('(23) (1value (setf (eat--t-face-italic face) nil))) ('(24) (1value (setf (eat--t-face-underline face) nil))) ('(25) (1value (setf (eat--t-face-blink face) nil))) ('(27) (1value (setf (eat--t-face-inverse face) nil))) ('(28) (1value (setf (eat--t-face-conceal face) nil))) ('(29) (1value (setf (eat--t-face-crossed face) nil))) (`(,(and (pred (lambda (color) (<= 30 color 37))) color)) (setf (eat--t-face-fg face) (face-foreground (aref (eat--t-term-color-faces eat--t-term) (- color 30)) nil t))) ('(38) (pcase (pop params) ('(2) (setf (eat--t-face-fg face) (let ((r (car (pop params))) (g (car (pop params))) (b (car (pop params)))) (when (and r (<= 0 r 255) g (<= 0 g 255) b (<= 0 b 255)) (format "#%02x%02x%02x" r g b))))) ('(5) (let ((color (car (pop params)))) (setf (eat--t-face-fg face) (when (and color (<= 0 color 255)) (face-foreground (aref (eat--t-term-color-faces eat--t-term) color) nil t))))))) ('(39) (1value (setf (eat--t-face-fg face) nil))) (`(,(and (pred (lambda (color) (<= 40 color 47))) color)) (setf (eat--t-face-bg face) (face-foreground (aref (eat--t-term-color-faces eat--t-term) (- color 40)) nil t))) ('(48) (setf (eat--t-face-bg face) (pcase (pop params) ('(2) (let ((r (car (pop params))) (g (car (pop params))) (b (car (pop params)))) (when (and r (<= 0 r 255) g (<= 0 g 255) b (<= 0 b 255)) (format "#%02x%02x%02x" r g b)))) ('(5) (let ((color (car (pop params)))) (when (and color (<= 0 color 255)) (face-foreground (aref (eat--t-term-color-faces eat--t-term) color) nil t))))))) ('(49) (1value (setf (eat--t-face-bg face) nil))) ('(58) (setf (eat--t-face-underline-color face) (pcase (pop params) ('(2) (let ((r (car (pop params))) (g (car (pop params))) (b (car (pop params)))) (when (and r (<= 0 r 255) g (<= 0 g 255) b (<= 0 b 255)) (format "#%02x%02x%02x" r g b)))) ('(5) (let ((color (car (pop params)))) (when (and color (<= 0 color 255)) (face-foreground (aref (eat--t-term-color-faces eat--t-term) color) nil t))))))) ('(59) (1value (setf (eat--t-face-underline-color face) nil))) (`(,(and (pred (lambda (color) (<= 90 color 97))) color)) (setf (eat--t-face-fg face) (face-foreground (aref (eat--t-term-color-faces eat--t-term) (- color 82)) nil t))) (`(,(and (pred (lambda (color) (<= 100 color 107))) color)) (setf (eat--t-face-bg face) (face-foreground (aref (eat--t-term-color-faces eat--t-term) (- color 92)) nil t))))) ;; Update face according to the attributes. (setf (eat--t-face-face face) `(,@(and-let* ((fg (or (if (eat--t-face-conceal face) (eat--t-face-bg face) (eat--t-face-fg face)) (cond ((eat--t-face-inverse face) (face-foreground 'default)) ((eat--t-face-conceal face) (face-background 'default)))))) (list (if (eat--t-face-inverse face) :background :foreground) fg)) ,@(and-let* ((bg (or (eat--t-face-bg face) (and (eat--t-face-inverse face) (face-background 'default))))) (list (if (eat--t-face-inverse face) :foreground :background) bg)) ,@(and-let* ((underline (eat--t-face-underline face))) (list :underline (list :color (eat--t-face-underline-color face) :style underline))) ,@(and-let* ((crossed (eat--t-face-crossed face))) ;; REVIEW: How about colors? No terminal supports ;; crossed attribute with colors, so we'll need to be ;; creative to add the feature. `(:strike-through t)) :inherit (,@(and-let* ((intensity (eat--t-face-intensity face))) (list intensity)) ,@(and-let* ((italic (eat--t-face-italic face))) (list italic)) ,@(and-let* ((blink (eat--t-face-blink face))) (list blink)) ,(eat--t-face-font face)))))) (defun eat--t-enable-keypad () "Enable keypad." (1value (setf (eat--t-term-keypad-mode eat--t-term) t))) (defun eat--t-disable-keypad () "Disable keypad." (1value (setf (eat--t-term-keypad-mode eat--t-term) nil))) (defun eat--t-enable-sgr-mouse-encoding () "Arrange that the following mouse events will be encoded like SGR." (setf (eat--t-term-mouse-encoding eat--t-term) 'sgr)) (defun eat--t-disable-sgr-mouse-encoding () "Arrange that the following mouse events won't be encoded like SGR." (setf (eat--t-term-mouse-encoding eat--t-term) nil)) (defun eat--t-set-mouse-mode (mode) "Set current mouse mode to MODE. MODE should be one of nil and `x10', `normal', `button-event', `any-event'." (setf (eat--t-term-mouse-mode eat--t-term) mode) ;; When MODE is nil, disable mouse. (unless mode (eat--t-disable-sgr-mouse-encoding)) ;; `x10' mouse mode doesn't need to keep track of the mouse buttons ;; pressed. (when (or (not mode) (eq mode 'x10)) (setf (eat--t-term-mouse-pressed eat--t-term) nil)) ;; Inform the UI. (funcall (eat--t-term-grab-mouse-fn eat--t-term) eat--t-term (pcase-exhaustive mode ('x10 :click) ('normal :modifier-click) ('button-event :drag) ('any-event :all) ('nil nil)))) (defun eat--t-enable-x10-mouse () "Enable X10 mouse tracking." (eat--t-set-mouse-mode 'x10)) (defun eat--t-enable-normal-mouse () "Enable normal mouse tracking." (eat--t-set-mouse-mode 'normal)) (defun eat--t-enable-button-event-mouse () "Enable button-event mouse tracking." (eat--t-set-mouse-mode 'button-event)) (defun eat--t-enable-any-event-mouse () "Enable any-event mouse tracking." (eat--t-set-mouse-mode 'any-event)) (defun eat--t-disable-mouse () "Disable mouse tracking." (eat--t-set-mouse-mode nil)) (defun eat--t-enable-focus-event () "Enable sending focus events." (1value (setf (eat--t-term-focus-event-mode eat--t-term) t)) (funcall (eat--t-term-set-focus-ev-mode-fn eat--t-term) eat--t-term t)) (defun eat--t-disable-focus-event () "Disable sending focus events." (1value (setf (eat--t-term-focus-event-mode eat--t-term) nil)) (funcall (eat--t-term-set-focus-ev-mode-fn eat--t-term) eat--t-term nil)) (defun eat--t-set-title (title) "Set the title of terminal to TITLE." ;; Update title. (setf (eat--t-term-title eat--t-term) title) ;; Inform the UI. (funcall (eat--t-term-set-title-fn eat--t-term) eat--t-term title)) (defun eat--t-set-cwd (url) "Set the working directory of terminal to URL." (setq url (url-generic-parse-url url)) (when (string= (url-type url) "file") (let ((host (url-host url)) (dir (expand-file-name (file-name-as-directory (url-unhex-string (url-filename url)))))) ;; Inform the UI. (funcall (eat--t-term-set-cwd-fn eat--t-term) eat--t-term host dir)))) (defun eat--t-send-device-attrs (n format) "Return device attributes. FORMAT is the format of parameters in output. N should be zero." (pcase-exhaustive format ('nil (when (= (or n 0) 0) (funcall (eat--t-term-input-fn eat--t-term) eat--t-term "\e[?12;4c"))) (?> (when (= (or n 0) 0) (funcall (eat--t-term-input-fn eat--t-term) eat--t-term "\e[>0;0;0c"))))) (defun eat--t-send-graphics-attrs (attr operation) "Send graphics attributes. ATTR is the attribute requested, OPERATION is the thing to do (only reading an attribute is supported)." (funcall (eat--t-term-input-fn eat--t-term) eat--t-term (if (memq operation '(1 4)) (pcase attr (1 ;; TODO: Maybe provide an user option to control the value? ;; count? (format "\e[?1;0;256S")) (2 ;; TODO: Maybe provide an user option to control the value? (let ((disp (eat--t-term-display eat--t-term))) (format "\e[?2;0;%i;%iS" (min (* (eat--t-disp-width disp) (eat--t-term-char-width eat--t-term)) 1000) (min (* (eat--t-disp-height disp) (eat--t-term-char-height eat--t-term)) 1000)))) (_ (format "\e[?%i;1S" attr))) (format "\e[?%i;%iS" attr (if (<= 1 attr 2) (if (<= 2 operation 3) 3 2) 1))))) (defun eat--t-report-foreground-color () "Report the current default foreground color to the client." (funcall (eat--t-term-input-fn eat--t-term) eat--t-term (let ((rgb (or (color-values (face-foreground 'default)) ;; On terminals like TTYs the above returns nil. ;; Terminals usually have a white foreground, so... '(255 255 255)))) (format "\e]10;rgb:%04x/%04x/%04x\e\\" (pop rgb) (pop rgb) (pop rgb))))) (defun eat--t-report-background-color () "Report the current default background color to the client." (funcall (eat--t-term-input-fn eat--t-term) eat--t-term (let ((rgb (or (color-values (face-background 'default)) ;; On terminals like TTYs the above returns nil. ;; Terminals usually have a black background, so... '(0 0 0)))) (format "\e]11;rgb:%04x/%04x/%04x\e\\" (pop rgb) (pop rgb) (pop rgb))))) (defun eat--t-manipulate-selection (targets data) "Set and send current selection. TARGETS is a string containing zero or more characters from the set `c', `p', `q', `s', `0', `1', `2', `3', `4', `5', `6', and `7'. DATA is the selection data encoded in base64." (when (string-empty-p targets) (setq targets "s0")) (if (string= data "?") ;; The client is requesting for clipboard content, let's try to ;; fulfill the request. (funcall (eat--t-term-input-fn eat--t-term) eat--t-term (let ((str nil) (n 0)) ;; Remove invalid and duplicate targets from TARGETS before ;; processing it and sending it back. (setq targets (apply #'string (cl-delete-duplicates (cl-delete-if-not (lambda (c) (or (<= ?0 c ?7) (memq c '(?c ?p ?q ?s)))) (string-to-list targets))))) (while (and (not str) (< n (length targets))) (setq str (pcase (aref targets n) ;; c, p, q and s targets are handled by the UI, and they ;; might refuse to give the clipboard content. (?c (funcall (eat--t-term-manipulate-selection-fn eat--t-term) eat--t-term :clipboard t)) (?p (funcall (eat--t-term-manipulate-selection-fn eat--t-term) eat--t-term :primary t)) (?q (funcall (eat--t-term-manipulate-selection-fn eat--t-term) eat--t-term :secondary t)) (?s (funcall (eat--t-term-manipulate-selection-fn eat--t-term) eat--t-term :select t)) ;; 0 to 9 targets are handled by us, and always work. ((and (pred (<= ?0)) (pred (>= ?7)) i) (aref (eat--t-term-cut-buffers eat--t-term) (- i ?0))))) (cl-incf n)) ;; No string to send, so send an empty string. (unless str (setq str "")) (format "\e]52;%s;%s\e\\" targets (base64-encode-string (encode-coding-string str locale-coding-system) 'no-line-break)))) ;; The client is requesting to set clipboard content, let's try to ;; fulfill the request. (let ((str (ignore-errors (decode-coding-string (base64-decode-string data) locale-coding-system)))) (seq-doseq (target targets) (pcase target ;; c, p, q and s targets are handled by the UI, and they ;; might reject the new clipboard content. (?c (funcall (eat--t-term-manipulate-selection-fn eat--t-term) eat--t-term :clipboard str)) (?p (funcall (eat--t-term-manipulate-selection-fn eat--t-term) eat--t-term :primary str)) (?q (funcall (eat--t-term-manipulate-selection-fn eat--t-term) eat--t-term :secondary str)) (?s (funcall (eat--t-term-manipulate-selection-fn eat--t-term) eat--t-term :select str)) ;; 0 to 7 targets are handled by us, and always work. ((and (pred (<= ?0)) (pred (>= ?7)) i) (aset (eat--t-term-cut-buffers eat--t-term) (- i ?0) str))))))) (defun eat--t-sixel-init () "Initialize Sixel mode." (let ((default-palette (eval-when-compile (vconcat '("#000000" "#3333cc" "#cc2121" "#33cc33" "#cc33cc" "#33cccc" "#cccc33" "#878787" "#424242" "#545499" "#994242" "#549954" "#995499" "#549999" "#999954" "#cccccc") (make-list 240 "#000000"))))) (dotimes (i 256) (setf (aref (eat--t-term-sixel-palette eat--t-term) i) (aref default-palette i)))) ;; We just follow XTerm and set the initial foreground color to 3. ;; But even the XTerm authors are unsure about what was the actual ;; default. (setf (eat--t-term-sixel-color eat--t-term) 3) (while (< (eat--t-term-sixel-buffer-size eat--t-term) (+ (* (eat--t-term-char-height eat--t-term) 2) 5)) (let ((new (cons (cons 0 (make-vector 1000 nil)) (cons (cadr (eat--t-term-sixel-buffer eat--t-term)) (eat--t-term-sixel-buffer eat--t-term))))) (setf (cddr (cadr (eat--t-term-sixel-buffer eat--t-term))) new) (setf (cadr (eat--t-term-sixel-buffer eat--t-term)) new) (setf (eat--t-term-sixel-buffer eat--t-term) new)) (cl-incf (eat--t-term-sixel-buffer-size eat--t-term))) (let* ((beg (eat--t-term-sixel-buffer eat--t-term)) (line beg) (loop t)) (while loop (cl-loop for i from 0 to (1- (caar line)) do (aset (cdar line) i nil)) (setf (caar line) 0) (setq line (cddr line)) (when (eq line beg) (setq loop nil)))) (let ((cursor (eat--t-disp-cursor (eat--t-term-display eat--t-term)))) (setf (eat--t-cur-sixel-x cursor) 0) (setf (eat--t-cur-sixel-y cursor) 0) (setf (eat--t-cur-sixel-beg cursor) (eat--t-term-sixel-buffer eat--t-term)) (unless (eat--t-term-sixel-scroll-mode eat--t-term) (setf (eat--t-term-sixel-initial-cursor-pos eat--t-term) (cons (eat--t-cur-y cursor) (eat--t-cur-x cursor))) (eat--t-goto 1 1)))) (defun eat--t-sixel-write (str beg end count) "Write substring [BEG..END) of STR COUNT times to Sixel buffer." (let ((cursor (eat--t-disp-cursor (eat--t-term-display eat--t-term)))) (dotimes (_ count) (cl-loop for i from beg to (1- end) do (when (= (eat--t-cur-sixel-x cursor) 1000) (setf (eat--t-cur-sixel-x cursor) 999)) (let ((bitmap (- (aref str i) ??)) (j 0) (line (eat--t-cur-sixel-beg cursor)) (color (aref (eat--t-term-sixel-palette eat--t-term) (eat--t-term-sixel-color eat--t-term)))) (while (< j 6) (when (/= (logand bitmap (ash 1 j)) 0) (aset (cdar line) (eat--t-cur-sixel-x cursor) color)) (setf line (cddr line)) (cl-incf j))) (cl-incf (eat--t-cur-sixel-x cursor)))) (let ((i 5) (line (eat--t-cur-sixel-beg cursor))) (while (>= i 0) (setf (caar line) (max (eat--t-cur-sixel-x cursor) (caar line))) (setf line (cddr line)) (cl-decf i))) (when (= (eat--t-cur-sixel-x cursor) 1000) (setf (eat--t-cur-sixel-x cursor) 999)))) (defun eat--t-sixel-render-bitmap (bitmap) "Render BITMAP. CHAR-SIZE is the width and height of a character." (let ((char-size (cons (length (aref bitmap 0)) (length bitmap)))) (pcase-exhaustive (eat--t-term-sixel-render-format eat--t-term) ('none) ('background (when-let* ((color (aref (aref bitmap 0) 0))) (put-text-property (point) (1+ (point)) 'face `(:background ,color)))) ('half-block (let ((fg (aref (aref bitmap (/ (cdr char-size) 2)) 0)) (bg (aref (aref bitmap 0) 0))) (when (or fg bg) (put-text-property (point) (1+ (point)) 'display (propertize "â–„" 'face `(,@(and bg `(:background ,bg)) :foreground ,(or fg (face-background 'default)))))))) ('svg (put-text-property (point) (1+ (point)) 'display `(image :type svg :data ,(apply #'concat (format "" (let ((strs '(""))) (dotimes (i (cdr char-size)) (dotimes (j (car char-size)) (when-let* ((color (aref (aref bitmap i) j))) (push (concat "" color)) strs)))) strs)) ,@(eat--t-term-sixel-image-extra-props eat--t-term)))) ('xpm (put-text-property (point) (1+ (point)) 'display `(image :type xpm :data ,(let ((color-map nil) (pixmap nil) (color-key-length (length (format "%x" (* (car char-size) (cdr char-size)))))) (dotimes (i (cdr char-size)) (push nil pixmap) (dotimes (j (car char-size)) (let ((idx (format (format "%%0%ix" color-key-length) (+ (* i (car char-size)) j))) (color (or (aref (aref bitmap i) j) "None"))) (push (format "%s c %s" idx color) color-map) (push idx (car pixmap))))) (concat "/* XPM */\n" "static char * XFACE[] = {\n" (format "\"%i %i %i %i\",\n" (car char-size) (cdr char-size) (* (car char-size) (cdr char-size)) color-key-length) (mapconcat (lambda (line) (format "\"%s\",\n" line)) color-map "") (mapconcat (lambda (row) (format "\"%s\"" (string-join (nreverse row)))) (nreverse pixmap) ",\n") "\n};")) ,@(eat--t-term-sixel-image-extra-props eat--t-term))))))) (defun eat--t-sixel-flush-line (nullify) "Flush current (not Sixel) line to the display. If NULLIFY is non-nil, nullify flushed part of Sixel buffer." (let* ((disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp)) (sixel-col-count 0) (char-count 0) (lines []) (char-size (cons (eat--t-term-char-width eat--t-term) (eat--t-term-char-height eat--t-term)))) (when (< (length lines) (cdr char-size)) (setq lines (make-vector (cdr char-size) nil))) (let ((line (eat--t-term-sixel-buffer eat--t-term))) (dotimes (i (cdr char-size)) (setq sixel-col-count (max sixel-col-count (caar line))) (aset lines i (car line)) (setf line (cddr line)))) (setq char-count (min (/ (+ sixel-col-count (1- (car char-size))) (car char-size)) (- (eat--t-disp-width disp) (1- (eat--t-cur-x cursor))))) (save-excursion (let ((j 0)) (dotimes (_ char-count) (unless (equal (get-text-property (point) 'eat--t-sixel-bitmap-size) char-size) (let ((color (unless (memq (char-after (point)) '(?\n nil)) (plist-get (get-text-property (point) 'face) :background))) (bitmap (make-vector (cdr char-size) nil))) (dotimes (i (cdr char-size)) (aset bitmap i (make-vector (car char-size) color))) (insert (propertize " " 'eat--t-sixel-bitmap-size char-size 'eat--t-sixel-bitmap bitmap)) (unless (memq (char-after (point)) '(?\n nil)) (delete-region (point) (1+ (point)))) (backward-char))) (let ((bitmap (get-text-property (point) 'eat--t-sixel-bitmap)) (i 0)) (while (and (< i (car char-size)) (< j 1000)) (dotimes (k (cdr char-size)) (when-let* ((color (aref (cdr (aref lines k)) j))) (setf (aref (aref bitmap k) i) color))) (cl-incf i) (cl-incf j)) (eat--t-sixel-render-bitmap bitmap)) (forward-char) (eat--t-fix-partial-multi-col-char 'preserve-face)))) (dotimes (_ (cdr char-size)) (let ((line (eat--t-term-sixel-buffer eat--t-term))) (when nullify (cl-loop for i from 0 to (1- (caar line)) do (aset (cdar line) i nil)) (setf (caar line) 0)) (setf (eat--t-term-sixel-buffer eat--t-term) (cddr line)))) (cl-decf (eat--t-cur-sixel-y cursor) (cdr char-size)))) (defun eat--t-sixel-newline () "Move to a new Sixel line." (let ((cursor (eat--t-disp-cursor (eat--t-term-display eat--t-term)))) (setf (eat--t-cur-sixel-x cursor) 0) (cl-incf (eat--t-cur-sixel-y cursor) 6) (dotimes (_ 6) (setf (eat--t-cur-sixel-beg cursor) (cddr (eat--t-cur-sixel-beg cursor)))) (while (>= (eat--t-cur-sixel-y cursor) (eat--t-term-char-height eat--t-term)) (eat--t-sixel-flush-line 'nullify) (if (eat--t-term-sixel-scroll-mode eat--t-term) (eat--t-index) (eat--t-cur-down))))) (defun eat--t-sixel-set-color-reg (reg spec) "Set Sixel color register REG as described by SPEC." (when (<= reg 255) (let ((color (cond ((= (car spec) 1) (when (and (<= (nth 1 spec) 360) (<= (nth 2 spec) 100) (<= (nth 3 spec) 100)) (let ((rgb (color-hsl-to-rgb (/ (nth 1 spec) 360.0) (/ (nth 3 spec) 100.0) (/ (nth 2 spec) 100.0)))) (color-rgb-to-hex (nth 0 rgb) (nth 1 rgb) (nth 2 rgb) 2)))) ((= (car spec) 2) (when (and (<= (nth 1 spec) 100) (<= (nth 2 spec) 100) (<= (nth 3 spec) 100)) (color-rgb-to-hex (/ (nth 1 spec) 100.0) (/ (nth 2 spec) 100.0) (/ (nth 3 spec) 100.0) 2)))))) (when color (aset (eat--t-term-sixel-palette eat--t-term) reg color))))) (defun eat--t-sixel-cleanup () "Cleanup before potential exit from Sixel mode." (cl-letf* ((cursor (eat--t-disp-cursor (eat--t-term-display eat--t-term))) ((eat--t-cur-sixel-y cursor) (eat--t-cur-sixel-y cursor)) ((eat--t-term-sixel-buffer eat--t-term) (eat--t-term-sixel-buffer eat--t-term))) (while (>= (eat--t-cur-sixel-y cursor) -5) (eat--t-sixel-flush-line nil) (if (eat--t-term-sixel-scroll-mode eat--t-term) (eat--t-index) (eat--t-cur-down)))) (unless (eat--t-term-sixel-scroll-mode eat--t-term) (eat--t-goto (car (eat--t-term-sixel-initial-cursor-pos eat--t-term)) (cdr (eat--t-term-sixel-initial-cursor-pos eat--t-term))))) (defun eat--t-sixel-enable-scrolling () "Enable Sixel scrolling mode." (setf (eat--t-term-sixel-scroll-mode eat--t-term) t)) (defun eat--t-sixel-disable-scrolling () "Disable Sixel scrolling mode." (setf (eat--t-term-sixel-scroll-mode eat--t-term) nil)) (defun eat--t-ui-cmd (cmd) "Call UI's UIC handler to handle CMD." (funcall (eat--t-term-ui-cmd-fn eat--t-term) eat--t-term cmd)) (defun eat--t-set-modes (params format) "Set modes according to PARAMS in format FORMAT." ;; Dispatch the request to appropriate function. (pcase format ('nil (while params (pcase (pop params) ('(4) (eat--t-insert-mode))))) (?? (while params (pcase (pop params) ('(1) (eat--t-enable-keypad)) ('(7) (eat--t-enable-auto-margin)) ('(9) (eat--t-enable-x10-mouse)) ('(12) (eat--t-blinking-cursor)) ('(25) (eat--t-show-cursor)) ('(80) (eat--t-sixel-disable-scrolling)) ('(1000) (eat--t-enable-normal-mouse)) ('(1002) (eat--t-enable-button-event-mouse)) ('(1003) (eat--t-enable-any-event-mouse)) ('(1004) (eat--t-enable-focus-event)) ('(1006) (eat--t-enable-sgr-mouse-encoding)) ('(1048) (eat--t-save-cur)) (`(,(or 1047 1049)) (eat--t-enable-alt-disp)) ('(2004) (eat--t-enable-bracketed-yank))))))) (defun eat--t-reset-modes (params format) "Reset modes according to PARAMS in format FORMAT." ;; Dispatch the request to appropriate function. (pcase format ('nil (while params (pcase (pop params) ('(4) (eat--t-replace-mode))))) (?? (while params (pcase (pop params) ('(1) (eat--t-disable-keypad)) ('(7) (eat--t-disable-auto-margin)) ('(12) (eat--t-non-blinking-cursor)) ('(25) (eat--t-hide-cursor)) ('(80) (eat--t-sixel-enable-scrolling)) (`(,(or 9 1000 1002 1003)) (eat--t-disable-mouse)) ('(1004) (eat--t-disable-focus-event)) ('(1006) (eat--t-disable-sgr-mouse-encoding)) ('(1047) (eat--t-disable-alt-disp 'dont-move-cursor)) ('(1048) (eat--t-restore-cur)) ('(1049) (eat--t-disable-alt-disp)) ('(2004) (eat--t-disable-bracketed-yank))))))) (defun eat--t-handle-output (output) "Parse and evaluate OUTPUT." (let ((index 0)) (while (/= index (length output)) (pcase-exhaustive (eat--t-term-parser-state eat--t-term) ('nil (let ((ins-beg index)) (while (and (/= index (length output)) (not (memq (aref output index) '( ?\0 ?\a ?\b ?\t ?\n ?\v ?\f ?\r ?\C-n ?\C-o ?\e #x7f)))) (cl-incf index)) (when (/= ins-beg index) ;; Insert. (eat--t-write output ins-beg index)) (when (/= index (length output)) ;; Dispatch control sequence. (cl-incf index) (pcase (aref output (1- index)) (?\a (eat--t-bell)) (?\b (eat--t-cur-left 1)) (?\t (eat--t-horizontal-tab 1)) (?\n (eat--t-line-feed)) (?\v (eat--t-index)) (?\f (eat--t-form-feed)) (?\r ;; Avoid going to line home just before a line feed, ;; we can just insert a new line if we are at the ;; end of display. (unless (and (/= index (length output)) (= (aref output index) ?\n)) (eat--t-carriage-return))) (?\C-n (eat--t-change-charset 'g1)) (?\C-o (eat--t-change-charset 'g0)) (?\e (1value (setf (eat--t-term-parser-state eat--t-term) '(read-esc)))) ;; Others are ignored. )))) ('(read-esc) (let ((type (aref output index))) (cl-incf index) (1value (setf (eat--t-term-parser-state eat--t-term) nil)) ;; Dispatch control sequence. (pcase type ;; ESC (. (?\( (setf (eat--t-term-parser-state eat--t-term) '(read-charset-standard g0 ""))) ;; ESC ). (?\) (setf (eat--t-term-parser-state eat--t-term) '(read-charset-standard g1 ""))) ;; ESC *. (?* (setf (eat--t-term-parser-state eat--t-term) '(read-charset-standard g2 ""))) ;; ESC +. (?+ (setf (eat--t-term-parser-state eat--t-term) '(read-charset-standard g3 ""))) ;; ESC -. (?- (setf (eat--t-term-parser-state eat--t-term) '(read-charset-vt300 g1 ""))) ;; ESC .. (?. (setf (eat--t-term-parser-state eat--t-term) '(read-charset-vt300 g2 ""))) ;; ESC /. (?/ (setf (eat--t-term-parser-state eat--t-term) '(read-charset-vt300 g3 ""))) ;; ESC 7. (?7 (eat--t-save-cur)) ;; ESC 8. (?8 (eat--t-restore-cur)) ;; ESC D. (?D (eat--t-index)) ;; ESC E. (?E (eat--t-line-feed)) ;; ESC M. (?M (eat--t-reverse-index)) ;; ESC P, or DCS. (?P (1value (setf (eat--t-term-parser-state eat--t-term) `(read-dcs-params (read-dcs-function) ,(list nil))))) ;; ESC X, or SOS. (?X (1value (setf (eat--t-term-parser-state eat--t-term) '(read-sos "")))) ;; ESC [, or CSI. (?\[ (1value (setf (eat--t-term-parser-state eat--t-term) '(read-csi-format)))) ;; ESC ], or OSC. (?\] (1value (setf (eat--t-term-parser-state eat--t-term) '(read-osc "")))) ;; ESC ^, or PM. (?^ (1value (setf (eat--t-term-parser-state eat--t-term) '(read-pm "")))) ;; ESC _, or APC. (?_ (1value (setf (eat--t-term-parser-state eat--t-term) '(read-apc "")))) ;; ESC c. (?c (eat--t-reset)) ;; ESC n. (?n (eat--t-change-charset 'g2)) ;; ESC o. (?o (eat--t-change-charset 'g3))))) ('(read-csi-format) (let ((format nil)) (pcase (aref output index) (?? (setq format ??) (cl-incf index)) (?> (setq format ?>) (cl-incf index)) (?= (setq format ?=) (cl-incf index))) (setf (eat--t-term-parser-state eat--t-term) `(read-csi-params ,format ,(list (list nil)))))) (`(read-csi-params ,format ,params) ;; Interpretion of the parameter depends on `format' and ;; other things (including things we haven't gotten yet) ;; according to the standard. We don't recognize any other ;; format of parameters, so we can skip any checks. (let ((loop t)) (while loop (cond ((= index (length output)) ;; Output exhausted. We need to wait for more. (setf (eat--t-term-parser-state eat--t-term) `(read-csi-params ,format ,params)) (setq loop nil)) ((not (<= ?0 (aref output index) ?\;)) ;; End of parameters. ;; NOTE: All parameter and their parts are in reverse ;; order! (setf (eat--t-term-parser-state eat--t-term) `(read-csi-function ,format ,params nil)) (setq loop nil)) (t (cond ((= (aref output index) ?:) ;; New parameter substring. (push nil (car params))) ((= (aref output index) ?\;) ;; New parameter. (push (list nil) params)) (t ; (<= ?0 (aref output index) ?9) ;; Number, save it. (setf (caar params) (+ (* (or (caar params) 0) 10) (- (aref output index) #x30))))) (cl-incf index)))))) (`(read-csi-function ,format ,params ,function) (let ((loop t)) (while loop (cond ((= index (length output)) (setf (eat--t-term-parser-state eat--t-term) `(read-csi-function ,format ,params ,function)) (setq loop nil))) (push (aref output index) function) (cl-incf index) (when (<= ?@ (car function) ?~) ;; Now we have enough information to execute it! (setq loop nil) (setf (eat--t-term-parser-state eat--t-term) nil) ;; NOTE: `function' and `params' are in reverse order! (pcase (list function format params) ;; CSI @. (`((?@) nil ((,n))) (eat--t-insert-char n)) ;; CSI A. ;; CSI k. (`((,(or ?A ?k)) nil ((,n))) (eat--t-cur-up n)) ;; CSI B. ;; CSI e. (`((,(or ?B ?e)) nil ((,n))) (eat--t-cur-down n)) ;; CSI C. ;; CSI a. (`((,(or ?C ?a)) nil ((,n))) (eat--t-cur-right n)) ;; CSI D. ;; CSI j. (`((,(or ?D ?j)) nil ((,n))) (eat--t-cur-left n)) ;; CSI E. (`((?E) nil ((,n))) (eat--t-beg-of-prev-line n)) ;; CSI F. (`((?F) nil ((,n))) (eat--t-beg-of-next-line n)) ;; CSI G. ;; CSI `. (`((,(or ?G ?`)) nil ((,n))) (eat--t-cur-horizontal-abs n)) ;; CSI ; H ;; CSI ; f (`((,(or ?H ?f)) nil ,(and (pred listp) params)) (eat--t-goto (caadr params) (caar params))) ;; CSI I. (`((?I) nil ((,n))) (eat--t-horizontal-tab n)) ;; CSI J. (`((?J) nil ((,n))) (eat--t-erase-in-disp n)) ;; CSI K. (`((?K) nil ((,n))) (eat--t-erase-in-line n)) ;; CSI L. (`((?L) nil ((,n))) (eat--t-insert-line n)) ;; CSI M. (`((?M) nil ((,n))) (eat--t-delete-line n)) ;; CSI P. (`((?P) nil ((,n))) (eat--t-delete-char n)) ;; CSI S. (`((?S) nil ((,n))) (eat--t-scroll-up n)) ;; CSI ? ; ; ... S. (`((?S) ?? ,(or `((,_) (,operation) (,attr)) `((,_) (,_) (,operation) (,attr)))) (eat--t-send-graphics-attrs attr operation)) ;; CSI T. (`((?T) nil ((,n))) (eat--t-scroll-down n)) ;; CSI X. (`((?X) nil ((,n))) (eat--t-erase-char n)) ;; CSI Z. (`((?Z) nil ((,n))) (eat--t-horizontal-backtab n)) ;; CSI b. (`((?b) nil ((,n))) (eat--t-repeat-last-char n)) ;; CSI c. ;; CSI > c. (`((?c) ,format ((,n))) (eat--t-send-device-attrs n format)) ;; CSI d. (`((?d) nil ((,n))) (eat--t-cur-vertical-abs n)) ;; CSI ... h. ;; CSI ? ... h. (`((?h) ,format ,(and (pred listp) params)) ;; Reverse `params' to get it into the correct ;; order. (setq params (nreverse params)) (let ((p params)) (while p (setf (car p) (nreverse (car p))) (setq p (cdr p)))) (eat--t-set-modes params format)) ;; CSI ... l. ;; CSI ? ... l. (`((?l) ,format ,(and (pred listp) params)) ;; Reverse `params' to get it into the correct ;; order. (setq params (nreverse params)) (let ((p params)) (while p (setf (car p) (nreverse (car p))) (setq p (cdr p)))) (eat--t-reset-modes params format)) ;; CSI ... m. (`((?m) nil ,(and (pred listp) params)) ;; Reverse `params' to get it into the correct ;; order. (setq params (nreverse params)) (let ((p params)) (while p (setf (car p) (nreverse (car p))) (setq p (cdr p)))) (eat--t-set-sgr-params params)) ;; CSI 6 n. (`((?n) nil ((,n))) (eat--t-device-status-report n)) ;; CSI SP q. (`((?q ?\ ) nil ((,n))) (eat--t-set-cursor-style n)) ;; CSI ; r. (`((?r) nil ,(and (pred listp) params)) (eat--t-change-scroll-region (caadr params) (caar params))) ;; CSI s. (`((?s) nil nil) (eat--t-save-cur)) ;; CSI u. (`((?u) nil nil) (eat--t-restore-cur))))))) (`(,(and (or 'read-sos 'read-osc 'read-pm 'read-apc) state) ,buf) ;; Find the end of string. (let ((match (string-match (if (eq state 'read-osc) (rx (or ?\a ?\\)) (rx ?\\)) output index))) (if (not match) (progn ;; Not found, store the text to process it later when ;; we get the end of string. (setf (eat--t-term-parser-state eat--t-term) `(,state ,(concat buf (substring output index)))) (setq index (length output))) ;; Matched! Get the string from the output and previous ;; runs. (let ((str (concat buf (substring output index match)))) (setq index (match-end 0)) ;; Is it really the end of string? (if (and (= (aref output match) ?\\) (not (or (zerop (length str)) (= (aref str (1- (length str))) ?\e)))) ;; No. Push the '\' character to process later. (setf (eat--t-term-parser-state eat--t-term) `(,state ,(concat str "\\"))) ;; Yes! It's the end! We can parse it. (when (= (aref output match) ?\\) (setq str (substring str 0 (1- (length str))))) (setf (eat--t-term-parser-state eat--t-term) nil) ;; Dispatch control sequence. (pcase state ('read-osc (pcase str ;; OSC 0 ; ST. ;; OSC 2 ; ST. ((rx string-start (or ?0 ?2) ?\; (let title (zero-or-more anything)) string-end) (eat--t-set-title title)) ;; OSC 7 ; ST. ((rx string-start ?7 ?\; (let url (zero-or-more anything)) string-end) (eat--t-set-cwd url)) ;; OSC 1 0 ; ? ST. ("10;?" (eat--t-report-foreground-color)) ;; OSC 1 1 ; ? ST. ("11;?" (eat--t-report-background-color)) ;; OSC 5 1 ; ST. ((rx string-start "51;" (let cmd (zero-or-more anything)) string-end) (eat--t-ui-cmd cmd)) ;; OSC 5 2 ; ; ST. ((rx string-start "52;" (let targets (zero-or-more (not (any ?\;)))) ?\; (let data (zero-or-more anything)) string-end) (eat--t-manipulate-selection targets data)))))))))) (`(read-dcs-params ,next-state ,params) ;; There is no standard format of device control strings, but ;; all DEC and XTerm DCS sequences (including those we ;; support) follow this particular format. (let ((loop t)) (while loop (cond ((= index (length output)) ;; Output exhausted. We need to wait for more. (setf (eat--t-term-parser-state eat--t-term) `(read-dcs-params ,next-state ,params)) (setq loop nil)) ((not (or (<= ?0 (aref output index) ?9) (= (aref output index) ?\;))) ;; End of parameters. ;; NOTE: All parameter and their parts are in reverse ;; order! (setf (eat--t-term-parser-state eat--t-term) `(,@next-state ,params)) (setq loop nil)) (t (if (= (aref output index) ?\;) ;; New parameter. (push nil params) ;; Number, save it. (setf (car params) (+ (* (or (car params) 0) 10) (- (aref output index) #x30)))) (cl-incf index)))))) (`(read-dcs-function ,params) (cl-incf index) (pcase (aref output (1- index)) (?q (setf (eat--t-term-parser-state eat--t-term) `(read-sixel init ,params))) (?\e (setf (eat--t-term-parser-state eat--t-term) '(read-potential-st (read-dcs-fallback)))) (_ (setf (eat--t-term-parser-state eat--t-term) '(read-dcs-fallback)) (cl-decf index)))) (`(read-potential-st ,else) (if (/= (aref output index) ?\\) (setf (eat--t-term-parser-state eat--t-term) else) (setf (eat--t-term-parser-state eat--t-term) nil) (cl-incf index))) (`(read-dcs-fallback) (let ((loop t)) (while (and loop (/= index (length output))) (when (= (aref output index) ?\e) (setf (eat--t-term-parser-state eat--t-term) '(read-potential-st (read-dcs-fallback))) (setq loop nil)) (cl-incf index)))) (`(read-sixel ,cmd ,params) (when cmd (pcase cmd ('init (eat--t-sixel-init)) ('set-color (when (and (= (length params) 1) (<= (or (car params) 0) 255)) (setf (eat--t-term-sixel-color eat--t-term) (or (car params) 0))) (when (= (length params) 5) (cl-destructuring-bind (z y x coord-sys reg) params (eat--t-sixel-set-color-reg (or reg 0) (list coord-sys (or x 0) (or y 0) (or z 0)))))) ('rle (eat--t-sixel-write output index (1+ index) (or (car params) 0)) (cl-incf index)) ('set-raster-attr ;; TODO: Implement. )) (setf (eat--t-term-parser-state eat--t-term) `(read-sixel nil nil))) (let ((loop t)) (while (and loop (/= index (length output))) (if (<= ?? (aref output index) ?~) (let ((ins-beg index)) (while (and (/= index (length output)) (<= ?? (aref output index) ?~)) (cl-incf index)) (eat--t-sixel-write output ins-beg index 1)) (cl-incf index) (pcase (aref output (1- index)) (?! (setf (eat--t-term-parser-state eat--t-term) `(read-dcs-params (read-sixel rle) ,(list nil))) (setq loop nil)) (?- (eat--t-sixel-newline)) (?$ (setf (eat--t-cur-sixel-x (eat--t-disp-cursor (eat--t-term-display eat--t-term))) 0)) (?\# (setf (eat--t-term-parser-state eat--t-term) `(read-dcs-params (read-sixel set-color) ,(list nil))) (setq loop nil)) (?\" (setf (eat--t-term-parser-state eat--t-term) `(read-dcs-params (read-sixel set-raster-attr) ,(list nil))) (setq loop nil)) (?\e (eat--t-sixel-cleanup) (setf (eat--t-term-parser-state eat--t-term) '(read-potential-st (read-dcs-fallback))) (setq loop nil))))))) (`(read-charset-standard ,slot ,buf) ;; Find the end. (let ((match (string-match (rx (any ?0 ?2 ?4 ?5 ?6 ?7 ?9 ?< ?= ?> ?? ?A ?B ?C ?E ?H ?K ?Q ?R ?Y ?Z ?f)) output index))) (if (not match) (progn ;; Not found, store the text to process it later when ;; we find the end. (setf (eat--t-term-parser-state eat--t-term) `(read-charset-standard ,slot ,(concat buf (substring output index)))) (setq index (length output))) ;; Got the end! (let ((str (concat buf (substring output index (match-end 0))))) (setq index (match-end 0)) (setf (eat--t-term-parser-state eat--t-term) nil) ;; Set the character set. (eat--t-set-charset slot (pcase str ;; ESC ( 0. ;; ESC ) 0. ;; ESC * 0. ;; ESC + 0. ("0" 'dec-line-drawing) ;; ESC ( B. ;; ESC ) B. ;; ESC * B. ;; ESC + B. ("B" 'us-ascii))))))) (`(read-charset-vt300 ,_slot) (cl-incf index) (setf (eat--t-term-parser-state eat--t-term) nil) ;; Nothing. This is here to just recognize the sequence. ))))) (defun eat--t-resize (width height) "Resize terminal to WIDTH x HEIGHT." (let* ((disp (eat--t-term-display eat--t-term)) (cursor (eat--t-disp-cursor disp)) (old-width (eat--t-disp-width disp)) (old-height (eat--t-disp-height disp))) ;; Don't do anything if size hasn't changed, or the new size is ;; too small. (when (and (not (and (eq old-width width) (eq old-height height))) (>= width 1) (>= height 1)) ;; Update state. (setf (eat--t-disp-width disp) width) (setf (eat--t-disp-height disp) height) (setf (eat--t-term-scroll-begin eat--t-term) 1) (setf (eat--t-term-scroll-end eat--t-term) (eat--t-disp-height disp)) (set-marker (eat--t-cur-position cursor) (point)) (if (eat--t-term-main-display eat--t-term) ;; For alternative display, just delete the part of the ;; display that went out of the edges. So if the terminal ;; was enlarged, we don't have anything to do. (when (or (< width old-width) (< height old-height)) ;; Go to the beginning of display. (goto-char (eat--t-disp-begin disp)) (let ((l 0)) (while (and (< l height) (not (eobp))) (eat--t-col-motion width) (delete-region (point) (car (eat--t-eol))) (unless (eobp) (if (< (1+ l) height) (forward-char) (delete-region (point) (point-max)) (let ((y (eat--t-cur-y cursor)) (x (eat--t-cur-x cursor))) (eat--t-goto 1 1) (eat--t-goto y x)))) (cl-incf l)))) ;; REVIEW: This works, but it is very simple. Most ;; terminals have more sophisticated mechanisms to do this. ;; It would be nice thing have them here. ;; Go to the beginning of display. (goto-char (eat--t-disp-begin disp)) ;; Try to move to the end of previous line, maybe that's a ;; part of a too long line. (unless (bobp) (backward-char)) ;; Join all long lines. (while (not (eobp)) (eat--t-join-long-line)) ;; Go to display beginning again and break long lines. (goto-char (eat--t-disp-begin disp)) (while (not (eobp)) (eat--t-break-long-line (eat--t-disp-width disp))) ;; Calculate the beginning position of display. (goto-char (point-max)) ;; TODO: This part needs explanation. (let ((disp-begin (car (eat--t-bol (- (1- height)))))) (when (< (eat--t-disp-begin disp) disp-begin) (goto-char (max (- (eat--t-disp-begin disp) 1) (point-min))) (set-marker (eat--t-disp-begin disp) disp-begin) (while (< (point) (1- (eat--t-disp-begin disp))) (eat--t-join-long-line (1- (eat--t-disp-begin disp)))))) ;; Update the cursor if needed. (when (< (eat--t-cur-position cursor) (eat--t-disp-begin disp)) (set-marker (eat--t-cur-position cursor) (eat--t-disp-begin disp))) ;; Update the coordinates of cursor. (goto-char (eat--t-cur-position cursor)) (setf (eat--t-cur-x cursor) (1+ (eat--t-current-col))) (goto-char (eat--t-disp-begin disp)) (setf (eat--t-cur-y cursor) (let ((y 0)) (while (< (point) (eat--t-cur-position cursor)) (condition-case nil (search-forward "\n" (eat--t-cur-position cursor)) (search-failed (goto-char (eat--t-cur-position cursor)))) (cl-incf y)) (when (or (= (point) (point-min)) (= (char-before) ?\n)) (cl-incf y)) (max y 1))))))) ;;;###autoload (defun eat-term-make (buffer position) "Make a Eat terminal at POSITION in BUFFER." (eat--t-make-term :buffer buffer :begin (copy-marker position t) :end (copy-marker position) :display (eat--t-make-disp :begin (copy-marker position) :old-begin (copy-marker position) :cursor (eat--t-make-cur :position (copy-marker position))))) (defun eat-term-p (object) "Return non-nil if OBJECT is a Eat terminal." (eat--t-term-p object)) (defun eat-term-live-p (object) "Return non-nil if OBJECT is a live Eat terminal." (and (eat-term-p object) (not (not (eat--t-term-buffer object))))) (defmacro eat--t-ensure-live-term (object) "Signal error if OBJECT is not a live Eat terminal." `(unless (eat-term-live-p ,object) (error "%s is not a live Eat terminal" ,(upcase (symbol-name object))))) (defmacro eat--t-with-env (terminal &rest body) "Setup the environment for TERMINAL and eval BODY in it." (declare (indent 1)) `(let ((eat--t-term ,terminal)) (eat--t-ensure-live-term ,terminal) (with-current-buffer (eat--t-term-buffer eat--t-term) (save-excursion (save-restriction (narrow-to-region (eat--t-term-begin eat--t-term) (eat--t-term-end eat--t-term)) (goto-char (eat--t-cur-position (eat--t-disp-cursor (eat--t-term-display eat--t-term)))) (unwind-protect (progn ,@body) (set-marker (eat--t-cur-position (eat--t-disp-cursor (eat--t-term-display eat--t-term))) (point)) (set-marker (eat--t-term-begin eat--t-term) (point-min)) (set-marker (eat--t-term-end eat--t-term) (point-max)))))))) (defun eat-term-delete (terminal) "Delete TERMINAL and do any cleanup to do." (eat--t-ensure-live-term terminal) (let ((inhibit-quit t) (eat--t-term terminal)) (with-current-buffer (eat--t-term-buffer eat--t-term) (save-excursion (save-restriction (narrow-to-region (eat--t-term-begin eat--t-term) (eat--t-term-end eat--t-term)) (eat--t-set-cursor-state :default) ;; Go to the beginning of display. (goto-char (eat--t-disp-begin (eat--t-term-display eat--t-term))) ;; Join all long lines. (unless (bobp) (backward-char)) (while (not (eobp)) (eat--t-join-long-line))))) (setf (eat--t-term-buffer eat--t-term) nil))) (defun eat-term-reset (terminal) "Reset TERMINAL." (let ((inhibit-quit t)) (eat--t-with-env terminal (eat--t-reset)))) (defun eat-term-parameter (terminal parameter) "Return the value of parameter PARAMETER of TERMINAL." (eat--t-ensure-live-term terminal) (gethash parameter (eat--t-term-params terminal))) (defun eat-term-parameters (terminal) "Return the parameter-alist of TERMINAL." (eat--t-ensure-live-term terminal) (let ((alist nil)) (maphash (lambda (key val) (push (cons key val) alist)) (eat--t-term-params terminal)))) (defun eat-term-set-parameter (terminal parameter value) "Set the value of parameter PARAMETER of TERMINAL to VALUE." (eat--t-ensure-live-term terminal) ;; Handle special parameters, and reject invalid values. (pcase parameter ('input-function (unless (functionp value) (signal 'wrong-type-argument (list 'functionp value))) (setf (eat--t-term-input-fn terminal) value)) ('ring-bell-function (unless (functionp value) (signal 'wrong-type-argument (list 'functionp value))) (setf (eat--t-term-bell-fn terminal) value)) ('set-cursor-function (unless (functionp value) (signal 'wrong-type-argument (list 'functionp value))) (setf (eat--t-term-set-cursor-fn terminal) value)) ('grab-mouse-function (unless (functionp value) (signal 'wrong-type-argument (list 'functionp value))) (setf (eat--t-term-grab-mouse-fn terminal) value)) ('grab-focus-events-function (unless (functionp value) (signal 'wrong-type-argument (list 'functionp value))) (setf (eat--t-term-set-focus-ev-mode-fn terminal) value)) ('manipulate-selection-function (unless (functionp value) (signal 'wrong-type-argument (list 'functionp value))) (setf (eat--t-term-manipulate-selection-fn terminal) value)) ('set-title-function (unless (functionp value) (signal 'wrong-type-argument (list 'functionp value))) (setf (eat--t-term-set-title-fn terminal) value)) ('set-cwd-function (unless (functionp value) (signal 'wrong-type-argument (list 'functionp value))) (setf (eat--t-term-set-cwd-fn terminal) value)) ('ui-command-function (unless (functionp value) (signal 'wrong-type-argument (list 'functionp value))) (setf (eat--t-term-ui-cmd-fn terminal) value)) ('char-dimensions (unless (and (consp value) (integerp (car value)) (> (car value) 0) (integerp (cdr value)) (> (cdr value) 0)) (signal 'wrong-type-argument (list 'consp value))) (setf (eat--t-term-char-width terminal) (car value)) (setf (eat--t-term-char-height terminal) (cdr value))) ('sixel-render-format (unless (memq value '(background half-block svg xpm none)) (error "`sixel-render-format' parameter must be set to one of \ the supported formats")) (setf (eat--t-term-sixel-render-format terminal) value)) ('sixel-image-extra-properties (setf (eat--t-term-sixel-image-extra-props terminal) value)) ('bold-face (unless (and (symbolp value) (facep value)) (signal 'wrong-type-argument (list '(symbolp facep) value))) (setf (eat--t-term-bold-face terminal) value)) ('faint-face (unless (and (symbolp value) (facep value)) (signal 'wrong-type-argument (list '(symbolp facep) value))) (setf (eat--t-term-faint-face terminal) value)) ('italic-face (unless (and (symbolp value) (facep value)) (signal 'wrong-type-argument (list '(symbolp facep) value))) (setf (eat--t-term-italic-face terminal) value)) ('slow-blink-face (unless (and (symbolp value) (facep value)) (signal 'wrong-type-argument (list '(symbolp facep) value))) (setf (eat--t-term-slow-blink-face terminal) value)) ('fast-blink-face (unless (and (symbolp value) (facep value)) (signal 'wrong-type-argument (list '(symbolp facep) value))) (setf (eat--t-term-fast-blink-face terminal) value)) ((and (pred symbolp) (let (rx string-start "color-" (let number (one-or-more (any (?0 . ?9)))) "-face" string-end) (symbol-name parameter)) (let (and (pred (<= 0)) (pred (>= 255)) index) (string-to-number number))) (unless (and (symbolp value) (facep value)) (signal 'wrong-type-argument (list '(symbolp facep) value))) (setf (aref (eat--t-term-color-faces terminal) index) value)) ((and (pred symbolp) (let (rx string-start "font-" (let number (one-or-more (any (?0 . ?9)))) "-face" string-end) (symbol-name parameter)) (let (and (pred (<= 0)) (pred (>= 255)) index) (string-to-number number))) (unless (and (symbolp value) (facep value)) (signal 'wrong-type-argument (list '(symbolp facep) value))) (setf (aref (eat--t-term-font-faces terminal) index) value))) ;; Set the parameter. (puthash parameter value (eat--t-term-params terminal))) (gv-define-setter eat-term-parameter (value terminal parameter) `(eat-term-set-parameter ,terminal ,parameter ,value)) (defun eat-term-cursor-type (terminal) "Return the cursor state of TERMINAL. The return value can be one of the following: `:invisible' Invisible cursor. `:block' Block (filled box) cursor (default). `:blinking-block' Blinking block cursor. `:bar' Vertical bar cursor. `:blinking-bar' Blinking vertical bar cursor. `:underline' Horizontal bar cursor. `:blinking-underline' Blinking horizontal bar cursor." (eat--t-ensure-live-term terminal) (if (eat--t-term-cur-visible-p terminal) (eat--t-term-cur-state terminal) :invisible)) (defun eat-term-beginning (terminal) "Return the beginning position of TERMINAL. Don't use markers to store the position, call this function whenever you need the position." (eat--t-ensure-live-term terminal) (eat--t-term-begin terminal)) (defun eat-term-end (terminal) "Return the end position of TERMINAL. This is also the end position of TERMINAL's display. Don't use markers to store the position, call this function whenever you need the position." (eat--t-ensure-live-term terminal) (eat--t-term-end terminal)) (defun eat-term-display-beginning (terminal) "Return the beginning position of TERMINAL's display." (eat--t-ensure-live-term terminal) (eat--t-disp-begin (eat--t-term-display terminal))) (defun eat-term-display-cursor (terminal) "Return the cursor's current position on TERMINAL's display." (eat--t-ensure-live-term terminal) (let* ((disp (eat--t-term-display terminal)) (cursor (eat--t-disp-cursor disp))) ;; The cursor might be after the edge of the display. But we ;; don't want the UI to show that, so show cursor at the edge. (if (> (eat--t-cur-x cursor) (eat--t-disp-width disp)) (1- (eat--t-cur-position cursor)) (eat--t-cur-position cursor)))) (defun eat-term-title (terminal) "Return the current title of TERMINAL." (eat--t-ensure-live-term terminal) (eat--t-term-title terminal)) (defun eat-term-size (terminal) "Return the size of TERMINAL as (WIDTH . HEIGHT)." (eat--t-ensure-live-term terminal) (let ((disp (eat--t-term-display terminal))) (cons (eat--t-disp-width disp) (eat--t-disp-height disp)))) (defun eat-term-process-output (terminal output) "Process OUTPUT from client and show it on TERMINAL's display." (let ((inhibit-quit t)) (eat--t-with-env terminal (eat--t-handle-output output)))) (defun eat-term-redisplay (terminal) "Prepare TERMINAL for displaying." (let ((inhibit-quit t)) (eat--t-with-env terminal (let ((disp (eat--t-term-display eat--t-term))) (when (< (eat--t-disp-old-begin disp) (eat--t-disp-begin disp)) ;; Join long lines. (let ((limit (copy-marker (1- (eat--t-disp-begin disp))))) (save-excursion (goto-char (max (1- (eat--t-disp-old-begin disp)) (point-min))) (while (< (point) limit) (eat--t-join-long-line limit)))) ;; Truncate scrollback. (when eat-term-scrollback-size (delete-region (point-min) (max (point-min) (- (point) eat-term-scrollback-size)))) (set-marker (eat--t-disp-old-begin disp) (eat--t-disp-begin disp))))))) (defun eat-term-resize (terminal width height) "Resize TERMINAL to WIDTH x HEIGHT." (let ((inhibit-quit t)) (eat--t-with-env terminal (eat--t-resize width height)))) (defun eat-term-in-alternative-display-p (terminal) "Return non-nil when TERMINAL is in alternative display mode." (eat--t-ensure-live-term terminal) (eat--t-term-main-display terminal)) (defun eat-term-input-event (terminal n event &optional ref-pos) "Send EVENT as input N times to TERMINAL. EVENT should be a event. It can be any standard Emacs event, or a event list of any of the following forms: (eat-focus-in) Terminal just gained focus. (eat-focus-out) Terminal just lost focus. REF-POS is a mouse position list pointing to the start of terminal display satisfying the predicate `posnp'. It is used to calculate the position of mouse events and `eat-mouse-drag' events on terminal when given. For mouse events, events should be sent on both mouse button press and release unless the mouse grabing mode is `:click', otherwise the client process may get confused." (eat--t-ensure-live-term terminal) (let ((disp (eat--t-term-display terminal))) (cl-flet ((send (str) (funcall (eat--t-term-input-fn terminal) terminal str))) (dotimes (_ (or n 1)) (pcase event ;; Arrow key, `insert', `delete', `deletechar', `home', ;; `end', `prior', `next' and their modifier variants. ((and (or 'up 'down 'right 'left 'C-up 'C-down 'C-right 'C-left 'M-up 'M-down 'M-right 'M-left 'S-up 'S-down 'S-right 'S-left 'C-M-up 'C-M-down 'C-M-right 'C-M-left 'C-S-up 'C-S-down 'C-S-right 'C-S-left 'M-S-up 'M-S-down 'M-S-right 'M-S-left 'C-M-S-up 'C-M-S-down 'C-M-S-right 'C-M-S-left 'insert 'C-insert 'M-insert 'S-insert 'C-M-insert 'C-S-insert 'M-S-insert 'C-M-S-insert 'delete 'C-delete 'M-delete 'S-delete 'C-M-delete 'C-S-delete 'M-S-delete 'C-M-S-delete 'deletechar 'C-deletechar 'M-deletechar 'S-deletechar 'C-M-deletechar 'C-S-deletechar 'M-S-deletechar 'C-M-S-deletechar 'home 'C-home 'M-home 'S-home 'C-M-home 'C-S-home 'M-S-home 'C-M-S-home 'end 'C-end 'M-end 'S-end 'C-M-end 'C-S-end 'M-S-end 'C-M-S-end 'prior 'C-prior 'M-prior 'S-prior 'C-M-prior 'C-S-prior 'M-S-prior 'C-M-S-prior 'next 'C-next 'M-next 'S-next 'C-M-next 'C-S-next 'M-S-next 'C-M-S-next) ev) (send (format "\e%s%c" (if (not (or (memq 'control (event-modifiers ev)) (memq 'meta (event-modifiers ev)) (memq 'shift (event-modifiers ev)))) (pcase (event-basic-type ev) ('insert "[2") ((or 'delete 'deletechar) "[3") ('prior "[5") ('next "[6") (_ (if (eat--t-term-keypad-mode terminal) "O" "["))) (format "[%c;%c" (pcase (event-basic-type ev) ('insert ?2) ((or 'delete 'deletechar) ?3) ('prior ?5) ('next ?6) (_ ?1)) (pcase-exhaustive (event-modifiers ev) ((and (pred (memq 'control)) (pred (memq 'meta)) (pred (memq 'shift))) ?8) ((and (pred (memq 'control)) (pred (memq 'meta))) ?7) ((and (pred (memq 'control)) (pred (memq 'shift))) ?6) ((and (pred (memq 'meta)) (pred (memq 'shift))) ?4) ((pred (memq 'control)) ?5) ((pred (memq 'meta)) ?3) ((pred (memq 'shift)) ?2)))) (pcase (event-basic-type ev) ('up ?A) ('down ?B) ('right ?C) ('left ?D) ('home ?H) ('end ?F) (_ ?~))))) ((or 'backspace ?\C-?) (send "\C-?")) ('C-backspace (send "\C-h")) ((or 'M-backspace (pred (lambda (ev) (and (equal (event-basic-type ev) ?\C-?) (equal (event-modifiers ev) '(meta)))))) (send "\e\C-?")) ('C-M-backspace (send "\e\C-h")) ('tab (send "\t")) ('backtab (send "\e[Z")) ;; Function keys. ((and (pred symbolp) fn-key (let (rx string-start "f" (let fn-num (one-or-more (any (?0 . ?9)))) string-end) (symbol-name fn-key)) (let (and (pred (<= 1)) (pred (>= 63)) key) (string-to-number fn-num))) (send (aref ["\eOP" "\eOQ" "\eOR" "\eOS" "\e[15~" "\e[17~" "\e[18~" "\e[19~" "\e[20~" "\e[21~" "\e[23~" "\e[24~" "\e[1;2P" "\e[1;2Q" "\e[1;2R" "\e[1;2S" "\e[15;2~" "\e[17;2~" "\e[18;2~" "\e[19;2~" "\e[20;2~" "\e[21;2~" "\e[23;2~" "\e[24;2~" "\e[1;5P" "\e[1;5Q" "\e[1;5R" "\e[1;5S" "\e[15;5~" "\e[17;5~" "\e[18;5~" "\e[19;5~" "\e[20;5~" "\e[21;5~" "\e[23;5~" "\e[24;5~" "\e[1;6P" "\e[1;6Q" "\e[1;6R" "\e[1;6S" "\e[15;6~" "\e[17;6~" "\e[18;6~" "\e[19;6~" "\e[20;6~" "\e[21;6~" "\e[23;6~" "\e[24;6~" "\e[1;3P" "\e[1;3Q" "\e[1;3R" "\e[1;3S" "\e[15;3~" "\e[17;3~" "\e[18;3~" "\e[19;3~" "\e[20;3~" "\e[21;3~" "\e[23;3~" "\e[24;3~" "\e[1;4P" "\e[1;4Q" "\e[1;4R"] (1- key)))) ((and (or (pred numberp) (pred symbolp)) char) ;; Adapted from Term source. (when (symbolp char) ;; Convert `return' to C-m, etc. (let ((tmp (get char 'event-symbol-elements))) (when tmp (setq char (car tmp))) (and (symbolp char) (setq tmp (get char 'ascii-character)) (setq char tmp)))) (when (numberp char) (let ((base (event-basic-type char)) (mods (event-modifiers char))) ;; Try to avoid event-convert-list if possible. (if (and (characterp char) (not (memq 'meta mods)) (not (and (memq 'control mods) (memq 'shift mods)))) (send (format "%c" char)) (when (memq 'control mods) (setq mods (delq 'shift mods))) (let ((ch (pcase (event-convert-list (append (remq 'meta mods) (list base))) (?\C-\s ?\C-@) (?\C-/ ?\C-?) (?\C-- ?\C-_) (c c)))) (when (characterp ch) (send (cond ((and (memq 'meta mods) (memq ch '(?\[ ?O))) "\e") (t (format (if (memq 'meta mods) "\e%c" "%c") ch)))))))))) ;; Mouse handling. ((and (guard (eat--t-term-mouse-mode terminal)) mouse (pred eventp) (or (and (let mouse-type (event-basic-type mouse)) (let (rx string-start "mouse-" (let key-num (one-or-more (any (?0 . ?9)))) string-end) (symbol-name mouse-type)) (let (and (pred (<= 1)) (pred (>= 11)) mouse-num) (string-to-number key-num))) (and (let 'wheel-up (event-basic-type mouse)) (let mouse-num 4)) (and (let 'wheel-down (event-basic-type mouse)) (let mouse-num 5)) (and (let 'wheel-right (event-basic-type mouse)) (let mouse-num 6)) (and (let 'wheel-left (event-basic-type mouse)) (let mouse-num 7)))) (let* ((modifiers (event-modifiers mouse)) (pos (if (memq 'drag modifiers) (event-end mouse) (event-start mouse))) (x-y (if (eval-when-compile (< emacs-major-version 29)) (posn-col-row pos) (with-suppressed-warnings ((callargs posn-col-row)) (posn-col-row pos 'use-window)))) (x (1+ (car x-y))) (y (1+ (cdr x-y))) (button (let ((b (aref [0 1 2 64 65 66 67 128 129 130 131] (1- mouse-num)))) (when (memq 'shift modifiers) (cl-incf b 4)) (when (memq 'meta modifiers) (cl-incf b 8)) (when (memq 'control modifiers) (cl-incf b 16)) b))) (when ref-pos (let ((ref-x-y (if (eval-when-compile (< emacs-major-version 29)) (posn-col-row ref-pos) (with-suppressed-warnings ((callargs posn-col-row)) (posn-col-row ref-pos 'use-window))))) (cl-decf x (car ref-x-y)) (cl-decf y (cdr ref-x-y)))) (when (and (<= 1 x (eat--t-disp-width disp)) (<= 1 y (eat--t-disp-height disp)) (or (eat--t-term-mouse-encoding terminal) (and (<= x 95) (<= y 95) (<= button 95)))) (if (eq (eat--t-term-mouse-mode terminal) 'x10) (when (and (< button 3) (or (memq 'click modifiers) (memq 'drag modifiers))) (send (if (eq (eat--t-term-mouse-encoding terminal) 'sgr) (format "\e[<%i;%i;%iM" button x y) (format "\e[M%c%c%c" (+ button 32) (+ x 32) (+ y 32))))) (cond ;; `down-mouse-1' and friends. ((memq 'down modifiers) ;; For `mouse-1', `mouse-2' and `mouse-3', keep ;; track the button's state, we'll need it when ;; button event mouse mode is enabled. (when (< (logand button 3) 3) (setf (eat--t-term-mouse-pressed terminal) ;; In XTerm and Kitty, mouse-1 is ;; prioritized over mouse-2, and mouse-2 ;; over mouse-3. However St doesn't keep ;; track of multiple buttons. (sort (cons button (eat--t-term-mouse-pressed terminal)) #'<))) (send (if (eq (eat--t-term-mouse-encoding terminal) 'sgr) (format "\e[<%i;%i;%iM" button x y) (format "\e[M%c%c%c" (+ button 32) (+ x 32) (+ y 32))))) ;; `mouse-1', `mouse-2', `mouse-3', and their ;; `drag'ged variants. ((and (or (memq 'click modifiers) (memq 'drag modifiers)) (<= mouse-num 3)) ;; For `mouse-1', `mouse-2' and `mouse-3', keep ;; track the button's state, we'll need it when ;; button event mouse mode is enabled. (setf (eat--t-term-mouse-pressed terminal) (cl-delete-if (lambda (b) (= (logand b 3) (logand button 3))) (eat--t-term-mouse-pressed terminal))) (send (if (eq (eat--t-term-mouse-encoding terminal) 'sgr) (format "\e[<%i;%i;%im" button x y) (format "\e[M%c%c%c" (+ (logior button 3) 32) (+ x 32) (+ y 32))))) ;; Mouse wheel, `mouse-4' and friends. (t (send (if (eq (eat--t-term-mouse-encoding terminal) 'sgr) (format "\e[<%i;%i;%iM" button x y) (format "\e[M%c%c%c" (+ button 32) (+ x 32) (+ y 32)))))))))) ;; Mouse movement tracking. ((and (guard (memq (eat--t-term-mouse-mode terminal) '(button-event any-event))) (pred mouse-movement-p) movement) (let* ((pos (event-start movement)) (x-y (if (eval-when-compile (< emacs-major-version 29)) (posn-col-row pos) (with-suppressed-warnings ((callargs posn-col-row)) (posn-col-row pos 'use-window)))) (x (1+ (car x-y))) (y (1+ (cdr x-y))) (button (if (car (eat--t-term-mouse-pressed terminal)) (+ (car (eat--t-term-mouse-pressed terminal)) 32) 35))) (when ref-pos (let ((ref-x-y (if (eval-when-compile (< emacs-major-version 29)) (posn-col-row ref-pos) (with-suppressed-warnings ((callargs posn-col-row)) (posn-col-row ref-pos 'use-window))))) (cl-decf x (car ref-x-y)) (cl-decf y (cdr ref-x-y)))) (when (and (or (eq (eat--t-term-mouse-mode terminal) 'any-event) (/= button 35)) (<= 1 x (eat--t-disp-width disp)) (<= 1 y (eat--t-disp-height disp)) (or (eat--t-term-mouse-encoding terminal) (and (<= x 95) (<= y 95) (<= button 95)))) (send (if (eq (eat--t-term-mouse-encoding terminal) 'sgr) (format "\e[<%i;%i;%iM" button x y) (format "\e[M%c%c%c" (+ button 32) (+ x 32) (+ y 32))))))) ;; Focus events. ('(eat-focus-in) (when (eat--t-term-focus-event-mode terminal) (send "\e[I"))) ('(eat-focus-out) (when (eat--t-term-focus-event-mode terminal) (send "\e[O")))))))) (defun eat-term-send-string (terminal string) "Send STRING to TERMINAL directly." (eat--t-ensure-live-term terminal) (funcall (eat--t-term-input-fn terminal) terminal string)) (defun eat-term-send-string-as-yank (terminal args) "Send ARGS to TERMINAL, honoring bracketed yank mode. Each argument in ARGS can be either string or character." (eat--t-ensure-live-term terminal) (funcall (eat--t-term-input-fn terminal) terminal (let ((str (mapconcat (lambda (s) (if (stringp s) s (string s))) args ""))) (if (eat--t-term-bracketed-yank terminal) ;; REVIEW: What if `str' itself contains these escape ;; sequences? St doesn't care and just wraps the ;; string with these magic escape sequences, while ;; Kitty tries to be smart. (format "\e[200~%s\e[201~" str) str)))) (defun eat-term-make-keymap (input-command categories exceptions) "Make a keymap binding INPUT-COMMAND to the events of CATEGORIES. CATEGORIES is a list whose elements should be a one of the following keywords: `:ascii' All self-insertable characters, plus `backspace', `DEL', `insert', `delete' and `deletechar' keys, with all possible modifiers. `:arrow' Arrow keys with all possible modifiers. `:navigation' Navigation keys: home, end, prior (or page up) and next (or page down) with all possible modifiers. `:function' Function keys (f1 - f63). `:mouse-click' `mouse-1', `mouse-2' and `mouse-3'. `:mouse-modifier' All mouse events except mouse movement. `:mouse-movement' Mouse movement. EXCEPTIONS is a list of key sequences to not bind. Don't use \"M-...\" key sequences in EXCEPTIONS, use \"ESC ...\" instead." (let ((map (make-sparse-keymap))) (cl-flet ((bind (key) (unless (member key exceptions) (define-key map key input-command)))) (when (memq :ascii categories) ;; Bind ASCII and self-insertable characters except ESC. (bind [remap self-insert-command]) (cl-loop for i from ?\C-@ to ?\C-? do (unless (= i meta-prefix-char) (bind (vector i)))) ;; Bind `tab', `backspace', `delete', `deletechar', and all ;; modified variants. (dolist (key '( tab backtab backspace C-backspace M-backspace C-M-backspace insert C-insert M-insert S-insert C-M-insert C-S-insert M-S-insert C-M-S-insert delete C-delete M-delete S-delete C-M-delete C-S-delete M-S-delete C-M-S-delete deletechar C-deletechar M-deletechar S-deletechar C-M-deletechar C-S-deletechar M-S-deletechar C-M-S-deletechar)) (bind (vector key))) ;; Bind these non-encodable keys. They are translated. (dolist (key '(?\C-- ?\C-? ?\C-\s)) (bind (vector key))) ;; Bind M- keys. (unless (member (vector meta-prefix-char) exceptions) (define-key map (vector meta-prefix-char) (make-sparse-keymap)) (cl-loop for i from ?\C-@ to ?\C-? do (unless (memq i '(?O ?\[)) (bind (vector meta-prefix-char i)))) (bind (vector meta-prefix-char meta-prefix-char)))) (when (memq :arrow categories) (dolist (key '( up down right left C-up C-down C-right C-left M-up M-down M-right M-left S-up S-down S-right S-left C-M-up C-M-down C-M-right C-M-left C-S-up C-S-down C-S-right C-S-left M-S-up M-S-down M-S-right M-S-left C-M-S-up C-M-S-down C-M-S-right C-M-S-left)) (bind (vector key)))) (when (memq :navigation categories) (dolist (key '( home C-home M-home S-home C-M-home C-S-home M-S-home C-M-S-home end C-end M-end S-end C-M-end C-S-end M-S-end C-M-S-end prior C-prior M-prior S-prior C-M-prior C-S-prior M-S-prior C-M-S-prior next C-next M-next S-next C-M-next C-S-next M-S-next C-M-S-next)) (bind (vector key)))) (when (memq :function categories) (cl-loop for i from 1 to 63 do (let ((key (intern (format "f%i" i)))) (bind (vector key))))) (when (memq :mouse-click categories) (dolist (key '(mouse-1 mouse-2 mouse-3)) (bind (vector key)))) (when (memq :mouse-modifier categories) (dolist (key '( down-mouse-1 drag-mouse-1 down-mouse-2 drag-mouse-2 down-mouse-3 drag-mouse-3 C-down-mouse-1 C-drag-mouse-1 C-down-mouse-2 C-drag-mouse-2 C-down-mouse-3 C-drag-mouse-3 M-down-mouse-1 M-drag-mouse-1 M-down-mouse-2 M-drag-mouse-2 M-down-mouse-3 M-drag-mouse-3 S-down-mouse-1 S-drag-mouse-1 S-down-mouse-2 S-drag-mouse-2 S-down-mouse-3 S-drag-mouse-3 C-M-down-mouse-1 C-M-drag-mouse-1 C-M-down-mouse-2 C-M-drag-mouse-2 C-M-down-mouse-3 C-M-drag-mouse-3 C-S-down-mouse-1 C-S-drag-mouse-1 C-S-down-mouse-2 C-S-drag-mouse-2 C-S-down-mouse-3 C-S-drag-mouse-3 M-S-down-mouse-1 M-S-drag-mouse-1 M-S-down-mouse-2 M-S-drag-mouse-2 M-S-down-mouse-3 M-S-drag-mouse-3 C-M-S-down-mouse-1 C-M-S-drag-mouse-1 C-M-S-down-mouse-2 C-M-S-drag-mouse-2 C-M-S-down-mouse-3 C-M-S-drag-mouse-3 mouse-1 mouse-2 mouse-3 mouse-4 mouse-5 mouse-6 mouse-7 mouse-8 mouse-9 mouse-10 mouse-11 C-mouse-1 C-mouse-2 C-mouse-3 C-mouse-4 C-mouse-5 C-mouse-6 C-mouse-7 C-mouse-8 C-mouse-9 C-mouse-10 C-mouse-11 M-mouse-1 M-mouse-2 M-mouse-3 M-mouse-4 M-mouse-5 M-mouse-6 M-mouse-7 M-mouse-8 M-mouse-9 M-mouse-10 M-mouse-11 S-mouse-1 S-mouse-2 S-mouse-3 S-mouse-4 S-mouse-5 S-mouse-6 S-mouse-7 S-mouse-8 S-mouse-9 S-mouse-10 S-mouse-11 C-M-mouse-1 C-M-mouse-2 C-M-mouse-3 C-M-mouse-4 C-M-mouse-5 C-M-mouse-6 C-M-mouse-7 C-M-mouse-8 C-M-mouse-9 C-M-mouse-10 C-M-mouse-11 C-S-mouse-1 C-S-mouse-2 C-S-mouse-3 C-S-mouse-4 C-S-mouse-5 C-S-mouse-6 C-S-mouse-7 C-S-mouse-8 C-S-mouse-9 C-S-mouse-10 C-S-mouse-11 M-S-mouse-1 M-S-mouse-2 M-S-mouse-3 M-S-mouse-4 M-S-mouse-5 M-S-mouse-6 M-S-mouse-7 M-S-mouse-8 M-S-mouse-9 M-S-mouse-10 M-S-mouse-11 C-M-S-mouse-1 C-M-S-mouse-2 C-M-S-mouse-3 C-M-S-mouse-4 C-M-S-mouse-5 C-M-S-mouse-6 C-M-S-mouse-7 C-M-S-mouse-8 C-M-S-mouse-9 C-M-S-mouse-10 C-M-S-mouse-11 wheel-up wheel-down wheel-right wheel-left C-wheel-up C-wheel-down C-wheel-right C-wheel-left M-wheel-up M-wheel-down M-wheel-right M-wheel-left S-wheel-up S-wheel-down S-wheel-right S-wheel-left C-M-wheel-up C-M-wheel-down C-M-wheel-right C-M-wheel-left C-S-wheel-up C-S-wheel-down C-S-wheel-right C-S-wheel-left M-S-wheel-up M-S-wheel-down M-S-wheel-right M-S-wheel-left C-M-S-wheel-up C-M-S-wheel-down C-M-S-wheel-right C-M-S-wheel-left)) (bind (vector key)))) (when (memq :mouse-movement categories) (bind [mouse-movement]))) map)) (defun eat-term-name () "Return the value of `TERM' environment variable for Eat." (if (stringp eat-term-name) eat-term-name (funcall eat-term-name))) (defun eat-term-get-suitable-term-name (&optional display) "Return the most suitable value for `TERM' for DISPLAY. If the number of colors supported by display (as returned by `display-color-cells') is more than 256, return \"eat-truecolor\", if it is more than 8 but less than or equal to 256, return \"eat-256color\", if is more than 1 but less than or equal to 8, return \"eat-color\", otherwise return \"eat-mono\"." (let ((colors (display-color-cells display))) (cond ((> colors 256) "eat-truecolor") ((> colors 8) "eat-256color") ((> colors 1) "eat-color") (t "eat-mono")))) (defun eat-term-filter-string (string) "Filter Eat's special text properties from STRING." (with-temp-buffer (insert string) ;; Join long lines. (goto-char (point-min)) (while (not (eobp)) (eat--t-join-long-line)) ;; Remove the invisible spaces used with multi-column characters. (goto-char (point-min)) (while (not (eobp)) (let ((invisible-p (get-text-property (point) 'eat--t-invisible-space)) (next-change (or (next-single-property-change (point) 'eat--t-invisible-space) (point-max)))) (when invisible-p (delete-region (point) next-change)) (goto-char next-change))) (remove-text-properties (point-min) (point-max) '( eat--t-char-width nil eat--t-sixel-bitmap-size nil eat--t-sixel-bitmap nil)) (buffer-string))) ;;;; Blink mode. (defvar eat--slow-blink-state nil "Current state of slowly blinking text, t means inverse video.") (defvar eat--fast-blink-state nil "Current state of rapidly blinking text, t means inverse video.") (defvar eat--slow-blink-remap nil "Face remapping cookie of slowly blinking face.") (defvar eat--fast-blink-remap nil "Face remapping cookie of rapidly blinking face.") (defvar eat--slow-blink-timer nil "Timer for blinking slowly blinking text.") (defvar eat--fast-blink-timer nil "Timer for blinking rapidly blinking text.") (declare-function face-remap-add-relative "face-remap" (face &rest specs)) (declare-function face-remap-remove-relative "face-remap" (cookie)) (defun eat--flip-slow-blink-state () "Flip the state of slowly blinking text." (face-remap-remove-relative eat--slow-blink-remap) (setq eat--slow-blink-remap (face-remap-add-relative 'eat-slow-blink `(:box nil :inverse-video ,(not eat--slow-blink-state))) eat--slow-blink-state (not eat--slow-blink-state))) (defun eat--flip-fast-blink-state () "Flip the state of rapidly blinking text." (face-remap-remove-relative eat--fast-blink-remap) (setq eat--fast-blink-remap (face-remap-add-relative 'eat-fast-blink `(:box nil :inverse-video ,(not eat--fast-blink-state))) eat--fast-blink-state (not eat--fast-blink-state))) (defun eat--blink-stop-timers () "Start blinking timers." (when eat--slow-blink-timer (cancel-timer eat--slow-blink-timer) (setq eat--slow-blink-timer nil)) (when eat--fast-blink-timer (cancel-timer eat--fast-blink-timer) (setq eat--fast-blink-timer nil))) (defun eat--blink-start-timers () "Start blinking timers." (eat--blink-stop-timers) (setq eat--slow-blink-timer (run-with-timer t (/ (float eat-slow-blink-frequency)) #'eat--flip-slow-blink-state)) (setq eat--fast-blink-timer (run-with-timer t (/ (float eat-fast-blink-frequency)) #'eat--flip-fast-blink-state))) (define-minor-mode eat-blink-mode "Toggle blinking of text with blink attribute." :lighter " Eat-Blink" (let ((locals '( eat--slow-blink-state eat--fast-blink-state eat--slow-blink-remap eat--fast-blink-remap eat--slow-blink-timer eat--fast-blink-timer))) (cond (eat-blink-mode (setq eat-blink-mode nil) (require 'face-remap) (setq eat-blink-mode t) (mapc #'make-local-variable locals) (setq eat--slow-blink-state nil) (setq eat--fast-blink-state nil) (setq eat--slow-blink-remap (face-remap-add-relative 'eat-term-slow-blink '(:box nil))) (setq eat--fast-blink-remap (face-remap-add-relative 'eat-term-fast-blink '(:box nil))) (add-hook 'pre-command-hook #'eat--blink-stop-timers nil t) (add-hook 'post-command-hook #'eat--blink-start-timers nil t)) (t (eat--blink-stop-timers) (face-remap-remove-relative eat--slow-blink-remap) (face-remap-remove-relative eat--fast-blink-remap) (remove-hook 'pre-command-hook #'eat--blink-stop-timers t) (remove-hook 'post-command-hook #'eat--blink-start-timers t) (mapc #'kill-local-variable locals))))) ;;;; Buffer-local Cursor Blinking. (defvar eat--cursor-blink-type nil "Type of blinking cursor.") (defvar eat--cursor-blink-state nil "Current state of slowly blinking text, non-nil means on.") (defvar eat--cursor-blink-timer nil "Timer for blinking slowly blinking text.") (defvar eat--cursor-blink-mode) (defun eat--flip-cursor-blink-state () "Flip the state of slowly blinking text." (when (and eat--cursor-blink-mode (display-graphic-p)) (setq-local cursor-type (if eat--cursor-blink-state (caddr eat--cursor-blink-type) (car eat--cursor-blink-type))) (setq eat--cursor-blink-state (not eat--cursor-blink-state)) ;; REVIEW: This is expensive, and some causes flickering. Any ;; better way? (when-let* ((window (get-buffer-window nil 'visible))) (redraw-frame (window-frame window))))) (defun eat--cursor-blink-stop-timers () "Stop blinking timers." (unless eat--cursor-blink-state (eat--flip-cursor-blink-state)) (when eat--cursor-blink-timer (cancel-timer eat--cursor-blink-timer) (setq eat--cursor-blink-timer nil))) (defun eat--cursor-blink-start-timers () "Start blinking timers." (eat--cursor-blink-stop-timers) (setq eat--cursor-blink-timer (run-with-timer t (/ (float (cadr eat--cursor-blink-type))) #'eat--flip-cursor-blink-state))) (define-minor-mode eat--cursor-blink-mode "Toggle blinking of cursor." :interactive nil (let ((locals '(eat--cursor-blink-state eat--cursor-blink-timer))) (cond (eat--cursor-blink-mode (mapc #'make-local-variable locals) (setq eat--cursor-blink-state nil) (setq eat--cursor-blink-timer nil) (add-hook 'pre-command-hook #'eat--cursor-blink-stop-timers nil t) (add-hook 'post-command-hook #'eat--cursor-blink-start-timers nil t) (add-hook 'kill-buffer-hook #'eat--cursor-blink-stop-timers nil t) (when (current-idle-time) (eat--cursor-blink-start-timers))) (t (eat--cursor-blink-stop-timers) (remove-hook 'pre-command-hook #'eat--cursor-blink-stop-timers t) (remove-hook 'post-command-hook #'eat--cursor-blink-start-timers t) (remove-hook 'kill-buffer-hook #'eat--cursor-blink-stop-timers t) (mapc #'kill-local-variable locals))))) ;;;; User Interface. (defvar eat-terminal nil "The terminal emulator.") (defvar eat--synchronize-scroll-function nil "Function to synchronize scrolling between terminal and window.") (defvar eat--shell-command-status 0 "If the current shell command has finished, its exit status.") (defvar eat--shell-prompt-begin nil "Beginning of last shell prompt.") (defvar eat--shell-prompt-mark nil "Display property used to put a mark before the previous prompt.") (defvar eat--shell-prompt-mark-overlays nil "List of overlay used to put marks before shell prompts.") (defvar eat--inhibit-auto-line-mode nil "Non-nil means don't enter line mode.") (defvar eat--auto-line-mode-prev-mode nil "The input mode active before line mode.") (defvar eat--auto-line-mode-pending-toggles nil "Automatic line mode toggles left to do. Don't change the toplevel value of this, let-bind instead.") (defun eat-reset () "Perform a terminal reset." (interactive) (when eat-terminal (let ((inhibit-read-only t)) (eat-term-reset eat-terminal) (eat-term-redisplay eat-terminal)) (run-hooks 'eat-update-hook))) (defun eat--set-cursor (_ state) "Set cursor type according to STATE. STATE can be one of the following: `:invisible' Invisible cursor. `:block' Block (filled box) cursor (default). `:blinking-block' Blinking block cursor. `:bar' Vertical bar cursor. `:blinking-bar' Blinking vertical bar cursor. `:underline' Horizontal bar cursor. `:blinking-underline' Blinking horizontal bar cursor. Any other value Block cursor." (setq-local eat--cursor-blink-type (pcase state (:invisible eat-invisible-cursor-type) (:block eat-default-cursor-type) (:blinking-block eat-very-visible-cursor-type) (:bar eat-vertical-bar-cursor-type) (:blinking-bar eat-very-visible-vertical-bar-cursor-type) (:underline eat-horizontal-bar-cursor-type) (:blinking-underline eat-very-visible-horizontal-bar-cursor-type) (_ eat-default-cursor-type))) (setq-local cursor-type (car eat--cursor-blink-type)) (when (xor (cadr eat--cursor-blink-type) eat--cursor-blink-mode) (eat--cursor-blink-mode (if (cadr eat--cursor-blink-type) +1 -1)))) (defun eat--manipulate-kill-ring (_ selection data) "Manipulate `kill-ring'. SELECTION can be one of `:clipboard', `:primary', `:secondary', `:select'. When DATA is a string, set the selection to that string, when DATA is nil, unset the selection, and when DATA is t, return the selection, or nil if none." (let ((inhibit-eol-conversion t) (select-enable-clipboard (eq selection :clipboard)) (select-enable-primary (eq selection :primary))) (pcase-exhaustive data ('t (when eat-enable-yank-to-terminal (ignore-error error (current-kill 0 'do-not-move)))) ('nil (when eat-enable-kill-from-terminal (kill-new ""))) ((and (pred stringp) str) (when eat-enable-kill-from-terminal (kill-new str)))))) (defun eat--bell (_) "Ring the bell." (ding t)) (defun eat--sixel-render-format () "Return the suitable Sixel render format." (cl-block nil (dolist (fmt eat-sixel-render-formats) (pcase-exhaustive fmt ('none (cl-return 'none)) ('background (cl-return 'background)) ('half-block (when (char-displayable-p ?â–„) (cl-return 'half-block))) ('svg (when (and (display-graphic-p) (image-type-available-p 'svg)) (cl-return 'svg))) ('xpm (when (and (display-graphic-p) (image-type-available-p 'xpm)) (cl-return 'xpm))))) 'none)) (defun eat--set-term-sixel-params () "Set Sixel related parameters of the terminal." (let* ((render-fmt (eat--sixel-render-format)) (dimensions (pcase render-fmt ((or 'background 'none) '(1 . 1)) ('half-block '(1 . 2)) (_ (cons (default-font-width) (default-font-height))))) (scale-x (* eat-sixel-aspect-ratio eat-sixel-scale)) (scale-y eat-sixel-scale)) (setq dimensions (cons (max 1 (round (/ (car dimensions) (float scale-x)))) (max 1 (round (/ (cdr dimensions) (float scale-y)))))) (setf (eat-term-parameter eat-terminal 'sixel-render-format) render-fmt) (setf (eat-term-parameter eat-terminal 'char-dimensions) dimensions) (unless (memq render-fmt '(none background half-block)) (let ((font-size (font-get (font-spec :name (face-font 'default)) :size))) (setf (eat-term-parameter eat-terminal 'sixel-image-extra-properties) `( :ascent center :height ,(cons (/ (float (default-font-height)) font-size) 'em) :width ,(cons (/ (float (default-font-width)) font-size) 'em))))))) (defun eat--set-cwd (_ host cwd) "Set CWD as the current working directory (`default-directory'). If HOST isn't the host Emacs is running on, don't do anything." (when (and eat-enable-directory-tracking (string= host (system-name))) (ignore-errors (cd-absolute cwd)))) (defun eat--set-cwd-uic (host path) "Set PATH to the CWD, if HOST is same as the host name." (let ((dir (ignore-errors (expand-file-name (file-name-as-directory (decode-coding-string (base64-decode-string path) locale-coding-system))))) (hostname (ignore-errors (decode-coding-string (base64-decode-string host) locale-coding-system)))) (when (and dir hostname) (eat--set-cwd nil hostname dir)))) (defun eat--pre-prompt () "Save the beginning position of shell prompt." (setq eat--shell-prompt-begin (point-marker)) ;; FIXME: It's a crime to touch processes in this section. (when (eq eat-query-before-killing-running-terminal 'auto) (set-process-query-on-exit-flag (eat-term-parameter eat-terminal 'eat--process) nil))) (defvar eat--line-mode) (defvar eat--semi-char-mode) (defvar eat--char-mode) (defun eat--line-mode-enter-auto-1 () "Enter line mode." (unless (or eat--inhibit-auto-line-mode eat--line-mode) (unless eat--line-mode (setq eat--auto-line-mode-prev-mode (cond (eat--semi-char-mode 'semi-char) (eat--char-mode 'char) (t 'emacs))) (eat-line-mode) ;; We're entering automatically, so we should be able to exit it ;; automatically. (setq eat--inhibit-auto-line-mode nil)))) (defun eat--line-mode-enter-auto () "Arrange that line mode will be enabled eventually." (push 'enter eat--auto-line-mode-pending-toggles)) (defun eat--line-mode-exit-auto-1 () "Exit line mode." (when (and (not eat--inhibit-auto-line-mode) eat--auto-line-mode-prev-mode) (pcase eat--auto-line-mode-prev-mode ('emacs (eat-emacs-mode)) ('semi-char (eat-semi-char-mode)) ('char (eat-char-mode))) (setq eat--auto-line-mode-prev-mode nil) (when (/= (eat-term-end eat-terminal) (point-max)) (eat-line-send)) ;; Toggle line mode _after_ we exit from ;; `eat-term-process-output'. (eat--line-mode -1) (setq buffer-undo-list nil))) (defun eat--line-mode-exit-auto () "Arrange that line mode will be disabled eventually." (push 'exit eat--auto-line-mode-pending-toggles)) (defun eat--line-mode-do-toggles () "Do the pending line mode toggle." (let* ((inhibit-quit t) (actions (nreverse eat--auto-line-mode-pending-toggles)) (toggle nil)) (while (setq toggle (pop actions)) (pcase-exhaustive toggle ('enter (eat--line-mode-enter-auto-1)) ('exit (eat--line-mode-exit-auto-1))) ;; Don't do extra unnecessary toggles. (let ((loop t)) (while loop (setq loop nil) (while (eq toggle (car actions)) (pop actions)) (while (and (car actions) (cadr actions) (not (eq (car actions) (cadr actions)))) (pop actions) (pop actions) (setq loop t))))) (setq eat--auto-line-mode-pending-toggles nil))) (defun eat--post-prompt () "Put a mark in the marginal area and enter line mode." (when eat-enable-shell-prompt-annotation (let ((indicator (if (zerop eat--shell-command-status) (propertize eat-shell-prompt-annotation-success-margin-indicator 'face '(eat-shell-prompt-annotation-success default)) (propertize eat-shell-prompt-annotation-failure-margin-indicator 'face '(eat-shell-prompt-annotation-failure default))))) ;; Update previous prompt's indicator using side-effect. (when eat--shell-prompt-mark (setf (cadr eat--shell-prompt-mark) indicator) (setq eat--shell-prompt-mark nil)) ;; Show this prompt's indicator. (when eat--shell-prompt-begin (when (< eat--shell-prompt-begin (point)) ;; Save it, we'll use side-effect. (setq eat--shell-prompt-mark `((margin ,eat-shell-prompt-annotation-position) ,indicator)) ;; Make overlay and put bookkeeping properties. (let ((identifier (gensym "eat--prompt-mark-identifier-")) (before-str (propertize " " 'display eat--shell-prompt-mark)) (ov (make-overlay eat--shell-prompt-begin (1+ eat--shell-prompt-begin)))) (overlay-put ov 'before-string before-str) (overlay-put ov 'eat--shell-prompt-mark-id identifier) (add-text-properties eat--shell-prompt-begin (1+ eat--shell-prompt-begin) (list 'eat--before-string before-str 'eat--shell-prompt-mark-id identifier 'eat--shell-prompt-mark-overlay ov)) (push ov eat--shell-prompt-mark-overlays)))))) (when eat--shell-prompt-begin (when (< eat--shell-prompt-begin (point)) ;; Put a text property for `eat-narrow-to-shell-prompt'. (put-text-property eat--shell-prompt-begin (1+ eat--shell-prompt-begin) 'eat--shell-prompt-begin t) ;; Put a text property to allow shell prompt navigation. (put-text-property (1- (point)) (point) 'eat--shell-prompt-end t))) (setq eat--shell-prompt-begin nil) (when eat-enable-auto-line-mode (eat--line-mode-enter-auto))) (defun eat--post-cont-prompt () "Enter line mode." (when eat-enable-auto-line-mode (eat--line-mode-enter-auto))) (defun eat--correct-shell-prompt-mark-overlays (buffer) "Correct all overlays used to add mark before shell prompt. BUFFER is the terminal buffer." (when (and (buffer-live-p buffer) (buffer-local-value 'eat-terminal buffer) eat-enable-shell-prompt-annotation) (with-current-buffer buffer (while-no-input ;; Delete all outdated overlays. (dolist (ov eat--shell-prompt-mark-overlays) (unless (and (<= (point-min) (overlay-start ov) (1- (point-max))) (eq (overlay-get ov 'eat--shell-prompt-mark-id) (get-text-property (overlay-start ov) 'eat--shell-prompt-mark-id))) (delete-overlay ov) (setq eat--shell-prompt-mark-overlays (delq ov eat--shell-prompt-mark-overlays)))) (save-excursion ;; Recreate overlays if needed. (goto-char (max (eat-term-beginning eat-terminal) (point-min))) (while (< (point) (min (eat-term-end eat-terminal) (point-max))) (when (get-text-property (point) 'eat--shell-prompt-mark-id) (let ((ov (get-text-property (point) 'eat--shell-prompt-mark-overlay))) (unless (and ov (overlay-buffer ov) (eq (overlay-get ov 'eat--shell-prompt-mark-id) (get-text-property (point) 'eat--shell-prompt-mark-id))) ;; Recreate. (when ov (delete-overlay ov) (setq eat--shell-prompt-mark-overlays (delq ov eat--shell-prompt-mark-overlays))) (setq ov (make-overlay (point) (1+ (point)))) (overlay-put ov 'before-string (get-text-property (point) 'eat--before-string)) (overlay-put ov 'eat--shell-prompt-mark-id (get-text-property (point) 'eat--shell-prompt-mark-id)) (push ov eat--shell-prompt-mark-overlays)))) (goto-char (or (next-single-property-change (point) 'eat--shell-prompt-mark-id nil (min (eat-term-end eat-terminal) (point-max))) (min (eat-term-end eat-terminal) (point-max)))))))))) (defun eat--set-cmd (cmd) "Add CMD to `shell-command-history'." (when-let* ((eat-enable-shell-command-history) (command (ignore-errors (decode-coding-string (base64-decode-string cmd) locale-coding-system)))) (add-to-history 'shell-command-history command))) (defun eat--pre-cmd () "Update shell prompt mark to indicate command is running." ;; FIXME: It's a crime to touch processes in this section. (when (eq eat-query-before-killing-running-terminal 'auto) (set-process-query-on-exit-flag (eat-term-parameter eat-terminal 'eat--process) t)) (when (and eat-enable-shell-prompt-annotation eat--shell-prompt-mark) (setf (cadr eat--shell-prompt-mark) (propertize eat-shell-prompt-annotation-running-margin-indicator 'face '(eat-shell-prompt-annotation-running default)))) (when eat-enable-auto-line-mode (eat--line-mode-exit-auto))) (defun eat--set-cmd-status (code) "Set CODE as the current shell command's exit status." (when eat-enable-shell-prompt-annotation ;; We'll update the mark later when the prompt appears. (setq eat--shell-command-status code))) (defun eat--before-new-prompt () "Allow entering line mode." (setq eat--inhibit-auto-line-mode nil)) (defun eat--get-shell-history (hist format) "Get shell history from HIST in format FORMAT." (pcase hist (`(,host . ,file) (setq host (ignore-errors (decode-coding-string (base64-decode-string host) locale-coding-system))) (setq file (ignore-errors (decode-coding-string (base64-decode-string file) locale-coding-system))) (if (and host file (string= host (system-name)) (file-readable-p file)) (let ((str nil)) (eat-term-send-string eat-terminal "\e]51;e;I;0\e\\") (with-temp-buffer (insert-file-contents file) (setq str (buffer-string))) (eat--line-populate-input-ring str format)) (eat-term-send-string eat-terminal (format "\e]51;e;I;%s\e\\" eat-line-input-ring-size)))) ((pred stringp) (eat--line-populate-input-ring (ignore-errors (decode-coding-string (base64-decode-string hist) locale-coding-system)) format)))) (defun eat--handle-message (name &rest args) "Handle message with handler name NAME and ARGS." (when-let* ((name (ignore-errors (decode-coding-string (base64-decode-string name) locale-coding-system))) (handler (assoc name eat-message-handler-alist))) (save-restriction (widen) (save-excursion (apply (cdr handler) (mapcar (lambda (arg) (ignore-errors (decode-coding-string (base64-decode-string arg) locale-coding-system))) args)))))) (defun eat--handle-uic (_ cmd) "Handle UI Command sequence CMD." (pcase cmd ;; In XTerm, OSC 51 is reserved for Emacs shell. I have no idea ;; why, but Vterm uses this OSC to set the current directory and ;; remotely execute Emacs Lisp code. Vterm uses the characters ;; 'A' and 'E' as the first character of second parameter of this ;; OSC. We use 'e' as the second parameter, followed by one or ;; more parameters. ;; UIC e ; A ; ; ST. ((rx string-start "e;A;" (let host (zero-or-more (not (any ?\;)))) ?\; (let path (zero-or-more anything)) string-end) (eat--set-cwd-uic host path)) ;; UIC e ; B ST. ("e;B" (eat--pre-prompt)) ;; UIC e ; C ST. ("e;C" (eat--post-prompt)) ;; UIC e ; D ST. ("e;D" ;; Start of continuation prompt. ;; Defined but unused. ) ;; UIC e ; E ST. ("e;E" (eat--post-cont-prompt)) ;; UIC e ; F ; ST. ((rx string-start "e;F;" (let cmd (zero-or-more anything)) string-end) (eat--set-cmd cmd)) ;; UIC e ; G ST ("e;G" (eat--pre-cmd)) ;; UIC e ; H ; ST. ((rx string-start "e;H;" (let status (one-or-more digit)) string-end) (eat--set-cmd-status (string-to-number status))) ;; UIC e ; I ; 0 ; ; ; ST. ((rx string-start "e;I;0;" (let format (zero-or-more (not (any ?\;)))) ?\; (let host (zero-or-more (not (any ?\;)))) ?\; (let path (zero-or-more anything)) string-end) (eat--get-shell-history (cons host path) format)) ;; UIC e ; I ; 1 ; ; ST. ((rx string-start "e;I;1;" (let format (zero-or-more (not (any ?\;)))) ?\; (let hist (zero-or-more anything)) string-end) (eat--get-shell-history hist format)) ;; UIC e ; J ST. ("e;J" (eat--before-new-prompt)) ;; UIC e ; M ; ... ST. ((rx string-start "e;M;" (let msg (zero-or-more anything)) string-end) (apply #'eat--handle-message (string-split msg ";"))))) (defun eat-previous-shell-prompt (&optional arg) "Go to the previous shell prompt. When numeric prefix argument, ARG, is given, go to ARGth previous shell prompt." (interactive "p") (dotimes (_ (or arg 1)) (let ((previous (previous-single-property-change (point) 'eat--shell-prompt-end))) (goto-char (or previous (point-min))) (when (get-text-property (point) 'eat--shell-prompt-end) (setq previous (previous-single-property-change (point) 'eat--shell-prompt-end)) (goto-char (or previous (point-min)))) (unless previous (user-error "No previous prompt"))))) (defun eat-next-shell-prompt (&optional arg) "Go to the next shell prompt. When numeric prefix argument, ARG, is given, go to ARGth next shell prompt." (interactive "p") (dotimes (_ (or arg 1)) (let ((next (next-single-property-change (point) 'eat--shell-prompt-end))) (goto-char (or next (point-max))) (when (get-text-property (point) 'eat--shell-prompt-end) (goto-char (or (next-single-property-change (point) 'eat--shell-prompt-end) (point-max)))) (unless next (user-error "No next prompt"))))) (defun eat-narrow-to-shell-prompt () "Narrow buffer to the shell prompt and following output at point." (interactive) (widen) (narrow-to-region (save-excursion (while (not (or (bobp) (get-text-property (point) 'eat--shell-prompt-begin))) (goto-char (or (previous-single-property-change (point) 'eat--shell-prompt-begin) (point-min)))) (point)) (save-excursion (when (and (not (eobp)) (get-text-property (point) 'eat--shell-prompt-begin)) (goto-char (or (next-single-property-change (point) 'eat--shell-prompt-begin) (point-max)))) (while (not (or (eobp) (get-text-property (point) 'eat--shell-prompt-begin))) (goto-char (or (next-single-property-change (point) 'eat--shell-prompt-begin) (point-max)))) (point)))) ;;;;; Input. (defvar eat--mouse-grabbing-type nil "Current mouse grabbing type/mode.") (defvar eat--mouse-pressed-buttons nil "Mouse buttons currently pressed.") (defvar eat--mouse-last-position nil "Last position of mouse, nil when not dragging.") (defvar eat--mouse-drag-transient-map-exit nil "Function to exit mouse dragging transient map.") (defun eat-self-input (n &optional e) "Send E as input N times. N defaults to 1 and E defaults to `last-command-event' and should be a event." (interactive (list (prefix-numeric-value current-prefix-arg) (if (and (> (length (this-command-keys)) 1) (eq (aref (this-command-keys) (- (length (this-command-keys)) 2)) meta-prefix-char)) ;; HACK: Capture meta modifier (ESC prefix) in terminal. (cond ((eq last-command-event meta-prefix-char) last-command-event) ((characterp last-command-event) (aref (kbd (format "M-%c" last-command-event)) 0)) ((symbolp last-command-event) (aref (kbd (format "M-<%S>" last-command-event)) 0)) (t last-command-event)) last-command-event))) (when (memq (event-basic-type e) '( mouse-1 mouse-2 mouse-3 mouse-4 mouse-5 mouse-6 mouse-7 mouse-8 mouse-9 mouse-10 mouse-11)) (select-window (posn-window (event-start e)))) (when eat-terminal (unless (mouse-movement-p e) (funcall eat--synchronize-scroll-function (eat--synchronize-scroll-windows 'force-selected))) (if (memq (event-basic-type e) '( mouse-1 mouse-2 mouse-3 mouse-4 mouse-5 mouse-6 mouse-7 mouse-8 mouse-9 mouse-10 mouse-11 mouse-movement)) (let ((disp-begin-posn (posn-at-point (eat-term-display-beginning eat-terminal))) (e (if (or (not eat--mouse-last-position) (eq (posn-window (if (memq 'drag (event-modifiers e)) (event-end e) (event-start e))) (posn-window eat--mouse-last-position))) e (pcase e (`(,type ,_) `(,type ,eat--mouse-last-position)) (`(,type ,start ,_) `(,type ,start ,eat--mouse-last-position)) (ev ev))))) (if (not (mouse-movement-p e)) (eat-term-input-event eat-terminal n e disp-begin-posn) (if (not eat--mouse-pressed-buttons) (when (eq eat--mouse-grabbing-type :all) (eat-term-input-event eat-terminal n e disp-begin-posn)) (when (memq eat--mouse-grabbing-type '(:all :drag)) (eat-term-input-event eat-terminal n e disp-begin-posn)) (setq eat--mouse-last-position (event-start e)))) (when (memq (event-basic-type e) '(mouse-1 mouse-2 mouse-3)) (when (or (memq 'click (event-modifiers e)) (memq 'drag (event-modifiers e))) (setq eat--mouse-pressed-buttons (delq (event-basic-type e) eat--mouse-pressed-buttons)) (unless eat--mouse-pressed-buttons (setq eat--mouse-last-position nil) (when eat--mouse-drag-transient-map-exit (funcall eat--mouse-drag-transient-map-exit) (setq eat--mouse-drag-transient-map-exit nil)))) (when (memq 'down (event-modifiers e)) (push (event-basic-type e) eat--mouse-pressed-buttons) (setq eat--mouse-last-position (event-start e)) (unless eat--mouse-drag-transient-map-exit (let ((old-track-mouse track-mouse) (buffer (current-buffer))) (setq track-mouse 'dragging) (setq eat--mouse-drag-transient-map-exit (set-transient-map (let ((map (eat-term-make-keymap #'eat-self-input '(:mouse-modifier :mouse-movement) nil))) ;; Some of the events will of course end up ;; looked up with a mode-line, header-line ;; or vertical-line prefix ... (define-key map [mode-line] map) (define-key map [header-line] map) (define-key map [tab-line] map) (define-key map [vertical-line] map) ;; ... and some maybe even with a right- or ;; bottom-divider prefix. (define-key map [right-divider] map) (define-key map [bottom-divider] map)) #'always (lambda () (with-current-buffer buffer (setq track-mouse old-track-mouse)))))))))) (eat-term-input-event eat-terminal n e)))) (defun eat-quoted-input () "Read a character and send it as INPUT." (declare (interactive-only "Use `eat-self-input' instead.")) (interactive) ;; HACK: Quick hack to allow inputting `C-g'. Any better way to do ;; this? (eat-self-input 1 (let ((inhibit-quit t) ;; Don't trigger `quit' exiting this `let'. (quit-flag nil)) (read-event)))) (defun eat-input-char (character count) "Input CHARACTER, COUNT times. Interactively, ask for the character CHARACTER to input. The numeric prefix argument COUNT specifies how many times to insert CHARACTER." (declare (interactive-only "Use `eat-self-input' instead.")) (interactive (list (read-char-by-name "Insert character (Unicode name or hex): ") (prefix-numeric-value current-prefix-arg))) (eat-self-input count character)) (defvar yank-transform-functions) ; In `simple'. (defun eat-yank (&optional arg) "Same as `yank', but for Eat. ARG is passed to `yank', which see." (interactive "*P") (when eat-terminal (funcall eat--synchronize-scroll-function (eat--synchronize-scroll-windows 'force-selected)) (eat-term-send-string-as-yank eat-terminal (let ((yank-hook (bound-and-true-p yank-transform-functions))) (with-temp-buffer (setq-local yank-transform-functions yank-hook) (yank arg) (buffer-string)))))) (defun eat-yank-from-kill-ring (string &optional arg) "Same as `yank-from-kill-ring', but for Eat. STRING and ARG are passed to `yank-pop', which see." (interactive (progn (unless (eval-when-compile (>= emacs-major-version 28)) (error "`eat-yank-from-kill-ring' requires at least Emacs 28")) (list (read-from-kill-ring "Yank from kill-ring: ") current-prefix-arg))) (unless (eval-when-compile (>= emacs-major-version 28)) (error "`eat-yank-from-kill-ring' requires at least Emacs 28")) (when eat-terminal (funcall eat--synchronize-scroll-function (eat--synchronize-scroll-windows 'force-selected)) (eat-term-send-string-as-yank eat-terminal (let ((yank-hook (bound-and-true-p yank-transform-functions))) (with-temp-buffer (setq-local yank-transform-functions yank-hook) (yank-from-kill-ring string arg) (buffer-string)))))) (defun eat-mouse-yank-primary (&optional event) "Send the primary selection to the terminal. EVENT is the mouse event." (interactive "e") (when select-active-regions (let (select-active-regions) (deactivate-mark))) (unless (windowp (posn-window (event-start event))) (error "Position not in text area of window")) (select-window (posn-window (event-start event))) (eat-term-send-string-as-yank eat-terminal (gui-get-primary-selection))) (defun eat-mouse-yank-secondary (&optional event) "Send the secondary selection to the terminal. EVENT is the mouse event." (interactive "e") (unless (windowp (posn-window (event-start event))) (error "Position not in text area of window")) (select-window (posn-window (event-start event))) (let ((secondary (gui-get-selection 'SECONDARY))) (if secondary (eat-term-send-string-as-yank eat-terminal secondary) (error "No secondary selection")))) (defun eat-xterm-paste (event) "Handle paste operation EVENT from XTerm." (interactive "e") (unless (eq (car-safe event) 'xterm-paste) (error "`eat-xterm-paste' must be bind to `xterm-paste' event")) (let ((pasted-text (nth 1 event))) (if (bound-and-true-p xterm-store-paste-on-kill-ring) ;; Put the text onto the kill ring and then insert it into the ;; buffer. (let ((interprogram-paste-function (lambda () pasted-text))) (eat-yank)) ;; Insert the text without putting it onto the kill ring. (eat-term-send-string-as-yank eat-terminal pasted-text)))) (defun eat-send-password () "Read password from minibuffer and send it to the terminal." (declare (interactive-only t)) (interactive) (unless eat-terminal (user-error "Process not running")) (eat-term-send-string eat-terminal (read-passwd "Password: ")) (eat-self-input 1 'return)) ;; When changing these keymaps, be sure to update the manual, README ;; and commentary. (defvar eat-mode-map (let ((map (make-sparse-keymap))) (define-key map [?\C-c ?\M-d] #'eat-char-mode) (define-key map [?\C-c ?\C-j] #'eat-semi-char-mode) (define-key map [?\C-c ?\C-l] #'eat-line-mode) (define-key map [?\C-c ?\C-k] #'eat-kill-process) (define-key map [?\C-c ?\C-p] #'eat-previous-shell-prompt) (define-key map [?\C-c ?\C-n] #'eat-next-shell-prompt) (define-key map [?\C-x ?n ?d] #'eat-narrow-to-shell-prompt) (define-key map [xterm-paste] #'ignore) map) "Keymap for Eat mode.") (defun eat--prepare-semi-char-mode-map () "Prepare `eat-semi-char-mode-map'." (let ((map (eat-term-make-keymap #'eat-self-input '(:ascii :arrow :navigation) `([?\C-c] [?\C-q] [?\C-y] [?\e ?y] ,@eat-semi-char-non-bound-keys)))) (define-key map [?\C-q] #'eat-quoted-input) (define-key map [?\C-y] #'eat-yank) (define-key map [?\M-y] #'eat-yank-from-kill-ring) (define-key map [?\C-c ?\C-c] #'eat-self-input) (define-key map [?\C-c ?\C-e] #'eat-emacs-mode) (define-key map [S-insert] #'eat-yank) (define-key map [remap insert-char] #'eat-input-char) (define-key map [remap mouse-yank-primary] #'eat-mouse-yank-primary) (define-key map [remap mouse-yank-secondary] #'eat-mouse-yank-secondary) (define-key map [xterm-paste] #'eat-xterm-paste) map)) (defvar eat-semi-char-mode-map (ignore-errors (eat--prepare-semi-char-mode-map)) "Keymap for Eat semi-char mode.") (defun eat-update-semi-char-mode-map () "Update \"semi-char\" keybinding mode's keymap." (setq eat-semi-char-mode-map (eat--prepare-semi-char-mode-map))) (defvar eat-char-mode-map (let ((map (eat-term-make-keymap #'eat-self-input '(:ascii :arrow :navigation :function) '([?\e ?\C-m])))) (define-key map [?\C-\M-m] #'eat-semi-char-mode) (define-key map [xterm-paste] #'eat-xterm-paste) map) "Keymap for Eat char mode.") (defvar eat-line-mode-map (let ((map (make-sparse-keymap))) (define-key map [?\C-c ?\C-e] #'eat-emacs-mode) (define-key map [?\t] #'completion-at-point) (define-key map [?\C-m] #'eat-line-send-input) (define-key map [?\C-d] #'eat-line-delchar-or-eof) (define-key map [?\C-c ?\C-c] #'eat-line-send-interrupt) (define-key map [?\C-c ?\s] #'newline) (define-key map [?\M-p] #'eat-line-previous-input) (define-key map [?\M-n] #'eat-line-next-input) (define-key map [C-up] #'eat-line-previous-input) (define-key map [C-down] #'eat-line-next-input) (define-key map [?\M-r] #'eat-line-history-isearch-backward-regexp) (define-key map [?\C-c ?\C-r] #'eat-line-find-input) (define-key map [?\C-c ?\M-r] #'eat-line-previous-matching-input-from-input) (define-key map [?\C-c ?\M-s] #'eat-line-next-matching-input-from-input) map) "Keymap for Eat line mode.") (defvar eat--mouse-click-mode-map (eat-term-make-keymap #'eat-self-input '(:mouse-click) nil) "Keymap for `eat--mouse-click-mode'.") (defvar eat--mouse-modifier-click-mode-map (eat-term-make-keymap #'eat-self-input '(:mouse-modifier) nil) "Keymap for `eat--mouse-modifier-click-mode'.") (defvar eat--mouse-movement-mode-map (eat-term-make-keymap #'eat-self-input '(:mouse-movement) nil) "Keymap for `eat--mouse-movement-mode'.") (define-minor-mode eat--semi-char-mode "Minor mode for semi-char mode keymap." :interactive nil :keymap eat-semi-char-mode-map) (define-minor-mode eat--char-mode "Minor mode for char mode keymap." :interactive nil :keymap eat-char-mode-map) (define-minor-mode eat--mouse-click-mode "Minor mode for mouse click keymap." :interactive nil) (define-minor-mode eat--mouse-modifier-click-mode "Minor mode for mouse click with modifiers keymap." :interactive nil) (define-minor-mode eat--mouse-movement-mode "Minor mode for mouse movement keymap." :interactive nil) (defun eat-emacs-mode () "Switch to Emacs keybindings mode." (interactive) (eat--line-mode-exit) (eat--semi-char-mode -1) (eat--char-mode -1) (setq buffer-read-only t) (eat--grab-mouse nil eat--mouse-grabbing-type) (force-mode-line-update)) (defun eat-semi-char-mode () "Switch to semi-char mode." (interactive) (unless eat-terminal (error "Process not running")) (setq buffer-read-only nil) (eat--line-mode-exit) (eat--char-mode -1) (eat--semi-char-mode +1) (eat--grab-mouse nil eat--mouse-grabbing-type) (force-mode-line-update)) (defun eat-char-mode () "Switch to char mode." (interactive) (unless eat-terminal (error "Process not running")) (setq buffer-read-only nil) (eat--line-mode-exit) (eat--semi-char-mode -1) (eat--char-mode +1) (eat--grab-mouse nil eat--mouse-grabbing-type) (force-mode-line-update)) (defvar eat--eshell-semi-char-mode) (defvar eat--eshell-char-mode) (defun eat--grab-mouse (_ mode) "Grab mouse. MODE should one of: nil Disable mouse. `:click' Pass `mouse-1', `mouse-2', and `mouse-3' clicks. `:modifier-click' Pass all mouse clicks, including control, meta and shift modifiers. `:drag' All of :modifier-click, plus dragging (moving mouse while pressed) information. `:all' Pass all mouse events, including movement. Any other value Disable mouse." (setq eat--mouse-grabbing-type mode) (pcase (and eat-enable-mouse (or eat--semi-char-mode eat--char-mode eat--eshell-semi-char-mode eat--eshell-char-mode) mode) (:all (setq track-mouse t) (eat--mouse-click-mode -1) (eat--mouse-modifier-click-mode +1) (eat--mouse-movement-mode +1)) ((or :modifier-click :drag) (setq track-mouse nil) (eat--mouse-click-mode -1) (eat--mouse-movement-mode -1) (eat--mouse-modifier-click-mode +1)) (:click (setq track-mouse nil) (eat--mouse-modifier-click-mode -1) (eat--mouse-movement-mode -1) (eat--mouse-click-mode +1)) (_ (setq track-mouse nil) (eat--mouse-click-mode -1) (eat--mouse-modifier-click-mode -1) (eat--mouse-movement-mode -1)))) ;;;;; Line Mode. (define-minor-mode eat--line-mode "Minor mode for line mode." :interactive nil :keymap eat-line-mode-map (if eat--line-mode (let ((inhibit-read-only t)) (add-hook 'pre-command-hook #'eat--line-move-to-input nil t) (add-text-properties (eat-term-beginning eat-terminal) (eat-term-end eat-terminal) '(front-sticky t rear-nonsticky t))) (remove-hook 'pre-command-hook #'eat--line-move-to-input t) (let ((inhibit-read-only t)) (when (/= (eat-term-beginning eat-terminal) (eat-term-end eat-terminal)) (remove-text-properties (eat-term-beginning eat-terminal) (eat-term-end eat-terminal) '(front-sticky nil rear-nonsticky nil)))))) (defun eat-line-mode () "Switch to line mode." (interactive) (unless eat-terminal (error "Process not running")) (eat--line-mode +1) (eat--semi-char-mode -1) (eat--char-mode -1) (eat--grab-mouse nil eat--mouse-grabbing-type) (setq buffer-read-only nil) ;; Delete the undo list so that `undo' doesn't mess up with the ;; terminal. (setq buffer-undo-list nil) ;; Don't let auto line mode exit line mode. (setq eat--inhibit-auto-line-mode t)) (defun eat--line-mode-exit () "Exit line mode, called only by interactive commands." (when eat--line-mode (when (/= (eat-term-end eat-terminal) (point-max)) (eat-line-send)) (eat--line-mode -1) (setq buffer-undo-list nil) (setq eat--inhibit-auto-line-mode t) (setq eat--auto-line-mode-prev-mode nil))) (defun eat--line-move-to-input () "Move point to the input line." (when (and eat-line-auto-move-to-input (< (point) (eat-term-end eat-terminal)) (eq #'self-insert-command this-command)) (deactivate-mark) (push-mark) (goto-char (point-max)))) (defun eat-line-send-default () "Send shell prompt input directly to the terminal." (eat-term-send-string eat-terminal (buffer-string)) ;; If output arrives after sending the string, new output may get ;; included in the narrowed region. So we narrow it again so that ;; we don't get a `text-read-only' for trying to delete text in the ;; terminal. (narrow-to-region (eat-term-end eat-terminal) (point-max))) (defun eat-line-send () "Send shell prompt input to the terminal." (save-excursion (save-restriction (narrow-to-region (eat-term-end eat-terminal) (point-max)) (funcall eat-line-input-send-function) (delete-region (point-min) (point-max)) (eat--line-reset-input-ring-vars) (setq buffer-undo-list nil))) (goto-char (eat-term-display-cursor eat-terminal))) (defvar eat--line-input-ring) (defun eat-line-send-input (&optional no-newline) "Send shell prompt input to the terminal. If called without any prefix argument, or if NO-NEWLINE is nil, append a newline to the input before sending it." (interactive "P") (if (not (<= (eat-term-end eat-terminal) (point))) (call-interactively #'newline) (unless (= (eat-term-end eat-terminal) (point-max)) (unless eat--line-input-ring (setq eat--line-input-ring (make-ring eat-line-input-ring-size))) (ring-insert eat--line-input-ring (buffer-substring-no-properties (eat-term-end eat-terminal) (point-max)))) (unless no-newline (goto-char (point-max)) (insert "\n")) (eat-line-send))) (defun eat-line-delchar-or-eof (arg) "Delete character or send shell prompt input to the terminal. ARG is the prefix arg, passed to `delete-char' when deleting character." (interactive "p") (if (not (= (eat-term-end eat-terminal) (point-max))) (delete-char arg) (insert "\C-d") (eat-line-send))) (defun eat-line-send-interrupt () "Clear the input and send `C-c' to the shell." (interactive) (delete-region (eat-term-end eat-terminal) (point-max)) (goto-char (point-max)) (insert "\C-c") (eat-line-send)) ;;;;;; History. ;; The following code in this page (or section) is adapted from ;; Comint source. (defvar eat--line-input-ring nil "Ring holding the history of inputs.") (defvar eat--line-input-ring-index nil "Index of last matched history element.") (defvar eat--line-stored-incomplete-input nil "Stored input for history cycling.") (defvar eat--line-matching-input-from-input-string "" "Input previously used to match input history.") (defvar eat--saved-line-input-history-isearch 'not-saved "Saved value of `eat-line-input-history-isearch'.") (defun eat--line-reset-input-ring-vars () "Reset variable after a new shell prompt." (setq eat--line-input-ring-index nil) (setq eat--line-stored-incomplete-input nil) (setq eat--line-matching-input-from-input-string "")) (defun eat--line-populate-input-ring (hist format) "Populate `eat--line-input-ring' from HIST in format FORMAT." (setq eat--line-input-ring (make-ring eat-line-input-ring-size)) (pcase format ("bash" (dolist (item (string-split hist "\n" 'omit-nulls)) (when (/= (aref item 0) ?#) (ring-insert eat--line-input-ring item)))) ("zsh" (dolist (item (string-split hist "\n" 'omit-nulls)) (ring-insert eat--line-input-ring (string-trim item (rx ": " (zero-or-more digit) ?: (zero-or-more digit) ?\;))))))) (defun eat-line-load-input-history-from-file (file format) "Load input history from FILE. FORMAT is the format of FILE." (interactive (let ((file (read-file-name "History file: "))) (list file (completing-read "History file format: " '("bash" "zsh") nil t (pcase (file-name-nondirectory file) (".bash_history" "bash") (".zsh_history" "zsh")))))) (let ((str nil)) (with-temp-buffer (insert-file-contents file) (setq str (buffer-string))) (eat--line-populate-input-ring str format))) (defun eat--line-ask-for-regexp-arg (prompt) "Return list of regexp and prefix arg using PROMPT." (let* (;; Don't clobber this. (last-command last-command) (regexp (read-from-minibuffer prompt nil nil nil 'minibuffer-history-search-history))) ;; If the user didn't enter anything, nothing is added to m-h-s-h. ;; Use the previous search regexp, if there is one. (list (if (string-equal regexp "") (or (car minibuffer-history-search-history) regexp) regexp) (prefix-numeric-value current-prefix-arg)))) (defun eat--line-search-arg (arg) "Check point, and return ARG, or one if ARG is zero." ;; First make sure there is a ring and that we are after the ;; terminal region. (cond ((< (point) (eat-term-end eat-terminal)) (user-error "Not at command line")) ((or (null eat--line-input-ring) (ring-empty-p eat--line-input-ring)) (user-error "Empty input ring")) ((zerop arg) ;; ARG zero resets search from beginning, and uses ARG 1. (setq eat--line-input-ring-index nil) 1) (t arg))) (defun eat-line-restore-input () "Restore unfinished input." (interactive) (when eat--line-input-ring-index (delete-region (eat-term-end eat-terminal) (point-max)) (when (> (length eat--line-stored-incomplete-input) 0) (insert eat--line-stored-incomplete-input) (message "Input restored")) (setq eat--line-input-ring-index nil))) (defun eat--line-search-start (arg) "Index to start a directional search, ARG indicates the direction." (if eat--line-input-ring-index ;; If a search is running, offset by 1 in direction of ARG. (mod (+ eat--line-input-ring-index (if (> arg 0) 1 -1)) (ring-length eat--line-input-ring)) ;; For a new search, start from end if ARG is negative, or from ;; beginning otherwise. (if (> arg 0) 0 (1- (ring-length eat--line-input-ring))))) (defun eat--line-prev-input-string (arg) "Return the string ARG places along the input ring. Moves relative to `eat--line-input-ring-index'." (ring-ref eat--line-input-ring (if eat--line-input-ring-index (mod (+ arg eat--line-input-ring-index) (ring-length eat--line-input-ring)) arg))) (defun eat-line-previous-input (arg) "Cycle backwards through input history, saving input. Negative ARG means search forward instead." (interactive "*p") (if (and eat--line-input-ring-index ;; Are we leaving the "end" of the ring? (or (and (< arg 0) ; going down (eq eat--line-input-ring-index 0)) (and (> arg 0) ; going up (eq eat--line-input-ring-index (1- (ring-length eat--line-input-ring))))) eat--line-stored-incomplete-input) (eat-line-restore-input) (eat-line-previous-matching-input "." arg))) (defun eat-line-next-input (arg) "Cycle forwards through input history, saving input. Negative ARG means search backward instead." (interactive "*p") (eat-line-previous-input (- arg))) (defun eat--line-prev-matching-input-str (regexp arg) "Return the string matching REGEXP ARG places along the input ring. Moves relative to `eat--line-input-ring-index'." (let* ((pos (eat--line-prev-matching-input-str-pos regexp arg))) (if pos (ring-ref eat--line-input-ring pos)))) (defun eat--line-prev-matching-input-str-pos (regexp arg &optional start) "Return the index matching REGEXP ARG places along the input ring. Moves relative to START, or `eat--line-input-ring-index'." (when (or (not (ring-p eat--line-input-ring)) (ring-empty-p eat--line-input-ring)) (user-error "No history")) (let* ((len (ring-length eat--line-input-ring)) (motion (if (> arg 0) 1 -1)) (n (mod (- (or start (eat--line-search-start arg)) motion) len)) (tried-each-ring-item nil) (prev nil)) ;; Do the whole search as many times as the argument says. (while (and (/= arg 0) (not tried-each-ring-item)) ;; Step once. (setq prev n) (setq n (mod (+ n motion) len)) ;; If we haven't reached a match, step some more. (while (and (< n len) (not tried-each-ring-item) (not (string-match regexp (ring-ref eat--line-input-ring n)))) (setq n (mod (+ n motion) len)) ;; If we have gone all the way around in this search. (setq tried-each-ring-item (= n prev))) (setq arg (if (> arg 0) (1- arg) (1+ arg)))) ;; Now that we know which ring element to use, if we found it, ;; return that. (when (string-match regexp (ring-ref eat--line-input-ring n)) n))) (defun eat-line-previous-matching-input (regexp n &optional restore) "Search backwards through input history for match for REGEXP. \(Previous history elements are earlier commands.) With prefix argument N, search for Nth previous match. If N is negative, find the next or Nth next match. If RESTORE is non-nil, restore input in case of wrap." (interactive (eat--line-ask-for-regexp-arg "Previous input matching (regexp): ")) (setq n (eat--line-search-arg n)) (let ((pos (eat--line-prev-matching-input-str-pos regexp n))) ;; Has a match been found? (if (null pos) (user-error "Not found") (if (and eat--line-input-ring-index restore (or (and (< n 0) (< eat--line-input-ring-index pos)) (and (> n 0) (> eat--line-input-ring-index pos)))) ;; We have a wrap; restore contents. (eat-line-restore-input) ;; If leaving the edit line, save partial input. (if (null eat--line-input-ring-index) ;not yet on ring (setq eat--line-stored-incomplete-input (buffer-substring-no-properties (eat-term-end eat-terminal) (point-max)))) (setq eat--line-input-ring-index pos) (unless isearch-mode (let ((message-log-max nil)) ; Do not write to *Messages*. (message "History item: %d" (1+ pos)))) (delete-region (eat-term-end eat-terminal) (point-max)) (insert (ring-ref eat--line-input-ring pos)))))) (defun eat-line-next-matching-input (regexp n) "Search forwards through input history for match for REGEXP. \(Later history elements are more recent commands.) With prefix argument N, search for Nth following match. If N is negative, find the previous or Nth previous match." (interactive (eat--line-ask-for-regexp-arg "Next input matching (regexp): ")) (eat-line-previous-matching-input regexp (- n))) (defun eat-line-previous-matching-input-from-input (n) "Search backwards through input history for match for current input. \(Previous history elements are earlier commands.) With prefix argument N, search for Nth previous match. If N is negative, search forwards for the -Nth following match." (interactive "p") (let ((opoint (point))) (unless (memq last-command '(eat-line-previous-matching-input-from-input eat-line-next-matching-input-from-input)) ;; Starting a new search (setq eat--line-matching-input-from-input-string (buffer-substring (eat-term-end eat-terminal) (point-max))) (setq eat--line-input-ring-index nil)) (eat-line-previous-matching-input (concat "^" (regexp-quote eat--line-matching-input-from-input-string)) n t) (when (eq eat-line-move-point-for-matching-input 'after-input) (goto-char opoint)))) (defun eat-line-next-matching-input-from-input (n) "Search forwards through input history for match for current input. \(Following history elements are more recent commands.) With prefix argument N, search for Nth following match. If N is negative, search backwards for the -Nth previous match." (interactive "p") (eat-line-previous-matching-input-from-input (- n))) (defun eat-line-find-input () "Find and insert input history using minibuffer." (declare (interactive-only t)) (interactive) (when (or (not (ring-p eat--line-input-ring)) (ring-empty-p eat--line-input-ring)) (user-error "No history")) (let ((str (completing-read "Input: " (seq-uniq (ring-elements eat--line-input-ring)) nil nil (buffer-substring (eat-term-end eat-terminal) (point-max)))) (i 0) (pos nil)) (while (and (< i (ring-length eat--line-input-ring)) (not pos)) (when (equal (ring-ref eat--line-input-ring i) str) (setq pos i)) (cl-incf i)) (when pos (setq eat--line-input-ring-index pos)) (delete-region (eat-term-end eat-terminal) (point-max)) (insert str))) (defun eat-line-history-isearch-backward () "Search for a string backward in input history using Isearch." (interactive) (setq eat--saved-line-input-history-isearch eat-line-input-history-isearch) (setq eat-line-input-history-isearch t) (isearch-backward nil t)) (defun eat-line-history-isearch-backward-regexp () "Search for a regular expression backward in input history using Isearch." (interactive) (setq eat--saved-line-input-history-isearch eat-line-input-history-isearch) (setq eat-line-input-history-isearch t) (isearch-backward-regexp nil t)) (defun eat--line-history-isearch-setup () "Set up Eat buffer for using Isearch to search the input history." (when (or (eq eat-line-input-history-isearch t) (and (eq eat-line-input-history-isearch 'dwim) (>= (point) (eat-term-end eat-terminal)))) (setq isearch-message-prefix-add "history ") (setq isearch-search-fun-function #'eat--line-history-isearch-search) (setq isearch-wrap-function #'eat--line-history-isearch-wrap) (setq isearch-push-state-function #'eat--line-history-isearch-push-state) (make-local-variable 'isearch-lazy-count) (setq isearch-lazy-count nil) (add-hook 'isearch-mode-end-hook 'eat--line-history-isearch-end nil t))) (defun eat--line-history-isearch-end () "Clean up after terminating Isearch." (setq isearch-message-prefix-add nil) (setq isearch-search-fun-function 'isearch-search-fun-default) (setq isearch-wrap-function nil) (setq isearch-push-state-function nil) ;; Force isearch to not change mark. (setq isearch-opoint (point)) (kill-local-variable 'isearch-lazy-count) (remove-hook 'isearch-mode-end-hook 'eat--line-history-isearch-end t) (unless (or isearch-suspended (eq eat--saved-line-input-history-isearch 'not-saved)) (setq eat-line-input-history-isearch eat--saved-line-input-history-isearch) (setq eat--saved-line-input-history-isearch 'not-saved))) (defun eat--line-goto-input (pos) "Put input history item of the absolute history position POS." ;; If leaving the edit line, save partial unfinished input. (when (null eat--line-input-ring-index) (setq eat--line-stored-incomplete-input (buffer-substring-no-properties (eat-term-end eat-terminal) (point-max)))) (setq eat--line-input-ring-index pos) (delete-region (eat-term-end eat-terminal) (point-max)) (if (and pos (not (ring-empty-p eat--line-input-ring))) (insert (ring-ref eat--line-input-ring pos)) ;; Restore partial unfinished input. (when (> (length eat--line-stored-incomplete-input) 0) (insert eat--line-stored-incomplete-input)))) (defun eat--line-history-isearch-search () "Return the proper search function, for Isearch in input history." (lambda (string bound noerror) (let ((search-fun (isearch-search-fun-default)) found) ;; Avoid lazy-highlighting matches in the input line and in the ;; output when searching forward. Lazy-highlight calls this ;; lambda with the bound arg, so skip the prompt and the output. (when (and bound isearch-forward (< (point) (eat-term-end eat-terminal))) (goto-char (eat-term-end eat-terminal))) (or ;; 1. First try searching in the initial input line (funcall search-fun string (if isearch-forward bound (eat-term-end eat-terminal)) noerror) ;; 2. If the above search fails, start putting next/prev ;; history elements in the input line successively, and search ;; the string in them. Do this only when bound is nil ;; (i.e. not while lazy-highlighting search strings in the ;; current input line). (unless bound (condition-case nil (progn (while (not found) (cond (isearch-forward ;; Signal an error here explicitly, because ;; `eat-line-next-input' doesn't signal an ;; error. (when (null eat--line-input-ring-index) (error "End of history; no next item")) (eat-line-next-input 1) (goto-char (eat-term-end eat-terminal))) (t ;; Signal an error here explicitly, because ;; `eat-line-previous-input' doesn't signal an ;; error. (when (eq eat--line-input-ring-index (1- (ring-length eat--line-input-ring))) (error "Beginning of history; no preceding item")) (eat-line-previous-input 1) (goto-char (point-max)))) (setq isearch-barrier (point)) (setq isearch-opoint (point)) ;; After putting the next/prev history element, ;; search the string in them again, until ;; `eat-line-next-input' or `eat-line-previous-input' ;; raises an error at the beginning/end of history. (setq found (funcall search-fun string (unless isearch-forward ;; For backward search, don't search ;; in the terminal region (eat-term-end eat-terminal)) noerror))) ;; Return point of the new search result (point)) ;; Return nil on the error "no next/preceding item" (error nil))))))) (defun eat--line-history-isearch-wrap () "Wrap the input history search when search fails. Move point to the first history element for a forward search, or to the last history element for a backward search." ;; When `eat--line-history-isearch-search' fails on reaching the ;; beginning/end of the history, wrap the search to the first/last ;; input history element. (if isearch-forward (eat--line-goto-input (1- (ring-length eat--line-input-ring))) (eat--line-goto-input nil)) (goto-char (if isearch-forward (eat-term-end eat-terminal) (point-max)))) (defun eat--line-history-isearch-push-state () "Save a function restoring the state of input history search. Save `eat--line-input-ring-index' to the additional state parameter in the search status stack." (let ((index eat--line-input-ring-index)) (lambda (cmd) (eat--line-history-isearch-pop-state cmd index)))) (defun eat--line-history-isearch-pop-state (_cmd hist-pos) "Restore the input history search state. Go to the history element by the absolute history position HIST-POS." (eat--line-goto-input hist-pos)) ;;;;; Major Mode. (defun eat--synchronize-scroll-windows (&optional force-selected) "Return the list of windows whose scrolling should be synchronized. When FORCE-SELECTED is non-nil, always include `buffer' and the selected window in the list if the window is showing the current buffer." `(,@(and (or force-selected eat--char-mode (= (eat-term-display-cursor eat-terminal) (point))) '(buffer)) ,@(seq-filter (lambda (window) (or (and force-selected (eq window (selected-window))) (= (eat-term-display-cursor eat-terminal) (window-point window)))) (get-buffer-window-list)))) (defun eat--synchronize-scroll (windows) "Synchronize scrolling and point between terminal and WINDOWS. WINDOWS is a list of windows. WINDOWS may also contain the special symbol `buffer', in which case the point of current buffer is set." (dolist (window windows) (if (eq window 'buffer) (goto-char (eat-term-display-cursor eat-terminal)) (with-selected-window window (set-window-point nil (eat-term-display-cursor eat-terminal)) (recenter (- (how-many "\n" (eat-term-display-beginning eat-terminal) (eat-term-display-cursor eat-terminal)) (cdr (eat-term-size eat-terminal)) (max 0 (- (floor (window-screen-lines)) (cdr (eat-term-size eat-terminal)))))))))) (defun eat--setup-glyphless-chars () "Setup the display of glyphless characters." (setq-local glyphless-char-display (copy-sequence (default-value 'glyphless-char-display))) (set-char-table-extra-slot glyphless-char-display 0 (if (display-graphic-p) 'empty-box 'thin-space))) (defun eat--filter-buffer-substring (begin end &optional delete) "Filter buffer substring from BEGIN to END and return that. When DELETE is given and non-nil, delete the text between BEGIN and END if it's safe to do so." (let ((str (buffer-substring begin end))) (remove-text-properties 0 (length str) '( read-only nil rear-nonsticky nil front-sticky nil field nil eat--before-string nil eat--shell-prompt-mark-id nil eat--shell-prompt-mark-overlay nil eat--shell-prompt-begin nil eat--shell-prompt-end nil) str) (setq str (eat-term-filter-string str)) (when (and delete (or (not eat-terminal) (and (<= (eat-term-end eat-terminal) begin) (<= (eat-term-end eat-terminal) end)) (and (<= begin (eat-term-beginning eat-terminal)) (<= end (eat-term-beginning eat-terminal))))) (delete-region begin end)) str)) (define-derived-mode eat-mode fundamental-mode "Eat" "Major mode for Eat." :group 'eat-ui (mapc #'make-local-variable '(buffer-read-only buffer-undo-list filter-buffer-substring-function mode-line-process mode-line-buffer-identification glyphless-char-display cursor-type track-mouse scroll-margin hscroll-margin eat-terminal eat--synchronize-scroll-function eat--mouse-grabbing-type eat--shell-command-status eat--shell-prompt-begin eat--shell-prompt-mark eat--shell-prompt-mark-overlays eat--inhibit-auto-line-mode eat--auto-line-mode-prev-mode eat--line-input-ring eat--line-input-ring-index eat--line-stored-incomplete-input eat--line-matching-input-from-input-string isearch-search-fun-function isearch-wrap-function isearch-push-state-function eat--pending-output-chunks eat--output-queue-first-chunk-time eat--process-output-queue-timer eat--shell-prompt-annotation-correction-timer)) ;; This is intended; input methods don't work on read-only buffers. (setq buffer-read-only nil) (setq scroll-margin 0) (setq hscroll-margin 0) (setq eat--synchronize-scroll-function #'eat--synchronize-scroll) (setq filter-buffer-substring-function #'eat--filter-buffer-substring) (setq bidi-paragraph-direction 'left-to-right) (setq eat--mouse-grabbing-type nil) (add-hook 'isearch-mode-hook 'eat--line-history-isearch-setup nil t) (setq mode-line-process '("" (:eval (when eat-terminal (cond (eat--semi-char-mode '("[" (:propertize "semi-char" help-echo "mouse-1: Switch to char mode, \ mouse-2: Switch to line mode, mouse-3: Switch to emacs mode" mouse-face mode-line-highlight local-map (keymap (mode-line . (keymap (down-mouse-1 . eat-char-mode) (down-mouse-2 . eat-line-mode) (down-mouse-3 . eat-emacs-mode))))) "]")) (eat--char-mode '("[" (:propertize "char" help-echo "mouse-1: Switch to semi-char mode, \ mouse-2: Switch to line mode, mouse-3: Switch to emacs mode" mouse-face mode-line-highlight local-map (keymap (mode-line . (keymap (down-mouse-1 . eat-semi-char-mode) (down-mouse-2 . eat-line-mode) (down-mouse-3 . eat-emacs-mode))))) "]")) (eat--line-mode '("[" (:propertize "line" help-echo "mouse-1: Switch to semi char mode, \ mouse-2: Switch to emacs mode, mouse-3: Switch to char mode" mouse-face mode-line-highlight local-map (keymap (mode-line . (keymap (down-mouse-1 . eat-semi-char-mode) (down-mouse-2 . eat-emacs-mode) (down-mouse-3 . eat-char-mode))))) "]")) (t '("[" (:propertize "emacs" help-echo "mouse-1: Switch to semi char mode, \ mouse-3: Switch to char mode" mouse-face mode-line-highlight local-map (keymap (mode-line . (keymap (down-mouse-1 . eat-semi-char-mode) (down-mouse-2 . eat-line-mode) (down-mouse-3 . eat-char-mode))))) "]"))))) ":%s")) (setq mode-line-buffer-identification `(12 ("" ,(nconc (propertized-buffer-identification "%b") '(" " (:propertize (:eval (when-let* ((eat-terminal) (title (eat-term-title eat-terminal)) ((not (string-empty-p title)))) (format "(%s)" (string-replace "%" "%%" title)))) help-echo "Title")))))) (eat-emacs-mode) ;; Make sure glyphless character don't display a huge box glyph, ;; that would break the display. (eat--setup-glyphless-chars) ;; Setup completion for line mode. (shell-completion-vars) (when eat-enable-blinking-text (eat-blink-mode +1)) (when eat-enable-shell-prompt-annotation (let ((margin-width (max (string-width eat-shell-prompt-annotation-running-margin-indicator) (string-width eat-shell-prompt-annotation-success-margin-indicator) (string-width eat-shell-prompt-annotation-failure-margin-indicator)))) (pcase-exhaustive eat-shell-prompt-annotation-position ('left-margin (setq left-margin-width margin-width)) ('right-margin (setq right-margin-width margin-width)))) ;; Make sure the marginal area is resized. (dolist (win (get-buffer-window-list)) (set-window-buffer win (current-buffer))))) ;;;;; Process Handling. (defvar eat--pending-output-chunks nil "The list of pending output chunks. The output chunks are pushed, so last output appears first.") (defvar eat--output-queue-first-chunk-time nil "Time when the first chunk in the current output queue was pushed.") (defvar eat--process-output-queue-timer nil "Timer to process output queue.") (defvar eat--shell-prompt-annotation-correction-timer nil "Timer to correct shell prompt annotations.") (defun eat-kill-process () "Kill Eat process in current buffer." (interactive) (when-let* ((eat-terminal) (proc (eat-term-parameter eat-terminal 'eat--process))) (delete-process proc))) (defun eat--send-string (process string) "Send to PROCESS the contents of STRING as input. This is equivalent to `process-send-string', except that long input strings are broken up into chunks of size `eat-input-chunk-size'. Processes are given a chance to output between chunks. This can help prevent processes from hanging when you send them long inputs on some OS's." (let ((i 0) (j eat-input-chunk-size) (l (length string))) (while (< i l) (process-send-string process (substring string i (min j l))) (accept-process-output) (cl-incf i eat-input-chunk-size) (cl-incf j eat-input-chunk-size)))) (defun eat--send-input (_ input) "Send INPUT to subprocess." (when-let* ((eat-terminal) (proc (eat-term-parameter eat-terminal 'eat--process))) (eat--send-string proc input))) (defun eat--process-output-queue (buffer) "Process the output queue on BUFFER." (when (buffer-live-p buffer) (with-current-buffer buffer (let ((inhibit-quit t) ; Don't disturb! (sync-windows (eat--synchronize-scroll-windows)) (eat--auto-line-mode-pending-toggles nil)) (save-restriction (widen) (let ((inhibit-read-only t) (inhibit-modification-hooks t) ;; Don't let `undo' mess up with the terminal. (buffer-undo-list t)) (when eat--process-output-queue-timer (cancel-timer eat--process-output-queue-timer)) (setq eat--output-queue-first-chunk-time nil) (while eat--pending-output-chunks (let ((queue eat--pending-output-chunks) (eat--output-queue-first-chunk-time t)) (setq eat--pending-output-chunks nil) (dolist (output (nreverse queue)) (eat-term-process-output eat-terminal output)))) (eat-term-redisplay eat-terminal) ;; Truncate output of previous dead processes. (when (and eat-term-scrollback-size (< eat-term-scrollback-size (- (point) (point-min)))) (delete-region (point-min) (max (point-min) (- (eat-term-display-beginning eat-terminal) eat-term-scrollback-size)))) (setq eat--shell-prompt-annotation-correction-timer (run-with-timer eat-shell-prompt-annotation-correction-delay nil #'eat--correct-shell-prompt-mark-overlays buffer)) (add-text-properties (eat-term-beginning eat-terminal) (eat-term-end eat-terminal) `( read-only t field eat-terminal ,@(when eat--line-mode '(front-sticky t rear-nonsticky t)))))) (eat--line-mode-do-toggles) (funcall eat--synchronize-scroll-function sync-windows)) (run-hooks 'eat-update-hook)))) (defun eat--filter (process output) "Handle OUTPUT from PROCESS." (when (buffer-live-p (process-buffer process)) (with-current-buffer (process-buffer process) (when eat--process-output-queue-timer (cancel-timer eat--process-output-queue-timer)) (when eat--shell-prompt-annotation-correction-timer (cancel-timer eat--shell-prompt-annotation-correction-timer)) (unless eat--output-queue-first-chunk-time (setq eat--output-queue-first-chunk-time (current-time))) (push output eat--pending-output-chunks) (unless (eq eat--output-queue-first-chunk-time t) (let ((time-left (- eat-maximum-latency (float-time (time-subtract nil eat--output-queue-first-chunk-time))))) (if (<= time-left 0) (eat--process-output-queue (current-buffer)) (setq eat--process-output-queue-timer (run-with-timer (min time-left eat-minimum-latency) nil #'eat--process-output-queue (current-buffer))))))))) (defun eat--sentinel (process message) "Sentinel for Eat buffers. PROCESS is the process and MESSAGE is the description of what happened to it." (let ((buffer (process-buffer process))) (when (memq (process-status process) '(signal exit)) (if (buffer-live-p buffer) (with-current-buffer buffer (let ((inhibit-read-only t) ;; We're is going to write outside of the terminal, ;; so we won't synchronize buffer scroll here as we ;; will set the buffer point automatically by ;; writing to the buffer. (eat--synchronize-scroll-function #'ignore)) (when eat--process-output-queue-timer (cancel-timer eat--process-output-queue-timer) (setq eat--process-output-queue-timer nil)) (eat--process-output-queue buffer) (when eat--shell-prompt-annotation-correction-timer (cancel-timer eat--shell-prompt-annotation-correction-timer) (setq eat--shell-prompt-annotation-correction-timer nil)) (when eat-enable-shell-prompt-annotation (eat--correct-shell-prompt-mark-overlays buffer) (setq eat--shell-command-status 0) (setq eat--shell-prompt-begin nil) (setq eat--shell-prompt-mark nil) (setq eat--shell-prompt-mark-overlays nil)) (when eat--line-mode (eat--line-mode -1) (delete-region (eat-term-end eat-terminal) (point-max))) (eat-emacs-mode) (remove-text-properties (eat-term-beginning eat-terminal) (eat-term-end eat-terminal) '(read-only nil field nil)) (eat-term-delete eat-terminal) (setq eat-terminal nil) (eat--set-cursor nil :default) (eat--grab-mouse nil nil) (goto-char (point-max)) (insert "\nProcess " (process-name process) " " message) (setq buffer-read-only nil)) (run-hook-with-args 'eat-exit-hook process) (delete-process process)) (set-process-buffer process nil))))) (defun eat--adjust-process-window-size (process windows) "Resize process window and terminal. Return new dimensions. PROCESS is the process whose window to resize, and WINDOWS is the list of window displaying PROCESS's buffer." (let ((size (funcall window-adjust-process-window-size-function process windows))) (when size (let ((width (max (car size) 1)) (height (max (cdr size) 1)) (inhibit-read-only t) (sync-windows (eat--synchronize-scroll-windows))) (eat-term-resize eat-terminal width height) (eat-term-redisplay eat-terminal) (funcall eat--synchronize-scroll-function sync-windows)) (pcase major-mode ('eat-mode (run-hooks 'eat-update-hook)) ('eshell-mode (run-hooks 'eat-eshell-update-hook)))) size)) (defun eat--kill-buffer (_process) "Kill current buffer." (kill-buffer (current-buffer))) ;; Adapted from Term. (defun eat-exec (buffer name command startfile switches) "Start up a process in BUFFER for Eat mode. Run COMMAND with SWITCHES. Set NAME as the name of the process. Blast any old process running in the buffer. Don't set the buffer mode. You can use this to cheaply run a series of processes in the same Eat buffer. The hook `eat-exec-hook' is run after each exec." (with-current-buffer buffer (let ((inhibit-read-only t)) (when-let* ((eat-terminal) (proc (eat-term-parameter eat-terminal 'eat--process))) (remove-hook 'eat-exit-hook #'eat--kill-buffer t) (delete-process proc)) ;; Ensure final newline. (goto-char (point-max)) (unless (or (= (point-min) (point-max)) (= (char-before (point-max)) ?\n)) (insert ?\n)) (unless (= (point-min) (point-max)) (insert "\n\n")) (setq eat-terminal (eat-term-make buffer (point))) (eat-semi-char-mode) (when-let* ((window (get-buffer-window nil t))) (with-selected-window window (eat-term-resize eat-terminal (window-max-chars-per-line) (floor (window-screen-lines))))) (setf (eat-term-parameter eat-terminal 'input-function) #'eat--send-input) (setf (eat-term-parameter eat-terminal 'set-cursor-function) #'eat--set-cursor) (setf (eat-term-parameter eat-terminal 'grab-mouse-function) #'eat--grab-mouse) (setf (eat-term-parameter eat-terminal 'manipulate-selection-function) #'eat--manipulate-kill-ring) (setf (eat-term-parameter eat-terminal 'ring-bell-function) #'eat--bell) (setf (eat-term-parameter eat-terminal 'set-cwd-function) #'eat--set-cwd) (setf (eat-term-parameter eat-terminal 'ui-command-function) #'eat--handle-uic) (eat--set-term-sixel-params) ;; Crank up a new process. (let* ((size (eat-term-size eat-terminal)) (process-environment (nconc (list (concat "TERM=" (eat-term-name)) (concat "TERMINFO=" eat-term-terminfo-directory) (concat "INSIDE_EMACS=" eat-term-inside-emacs) (concat "EAT_SHELL_INTEGRATION_DIR=" eat-term-shell-integration-directory)) process-environment)) (process-connection-type t) ;; We should suppress conversion of end-of-line format. (inhibit-eol-conversion t) (process (make-process :name name :buffer buffer :command `("/usr/bin/env" "sh" "-c" ,(format "stty -nl echo rows %d columns \ %d sane 2>%s ; if [ $1 = .. ]; then shift; fi; exec \"$@\"" (cdr size) (car size) null-device) ".." ,command ,@switches) :filter #'eat--filter :sentinel #'eat--sentinel :file-handler t))) (process-put process 'adjust-window-size-function #'eat--adjust-process-window-size) (set-process-query-on-exit-flag process eat-query-before-killing-running-terminal) ;; Jump to the end, and set the process mark. (goto-char (point-max)) (set-marker (process-mark process) (point)) (setf (eat-term-parameter eat-terminal 'eat--process) process) (setf (eat-term-parameter eat-terminal 'eat--input-process) process) (setf (eat-term-parameter eat-terminal 'eat--output-process) process) (when eat-kill-buffer-on-exit (add-hook 'eat-exit-hook #'eat--kill-buffer 90 t)) ;; Feed it the startfile. (when startfile ;; This is guaranteed to wait long enough ;; but has bad results if the shell does not prompt at all ;; (while (= size (buffer-size)) ;; (sleep-for 1)) ;; I hope 1 second is enough! (sleep-for 1) (goto-char (point-max)) (insert-file-contents startfile) (process-send-string process (delete-and-extract-region (point) (point-max))))) (eat-term-redisplay eat-terminal)) (run-hook-with-args 'eat-exec-hook (eat-term-parameter eat-terminal 'eat--process)) buffer)) ;;;;; Entry Points. (defun eat-make (name program &optional startfile &rest switches) "Make a Eat process NAME in a buffer, running PROGRAM. The name of the buffer is made by surrounding NAME with `*'s. If there is already a running process in that buffer, it is not restarted. Optional third arg STARTFILE is the name of a file to send the contents of to the process. SWITCHES are the arguments to PROGRAM." (let ((buffer (get-buffer-create (concat "*" name "*")))) ;; If no process, or nuked process, crank up a new one and put ;; buffer in Eat mode. Otherwise, leave buffer and existing ;; process alone. (when (not (let ((proc (get-buffer-process buffer))) (and proc (memq (process-status proc) '(run stop open listen connect))))) (with-current-buffer buffer (eat-mode)) (eat-exec buffer name program startfile switches)) buffer)) (defun eat--1 (program arg display-buffer-fn) "Start a new Eat terminal emulator in a buffer. PROGRAM and ARG is same as in `eat' and `eat-other-window'. DISPLAY-BUFFER-FN is the function to display the buffer." (let ((program (or program (or explicit-shell-file-name (getenv "ESHELL") shell-file-name))) (buffer (cond ((numberp arg) (get-buffer-create (format "%s<%d>" eat-buffer-name arg))) (arg (generate-new-buffer eat-buffer-name)) (t (get-buffer-create eat-buffer-name))))) (with-current-buffer buffer (unless (eq major-mode #'eat-mode) (eat-mode)) (funcall display-buffer-fn buffer) (unless (and eat-terminal (eat-term-parameter eat-terminal 'eat--process)) (eat-exec buffer (buffer-name) "/usr/bin/env" nil (list "sh" "-c" program))) buffer))) ;;;###autoload (defun eat (&optional program arg) "Start a new Eat terminal emulator in a buffer. Start a new Eat session, or switch to an already active session. Return the buffer selected (or created). With a non-numeric prefix ARG, create a new session. With a numeric prefix ARG (like \\[universal-argument] 42 \\[eat]), switch to the session with that number, or create it if it doesn't already exist. With double prefix argument ARG, ask for the program to run and run it in a newly created session. PROGRAM can be a shell command." (interactive (list (when (equal current-prefix-arg '(16)) (read-shell-command "Run program: " (or explicit-shell-file-name (getenv "ESHELL") shell-file-name))) current-prefix-arg)) (eat--1 program arg #'pop-to-buffer-same-window)) ;;;###autoload (defun eat-other-window (&optional program arg) "Start a new Eat terminal emulator in a buffer in another window. Start a new Eat session, or switch to an already active session. Return the buffer selected (or created). With a non-numeric prefix ARG, create a new session. With a numeric prefix ARG switch to the session with that number, or create it if it doesn't already exist. With double prefix argument ARG, ask for the program to run and run it in a newly created session. PROGRAM can be a shell command." (interactive (list (when (equal current-prefix-arg '(16)) (read-shell-command "Run program: " (or explicit-shell-file-name (getenv "ESHELL") shell-file-name))) current-prefix-arg)) (eat--1 program arg #'pop-to-buffer)) ;;;; Eshell integration. ;;;;; Input. ;; When changing these keymaps, be sure to update the manual, README ;; and commentary. (defvar eat-eshell-emacs-mode-map (let ((map (make-sparse-keymap))) (define-key map [?\C-c ?\C-j] #'eat-eshell-semi-char-mode) (define-key map [remap eshell-toggle-direct-send] ; C-c M-d #'eat-eshell-char-mode) (define-key map [remap undo] #'undefined) ; Disable `undo'. (define-key map [xterm-paste] #'ignore) map) "Keymap for Eat Eshell \"emacs\" mode.") (defun eat--eshell-prepare-semi-char-mode-map () "Prepare `eat-eshell-semi-char-mode-map'." (let ((map (eat-term-make-keymap #'eat-self-input '(:ascii :arrow :navigation) `([?\C-c] [?\C-q] [?\C-y] [?\e ?y] ,@eat-eshell-semi-char-non-bound-keys)))) (define-key map [?\C-q] #'eat-quoted-input) (define-key map [?\C-y] #'eat-yank) (define-key map [?\M-y] #'eat-yank-from-kill-ring) (define-key map [?\C-c ?\C-e] #'eat-eshell-emacs-mode) (define-key map [S-insert] #'eat-yank) (define-key map [remap insert-char] #'eat-input-char) (define-key map [remap mouse-yank-primary] #'eat-mouse-yank-primary) (define-key map [remap mouse-yank-secondary] #'eat-mouse-yank-secondary) (define-key map [xterm-paste] #'eat-xterm-paste) map)) (defvar eat-eshell-semi-char-mode-map (ignore-errors (eat--eshell-prepare-semi-char-mode-map)) "Keymap for Eat Eshell semi-char mode.") (defun eat-eshell-update-semi-char-mode-map () "Update \"semi-char\" keybinding mode's keymap in Eshell." (setq eat-eshell-semi-char-mode-map (eat--eshell-prepare-semi-char-mode-map))) (defvar eat-eshell-char-mode-map (let ((map (eat-term-make-keymap #'eat-self-input '(:ascii :arrow :navigation :function) '([?\e ?\C-m])))) (define-key map [?\C-\M-m] #'eat-eshell-semi-char-mode) (define-key map [xterm-paste] #'eat-xterm-paste) map) "Keymap for Eat Eshell char mode.") (define-minor-mode eat--eshell-process-running-mode "Minor mode for \"emacs\" mode keymap when process is running." :interactive nil :keymap eat-eshell-emacs-mode-map) (define-minor-mode eat--eshell-semi-char-mode "Minor mode for semi-char mode keymap." :interactive nil :keymap eat-eshell-semi-char-mode-map ;; HACK: Some keys like `C-c' are overriden by other keymaps ;; (possibly by the keymaps of other minor modes), so we also put ;; the keymap to `minor-mode-overriding-map-alist' to make Emacs ;; prioritize us. (setq minor-mode-overriding-map-alist (delete (cons #'eat--eshell-semi-char-mode eat-eshell-semi-char-mode-map) minor-mode-overriding-map-alist)) (when eat--eshell-semi-char-mode (push (cons #'eat--eshell-semi-char-mode eat-eshell-semi-char-mode-map) minor-mode-overriding-map-alist))) (define-minor-mode eat--eshell-char-mode "Minor mode for char mode keymap." :interactive nil :keymap eat-eshell-char-mode-map ;; HACK: Some keys like `C-c' are overriden by other keymaps ;; (possibly by the keymaps of other minor modes), so we also put ;; the keymap to `minor-mode-overriding-map-alist' to make Emacs ;; prioritize us. (setq minor-mode-overriding-map-alist (delete (cons #'eat--eshell-char-mode eat-eshell-char-mode-map) minor-mode-overriding-map-alist)) (when eat--eshell-char-mode (push (cons #'eat--eshell-char-mode eat-eshell-char-mode-map) minor-mode-overriding-map-alist))) (defun eat-eshell-emacs-mode () "Switch to Emacs keybindings mode." (interactive) (eat--eshell-semi-char-mode -1) (eat--eshell-char-mode -1) (setq buffer-read-only t) (eat--grab-mouse nil eat--mouse-grabbing-type) (force-mode-line-update)) (defun eat-eshell-semi-char-mode () "Switch to semi-char mode." (interactive) (when eat-terminal (setq buffer-read-only nil) (eat--eshell-char-mode -1) (eat--eshell-semi-char-mode +1) (eat--grab-mouse nil eat--mouse-grabbing-type) (force-mode-line-update))) (defun eat-eshell-char-mode () "Switch to char mode." (interactive) (when eat-terminal (setq buffer-read-only nil) (eat--eshell-semi-char-mode -1) (eat--eshell-char-mode +1) (eat--grab-mouse nil eat--mouse-grabbing-type) (force-mode-line-update))) ;;;;; Process Handling. (defvar eat--eshell-invocation-directory nil "The directory from where the current process was started.") (defvar eshell-last-output-start) ; In `esh-mode'. (defvar eshell-last-output-end) ; In `esh-mode'. (defvar eshell-output-filter-functions) ; In `esh-mode'. (defvar eshell-parent-buffer) ; In `em-term'. (declare-function eshell-head-process "esh-cmd" ()) (declare-function eshell-resume-eval "esh-cmd" ()) (defun eat--eshell-handle-uic (_ cmd) "Handle UI Command sequence CMD." (pcase cmd ;; UIC e ; A ; ; ST. ((rx string-start "e;A;" (let host (zero-or-more (not (any ?\;)))) ?\; (let path (zero-or-more anything)) string-end) (eat--set-cwd-uic host path)) ;; UIC e ; F ; ST. ((rx string-start "e;F;" (let cmd (zero-or-more anything)) string-end) (eat--set-cmd cmd)) ;; UIC e ; I ; 0 ; ST. ((rx string-start "e;I;0;" (zero-or-more anything) string-end) (eat-term-send-string eat-terminal "\e]51;e;I;0\e\\")) ;; UIC e ; M ; ... ST. ((rx string-start "e;M;" (let msg (zero-or-more anything)) string-end) (apply #'eat--handle-message (string-split msg ";"))) ;; Other sequences are ignored. )) (defun eat--eshell-term-name (&rest _) "Return the value of `TERM' environment variable for Eshell." (eat-term-name)) (defun eat--eshell-output-filter () "Handle output from subprocess." (let ((inhibit-quit t) ; Don't disturb! (str (buffer-substring-no-properties eshell-last-output-start eshell-last-output-end))) (let ((inhibit-read-only t)) (delete-region eshell-last-output-start eshell-last-output-end)) (let ((sync-windows (eat--synchronize-scroll-windows)) (inhibit-read-only t)) (eat-term-process-output eat-terminal str) (eat-term-redisplay eat-terminal) (funcall eat--synchronize-scroll-function sync-windows)) (let ((inhibit-read-only t)) (let ((end (eat-term-end eat-terminal))) (set-marker eshell-last-output-start end) (set-marker eshell-last-output-end end) (set-marker (process-mark (eat-term-parameter eat-terminal 'eat--output-process)) end)))) (run-hooks 'eat-eshell-update-hook)) (defun eat--eshell-setup-proc-and-term (proc) "Setup process PROC and a new terminal for it." (unless eat-terminal (process-put proc 'adjust-window-size-function #'eat--adjust-process-window-size) (setq eat-terminal (eat-term-make (current-buffer) (if (marker-buffer (process-mark proc)) (process-mark proc) (point-max)))) (set-marker (process-mark proc) (eat-term-end eat-terminal)) (setf (eat-term-parameter eat-terminal 'input-function) #'eat--send-input) (setf (eat-term-parameter eat-terminal 'set-cursor-function) #'eat--set-cursor) (setf (eat-term-parameter eat-terminal 'grab-mouse-function) #'eat--grab-mouse) (setf (eat-term-parameter eat-terminal 'manipulate-selection-function) #'eat--manipulate-kill-ring) (setf (eat-term-parameter eat-terminal 'ring-bell-function) #'eat--bell) (setf (eat-term-parameter eat-terminal 'set-cwd-function) #'eat--set-cwd) (setf (eat-term-parameter eat-terminal 'ui-command-function) #'eat--eshell-handle-uic) (eat--set-term-sixel-params) (setf (eat-term-parameter eat-terminal 'eat--process) proc) (unless (eval-when-compile (>= emacs-major-version 29)) (setf (eat-term-parameter eat-terminal 'eat--input-process) proc)) (setf (eat-term-parameter eat-terminal 'eat--output-process) proc) (when-let* ((window (get-buffer-window nil t))) (with-selected-window window (eat-term-resize eat-terminal (window-max-chars-per-line) (floor (window-screen-lines))))) (eat-term-redisplay eat-terminal) (setq-local eshell-output-filter-functions '(eat--eshell-output-filter)) (eat--eshell-process-running-mode +1) (eat-eshell-semi-char-mode) (run-hooks 'eat-eshell-exec-hook))) (defun eat--eshell-cleanup () "Cleanup everything." (when eat-terminal (let ((inhibit-read-only t)) (cd-absolute eat--eshell-invocation-directory) (goto-char (eat-term-end eat-terminal)) (unless (or (= (point) (point-min)) (= (char-before) ?\n)) (insert ?\n)) (set-marker eshell-last-output-start (point)) (set-marker eshell-last-output-end (point)) (eat--cursor-blink-mode -1) (eat--grab-mouse nil nil) (set-process-filter (eat-term-parameter eat-terminal 'eat--output-process) (if (eval-when-compile (< emacs-major-version 30)) #'eshell-output-filter #'eshell-interactive-process-filter)) (eat-term-delete eat-terminal) (setq eat-terminal nil) (kill-local-variable 'eshell-output-filter-functions) (eat--eshell-semi-char-mode -1) (eat--eshell-char-mode -1) (eat--eshell-process-running-mode -1) (setq buffer-read-only nil)) (run-hooks 'eat-eshell-exit-hook))) (declare-function eshell-output-filter "esh-mode" (process string)) (declare-function eshell-interactive-process-filter "esh-mode" (process string)) (defun eat--eshell-process-output-queue (process buffer) "Process the output queue on BUFFER from PROCESS." (when (buffer-live-p buffer) (with-current-buffer buffer (when eat--process-output-queue-timer (cancel-timer eat--process-output-queue-timer)) (setq eat--output-queue-first-chunk-time nil) (while eat--pending-output-chunks (let ((queue eat--pending-output-chunks) (eat--output-queue-first-chunk-time t)) (setq eat--pending-output-chunks nil) (if (eval-when-compile (< emacs-major-version 27)) (eshell-output-filter process (string-join (nreverse queue))) (combine-change-calls (eat-term-beginning eat-terminal) (eat-term-end eat-terminal) ;; TODO: Is `string-join' OK or should we use a loop? (if (eval-when-compile (< emacs-major-version 30)) (eshell-output-filter process (string-join (nreverse queue))) (eshell-interactive-process-filter process (string-join (nreverse queue))))))))))) (defun eat--eshell-filter (process string) "Process output STRING from PROCESS." (when (buffer-live-p (process-buffer process)) (with-current-buffer (process-buffer process) (when eat--process-output-queue-timer (cancel-timer eat--process-output-queue-timer)) (unless eat--output-queue-first-chunk-time (setq eat--output-queue-first-chunk-time (current-time))) (push string eat--pending-output-chunks) (unless (eq eat--output-queue-first-chunk-time t) (let ((time-left (- eat-maximum-latency (float-time (time-subtract nil eat--output-queue-first-chunk-time))))) (if (<= time-left 0) (eat--eshell-process-output-queue process (current-buffer)) (setq eat--process-output-queue-timer (run-with-timer (min time-left eat-minimum-latency) nil #'eat--eshell-process-output-queue process (current-buffer))))))))) (declare-function eshell-sentinel "esh-proc" (proc string)) (defun eat--eshell-sentinel (process message) "Process status message MESSAGE from PROCESS." (when (buffer-live-p (process-buffer process)) (with-current-buffer (process-buffer process) ;; Eshell is going to write outside of the terminal, so we won't ;; synchronize buffer scroll here as it'll interfare with ;; Eshell. (cl-letf* ((eat--synchronize-scroll-function #'ignore) (process-send-string (symbol-function #'process-send-string)) ((symbol-function #'process-send-string) (lambda (proc string) (when (or (not (eq proc process)) (process-live-p proc)) (funcall process-send-string proc string))))) (eat--eshell-process-output-queue process (current-buffer))) (when (memq (process-status process) '(signal exit)) (eat--eshell-cleanup)))) (eshell-sentinel process message)) (declare-function eshell-search-path "esh-ext" (name)) (declare-function eshell-interactive-output-p "esh-io" (&optional index handles)) (defvar eshell-current-subjob-p) ; In `esh-proc'. ;; HACK: This is a dirty hack, it can break easily. (defun eat--eshell-adjust-make-process-args (fn command args) "Setup an environment to adjust `make-process' arguments. Call FN with COMMAND and ARGS, and whenever `make-process' is called, modify its argument to change the filter, the sentinel and invoke `stty' from the new process." (if (or eshell-current-subjob-p (not (eshell-interactive-output-p)) (and (not (eshell-search-path "stty")) (pcase eat-eshell-fallback-if-stty-not-available ('nil nil) ('t t) ('ask (not (y-or-n-p "The program stty can't be \ found, input won't be shown if terminal emulation is enabled. \ Disable terminal emulation? "))) ((and (pred functionp) function) (apply function command args))))) (funcall fn command args) (let ((hook (lambda (proc) (set-process-filter proc #'eat--eshell-filter) (set-process-sentinel proc #'eat--eshell-sentinel) (eat--eshell-setup-proc-and-term proc)))) (add-hook 'eshell-exec-hook hook 99) (unwind-protect (cond ;; Emacs 29 and above. ((eval-when-compile (>= emacs-major-version 29)) (cl-letf* ((make-process (symbol-function #'make-process)) ((symbol-function #'make-process) (lambda (&rest plist) ;; Make sure we don't attack wrong process. (if (not (equal (plist-get plist :command) (cons (file-local-name (expand-file-name command)) args))) (apply make-process plist) (setf (plist-get plist :command) `("/usr/bin/env" "sh" "-c" ,(format "stty -nl echo rows %d columns\ %d sane 2>%s ; if [ $1 = .. ]; then shift; fi; exec \"$@\"" (floor (window-screen-lines)) (window-max-chars-per-line) null-device) ".." ,@(plist-get plist :command))) (apply make-process plist))))) (funcall fn command args))) ;; Emacs 28. (t (cl-letf* ((start-file-process (symbol-function #'start-file-process)) ((symbol-function #'start-file-process) (lambda (name buffer &rest command) (apply start-file-process name buffer "/usr/bin/env" "sh" "-c" (format "stty -nl echo rows %d columns %d \ sane 2>%s ; if [ $1 = .. ]; then shift; fi; exec \"$@\"" (floor (window-screen-lines)) (window-max-chars-per-line) null-device) ".." command)))) (funcall fn command args)))) (remove-hook 'eshell-exec-hook hook))))) (defun eat--eshell-set-input-process (&rest _) "Set the process that gets user input." (when eat-terminal (setf (eat-term-parameter eat-terminal 'eat--input-process) (eshell-head-process)))) ;;;;; Minor Modes. (defun eat--eshell-synchronize-scroll (windows) "Synchronize scrolling and point between terminal and WINDOWS. WINDOWS is a list of windows. WINDOWS may also contain the special symbol `buffer', in which case the point of current buffer is set." (dolist (window windows) (if (eq window 'buffer) (goto-char (eat-term-display-cursor eat-terminal)) (with-selected-window window (set-window-point nil (eat-term-display-cursor eat-terminal)) (recenter (- (1+ (how-many "\n" (eat-term-display-cursor eat-terminal) (eat-term-end eat-terminal))))))))) (defun eat--eshell-update-cwd () "Update the current working directory." (setq eat--eshell-invocation-directory default-directory)) (defvar eshell-variable-aliases-list) ; In `esh-var'. (define-minor-mode eat--eshell-local-mode "Toggle Eat terminal emulation is Eshell." :interactive nil (let ((locals '(cursor-type glyphless-char-display scroll-margin hscroll-margin track-mouse filter-buffer-substring-function eat-terminal eat--synchronize-scroll-function eat--mouse-grabbing-type eat--pending-output-chunks eat--output-queue-first-chunk-time eat--process-output-queue-timer eat--eshell-invocation-directory))) (cond (eat--eshell-local-mode (mapc #'make-local-variable locals) (setq scroll-margin 0) (setq hscroll-margin 0) (setq eat--synchronize-scroll-function #'eat--eshell-synchronize-scroll) (setq filter-buffer-substring-function #'eat--filter-buffer-substring) (make-local-variable 'eshell-variable-aliases-list) (setq eshell-variable-aliases-list `(("TERM" eat--eshell-term-name t) ("TERMINFO" eat-term-terminfo-directory t) ("INSIDE_EMACS" eat-term-inside-emacs t) ("EAT_SHELL_INTEGRATION_DIR" eat-term-shell-integration-directory t) ,@eshell-variable-aliases-list)) ;; Make sure glyphless character don't display a huge box glyph, ;; that would break the display. (eat--setup-glyphless-chars) (eat--eshell-update-cwd) (when eat-enable-blinking-text (eat-blink-mode +1))) (t (when eat-enable-blinking-text (eat-blink-mode -1)) (mapc #'kill-local-variable locals) (setq eshell-variable-aliases-list (cl-delete-if (lambda (elem) (member elem '(("TERM" eat--eshell-term-name t) ("TERMINFO" eat-term-terminfo-directory t) ("INSIDE_EMACS" eat-term-inside-emacs t) ("EAT_SHELL_INTEGRATION_DIR" eat-term-shell-integration-directory t)))) eshell-variable-aliases-list)))))) (declare-function eshell-gather-process-output "esh-proc" (command args)) (defvar eshell-last-async-proc) ; In `esh-cmd'. (defvar eshell-last-async-procs) ; In `esh-cmd'. ;;;###autoload (define-minor-mode eat-eshell-mode "Toggle Eat terminal emulation in Eshell." :global t :lighter (eat--eshell-local-mode (" Eat-Eshell" (:eval (when eat-terminal (cond (eat--eshell-semi-char-mode `("[" (:propertize "semi-char" help-echo ,(concat "mouse-1: Switch to char mode, " "mouse-3: Switch to emacs mode") mouse-face mode-line-highlight local-map (keymap (mode-line . (keymap (down-mouse-1 . eat-eshell-char-mode) (down-mouse-3 . eat-eshell-emacs-mode))))) "]")) (eat--eshell-char-mode '("[" (:propertize "char" help-echo ,(concat "mouse-1: Switch to semi-char mode, " "mouse-3: Switch to emacs mode") mouse-face mode-line-highlight local-map (keymap (mode-line . (keymap (down-mouse-1 . eat-eshell-semi-char-mode) (down-mouse-3 . eat-eshell-emacs-mode))))) "]")) (t `("[" (:propertize "emacs" help-echo ,(concat "mouse-1: Switch to semi-char mode, " "mouse-3: Switch to char mode") mouse-face mode-line-highlight local-map (keymap (mode-line . (keymap (down-mouse-1 . eat-eshell-semi-char-mode) (down-mouse-3 . eat-eshell-char-mode))))) "]"))))))) :group 'eat-eshell (cond (eat-eshell-mode (let ((buffers nil)) (setq eat-eshell-mode nil) (require 'esh-mode) (require 'esh-proc) (require 'esh-var) (require 'esh-cmd) (dolist (buffer (buffer-list)) (with-current-buffer buffer (when (eq major-mode #'eshell-mode) (when (if (eval-when-compile (< emacs-major-version 29)) (bound-and-true-p eshell-last-async-proc) (bound-and-true-p eshell-last-async-procs)) (user-error (concat "Can't toggle Eat Eshell mode while" " any Eshell process is running"))) (push buffer buffers)))) (setq eat-eshell-mode t) (dolist (buffer buffers) (with-current-buffer buffer (eat--eshell-local-mode +1)))) (add-hook 'eshell-mode-hook #'eat--eshell-local-mode) (add-hook 'eshell-directory-change-hook #'eat--eshell-update-cwd) (advice-add #'eshell-gather-process-output :around #'eat--eshell-adjust-make-process-args) (when (eval-when-compile (>= emacs-major-version 29)) (advice-add #'eshell-resume-eval :after #'eat--eshell-set-input-process))) (t (let ((buffers nil)) (setq eat-eshell-mode t) (dolist (buffer (buffer-list)) (with-current-buffer buffer (when (and (eq major-mode #'eshell-mode) eat--eshell-local-mode) (when (if (eval-when-compile (< emacs-major-version 29)) (bound-and-true-p eshell-last-async-proc) (bound-and-true-p eshell-last-async-procs)) (user-error (concat "Can't toggle Eat Eshell mode while" " any Eshell process is running"))) (push buffer buffers)))) (setq eat-eshell-mode nil) (dolist (buffer buffers) (with-current-buffer buffer (eat--eshell-local-mode -1)))) (remove-hook 'eshell-mode-hook #'eat--eshell-local-mode) (remove-hook 'eshell-directory-change-hook #'eat--eshell-update-cwd) (advice-remove #'eshell-gather-process-output #'eat--eshell-adjust-make-process-args) (when (eval-when-compile (>= emacs-major-version 29)) (advice-remove #'eshell-resume-eval #'eat--eshell-set-input-process))))) ;;;; Eshell Visual Command Handling. (defvar eshell-destroy-buffer-when-process-dies) ; In `em-term'. ;; Adapted from `em-term'. (defun eat--eshell-visual-sentinel (proc _msg) "Clean up the buffer visiting PROC. If `eshell-destroy-buffer-when-process-dies' is non-nil, destroy the buffer. MSG describes PROC's status." (when eshell-destroy-buffer-when-process-dies (let ((proc-buf (process-buffer proc))) (when (and proc-buf (buffer-live-p proc-buf) (not (eq 'run (process-status proc))) (= (process-exit-status proc) 0)) (if (eq (current-buffer) proc-buf) (when-let* ((buf (and (boundp 'eshell-parent-buffer) (buffer-live-p eshell-parent-buffer) eshell-parent-buffer))) (switch-to-buffer buf))) (kill-buffer proc-buf))))) (defvar eshell-interpreter-alist) ; In `esh-ext'. (declare-function eshell-find-interpreter "esh-ext" (file args &optional no-examine-p)) (declare-function eshell-stringify-list "esh-util" (args)) (defun eat--eshell-exec-visual (&rest args) "Run the specified PROGRAM in a terminal emulation buffer. ARGS are passed to the program. At the moment, no piping of input is allowed." (require 'esh-ext) (require 'esh-util) (let* ((eshell-interpreter-alist nil) (interp (eshell-find-interpreter (car args) (cdr args))) (program (car interp)) (args (flatten-tree (eshell-stringify-list (append (cdr interp) (cdr args))))) (eat-buf (generate-new-buffer (concat "*" (file-name-nondirectory program) "*"))) (eshell-buf (current-buffer))) (with-current-buffer eat-buf (switch-to-buffer eat-buf) (eat-mode) (setq-local eshell-parent-buffer eshell-buf) (setq-local eat-kill-buffer-on-exit nil) (eat-exec eat-buf program program nil args) (let ((proc (get-buffer-process eat-buf))) (if (and proc (eq 'run (process-status proc))) (let ((sentinel (process-sentinel proc))) (add-function :after (var sentinel) #'eat--eshell-visual-sentinel) (set-process-sentinel proc sentinel)) (error "Failed to invoke visual command"))) (eat-semi-char-mode))) nil) (declare-function eshell-exec-visual "em-term" (&rest args)) ;;;###autoload (define-minor-mode eat-eshell-visual-command-mode "Toggle running Eshell visual commands with Eat." :group 'eat-eshell :global t (if eat-eshell-visual-command-mode (advice-add #'eshell-exec-visual :override #'eat--eshell-exec-visual) (advice-remove #'eshell-exec-visual #'eat--eshell-exec-visual))) ;;;; Project Integration. (declare-function project-root "project" (project)) (declare-function project-prefixed-buffer-name "project" (mode)) ;;;###autoload (defun eat-project (&optional arg) "Start Eat in the current project's root directory. Start a new Eat session, or switch to an already active session. Return the buffer selected (or created). With a non-numeric prefix ARG, create a new session. With a numeric prefix ARG (like \\[universal-argument] 42 \\[eat-project]), switch to the session with that number, or create it if it doesn't already exist." (interactive "P") (require 'project) (let* ((default-directory (project-root (project-current t))) (eat-buffer-name (project-prefixed-buffer-name "eat"))) (eat nil arg))) ;;;###autoload (defun eat-project-other-window (&optional arg) "Start Eat in the current project root directory in another window. Start a new Eat session, or switch to an already active session. Return the buffer selected (or created). With a non-numeric prefix ARG, create a new session. With a numeric prefix ARG (like \\[universal-argument] 42 \\[eat-project]), switch to the session with that number, or create it if it doesn't already exist." (interactive "P") (require 'project) (let* ((default-directory (project-root (project-current t))) (eat-buffer-name (project-prefixed-buffer-name "eat"))) (eat-other-window nil arg))) ;;;; Tracing. ;;;;; Recording Trace Data. (defconst eat--trace-recorded-variables '(eat-term-scrollback-size eat-enable-alternative-display) "The variable to record in trace output.") (defvar eat--trace-output-buffer nil "Buffer where the trace data is written to.") (defun eat--trace-log (time operation &rest args) "Log TIME, OPERATION and ARGS into trace output. TIME defaults to the current time. The current buffer should be the trace output buffer. Move the point to the end of (accessible portion of) buffer." (goto-char (point-max)) ;; Hope that `float-time' won't roll over while tracing. ;-) (insert (replace-regexp-in-string (rx (any (0 . 31))) (lambda (string) (format "\\\\x%02x" (aref string 0))) (format "%S" `(,(float-time time) ,operation ,@args))) ?\n)) (defun eat--trace-stop () "Stop tracing the terminal in current buffer." (when eat--trace-output-buffer (with-current-buffer eat--trace-output-buffer (eat--trace-log nil 'finish))) (remove-hook 'kill-buffer-hook #'eat--trace-stop t) (kill-local-variable 'eat--trace-output-buffer)) (defun eat--trace-exec (fn buffer name command startfile switches) "Trace `eat-exec'. BUFFER is the buffer and COMMAND and SWITCHES are the invocation command. BUFFER, NAME, COMMAND, STARTFILE and SWITCHES are passed to FN, `eat-exec', which see." (let ((time (current-time))) (prog1 (funcall fn buffer name command startfile switches) (let ((buf (generate-new-buffer (format "*eat-trace %s*: %s" (buffer-name buffer) (mapconcat #'shell-quote-argument (cons command switches) " ")))) (width nil) (height nil) (variables nil)) (with-current-buffer buffer (setq-local eat--trace-output-buffer buf) (add-hook 'kill-buffer-hook #'eat--trace-stop nil t) (let ((size (eat-term-size eat-terminal))) (setq width (car size)) (setq height (cdr size))) (dolist (var eat--trace-recorded-variables) (push (cons var (symbol-value var)) variables))) (with-current-buffer buf (when (fboundp 'lisp-data-mode) (lisp-data-mode)) (insert ";; -*- mode: lisp-data -*-\n") (eat--trace-log time 'create 'eat width height variables)))))) (defun eat--trace-process-output-queue (fn buffer) "Trace `eat--process-output-queue'. BUFFER is passed to FN, `eat--process-output-queue', which see." (if (or (not (buffer-live-p buffer)) (not (buffer-local-value 'eat--trace-output-buffer buffer))) (funcall fn buffer) (cl-letf* ((eat-term-process-output (symbol-function #'eat-term-process-output)) ((symbol-function #'eat-term-process-output) (lambda (terminal output) (when (buffer-live-p eat--trace-output-buffer) (with-current-buffer eat--trace-output-buffer (eat--trace-log nil 'output output))) (funcall eat-term-process-output terminal output))) (eat-term-redisplay (symbol-function #'eat-term-redisplay)) ((symbol-function #'eat-term-redisplay) (lambda (terminal) (when (buffer-live-p eat--trace-output-buffer) (with-current-buffer eat--trace-output-buffer (eat--trace-log nil 'redisplay))) (funcall eat-term-redisplay terminal)))) (funcall fn buffer)))) (defun eat--trace-adjust-process-window-size (fn process windows) "Trace `eat--adjust-process-window-size'. PROCESS and WINDOWS are passed to FN, `eat--adjust-process-window-size', which see." (cl-letf* ((eat-term-resize (symbol-function #'eat-term-resize)) ((symbol-function #'eat-term-resize) (lambda (terminal width height) (when (buffer-live-p eat--trace-output-buffer) (with-current-buffer eat--trace-output-buffer (eat--trace-log nil 'resize width height))) (funcall eat-term-resize terminal width height))) (eat-term-redisplay (symbol-function #'eat-term-redisplay)) ((symbol-function #'eat-term-redisplay) (lambda (terminal) (when (buffer-live-p eat--trace-output-buffer) (with-current-buffer eat--trace-output-buffer (eat--trace-log nil 'redisplay))) (funcall eat-term-redisplay terminal)))) (funcall fn process windows))) (defun eat--trace-sentinel (fn &rest args) "Trace `eat--sentinel'. Elements of ARGS are passed to FN, `eat--sentinel', which see." (cl-letf* ((eat-term-delete (symbol-function #'eat-term-delete)) ((symbol-function #'eat-term-delete) (lambda (terminal) (when (buffer-live-p eat--trace-output-buffer) (eat--trace-stop)) (funcall eat-term-delete terminal)))) (apply fn args))) (defun eat--trace-reset (fn) "Trace `eat-reset'. FN is original definition of `eat-reset'." (cl-letf* ((eat-term-reset (symbol-function #'eat-term-reset)) ((symbol-function #'eat-term-reset) (lambda (terminal) (when (buffer-live-p eat--trace-output-buffer) (with-current-buffer eat--trace-output-buffer (eat--trace-log nil 'reset))) (funcall eat-term-reset terminal))) (eat-term-redisplay (symbol-function #'eat-term-redisplay)) ((symbol-function #'eat-term-redisplay) (lambda (terminal) (when (buffer-live-p eat--trace-output-buffer) (with-current-buffer eat--trace-output-buffer (eat--trace-log nil 'redisplay))) (funcall eat-term-redisplay terminal)))) (funcall fn))) (defun eat--trace-eshell-adjust-make-process-args (fn &rest args) "Trace `eat--eshell-adjust-make-process-args'. ARGS is passed to FN, `eat--eshell-adjust-make-process-args', which see." (cl-letf* ((command nil) (make-process (symbol-function #'make-process)) ((symbol-function #'make-process) (lambda (&rest plist) (prog1 (apply make-process plist) (setq command (nthcdr 5 (plist-get plist :command)))))) (eat--eshell-setup-proc-and-term (symbol-function #'eat--eshell-setup-proc-and-term)) ((symbol-function #'eat--eshell-setup-proc-and-term) (lambda (proc) (let ((time (current-time))) (prog1 (funcall eat--eshell-setup-proc-and-term proc) (when (eq (eat-term-parameter eat-terminal 'eat--output-process) proc) (let ((buf (generate-new-buffer (format "*eat-trace %s*: %s" (buffer-name) (mapconcat #'shell-quote-argument command " ")))) (width nil) (height nil) (variables nil)) (setq-local eat--trace-output-buffer buf) (add-hook 'kill-buffer-hook #'eat--trace-stop nil t) (let ((size (eat-term-size eat-terminal))) (setq width (car size)) (setq height (cdr size))) (dolist (var eat--trace-recorded-variables) (push (cons var (symbol-value var)) variables)) (with-current-buffer buf (when (fboundp 'lisp-data-mode) (lisp-data-mode)) (insert ";; -*- lisp-data -*-\n") (eat--trace-log time 'create 'eshell width height variables))))))))) (apply fn args))) (defun eat--trace-eshell-output-filter (fn) "Trace `eat--eshell-output-filter'. FN is the original definition of `eat--eshell-output-filter', which see." (if (not (buffer-live-p eat--trace-output-buffer)) (funcall fn) (cl-letf* ((eat-term-process-output (symbol-function #'eat-term-process-output)) ((symbol-function #'eat-term-process-output) (lambda (terminal output) (with-current-buffer eat--trace-output-buffer (eat--trace-log nil 'output output)) (funcall eat-term-process-output terminal output))) (eat-term-redisplay (symbol-function #'eat-term-redisplay)) ((symbol-function #'eat-term-redisplay) (lambda (terminal) (with-current-buffer eat--trace-output-buffer (eat--trace-log nil 'redisplay)) (funcall eat-term-redisplay terminal)))) (funcall fn)))) (defun eat--trace-eshell-cleanup (fn) "Trace `eat--eshell-cleanup'. FN is the original definition of `eat--eshell-cleanup', which see." (if (not (buffer-live-p eat--trace-output-buffer)) (funcall fn) (cl-letf* ((eat-term-delete (symbol-function #'eat-term-delete)) ((symbol-function #'eat-term-delete) (lambda (terminal) (eat--trace-stop) (funcall eat-term-delete terminal)))) (funcall fn)))) (define-minor-mode eat-trace-mode "Toggle tracing Eat terminal." :global t :require 'eat :lighter " Eat-Trace" (if eat-trace-mode (progn (advice-add #'eat-exec :around #'eat--trace-exec) (advice-add #'eat--process-output-queue :around #'eat--trace-process-output-queue) (advice-add #'eat--adjust-process-window-size :around #'eat--trace-adjust-process-window-size) (advice-add #'eat--sentinel :around #'eat--trace-sentinel) (advice-add #'eat-reset :around #'eat--trace-reset) (advice-add #'eat--eshell-adjust-make-process-args :around #'eat--trace-eshell-adjust-make-process-args) (advice-add #'eat--eshell-output-filter :around #'eat--trace-eshell-output-filter) (advice-add #'eat--eshell-cleanup :around #'eat--trace-eshell-cleanup)) (advice-remove #'eat-exec #'eat--trace-exec) (advice-remove #'eat--process-output-queue #'eat--trace-process-output-queue) (advice-remove #'eat--adjust-process-window-size #'eat--trace-adjust-process-window-size) (advice-remove #'eat--sentinel #'eat--trace-sentinel) (advice-remove #'eat-reset #'eat--trace-reset) (advice-remove #'eat--eshell-adjust-make-process-args #'eat--trace-eshell-adjust-make-process-args) (advice-remove #'eat--eshell-output-filter #'eat--trace-eshell-output-filter) (advice-remove #'eat--eshell-cleanup #'eat--trace-eshell-cleanup) (dolist (buffer (buffer-list)) (when (buffer-local-value 'eat--trace-output-buffer buffer) (with-current-buffer buffer (setq-local eat--trace-output-buffer nil)))))) ;;;;; Trace Data Replay. (defvar eat--trace-replay-buffer nil "The buffer replaying the trace data in current buffer.") (defvar eat--trace-replay-marker nil "The point from where to read the next sexp.") (defvar eat--trace-replay-current-sexp-overlay nil "Overlay indicating the current sexp.") (defvar eat--trace-replay-source-buffer nil "The source buffer containing the trace output.") (defvar eat--trace-replay-recording-start-time 0.0 "Time when recording was started.") (defvar eat--trace-replay-frame-count 0 "The number of the frames in the trace output.") (defvar eat--trace-replay-progress-frame 0 "The number of the frames before the current position.") (defvar eat--trace-replay-progress nil "The number of seconds of trace output was shown.") (defun eat--trace-replay-eval (data) "Evalulate DATA as trace output." (let ((inhibit-read-only t)) (setq eat--trace-replay-progress (- (car data) eat--trace-replay-recording-start-time)) (pcase-exhaustive data (`(,time create ,_ui ,width ,height ,variables) (setq eat--trace-replay-recording-start-time time) (setq eat--trace-replay-progress 0) (dolist (var eat--trace-recorded-variables) (set (make-local-variable var) (alist-get var variables))) (setq eat-terminal (eat-term-make (current-buffer) (point))) (setf (eat-term-parameter eat-terminal 'set-cursor-function) #'eat--set-cursor) (setf (eat-term-parameter eat-terminal 'ring-bell-function) #'eat--bell) (eat-term-resize eat-terminal width height) (eat-term-redisplay eat-terminal)) (`(,_time output ,string) (eat-term-process-output eat-terminal string)) (`(,_time redisplay) (eat-term-redisplay eat-terminal)) (`(,_time resize ,width ,height) (eat-term-resize eat-terminal width height)) (`(,_time reset) (eat-term-reset eat-terminal)) (`(,_time finish) (eat-term-delete eat-terminal))) (eat--synchronize-scroll (get-buffer-window-list)))) (defun eat--trace-replay-eval-next () "Evaluate next sexp in trace output." (with-current-buffer eat--trace-replay-source-buffer (goto-char eat--trace-replay-marker) (ignore-error end-of-file (let ((data (read (current-buffer)))) (set-marker eat--trace-replay-marker (point)) (backward-list) (move-overlay eat--trace-replay-current-sexp-overlay (point) (point)) (when-let* ((window (get-buffer-window))) (set-window-point window (point))) (with-current-buffer eat--trace-replay-buffer (cl-incf eat--trace-replay-progress-frame) (eat--trace-replay-eval data)))))) (defun eat-trace-replay () "Replay terminal according to trace output in current buffer." (interactive) (unless (buffer-live-p eat--trace-replay-buffer) (setq-local eat--trace-replay-buffer (generate-new-buffer (format "*eat-trace-replay*: %s" (buffer-name)))) (setq-local eat--trace-replay-marker (point-min-marker)) (let ((ov (make-overlay (point-min) (point-min)))) (overlay-put ov 'before-string #(" " 0 1 (display (left-fringe right-triangle)))) (setq-local eat--trace-replay-current-sexp-overlay ov)) (goto-char (point-min)) (let ((source (current-buffer)) (frame-count 0)) (ignore-error end-of-file (while (read (current-buffer)) (cl-incf frame-count))) (goto-char (point-min)) (with-current-buffer eat--trace-replay-buffer (eat-trace-replay-mode) (setq eat--trace-replay-source-buffer source) (setq eat--trace-replay-frame-count frame-count)))) (display-buffer eat--trace-replay-buffer)) (defun eat-trace-replay-next-frame (&optional n) "Show the Nth next frame. N defaults to 1. Interactively, N is the prefix argument." (interactive "p") (dotimes (_ n) (eat--trace-replay-eval-next))) (defun eat-trace--cleanup () "Clean up the source buffer before the terminal being killed." (when (buffer-live-p eat--trace-replay-source-buffer) (with-current-buffer eat--trace-replay-source-buffer (setq eat--trace-replay-buffer nil) (setq eat--trace-replay-marker nil) (delete-overlay eat--trace-replay-current-sexp-overlay)))) (defvar eat-trace-replay-mode-map (let ((map (make-sparse-keymap))) (define-key map (kbd "n") #'eat-trace-replay-next-frame) (define-key map (kbd "") #'eat-trace-replay-next-frame) (define-key map (kbd "q") #'quit-window) map) "Keymap for Eat-Trace-Replay mode.") (define-derived-mode eat-trace-replay-mode special-mode "Eat-Trace-Replay" "Major mode for replaying terminal according to trace output." (mapc #'make-local-variable '(eat-terminal eat--trace-replay-source-buffer eat--trace-replay-recording-start-time eat--trace-replay-progress eat--trace-replay-frame-count eat--trace-replay-progress-frame)) (setq-local mode-line-process '("[" (:eval (number-to-string eat--trace-replay-progress-frame)) "/" (:eval (number-to-string eat--trace-replay-frame-count)) "]")) (add-hook 'kill-buffer-hook #'eat-trace--cleanup nil t)) ;;;; Miscellaneous. (defun eat-compile-terminfo () "Compile terminfo databases of Eat." (interactive) ;; Check for required files and programs. (let ((source-path (expand-file-name "eat.ti" eat--install-path)) (tic-path (executable-find "tic"))) (unless (file-exists-p source-path) (error "Eat not installed properly: %s" "Terminfo source file not found")) (unless tic-path (error "Terminfo compiler `tic' not found")) (message "Compiling terminfo databases...") ;; Compile. (let* ((command (format "env TERMINFO=\"%s\" %s -x %s" eat-term-terminfo-directory tic-path source-path)) (status (with-temp-buffer (make-directory eat-term-terminfo-directory 'parents) (let ((proc (start-process-shell-command "eat-terminfo-compile" (current-buffer) command))) (while (process-live-p proc) (sleep-for 0.02)) (process-exit-status proc))))) (if (= status 0) (message "Compiling terminfo databases...done") (message "Compiling terminfo databases...error") (error "Command `%s' exited with non-zero exit code %i" command status))))) ;;;; Footer. (defun eat-reload () "Reload Eat." (interactive) (unless eat--being-loaded ;; Remove .elc suffix to load native compiled version if possible. (load (string-remove-suffix ".elc" eat--load-file-path)))) (setq eat--being-loaded nil) (provide 'eat) ;;; eat.el ends here emacs-eat/eat.texi000066400000000000000000001573071453707721100144050ustar00rootroot00000000000000\input texinfo @comment %**start of header @setfilename eat.info @set UPDATED 15 December 2023 @set VERSION 0.9.4 @documentencoding UTF-8 @codequotebacktick on @codequoteundirected on @syncodeindex fn cp @syncodeindex vr cp @syncodeindex ky cp @settitle Eat User Manual, version @value{VERSION} @comment %**end of header @copying This manual is for Eat (version @value{VERSION}, @value{UPDATED}), a terminal emulator for Emacs. Copyright @copyright{} 2022, 2023 Akib Azmain Turja. @quotation Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License''. @end quotation @end copying @dircategory Emacs @direntry * Eat: (eat). Emulate A Terminal. @end direntry @titlepage @title Eat User Manual @subtitle For version @value{VERSION} @author Akib Azmain Turja @page @vskip 0pt plus 1filll @insertcopying @end titlepage @contents @ifnottex @node Top @top Eat Manual @insertcopying @end ifnottex @menu Introduction * Intro:: What is Eat? * Hello Terminal:: Starting Eat's terminal for the first time. * Project-local Terminal:: One project, one terminal. * Eshell Terminal:: Eat's terminal emulation in Eshell. Basic Operations * Keyboard:: Most keyboard keys are captured by Eat. * Mouse:: Eat supports mouse. * Input Modes:: Input modes for various use-cases. * Password Input:: Safely inputting credentials. Advanced Customizations * Shell Integration:: Getting the most from Eat and your shell. * Querying Before Kill:: Confirming before you kill your terminal. * Display:: The region where everything is shown. * Scrollback:: Region for things that went out of display. * Cursor Types:: Cursor can displayed in many forms. * Mouse Tracking:: Eat tracks mouse, but this can be changed. * Clipboard:: Integrating kill ring with terminal. * Colors:: Eat can show more than sixteen million colors. * Fonts:: Eat can show up to sixteen different fonts. * Blinking Text:: Annoying blinking texts. * Performance Tuning:: Fine tuning to maximize performance. Recovering from Problems * Common Problems:: Common problem, and fixes. * Reporting Bugs:: How to report problems? * Tracing the Terminal:: Gathering some crucial information to reproduce a bug. Appendices * GNU General Public License:: Copying condition for Eat. * GNU Free Documentation License:: Copying conditions of this manual. * Index:: A list of various things. @end menu @part Part I:@* Introduction @node Intro @cindex introduction @chapter Introduction @abbr{Eat, Emulate A Terminal} is a terminal emulator for Emacs. It emulates a XTerm-like terminal, just like many other terminal emulators. But it has some key features that make Eat distinct from other terminal emulators. Firstly, it's in Emacs, which means you don't need to leave the comfort of Emacs to use terminal. Secondly, it's easy and convenient to use. It is tries to stay out of your way, allowing you to maximize your productivity. Finally, special care has been taken while designing the keybindings, so that the terminal doesn't conflict with Emacs default keybindings on both graphical display and text display, while still allowing you to run full screen programs like Emacs within the terminal. @node Hello Terminal @cindex hello terminal @cindex terminal, hello @chapter Hello Terminal @findex eat The terminal can be started with @kbd{M-x eat}. It'll create a terminal and run the default shell (@pxref{Interactive Shell,,, emacs, GNU Emacs Manual}) in it. You should get a shell prompt and be able to write shell commands and execute them. Full screen programs like @samp{htop}, @samp{lynx} and Emacs will work inside it, just like any other terminal. (If the terminal doesn't work as expected, @pxref{Common Problems}.) If an Eat terminal already exists, @kbd{M-x eat} will switch to it. To create a new terminal, call it with a prefix argument like this, @kbd{C-u M-x eat}. If you give it a numeric prefix argument N, for example @kbd{C-u 42 M-x eat}, it'll switch to a terminal in the buffer @file{*eat*}, @file{*eat*<42>} for example, or it'll create a new terminal if that buffer doesn't exist. If you give it double prefix argument, for example @kbd{C-u C-u M-x eat}, you'll be prompted for the program or shell command to run, and it'll be run in a newly created terminal. @node Project-local Terminal @cindex project-local terminal @cindex terminal, project-local @cindex project's terminal @cindex project terminal @cindex terminal, project @chapter Project-local Terminal @findex eat-project Usually, you don't use a single terminal for everything, instead you open a terminal for each project that needs it. So there is command named @command{eat-project}. It opens a new terminal in project root directory, or switches to a already existing project terminal. It too accepts prefix argument, just like the ordinary @command{eat} command. @node Eshell Terminal @cindex eshell terminal @cindex terminal, eshell @cindex eshell terminal emulation @cindex terminal emulation, eshell @cindex eat, eshell @cindex eshell, eat @cindex eshell @chapter Eshell Terminal Emulation Eat also supports terminal emulation outside Eat's terminal. So you can emulate terminal in Eshell (@pxref{Top,,, eshell, Eshell manual}) with Eat. After configuring Eshell to use Eat for terminal emulation, you can run any full screen terminal program in Eshell. @findex eat-eshell-mode To enable terminal emulation in Eshell, enable the global minor mode @command{eat-eshell-mode}. It will enable Eat's terminal emulation in Eshell. To disable the terminal emulation, disable the minor mode. You can't toggle the global minor mode while any Eshell command is running, so terminate any Eshell command or wait them to finish before toggling the mode. Unless stated otherwise, everything described in this manual about Eat terminal also applies to Eshell terminal emulation. You might also want to set @code{eshell-visual-commands} user option to nil, since they'll work in Eshell when @code{eat-eshell-mode} is enabled. If you want to run Eshell visual commands with Eat, you can enable the global minor mode @command{eat-eshell-visual-command-mode}. @part Part II:@* Basic Operations @node Keyboard @cindex keyboard @chapter Keyboard Just like any other text terminal, the primary interaction device with Eat terminal is the keyboard. Eat forwards all supported keyboard events like @kbd{a}, @kbd{E}, @kbd{RET}, @kbd{C-a} to the terminal. However, this conflict with Emacs keybinding conventions, and makes it almost impossible to call any other Emacs command. So, by default, Eat doesn't intercept the key sequences beginning with the following keys and lets Emacs to handle them: @kbd{C-\}, @kbd{C-c}, @kbd{C-x}, @kbd{C-g}, @kbd{C-h}, @kbd{C-M-c}, @kbd{C-u}, @kbd{C-q}, @kbd{M-x}, @kbd{M-:}, @kbd{M-!} and @kbd{M-&}. To input the above key sequences, prefix them with @kbd{C-q}. @kbd{C-q} reads the next event and sends to directly to the terminal. For example, to input @kbd{M-:}, use the key sequence @kbd{C-q M-:}. For an alternative way to input these exceptional characters, @pxref{Char Mode}. @node Mouse @cindex mouse @chapter Mouse Eat supports mouse tracking. That means in programs like Emacs, @samp{htop}, etc, that support mouse, you can hover and click on text and buttons. You can also use your mouse wheel to scroll text, if the program supports it. @xref{Mouse Tracking} to configure mouse tracking. @node Input Modes @cindex input modes @cindex modes, input @cindex keybinding modes @cindex modes, keybinding @chapter Input Modes By default, Eat forwards all supported keys to terminals, except some exceptions. It is possible to input them with @kbd{C-q}, but it is not very convenient. To conveniently input those character, they should be bound to input themselves to the terminal (i.e. pressing @kbd{M-x} will input @kbd{M-x}, bypassing Emacs). But this is conflicts with Emacs's default keybindings, so this can't done, at least by default. To overcome the problem, Eat implements several ``input modes''. Each input mode has a different set of keybindings for different applications. @anchor{Semi-char Mode} @cindex semi-char mode @cindex mode, semi-char @cindex keybindings, semi-char mode @cindex keybinding mode, semi-char @cindex input mode, semi-char @section Semi-char Mode ``Semi-char mode'' is the default input mode of Eat. This works for most inputs. It forwards all keys, except @kbd{C-\}, @kbd{C-c}, @kbd{C-x}, @kbd{C-g}, @kbd{C-h}, @kbd{C-M-c}, @kbd{C-u}, @kbd{M-x}, @kbd{C-q}, @kbd{M-:}, @kbd{M-!}, @kbd{M-&} and some other keys, Emacs handles them. @cindex inputting exceptional characters @kindex C-q @r{(``semi-char mode'')} To input these exceptions, there is a key @kbd{C-q}. This reads the next input event and sends that as the input. For example, the key sequences @kbd{C-q M-:} inputs @kbd{M-:}, @kbd{C-q C-g} inputs @kbd{C-g}. Input methods (@pxref{Input Methods,,, emacs, GNU Emacs Manual}) work in this mode, so, unlike Term (@pxref{Terminal emulator, Emacs Terminal Emulator, Emacs Terminal Emulator, emacs, GNU Emacs Manual}), Emacs built-in terminal emulator, you can still input any character. @kindex C-c C-c @r{(``semi-char mode'')} @kindex C-c C-k @r{(``semi-char mode'')} In ``semi-char mode'', @kbd{C-c C-c} sends a @kbd{C-c}, just for convenience, and @kbd{C-c C-k} kills the terminal program. @cindex customizing semi-char mode @cindex customizing semi-char mode keys @cindex customizing semi-char mode keybindings @cindex mode, semi-char, customizing @cindex keybindings, semi-char mode, customizing @cindex keybinding mode, semi-char, customizing @cindex input mode, semi-char, customizing @cindex mode, semi-char, adding exceptions @cindex keybindings, semi-char mode, adding exceptions @cindex keybinding mode, semi-char, adding exceptions @cindex input mode, semi-char, adding exceptions @cindex mode, semi-char, exception, add @cindex keybindings, semi-char mode, exception, add @cindex keybinding mode, semi-char, exception, add @cindex input mode, semi-char, exception, add @vindex eat-semi-char-non-bound-keys @vindex eat-eshell-semi-char-non-bound-keys You can customize the exceptions by customizing the user option @code{eat-semi-char-non-bound-keys}, and @code{eat-eshell-semi-char-non-bound-keys} for Eshell integration. Both user options contain a list of keys of form @code{[@var{key}]}, where @var{key} is a key not to bind. @var{key} mustn't contain meta modifier. To not bind a key with meta modifier, use a vector of form @code{[?\e @var{key}]}, where @var{key} is the key without meta modifier. These user options contain all the ``semi-char'' mode exceptions listed above, plus some more exceptions. @findex eat-update-semi-char-mode-map @findex eat-eshell-update-semi-char-mode-map @findex eat-reload If you set the user options manually (for example, with @code{setq}), you must call @code{eat-update-semi-char-mode-map} or @code{eat-eshell-update-semi-char-mode-map} respectively, and finally reload Eat (you can do this with the command @command{eat-reload}). Or alternatively you can set the user options before Eat is loaded. @anchor{Char Mode} @cindex char mode @cindex mode, char @cindex keybindings, char mode @cindex keybinding mode, char @cindex input mode, char @section Char Mode By default, Eat is in ``semi-char mode''. In this input mode, Eat forwards all supported keys to terminals, except some exceptions, @pxref{Semi-char Mode}. It is possible to input them with @kbd{C-q}, but it is not very convenient. @kindex C-c M-d @r{(``semi-char mode'')} To overcome this problem, Eat implements another input mode called ``char mode''. To switch to ``char mode'', press @kbd{C-c M-d} in ``semi-char mode''. In Eshell, the command @command{eshell-toggle-direct-send} is remapped to enable ``char-mode'', which is usually bound to @kbd{C-c M-d}. In this input mode, Eat forwards all supported keys. However, input methods still work in this mode, so you can still input keys that are not on your keyboard. @kindex C-M-m @r{(``char mode'')} @kindex M-RET @r{(``char mode'')} To get out of ``char mode'', press @kbd{C-M-m} or @kbd{M-@key{RET}}, this switches back to ``semi-char mode''. @anchor{Emacs Mode} @cindex emacs mode @cindex mode, emacs @cindex keybindings, emacs mode @cindex keybinding mode, emacs @cindex input mode, emacs @section Emacs Mode In ``emacs mode'', no input events are send to the terminal. In this mode, you can interact with the terminal buffer just like a regular buffer. However, you are not allowed to change the buffer contents. @kindex C-c C-e @r{(``semi-char mode'')} To switch to ``emacs mode'', press @kbd{C-c C-e} from ``semi-char mode''. @kindex C-c C-k @r{(``emacs mode'')} In this mode, @kbd{C-c C-k} kills the terminal program like in ``semi-char mode''. @kindex C-c C-j @r{(``emacs mode'')} @kindex C-c M-d @r{(``emacs mode'')} From ``emacs mode'', you can switch to ``semi-char mode'' with @kbd{C-c C-j} and to ``char mode'' with @kbd{C-c M-d}. In Eshell, the command @command{eshell-toggle-direct-send} is remapped to enable ``char-mode'', which is usually bound to @kbd{C-c M-d}. @anchor{Line Mode} @cindex line mode @cindex mode, line @cindex keybindings, line mode @cindex keybinding mode, line @cindex input mode, line @section Line Mode In ``line mode'', input is sent one line at a line, similar to Comint or Shell mode (@pxref{Interactive Shell,,, emacs, GNU Emacs Manual}). Like ``emacs mode'', you can interact with buffer as you wish; but at the very end of the buffer, you can edit the input line with usual Emacs commands. This is not available in Eshell. @kindex C-c C-l @r{(``semi-char mode'')} @kindex C-c C-l @r{(``emacs mode'')} To enter ``line mode'', press @kbd{C-c C-l} from ``semi-char mode'' or ``emacs mode''. @kindex @key{RET} @r{(``line mode'')} @kindex C-c @key{SPC} @r{(``line mode'')} @kbd{@key{RET}} sends the current input with a trailing newline, and clear the input area for new input to be written. When called with a prefix argument, no newline is appended before sending input. To input a newline character without actually sending it, you can press @kbd{C-c @key{SPC}}. @kindex C-c C-c @r{(``line mode'')} @kindex C-d @r{(``line mode'')} @kbd{C-c C-c} discards the input completely and sends an interrupt to the terminal. @kbd{C-d} deletes the character after the point, or sends EOF if the input is empty. @vindex eat-line-auto-move-to-input You can't modify the terminal in ``line mode'', you can write only in the input line. Eat automatically moves the point to the input line if you try to insert character in the terminal region. This behavior can be disabled by customizing @code{eat-line-auto-move-to-input} to @code{nil}. @kindex C-c C-e @r{(``line mode'')} @kindex C-c C-j @r{(``line mode'')} @kindex C-c M-d @r{(``line mode'')} You can exit to ``emacs mode'', ``semi-char mode'' or ``char mode'' with @kbd{C-c C-e}, @kbd{C-c C-j} or @kbd{C-c M-d} respectively. If there's any pending input when exiting line mode, it is sent as is to the terminal. @kindex M-p @r{(``line mode'')} @kindex M-n @r{(``line mode'')} @kindex C-@key{up} @r{(``line mode'')} @kindex C-@key{down} @r{(``line mode'')} @kindex M-r @r{(``line mode'')} @kindex C-c M-r @r{(``line mode'')} @kindex C-c M-s @r{(``line mode'')} @kindex C-c C-r @r{(``line mode'')} @kindex @key{TAB} @r{(``line mode'')} The input history is recorded. You can cycle through the history with @kbd{M-p} and @kbd{M-n}, and also with @kbd{C-@key{up}} and @kbd{C-@key{down}}. @kbd{C-c M-r} and @kbd{C-c M-s} searches the input history taking the current input as the query. @kbd{C-c M-r} searches backward while @kbd{C-c M-s} searches forward. And @kbd{C-c C-r} searches the input history using minibuffer with completion, useful specially if you use any minibuffer completion UI/framework. You can also use Isearch (@pxref{Incremental Search,,, emacs, GNU Emacs Manual}) to search through the input history, with @kbd{M-r}. You can also customize @code{eat-line-input-history-isearch} to use all standard Isearch commands to search the input history. @vindex eat-line-input-history-isearch @defopt eat-line-input-history-isearch Controls where Isearch searches in Eat buffer. If @code{t}, usual Isearch commands in Eat buffer search in the input history. If @code{dwim}, Isearch keys search in the input history only when initial point position is on input line. When starting Isearch from other parts of the Eat buffer, they search in the Eat buffer. If @code{nil}, Isearch operates on the whole Eat buffer. @end defopt Input history is not loaded from the shell history file, to do that, @xref{Line Mode Integration}. @node Password Input @cindex password input @cindex credential input @cindex input, password @cindex input, credential @chapter Password Input By default, every keystroke gets recorded in the lossage, which can be seen by pressing @kbd{C-h l}. This is actually a good thing, unless you're inputting password. @findex eat-send-command Emacs doesn't record keystrokes when a password is read from the minibuffer. However, when the password prompt is in the terminal, the keys you use to type in your password gets recorded. To prevent this from happening, you can use the command @command{eat-send-command}, it'll read password from the minibuffer and send it. Since the password is read from the minibuffer, it's not recorded. @part Part III:@* Advanced Customizations @node Shell Integration @cindex shell integration @cindex integration, shell @chapter Shell Integration Eat comes with shell scripts to integrate your favorite shell with Eat. When shell integration is enabled and the script is loaded in your shell, it'll take care of everything and provide many useful features. Currently only GNU Bash and Zsh are supported. If you use GNU Bash, put the following in your @samp{.bashrc} file: @example [ -n "$EAT_SHELL_INTEGRATION_DIR" ] && \ source "$EAT_SHELL_INTEGRATION_DIR/bash" @end example If you use Zsh, put the following in your @samp{.zshrc} file: @example [ -n "$EAT_SHELL_INTEGRATION_DIR" ] && \ source "$EAT_SHELL_INTEGRATION_DIR/zsh" @end example @anchor{Directory tracking} @cindex directory tracking @cindex tracking directory @cindex working directory tracking @cindex tracking working directory @cindex cwd tracking @cindex tracking cwd @section Directory tracking After you've setup shell integration, the Eat will track the working directory of your shell. That means @command{find-file} will start from your shell's current working directory. This also works in Eshell, but after the program exits, the current working directory is changed back to the directory from where the program was invoked. @vindex eat-enable-directory-tracking @defopt eat-enable-directory-tracking This controls directory tracking. When set to non-@code{nil}, Eat tracks the current working directory of programs. @end defopt @anchor{Shell Prompt Navigation} @cindex shell prompt navigation @cindex shell navigation, shell prompt @cindex prompt navigation @cindex navigation, prompt @section Shell Prompt Navigation @kindex C-c C-p @r{(``emacs mode''}) @kindex C-c C-p @r{(``semi-char mode'')} @findex eat-previous-shell-prompt @kindex C-c C-n @r{(``emacs mode'')} @kindex C-c C-n @r{(``semi-char mode'')} @findex eat-next-shell-prompt You can navigate shell prompts in ``emacs'' and ``semi-char'' mode. @kbd{C-c C-p}, bound to @command{eat-previous-shell-prompt}, goes to the previous shell prompt. @kbd{C-c C-n}, bound to @command{eat-next-shell-prompt}, is the opposite, it goes to the next shell prompt. This doesn't work in Eshell. @cindex narrow to shell prompt @cindex narrow to prompt @cindex narrow, shell prompt @cindex narrow, prompt @kindex C-x n d @r{(``emacs mode'')} @kindex C-x n d @r{(``semi-char mode'')} @findex eat-narrow-to-shell-prompt You can narrow (@pxref{Narrowing,,, emacs, GNU Emacs Manual}) down Eat buffer to a shell prompt and its output (if any) using the key sequence @kbd{C-x n d}, bound to @command{eat-narrow-to-shell-prompt}. @anchor{Shell Prompt Annotation} @cindex shell prompt annotation @cindex annotation, shell prompt @cindex annotate, shell prompt @cindex prompt annotation @cindex annotation, prompt @cindex annotate, prompt @section Shell Prompt Annotation When shell integration is setup, Eat annotates each shell prompt. Eat puts a mark on the shell prompt indicating the whether the command entered in that prompt is running, exited successfully or exited with non-zero status. This doesn't work in Eshell. You can disable this feature if you want. @vindex eat-enable-shell-prompt-annotation @defopt eat-enable-shell-prompt-annotation This controls shell prompt annotation by Eat. When set to non-@code{nil}, Eat annotates shell prompts to indicate the status of the command entered in that prompt. @end defopt Eat uses the marginal area (@pxref{Display Margins,,, elisp, GNU Emacs Lisp Reference Manual}) on the left side to display the annotation. You also use the right margin. @vindex eat-shell-prompt-annotation-position @defopt eat-shell-prompt-annotation-position This controls where the shell prompt annotations are displayed. When set to @code{left-margin} (the default), Eat uses the left margin. When set to @code{right-margin}, Eat uses the right margin. @end defopt @vindex eat-shell-prompt-annotation-running-margin-indicator @vindex eat-shell-prompt-annotation-running @vindex eat-shell-prompt-annotation-success-margin-indicator @vindex eat-shell-prompt-annotation-success @vindex eat-shell-prompt-annotation-failure-margin-indicator @vindex eat-shell-prompt-annotation-failure Eat uses the strings ``-'', ``0'' and ``X'' respectively to indicate the command is running, the command has succeeded and the command has failed. You can also customize the them. The user option @code{eat-shell-prompt-annotation-running-margin-indicator} and the face @code{eat-shell-prompt-annotation-running} control the indicator used to indicate the command is running. The user option @code{eat-shell-prompt-annotation-success-margin-indicator} and the face @code{eat-shell-prompt-annotation-success} control the indicator used to indicate the command has exited successfully. And finally the user option @code{eat-shell-prompt-annotation-failure-margin-indicator} and the face @code{eat-shell-prompt-annotation-failure} control the indicator used to indicate the command has exited unsuccessfully with non-zero exit status. @anchor{Message Passing} @section Message Passing After enabling shell integration, you can send messages to Emacs from your shell. Then you can handle the message on Emacs side using usual Emacs Lisp function. When shell integration script is loaded, a function named @command{_eat_msg} is defined in your shell. You can use this to send any message to Emacs. (The @samp{_} in the beginning of the function name is intentional to prevent shadowing any actual command.) @deffn Command _eat_msg @var{handler-name} @var{message}... Send message @var{message}, handled by the handler named @var{handler-name} in Emacs. @end deffn The messages are handled with the handlers defined in @code{eat-message-handler-alist}. @vindex eat-message-handler-alist @defopt eat-message-handler-alist Alist of message handler name and its handler function. The keys are the names of message handlers (i.e. the @var{handler-name} argument of @command{_eat_msg}), and the values are their respective handler functions. The handler function is called with the @var{message} arguments of @command{_eat_msg}. Messages with undefined handlers are ignored. To disable message passing, set this to nil. @end defopt Beware, messages can be sent by malicious and/or buggy programs running in the shell, therefore you should always verify the message before doing anywhere. @anchor{Line Mode Integration} @cindex line mode integration @cindex line mode shell integration @cindex integration, shell, line mode @section Line Mode Integration When shell integration is enabled, the input history of line mode is automatically filled with the shell history when the shell starts. Also you can make Eat automatically switch to ``line mode'' (@pxref{Line Mode}) for you when the shell prompt appears. @vindex eat-enable-auto-line-mode @defopt eat-enable-auto-line-mode When non-nil, automatically switch to line mode the shell prompt appears. @end defopt @node Querying Before Kill @cindex querying before kill @cindex querying before kill terminal @cindex confirm before kill @cindex confirm before kill terminal @cindex terminal, kill, query @cindex terminal, kill, confirm @chapter Querying Before Kill When a terminal is killed, the terminal process is also killed. Since the process can do some important things, Eat asks for confirmation before killing a terminal with running process by default. Eat provides the user option @code{eat-query-before-killing-running-terminal} to control this. @vindex eat-query-before-killing-running-terminal @defopt eat-query-before-killing-running-terminal When set to @code{nil}, Eat would never ask. When set to @code{t}, Eat would always ask for confirmation. When set to @code{auto}, Eat would ask only if a shell command is running inside the shell running in the terminal. This is effective only after shell integration is enabled in the shell (@pxref{Shell Integration}) (i.e. after the shell integration code is executed on shell); before that it is essentially same as @code{t}, and Eat will always query. @end defopt @node Display @cindex display @chapter Display Display is the region you see on the terminal. The program writes to the display and manipulates the text on the display. The display can be of any size. The cursor is always on the display (though it might be invisible sometimes, @pxref{Cursor Types}). @vindex window-adjust-process-window-size-function You can resize the display by resizing the terminal window. The display size is controlled by the Emacs user option @code{window-adjust-process-window-size-function}. @xref{Process Buffers,,, elisp, GNU Emacs Lisp Reference Manual} for the possible values of the user option. @node Scrollback @cindex scrollback @chapter Scrollback When you go too downward on the terminal, the terminal starts to ``scroll''. This causes the line at the upper side of the terminal to go out of the display and become hidden. But these line are not deleted, they are just put in the scrollback region. Scrollback region is a region just above the display of the terminal. This contains the lines that went out of display due to scrolling up. Scrollback region is not unlimited by default, to avoid using too much memory. You can change the limit, or remove it altogether. @vindex eat-term-scrollback-size @defopt eat-term-scrollback-size This controls the size of scrollback region. It is expressed in character. If set to @var{size}, Eat won't store more than @var{size} characters in the scrollback region. If set to @code{nil}, the scrollback region is unlimited. @end defopt @node Cursor Types @cindex cursor types @cindex types, cursor @cindex changing cursor @cindex customizing cursor @cindex cursor blinking @cindex blinking cursor @chapter Cursor Types In terminal, cursor can be of up to three type: ``visible'', ``invisible'' and ``very visible''. ``Visible'' is the default cursor type, which is the cursor you usually see in a shell (unless the shell changes the cursor type). ``Invisible'' is, as the name suggests, invisible, you can't see it. ``Very visible'' cursor is a blinking cursor, programs use this to help you not lose the cursor. The cursor type can customized with three user options for the three types of cursor. Each of the user options share the same format. @vindex eat-default-cursor-type @defopt eat-default-cursor-type This controls the cursor shape of the ``visible'' cursor type. @end defopt @vindex eat-invisible-cursor-type @defopt eat-invisible-cursor-type This controls the cursor shape of the ``invisible'' cursor type. @end defopt @vindex eat-very-visible-cursor-type @defopt eat-very-visible-cursor-type This controls the cursor shape of the ``very visible'' cursor type. This cursor blinks, switching between the default cursor shape and a hollow box. @end defopt @vindex eat-vertical-bar-cursor-type @defopt eat-vertical-bar-cursor-type This controls the cursor shape of the ``vertical bar'' cursor type. @end defopt @vindex eat-very-visible-vertical-bar-cursor-type @defopt eat-very-visible-vertical-bar-cursor-type This controls the cursor shape of the ``very visible vertical bar'' cursor type. This cursor blinks. @end defopt @vindex eat-horizontal-bar-cursor-type @defopt eat-horizontal-bar-cursor-type This controls the cursor shape of the ``horizontal bar'' cursor type. @end defopt @vindex eat-very-visible-horizontal-bar-cursor-type @defopt eat-very-visible-horizontal-bar-cursor-type This controls the cursor shape of the ``very visible horizontal bar'' cursor type. This cursor blinks. @end defopt The value type of all these user options is a list. The list is of form (@var{cursor-on} @var{blinking-frequency} @var{cursor-off}). @var{blinking-frequency} is the frequency of blinking of cursor. It is a number, controlling how many times the cursor will blink a second. This can also be @code{nil}, this will disable cursor blinking. @var{cursor-on} is the default cursor shape, only this shape is shown on the display when blinking is disabled. This uses the same format as Emacs's @code{cursor-type} user option (@pxref{Cursor Display,,, emacs, GNU Emacs Manual}). When @var{blinking-frequency} is a number, Eat will consult to the third element of the list, @var{cursor-off}, whose format same as @var{cursor-on}. The blinking cursor switches between @var{cursor-on} and @var{cursor-off} cursor shape. @node Mouse Tracking @cindex mouse tracking @cindex tracking mouse @chapter Mouse Tracking Eat tracks mouse by default, when the program supports mouse. But sometimes, you may want to avoid using mouse, or you might not have a mouse at all. So mouse tracking can be toggled. @vindex eat-enable-mouse @defopt eat-enable-mouse This user option controls mouse tracking. When set to non-@code{nil}, mouse tracking is enabled. Set to this to @code{nil} to disable mouse tracking. This is enabled by default. @end defopt @node Clipboard @cindex clipboard @chapter Clipboard @cindex yanking @findex eat-yank @kindex C-y @r{(``semi-char mode'')} @findex eat-yank-pop @kindex M-y @r{(``semi-char mode'')} Just like any other buffer, you can yank text in terminal with @kbd{C-y} (bound to @command{eat-yank}) or @kbd{M-y} (bound to @command{eat-yank-pop}) in ``semi-char mode''. @cindex clipboard integration Programs can also request to the terminal to kill (@pxref{Killing,,, emacs, GNU Emacs Manual}) something. It is up to Eat whether the request will be fulfilled or not. By default, Eat fulfills the request and kills the text. This can sometimes be annoying, when the program automatically kills text without user interaction. This killing can be configured with the following user option: @vindex eat-enable-kill-from-terminal @defopt eat-enable-kill-from-terminal This controls killing texts from terminal. When set to non-@code{nil}, killing something from terminal add the text to Emacs's kill ring (@pxref{Kill Ring,,, emacs, GNU Emacs Manual}). This is enabled by default. @end defopt Programs can also request the text in kill ring. Again, this is up to Eat whether the request will be fulfilled or not. You can customize the following user option to configure this: @vindex eat-enable-yank-to-terminal @defopt eat-enable-yank-to-terminal This controls sending kill ring texts to terminal. When set to non-@code{nil}, programs can receive the kill ring contents. This is disabled by default for security reasons. @end defopt @node Colors @cindex colors @cindex customizing colors @chapter Colors Eat can show more than 16 million colors (16,777,216 colors exactly). Eat has also a palette of 256 colors, which is more than enough for most applications. Programs usually use this color palette. Each of these 256 colors can customized. There are 256 faces for the 256 colors, one face for each color. They are named like @code{eat-term-color-@var{n}}, which corresponds to color @var{n}, and @var{n} can be any number between 0 and 255 (inclusive). For example, color 42 is can be changed by customizing @code{eat-term-color-42}. The foreground attribute contains the color value to use for the corresponding color. Other attributes are currently ignored and reserved for future changes. @cindex color aliases @cindex face aliases @cindex aliases, face @cindex aliases, color Each of the first 16 colors, from @code{eat-term-color-0} to @code{eat-term-color-15} also have a alias. They are respectively @code{eat-term-color-black}, @code{eat-term-color-red}, @code{eat-term-color-green}, @code{eat-term-color-yellow}, @code{eat-term-color-blue}, @code{eat-term-color-magenta}, @code{eat-term-color-cyan}, @code{eat-term-color-white}, @code{eat-term-color-bright-black}, @code{eat-term-color-bright-red}, @code{eat-term-color-bright-green}, @code{eat-term-color-bright-yellow}, @code{eat-term-color-bright-blue}, @code{eat-term-color-bright-magenta}, @code{eat-term-color-bright-cyan} and @code{eat-term-color-bright-white}. Eat also supports 24-bit colors, or so called ``truecolor''. Programs like Emacs can give a RGB triplet to use as the color of some text. As the programs directly specify the color in this case, you can't customize these color. But you may configure the program sending the color codes. @cindex color advertisement @cindex advertising colors Eat doesn't always advertise color support depending on the display Eat is running. For example, if you are on a Linux console which supports only eight colors, Eat will advertise eight color support to the programs, while on graphical displays with 24-bit color support, Eat will report 24-bit color support. This is because Eat supports more colors, the display doesn't always support them. @vindex TERM @cindex @env{TERM} environment variable @cindex environment variable, @env{TERM} Eat does the trick by setting the @env{TERM} environment variable of the program. The value of @env{TERM} depends on the number of the available colors on the display. This environment variable is controlled by the following user option: @vindex eat-term-name @defopt eat-term-name The value of @env{TERM} environment variable as a string. The value can also be a function taking no arguments, that function should return a string which used as the value of @env{TERM}. The default value is @code{eat-term-get-suitable-term-name}, which is responsible for the behavior described above. @end defopt @node Fonts @cindex fonts @chapter Fonts Programs may request the terminal to change the text font. It can change text weight, use italic text, or even change the font family altogether. @cindex bold text @cindex text, bold @vindex eat-term-bold Programs may request the terminal to show some text bolder than normal. Bold text uses the face @code{eat-term-bold}. @cindex faint text @cindex text, faint @vindex eat-term-bold Programs may also request the terminal to show some text fainter than normal. Faint text uses the face @code{eat-term-faint}. @cindex italic text @cindex slant text @cindex text, italic @cindex text, slant @vindex eat-term-italic Programs may request the terminal to show italic text too. Italic text uses the customizable face @code{eat-term-italic}. @cindex font family @cindex text, font family The number of available fonts is ten. Most of the programs doesn't change the font. Following many other terminal emulator, Eat actually uses the same font, the default font, regardless of the font requested by the program, by default. @cindex customizing font families There are ten faces for ten fonts, one face for each. They are named like @code{eat-term-font-@var{n}}, which corresponds to color @var{n}, and @var{n} can be any number between 0 and 9 (inclusive). For example, the font 6 is can be changed by customizing @code{eat-term-font-6}. Font 0 is the default font. @node Sixel @cindex sixel @chapter Sixel Eat can show Sixel graphics. Sixel is a bitmap graphics format that can be used to display graphics in a terminal, for example, images, or plotting graphs. You can control the display of Sixel images by customizing the following user options. @vindex eat-sixel-scale @defopt eat-sixel-scale This is a non-negative number that specifies the amount to scale the image by. @end defopt @vindex eat-sixel-aspect-ratio @defopt eat-sixel-aspect-ratio This is a non-negative number that specifies the aspect ratio, i.e. the ratio of width and height of a Sixel pixel. For example, the value of 2 means the width of a Sixel pixel is the double of its height. @end defopt Eat converts Sixel graphics to an image format Emacs can natively display. This preference of image formats can be configured by customizing the @code{eat-sixel-render-formats} user option. @vindex eat-sixel-render-formats @defopt eat-sixel-render-formats List of formats to render Sixel, in order of preference. Each element of the list is one of @code{xpm}, @code{svg}, @code{half-block}, @code{background}, @code{none}. @code{xpm} and @code{svg} means to use XPM and SVG image format respectively, @code{half-block} means to use UTF-8 half block characters, @code{background} means to just use background color, and @code{none} means to not render the image, instead just clear the area. @end defopt @node Blinking Text @cindex blinking text @cindex text, blinking @chapter Blinking Text Programs can request the terminal to blink some text. This helps to get user attention. But however, often this annoying to many people and also has accessiblity problems. So this is disabled by default. @vindex eat-enable-blinking-text @defopt eat-enable-blinking-text This controls the blinking of text with blink attribute. When set to non-@code{nil}, Eat arranges that text with blink attribute will blink at a certain interval. @end defopt @findex eat-blink-mode You can toggle blinking temporarily by toggle the buffer-local minor mode @command{eat-blink-mode}. This is only effective in the buffer where the mode is toggled. By default, @code{eat-enable-blinking-text} is set to @code{nil}. This disables text blinking and causes the text with blink attribute to be displayed in inverse video (swapped foreground and background). @vindex eat-term-slow-blink @vindex eat-term-fast-blink Programs may also request to blink some text more rapidly that other blinking text. When blinking is disabled, the face @code{eat-term-slow-blink} is used for slowly blinking text, and @code{eat-term-fast-blink} for rapidly blinking text. When blinking is enabled, by setting @code{eat-enable-blinking-text} to non-@code{nil} value, the following user options can be customized to change the rate of blinking: @vindex eat-slow-blink-frequency @defopt eat-slow-blink-frequency The blinking rate of slowly blinking text. When set to a number N, it causes slowly blinking text to blink N times a second. The value can also be a floating point number. The default value is 2, meaning that the slowing text will blink two times a second. @end defopt @vindex eat-fast-blink-frequency @defopt eat-fast-blink-frequency The blinking rate of rapidly blinking text. When set to a number N, it causes rapidly blinking text to blink N times a second. The value can also be a floating point number as well. The default value is 3, meaning that the slowing text will blink three times a second. @end defopt @node Performance Tuning @cindex performance tuning @cindex tuning performance @chapter Performance Tuning Eat tries to be as fast as possible. So Eat employs some techniques to maximize performance. Some program choke and hang when given too much input at once. So Eat divides large input to smaller chunks and sends the chunks one at a time. The maximum size of a input chunk is controlled by @code{eat-input-chunk-size}. @vindex eat-input-chunk-size @defopt eat-input-chunk-size The value is a integer. Eat treat input larger than this many character as large and breaks it into chunks of at most this size before sending the input. @end defopt @cindex flickering @cindex reason behind flickering @cindex cause of flickering Programs also break large output into smaller chunks before sending it to the terminal, for same reason. Eat doesn't suffer from the problem, but there isn't any standard way to inform programs about this, and usually there are other obstructions sending large amount of data at once. These small chunks create another problem for Eat, flickering. When updating the whole display, the output is usually pretty large and the programs break them into smaller chunks. Each of the chunks update the display partially. After receiving the last chunk, the update is complete and the display can be updated. But it is impossible for Eat to guess the last chunk, so Eat has to redisplay or update the display after receiving each chunk. This is the reason why sometimes the terminal shows some old contents and some new. This only lasts for a fraction of a second until the next chunk is received and processed. This is flickering. This also degrades performance, because redisplay is an expensive process and takes some time. @cindex fixing flickering @cindex flickering fix @cindex latency Fixing the flickering completely is not possible. Eat tries to decrease flickering by deferring redisplay. After receiving a chunk, Eat waits for a tiny fraction of a second. If another chunk arrives within the time, the redisplay is postponed. Then Eat waits for the same amount of time and this goes on. When timeout occurs, Eat processing the output and displays the output. This causes a small latency between output arrive and redisplay, but this is usually not long enough for human eyes to catch it. This waiting time can be configured with the following user option: @vindex eat-minimum-latency @defopt eat-minimum-latency The value is the time in seconds to wait for the next chunk to arrive. This is the minimum latency between the first chunk after a redisplay and the next redisplay. For example, if you press @kbd{@key{RET}} in an empty Bash prompt, the next prompt won't appear before this much time. You should set the time to something comfortable for you. You can also set this to zero to disable waiting and showing the output instantly, but this would likely cause a lot of flickering. @end defopt However, this waiting raises another problem. What if you execute the POSIX command @samp{yes} in the terminal? It will write infinite ``y''s in the terminal without any delay between them anywhere. Eat will wait indefinitely for a delay between two chunks, which will never happen, unless the program is executed remotely and the connection is slow enough. So Eat has a limit for waiting, the display will be always be updated after this time. This limit also customizable: @vindex eat-maximum-latency @defopt eat-maximum-latency The value is the time in seconds to wait at most for chunk. In case of large burst of output, redisplay is never deferred more than this many seconds, and cause a latency of up to this many seconds. You should set the time to something comfortable for you. You can also set this to zero to disable waiting and showing the output instantly, but this would likely cause a lot of flickering. @end defopt Due to some limitations, shell prompt annotations (@pxref{Shell Integration}) can get messed up sometimes. Eat automatically corrects them after each terminal redisplay. However, this can have some performance impact when the terminal scrollback and display is large enough (unless the buffer is narrowed). So Eat defers the correction. @vindex eat-shell-prompt-annotation-delay @defopt eat-shell-prompt-annotation-delay The value is the time in seconds to wait for more output before correcting shell prompt annotations. You should set the time to something comfortable for you. You can also set this to zero to disable waiting and correct annotations instantly, but this may cause the terminal to slow down in some cases. @end defopt The user options described in this chapter have reasonable default values, but the default values may change anytime. @part Part IV:@* Recovering from Problems @node Common Problems @cindex common problems @cindex problem, common @chapter Common Problems This chapter describe how to recognize and handle situations in which Eat does something unexpected, such as hangs, garbled text, etc. @menu * Not Recognized:: The program can't recognize Eat. * Garbled Text:: When you get garbage on your terminal. * Input Invisible:: The input is not shown. * Not Responding:: What to do if Eat is unresponsive. * Signaled an Error:: The worst and the most unlikely bug. * Bugs in Manual:: What if there are problems in this manual? @end menu @node Not Recognized @cindex not recognized @cindex problem, not recognized @cindex terminal not recognized @cindex problem, terminal not recognized @section Terminal Not Recognized If your program says that it can't recognize the terminal, probably the @env{TERM} environment variable has a wrong value. @vindex eat-term-name Check the value of @env{TERM}, if it's not set to something like @samp{eat-...}, check the user option @code{eat-term-name}. If that's correct that your shell might be changing the @env{TERM} environment variable. If @code{eat-term-name} isn't correct, customize to a suitable value and try again, your problem should be fixed. @vindex eat-term-terminfo-directory If @env{TERM} has the correct value, then probably the Terminfo databases of Eat are missing. This can happen if you have installed Eat without using the package from NonGNU ELPA (@pxref{Packages,,, emacs, GNU Emacs Manual}). Check that whether the values of the environment variable @env{TERMINFO} and the user option @code{eat-term-terminfo-directory} match. If they match, customize @code{eat-term-terminfo-directory} to the directory containing the Terminfo databases, the program should now recognize Eat. If they don't match, then your shell is probably responsible for the problem. @findex eat-compile-terminfo If the program is not recognizing the terminal even when the correct directory is set as @code{eat-term-terminfo-directory}, probably the precompiled Terminfo databases aren't working properly on your system. You can invoke the command @command{eat-compile-terminfo} to recompile it for your system. If you can't find the directory containing Terminfo databases, you can compile it yourself. First, set @code{eat-term-terminfo-directory} to the directory where to put the Terminfo databases. Then invoke the command @command{eat-compile-terminfo} to compile the Terminfo databases. @node Garbled Text @cindex garbled text @cindex problem, garbled text @cindex problem, text garbled @section Garbled Text on Terminal If the text on the terminal looks wrong, first check out the value of @env{TERM}. Usually @env{TERM} has a wrong value set, making programs send invalid escape sequences. First, @pxref{Not Recognized}; the problem is most likely because the program doesn't recognize Eat, and it stays silent instead of reporting that. If the problem isn't resolved after following the instructions in the previous section, probably the precompiled Terminfo databases aren't working properly on your system. Running the command @command{eat-compile-terminfo} will recompile it for your system. If the problem still persists, may be your program is blindly assuming that the terminal is XTerm-compatible. If so, what you are seeing is the current state of ``XTerm-compliance''. Though it's not really a bug, we really want to know what's problem so that we can fix it and improve XTerm-compliance. @xref{Reporting Bugs} for instructions on sending bug reports or feature request. The other potential reason is that Eat is not working. This is definitely a bug, so please report it. @node Input Invisible @cindex input invisible @cindex input, invisible @cindex invisible input @cindex invisible, input @section Input Invisible This can happen if the @samp{stty} program is unavailable on the system. Eat uses @samp{stty} to set various terminal settings including input echoing. Please install the @samp{stty} program to fix the problem. @vindex eat-eshell-fallback-if-stty-not-available If you are using Eat from Eshell (@pxref{Eshell Terminal}), you might want to set @code{eat-eshell-fallback-if-stty-not-available} to handle such cases. The user option can be set to three possible value, @code{t} to automatically fallback to bare Eshell when @samp{stty} is not available, @code{nil} to do nothing, and @samp{ask} to ask interactively. @node Not Responding @cindex not responding, eat @cindex not responding, emacs @cindex eat not responding @cindex emacs not responding @cindex problem, eat not responding @cindex problem, emacs not responding @cindex problem, not responding, eat @cindex problem, not responding, emacs @section Emacs or Eat Not Responding If you run something that outputs huge amount of data, your Emacs may not respond, and even quitting may not work. Quitting doesn't work while doing something terminal related (output processing, for example), and that's intentional, because quitting would mess up the terminal. The best way to fix it is to stop the program, so that Eat is not overloaded. To avoid the problem in future, it is recommended to run those programs in faster terminals like bare Eshell (i.e. without Eat-Eshell), Comint, or external terminal emulators. @node Signaled an Error @cindex signaled an error @cindex eat signaled an error @cindex problem, signaled an error @cindex problem, eat signaled an error @section Eat Signaled an Error The worst thing that happen is that Eat might signal an error. It is the worst thing possible, because it messes up the terminal, and also a security hole. Fortunately, this is very rare. If you ever find any such bug, you should report the bug (@pxref{Reporting Bugs}) as soon as possible with as much information as possible. @findex eat-reset Once the error signaled, your best option is to delete the terminal and start a new one. But if you don't want to delete the terminal, you can try invoking the command @command{reset} from your shell. If for some reason you can't do that, invoke the Emacs command @command{eat-reset}. This will reset most of the terminal state and give you a clean terminal to work with. However, it mayn't work if you're really unlucky, in that case deleting the terminal and starting a new one is your only option. @node Bugs in Manual @cindex bugs in manual @cindex typos in manual @cindex bugs, manual @cindex typos, manual @cindex manual, bugs @cindex manual, typos @cindex problems, bugs in manual @cindex problems, typos in manual @cindex problems, bugs, manual @cindex problems, typos, manual @cindex problems, manual, bugs @cindex problems, manual, typos @section Bugs in Manual Human makes mistake, and we are no exceptions. But we are trying hard to improve Eat and it's manual. If you don't understand something even after a careful rereading, that's a bug. If you find anything unclear in this manual, that's a bug. This manual's job is make everything clear, so failing to do that indicates a bug. If the built-in documentation and this manual don't agree, one of them must be wrong, and that's a bug. If you Eat doesn't behave as this manual describes, that's a bug. If you find any typing mistakes, that's a bug. If you find a bug, please report it. @xref{Reporting Bugs} for instruction on how to report it. @node Reporting Bugs @cindex reporting bugs @cindex bug reporting @cindex problem, reporting @chapter Reporting Bugs We welcome bug reports and feature request. If you think you have found a bug, please report it. If you're doubt, please send it anyway. We can't promise that we'll fix the bug or implement your feature idea, or always agree that it's a bug, but we always want to hear from you. Please report bugs at @url{https://codeberg.org/akib/emacs-eat/issues/}. You may send the bug report by emailing to the maintainer (@kbd{M-x describe-package @key{RET} eat @key{RET}} would show the email address), but we prefer the former method, since the report is visible to everyone immediately. The most important principle in reporting a bug is to report @emph{facts}. Hypotheses and verbal descriptions are useful when they are more guesses, but in no way substitute for detailed raw data. You are encouraged to send you finding about bug, but please make sure to send the raw data needed to reproduce the bug. For bug reports, please try to reproduce the bug with @samp{emacs -Q}. This will disable loading your Emacs configuration, ruling out the potential bugs in your customizations. Please include enough information for us to reproduce the bug with @samp{emacs -Q}, so that we have (or can get) enough information about the bug to fix it. Some bugs are hard to reproduce with @samp{emacs -Q}, and some are not easily reproducible at all, in that case please give us the as much information as possible about Emacs configuration. Generally speaking, enough information includes (but not limited to): @itemize @bullet @item A description of the bug. @item The version of Eat you're running. (@kbd{M-x describe-package RET eat RET} would show the version.) @item The version of Emacs you're running. @item Your Eat configuration, precisely. @item If you didn't or can't reproduce the bug with @code{emacs -Q}, as much information as possible about Emacs configuration. @item Precisely what you did. @item Trace output, if you can generate it. (@xref{Tracing the Terminal} for instruction to generate it.) @item Hardware and operating system names and versions. @item Anything else that you think would be helpful. @end itemize When in doubt whether to include something or not, please include it. It is better to include many useless information than to leave out something useful. It is critical to send enough information to reproduce the bug. What is not critical to ``narrow down'' the example to the smallest possible -- anything that reproduces the bug will suffice. (Of course, if you like doing experiments, the smaller the example, the better.) @node Tracing the Terminal @cindex tracing the terminal @cindex terminal tracing @cindex terminal recording @chapter Tracing the Terminal When you run into a bug and want to report it, you'll want to trace the terminal. Tracing means recording all the terminal activity, including creation, output, resizing and deleting. @findex eat-trace-mode To enable tracing, enable the global minor mode @command{eat-trace-mode}. This will trace all new terminals, including the terminal created inside Eshell. Trace output for each command will be output in a buffer named @samp{*eat-trace @var{buffer-name}*: @var{command}}, where @var{buffer-name} is the buffer showing the terminal, and @var{command} is the command run in the terminal. Only the terminals created after the trace mode is enabled are traced. So if you don't have the mode enabled when you have found a bug, tracing can't give you any information (as tracing is disabled, nothing has been recorded). While submitting bug reports, please include the whole output in the trace output buffer. This contains many crucial information required to reproduce your bug. @findex eat-trace-replay You can replay the terminal by executing the command @command{eat-trace-replay} is the trace output buffer. You can use the key @kbd{n} or the key @kbd{@key{down}} to show the next frame. This is not intended for ordinary users, it's documented here only to help you debug Eat. You mustn't rely on the behavior of this functionality to do anything else. @part Part V:@* Appendices @node GNU General Public License @appendix GNU General Public License @include gpl.texi @node GNU Free Documentation License @appendix GNU Free Documentation License @include fdl.texi @node Index @appendix Index @printindex cp @bye emacs-eat/eat.ti000066400000000000000000000070771453707721100140460ustar00rootroot00000000000000# The code here is forced by the interface, and is not subject to # copyright, constituting the only possible expression of the # algorithm in this format. # # When updating this file, files in e/ should be regenerated by # running in "make" or "make terminfo" here. eat-mono|Emacs Eat without colors, cols#80, lines#24, am, clear=\e[2J, cr=\r, bel=^G, home=\e[H, cub1=\b, cuf1=\e[C, cuu1=\e[A, cud1=^K, ind=\n, indn=\e[%p1%dS, ri=\eM, rin=\e[%p1%dT, nel=\eE, cup=\e[%i%p1%d;%p2%dH, hpa=\e[%i%p1%dG, vpa=\e[%i%p1%dd, cub=\e[%p1%dD, cuf=\e[%p1%dC, cuu=\e[%p1%dA, cud=\e[%p1%dB, smcup=\e[?1049h, rmcup=\e[?1049l, el=\e[K, el1=\e[1K, ed=\e[J, il1=\e[L, il=\e[%p1%dL, dl1=\e[M, dl=\e[%p1%dM, csr=\e[%i%p1%d;%p2%dr, sc=\e7, rc=\e8, smir=\e[4h, rmir=\e[4l, # ich1=\e[@, ich=\e[%p1%d@, mir, dch1=\e[P, dch=\e[%p1%dP, ech=\e[%p1%dX, smso=\e[7m, rmso=\e[27m, smul=\e[4m, rmul=\e[24m, sitm=\e[3m, ritm=\e[23m, blink=\e[5m, bold=\e[1m, dim=\e[2m, invis=\e[8m, rev=\e[7m, sgr0=\e(B\e[m, sgr=%?%p9%t\e(0%e\e(B%; \e[0%?%p6%t;1%; %?%p5%t;2%; %?%p2%t;4%; %?%p1%p3%|%t;7%; %?%p4%t;5%; %?%p7%t;8%;m, msgr, cvvis=\e[?12;25h, civis=\e[?25l, cnorm=\e[?12l\e[?25h, smkx=\e[?1h, rmkx=\e[?1l, ht=\t, cbt=\e[Z, it#8, rs1=\e\\\ec, hs, tsl=\e]2;, fsl=\e\\, dsl=\e]2;\e\\, xenl, acsc=++\,\,--..00``aaffggiihhjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, smacs=\e(0, rmacs=\e(B, npc, rep=%p1%c\E[%p2%{1}%-%db, u6=\e[%i%d;%dR, u7=\e[6n, smam=\e[?7h, rmam=\e[?7l, smxx=\e[9m, rmxx=\e[29m, kcbt=\E[Z, kbs=^?, kcuu1=\eOA, kri=\e[1;2A, kcud1=\eOB, kind=\e[1;2B, kcuf1=\eOC, kRIT=\e[1;2C, kcub1=\eOD, kLFT=\e[1;2D, kich1=\e[2~, kIC=\e[2;2~, kdch1=\e[3~, kDC=\e[3;2~, khome=\eOH, kHOM=\e[1;2H, kend=\eOF, kEND=\e[1;2F, kpp=\e[5~, kPRV=\e[5;2~, knp=\e[6~, kNXT=\e[6;2~, kf1=\eOP, kf2=\eOQ, kf3=\eOR, kf4=\eOS, kf5=\e[15~, kf6=\e[17~, kf7=\e[18~, kf8=\e[19~, kf9=\e[20~, kf10=\e[21~, kf11=\e[23~, kf12=\e[24~, kf13=\e[1;2P, kf14=\e[1;2Q, kf15=\e[1;2R, kf16=\e[1;2S, kf17=\e[15;2~, kf18=\e[17;2~, kf19=\e[18;2~, kf20=\e[19;2~, kf21=\e[20;2~, kf22=\e[21;2~, kf23=\e[23;2~, kf24=\e[24;2~, kf25=\e[1;5P, kf26=\e[1;5Q, kf27=\e[1;5R, kf28=\e[1;5S, kf29=\e[15;5~, kf30=\e[17;5~, kf31=\e[18;5~, kf32=\e[19;5~, kf33=\e[20;5~, kf34=\e[21;5~, kf35=\e[23;5~, kf36=\e[24;5~, kf37=\e[1;6P, kf38=\e[1;6Q, kf39=\e[1;6R, kf40=\e[1;6S, kf41=\e[15;6~, kf42=\e[17;6~, kf43=\e[18;6~, kf44=\e[19;6~, kf45=\e[20;6~, kf46=\e[21;6~, kf47=\e[23;6~, kf48=\e[24;6~, kf49=\e[1;3P, kf50=\e[1;3Q, kf51=\e[1;3R, kf52=\e[1;3S, kf53=\e[15;3~, kf54=\e[17;3~, kf55=\e[18;3~, kf56=\e[19;3~, kf57=\e[20;3~, kf58=\e[21;3~, kf59=\e[23;3~, kf60=\e[24;3~, kf61=\e[1;4P, kf62=\e[1;4Q, kf63=\e[1;4R, kmous=\e[M, eat-color|Emacs Eat with eight colors, use=eat-mono, colors#0x8, pairs#0x40, bce, op=\e[39;49m, setab=\e[4%p1%dm, setaf=\e[3%p1%dm, setb=\e[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, setf=\e[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, eat-256color|Emacs Eat with 256 colors, use=eat-color, colors#0x100, pairs#0x10000, setab=\e[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m, setaf=\e[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m, setb@, setf@, eat-truecolor|Emacs Eat with truecolor, use=eat-256color, Tc, emacs-eat/fdl.texi000066400000000000000000000556121453707721100143750ustar00rootroot00000000000000@c The GNU Free Documentation License. @center Version 1.3, 3 November 2008 @c This file is intended to be included within another document, @c hence no sectioning command or @node. @display Copyright @copyright{} 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. @uref{https://fsf.org/} Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @end display @enumerate 0 @item PREAMBLE The purpose of this License is to make a manual, textbook, or other functional and useful document @dfn{free} in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others. This License is a kind of ``copyleft'', which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software. We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference. @item APPLICABILITY AND DEFINITIONS This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The ``Document'', below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as ``you''. You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law. A ``Modified Version'' of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language. A ``Secondary Section'' is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them. The ``Invariant Sections'' are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none. The ``Cover Texts'' are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words. A ``Transparent'' copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not ``Transparent'' is called ``Opaque''. Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, La@TeX{} input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG@. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only. The ``Title Page'' means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, ``Title Page'' means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text. The ``publisher'' means any person or entity that distributes copies of the Document to the public. A section ``Entitled XYZ'' means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as ``Acknowledgements'', ``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' of such a section when you modify the Document means that it remains a section ``Entitled XYZ'' according to this definition. The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License. @item VERBATIM COPYING You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3. You may also lend copies, under the same conditions stated above, and you may publicly display copies. @item COPYING IN QUANTITY If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects. If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages. If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public. It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document. @item MODIFICATIONS You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version: @enumerate A @item Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission. @item List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement. @item State on the Title page the name of the publisher of the Modified Version, as the publisher. @item Preserve all the copyright notices of the Document. @item Add an appropriate copyright notice for your modifications adjacent to the other copyright notices. @item Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below. @item Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice. @item Include an unaltered copy of this License. @item Preserve the section Entitled ``History'', Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled ``History'' in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence. @item Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the ``History'' section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission. @item For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein. @item Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles. @item Delete any section Entitled ``Endorsements''. Such a section may not be included in the Modified Version. @item Do not retitle any existing section to be Entitled ``Endorsements'' or to conflict in title with any Invariant Section. @item Preserve any Warranty Disclaimers. @end enumerate If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles. You may add a section Entitled ``Endorsements'', provided it contains nothing but endorsements of your Modified Version by various parties---for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard. You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one. The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version. @item COMBINING DOCUMENTS You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers. The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work. In the combination, you must combine any sections Entitled ``History'' in the various original documents, forming one section Entitled ``History''; likewise combine any sections Entitled ``Acknowledgements'', and any sections Entitled ``Dedications''. You must delete all sections Entitled ``Endorsements.'' @item COLLECTIONS OF DOCUMENTS You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects. You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document. @item AGGREGATION WITH INDEPENDENT WORKS A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an ``aggregate'' if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document. If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate. @item TRANSLATION Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail. If a section in the Document is Entitled ``Acknowledgements'', ``Dedications'', or ``History'', the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title. @item TERMINATION You may not copy, modify, sublicense, or distribute the Document except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, or distribute it is void, and will automatically terminate your rights under this License. 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, receipt of a copy of some or all of the same material does not give you any rights to use it. @item FUTURE REVISIONS OF THIS LICENSE The Free Software Foundation may publish new, revised versions of the GNU Free Documentation 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. See @uref{https://www.gnu.org/licenses/}. Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License ``or any later version'' applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. If the Document specifies that a proxy can decide which future versions of this License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Document. @item RELICENSING ``Massive Multiauthor Collaboration Site'' (or ``MMC Site'') means any World Wide Web server that publishes copyrightable works and also provides prominent facilities for anybody to edit those works. A public wiki that anybody can edit is an example of such a server. A ``Massive Multiauthor Collaboration'' (or ``MMC'') contained in the site means any set of copyrightable works thus published on the MMC site. ``CC-BY-SA'' means the Creative Commons Attribution-Share Alike 3.0 license published by Creative Commons Corporation, a not-for-profit corporation with a principal place of business in San Francisco, California, as well as future copyleft versions of that license published by that same organization. ``Incorporate'' means to publish or republish a Document, in whole or in part, as part of another Document. An MMC is ``eligible for relicensing'' if it is licensed under this License, and if all works that were first published under this License somewhere other than this MMC, and subsequently incorporated in whole or in part into the MMC, (1) had no cover texts or invariant sections, and (2) were thus incorporated prior to November 1, 2008. The operator of an MMC Site may republish an MMC contained in the site under CC-BY-SA on the same site at any time before August 1, 2009, provided the MMC is eligible for relicensing. @end enumerate @page @heading ADDENDUM: How to use this License for your documents To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page: @smallexample @group Copyright (C) @var{year} @var{your name}. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License''. @end group @end smallexample If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the ``with@dots{}Texts.''@: line with this: @smallexample @group with the Invariant Sections being @var{list their titles}, with the Front-Cover Texts being @var{list}, and with the Back-Cover Texts being @var{list}. @end group @end smallexample If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation. If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software. @c Local Variables: @c ispell-local-pdict: "ispell-dict" @c End: emacs-eat/gitlog-to-changelog000077500000000000000000000412731453707721100165130ustar00rootroot00000000000000#!/bin/sh #! -*-perl-*- # Convert git log output to ChangeLog format. # Copyright (C) 2008-2022 Free Software Foundation, Inc. # Copyright (C) 2022 Akib Azmain Turja. # # 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 . # # Written by Jim Meyering # '--ignore-commits' implemented by Akib Azmain Turja. # This is a prologue that allows to run a perl script as an executable # on systems that are compliant to a POSIX version before POSIX:2017. # On such systems, the usual invocation of an executable through execlp() # or execvp() fails with ENOEXEC if it is a script that does not start # with a #! line. The script interpreter mentioned in the #! line has # to be /bin/sh, because on GuixSD systems that is the only program that # has a fixed file name. The second line is essential for perl and is # also useful for editing this file in Emacs. The next two lines below # are valid code in both sh and perl. When executed by sh, they re-execute # the script through the perl program found in $PATH. The '-x' option # is essential as well; without it, perl would re-execute the script # through /bin/sh. When executed by perl, the next two lines are a no-op. eval 'exec perl -wSx "$0" "$@"' if 0; my $VERSION = '2022-11-29 10:18'; # UTC # The definition above must lie within the first 8 lines in order # for the Emacs time-stamp write hook (at end) to update it. # If you change this file with Emacs, please let the write hook # do its job. Otherwise, update this string manually. use strict; use warnings; use Getopt::Long; use POSIX qw(strftime); (my $ME = $0) =~ s|.*/||; # use File::Coda; # https://meyering.net/code/Coda/ END { defined fileno STDOUT or return; close STDOUT and return; warn "$ME: failed to close standard output: $!\n"; $? ||= 1; } sub usage ($) { my ($exit_code) = @_; my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR); if ($exit_code != 0) { print $STREAM "Try '$ME --help' for more information.\n"; } else { print $STREAM < ChangeLog $ME -- -n 5 foo > last-5-commits-to-branch-foo SPECIAL SYNTAX: The following types of strings are interpreted specially when they appear at the beginning of a log message line. They are not copied to the output. Copyright-paperwork-exempt: Yes Append the "(tiny change)" notation to the usual "date name email" ChangeLog header to mark a change that does not require a copyright assignment. Co-authored-by: Joe User List the specified name and email address on a second ChangeLog header, denoting a co-author. Signed-off-by: Joe User These lines are simply elided. In a FILE specified via --amend, comment lines (starting with "#") are ignored. FILE must consist of pairs where SHA is a 40-byte SHA1 (alone on a line) referring to a commit in the current project, and CODE refers to one or more consecutive lines of Perl code. Pairs must be separated by one or more blank line. Here is sample input for use with --amend=FILE, from coreutils: 3a169f4c5d9159283548178668d2fae6fced3030 # fix typo in title: s/all tile types/all file types/ 1379ed974f1fa39b12e2ffab18b3f7a607082202 # Due to a bug in vc-dwim, I mis-attributed a patch by Paul to myself. # Change the author to be Paul. Note the escaped "@": s,Jim .*>,Paul Eggert , EOF } exit $exit_code; } # If the string $S is a well-behaved file name, simply return it. # If it contains white space, quotes, etc., quote it, and return the new string. sub shell_quote($) { my ($s) = @_; if ($s =~ m![^\w+/.,-]!) { # Convert each single quote to '\'' $s =~ s/\'/\'\\\'\'/g; # Then single quote the string. $s = "'$s'"; } return $s; } sub quoted_cmd(@) { return join (' ', map {shell_quote $_} @_); } # Parse file F. # Comment lines (starting with "#") are ignored. # F must consist of pairs where SHA is a 40-byte SHA1 # (alone on a line) referring to a commit in the current project, and # CODE refers to one or more consecutive lines of Perl code. # Pairs must be separated by one or more blank line. sub parse_amend_file($) { my ($f) = @_; open F, '<', $f or die "$ME: $f: failed to open for reading: $!\n"; my $fail; my $h = {}; my $in_code = 0; my $sha; while (defined (my $line = )) { $line =~ /^\#/ and next; chomp $line; $line eq '' and $in_code = 0, next; if (!$in_code) { $line =~ /^([[:xdigit:]]{40})$/ or (warn "$ME: $f:$.: invalid line; expected an SHA1\n"), $fail = 1, next; $sha = lc $1; $in_code = 1; exists $h->{$sha} and (warn "$ME: $f:$.: duplicate SHA1\n"), $fail = 1, next; } else { $h->{$sha} ||= ''; $h->{$sha} .= "$line\n"; } } close F; $fail and exit 1; return $h; } # git_dir_option $SRCDIR # # From $SRCDIR, the --git-dir option to pass to git (none if $SRCDIR # is undef). Return as a list (0 or 1 element). sub git_dir_option($) { my ($srcdir) = @_; my @res = (); if (defined $srcdir) { my $qdir = shell_quote $srcdir; my $cmd = "cd $qdir && git rev-parse --show-toplevel"; my $qcmd = shell_quote $cmd; my $git_dir = qx($cmd); defined $git_dir or die "$ME: cannot run $qcmd: $!\n"; $? == 0 or die "$ME: $qcmd had unexpected exit code or signal ($?)\n"; chomp $git_dir; push @res, "--git-dir=$git_dir/.git"; } @res; } { my $since_date; my $until_date; my $format_string = '%s%n%b%n'; my $amend_file; my $append_dot = 0; my $cluster = 1; my $ignore_commits = ''; my $ignore_matching; my $ignore_line; my $strip_tab = 0; my $strip_cherry_pick = 0; my $srcdir; GetOptions ( help => sub { usage 0 }, version => sub { print "$ME version $VERSION\n"; exit }, 'since=s' => \$since_date, 'until=s' => \$until_date, 'format=s' => \$format_string, 'amend=s' => \$amend_file, 'append-dot' => \$append_dot, 'cluster!' => \$cluster, 'ignore-commits=s' => \$ignore_commits, 'ignore-matching=s' => \$ignore_matching, 'ignore-line=s' => \$ignore_line, 'strip-tab' => \$strip_tab, 'strip-cherry-pick' => \$strip_cherry_pick, 'srcdir=s' => \$srcdir, ) or usage 1; defined $since_date and unshift @ARGV, "--since=$since_date"; defined $until_date and unshift @ARGV, "--until=$until_date"; # This is a hash that maps an SHA1 to perl code (i.e., s/old/new/) # that makes a correction in the log or attribution of that commit. my $amend_code = defined $amend_file ? parse_amend_file $amend_file : {}; my @cmd = ('git', git_dir_option $srcdir, qw(log --log-size), '--pretty=format:%H:%ct %an <%ae>%n%n'.$format_string, @ARGV); open PIPE, '-|', @cmd or die ("$ME: failed to run '". quoted_cmd (@cmd) ."': $!\n" . "(Is your Git too old? Version 1.5.1 or later is required.)\n"); my $prev_multi_paragraph; my $prev_date_line = ''; my @prev_coauthors = (); my @skipshas = (); while (1) { defined (my $in = ) or last; $in =~ /^log size (\d+)$/ or die "$ME:$.: Invalid line (expected log size):\n$in"; my $log_nbytes = $1; my $log; my $n_read = read PIPE, $log, $log_nbytes; $n_read == $log_nbytes or die "$ME:$.: unexpected EOF\n"; # Extract leading hash. my ($sha, $rest) = split ':', $log, 2; defined $sha or die "$ME:$.: malformed log entry\n"; $sha =~ /^[[:xdigit:]]{40}$/ or die "$ME:$.: invalid SHA1: $sha\n"; my $skipflag = 0; if (@skipshas) { foreach(@skipshas) { if ($sha =~ /^$_/) { $skipflag = $_; last; } } } # If this commit's log requires any transformation, do it now. my $code = $amend_code->{$sha}; if (defined $code) { eval 'use Safe'; my $s = new Safe; # Put the unpreprocessed entry into "$_". $_ = $rest; # Let $code operate on it, safely. my $r = $s->reval("$code") or die "$ME:$.:$sha: failed to eval \"$code\":\n$@\n"; # Note that we've used this entry. delete $amend_code->{$sha}; # Update $rest upon success. $rest = $_; } # Remove lines inserted by "git cherry-pick". if ($strip_cherry_pick) { $rest =~ s/^\s*Conflicts:\n.*//sm; $rest =~ s/^\s*\(cherry picked from commit [\da-f]+\)\n//m; } my @line = split /[ \t]*\n/, $rest; my $author_line = shift @line; defined $author_line or die "$ME:$.: unexpected EOF\n"; $author_line =~ /^(\d+) (.*>)$/ or die "$ME:$.: Invalid line " . "(expected date/author/email):\n$author_line\n"; # Format 'Copyright-paperwork-exempt: Yes' as a standard ChangeLog # `(tiny change)' annotation. my $tiny = (grep (/^(?:Copyright-paperwork-exempt|Tiny-change):\s+[Yy]es$/, @line) ? ' (tiny change)' : ''); my $date_line = sprintf "%s %s$tiny\n", strftime ("%Y-%m-%d", localtime ($1)), $2; my @coauthors = grep /^Co-authored-by:.*$/, @line; # Omit meta-data lines we've already interpreted. @line = grep !/^(?:Signed-off-by:[ ].*>$ |Co-authored-by:[ ] |Copyright-paperwork-exempt:[ ] |Tiny-change:[ ] )/x, @line; # Remove leading and trailing blank lines. if (@line) { while ($line[0] =~ /^\s*$/) { shift @line; } while ($line[$#line] =~ /^\s*$/) { pop @line; } } # Handle Emacs gitmerge.el "skipped" commits. # Yes, this should be controlled by an option. So sue me. if ( grep /^(; )?Merge from /, @line ) { my $found = 0; foreach (@line) { if (grep /^The following commit.*skipped:$/, $_) { $found = 1; ## Reset at each merge to reduce chance of false matches. @skipshas = (); next; } if ($found && $_ =~ /^([[:xdigit:]]{7,}) [^ ]/) { push ( @skipshas, $1 ); } } } # Ignore commits that's in --ignore-commits, if specified. my $ignored = 0; foreach(split ',', $ignore_commits) { if ($sha =~ /^$_/) { $ignored = 1; last; } } # Ignore commits that match the --ignore-matching pattern, if specified. if ($ignored || (defined $ignore_matching && @line && $line[0] =~ /$ignore_matching/)) { $skipflag = 1; } elsif ($skipflag) { ## Perhaps only warn if a pattern matches more than once? warn "$ME: warning: skipping $sha due to $skipflag\n"; } if (! $skipflag) { if (defined $ignore_line && @line) { @line = grep ! /$ignore_line/, @line; while ($line[$#line] =~ /^\s*$/) { pop @line; } } # Record whether there are two or more paragraphs. my $multi_paragraph = grep /^\s*$/, @line; # Format 'Co-authored-by: A U Thor ' lines in # standard multi-author ChangeLog format. for (@coauthors) { s/^Co-authored-by:\s*/\t /; s/\s*/ or warn "$ME: warning: missing email address for " . substr ($_, 5) . "\n"; } # If clustering of commit messages has been disabled, if this header # would be different from the previous date/name/etc. header, # or if this or the previous entry consists of two or more paragraphs, # then print the header. if ( ! $cluster || $date_line ne $prev_date_line || "@coauthors" ne "@prev_coauthors" || $multi_paragraph || $prev_multi_paragraph) { $prev_date_line eq '' or print "\n"; print $date_line; @coauthors and print join ("\n", @coauthors), "\n"; } $prev_date_line = $date_line; @prev_coauthors = @coauthors; $prev_multi_paragraph = $multi_paragraph; # If there were any lines if (@line == 0) { warn "$ME: warning: empty commit message:\n" . " commit $sha\n $date_line\n"; } else { if ($append_dot) { # If the first line of the message has enough room, then if (length $line[0] < 72) { # append a dot if there is no other punctuation or blank # at the end. $line[0] =~ /[[:punct:]\s]$/ or $line[0] .= '.'; } } # Remove one additional leading TAB from each line. $strip_tab and map { s/^\t// } @line; # Prefix each non-empty line with a TAB. @line = map { length $_ ? "\t$_" : '' } @line; print "\n", join ("\n", @line), "\n"; } } defined ($in = ) or last; $in ne "\n" and die "$ME:$.: unexpected line:\n$in"; } close PIPE or die "$ME: error closing pipe from " . quoted_cmd (@cmd) . "\n"; # FIXME-someday: include $PROCESS_STATUS in the diagnostic # Complain about any unused entry in the --amend=F specified file. my $fail = 0; foreach my $sha (keys %$amend_code) { warn "$ME:$amend_file: unused entry: $sha\n"; $fail = 1; } exit $fail; } # Local Variables: # mode: perl # indent-tabs-mode: nil # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-line-limit: 50 # time-stamp-start: "my $VERSION = '" # time-stamp-format: "%:y-%02m-%02d %02H:%02M" # time-stamp-time-zone: "UTC0" # time-stamp-end: "'; # UTC" # End: emacs-eat/gpl.texi000066400000000000000000001044211453707721100144030ustar00rootroot00000000000000@c The GNU General Public License. @center Version 3, 29 June 2007 @c This file is intended to be included within another document, @c hence no sectioning command or @node. @display Copyright @copyright{} 2007 Free Software Foundation, Inc. @url{https://fsf.org/} Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @end display @heading 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. @heading TERMS AND CONDITIONS @enumerate 0 @item 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. @item 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. @item 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. @item 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. @item 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. @item 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: @enumerate a @item The work must carry prominent notices stating that you modified it, and giving a relevant date. @item 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''. @item 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. @item 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. @end enumerate 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. @item 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: @enumerate a @item 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. @item 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. @item 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. @item 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. @item 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. @end enumerate 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. @item 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: @enumerate a @item Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or @item 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 @item 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 @item Limiting the use for publicity purposes of names of licensors or authors of the material; or @item Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or @item 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. @end enumerate 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. @item 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. @item 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. @item 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. @item 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. @item 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. @item 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. @item 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. @item 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. @item 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. @item 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 enumerate @heading END OF TERMS AND CONDITIONS @heading 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. @smallexample @var{one line to give the program's name and a brief idea of what it does.} Copyright (C) @var{year} @var{name of author} 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 @url{https://www.gnu.org/licenses/}. @end smallexample 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: @smallexample @var{program} Copyright (C) @var{year} @var{name of author} This program comes with ABSOLUTELY NO WARRANTY; for details type @samp{show w}. This is free software, and you are welcome to redistribute it under certain conditions; type @samp{show c} for details. @end smallexample The hypothetical commands @samp{show w} and @samp{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 @url{https://www.gnu.org/licenses/}. 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 @url{https://www.gnu.org/licenses/why-not-lgpl.html}. emacs-eat/integration/000077500000000000000000000000001453707721100152475ustar00rootroot00000000000000emacs-eat/integration/bash000066400000000000000000000100741453707721100161110ustar00rootroot00000000000000# integration/bash --- Bash integration # Copyright (C) 2022 Akib Azmain Turja. # This file is not part of GNU Emacs. # This file 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. # 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. # For a full copy of the GNU General Public License # see . __eat_prompt_command () { # Send exit status. if test -n "$__eat_current_command" then printf '\e]51;e;H;%i\e\\' "$__eat_exit_status" fi __eat_current_command="" # Inform that a new prompt is going to be printed. printf '\e]51;e;J\e\\' # Send the current working directory, for directory tracking. printf '\e]51;e;A;%s;%s\e\\' "$(printf "%s" "$HOSTNAME" | base64)" \ "$(printf "%s" "$PWD" | base64)" # Update title. # "${PWD/$HOME/'~'}" converts "/home/akib/org/" to "~/org/". # The next one is substituted with '$', or '#' if we're "root". printf '\e]2;%s@%s:%s%s\e\\' "$USER" "$HOSTNAME" \ "${PWD/$HOME/'~'}" \ "$(test $UID -eq 0 && echo '#' || echo '$')" } __eat_preexec () { # Get the real command typed by the user from the history. __eat_current_command="$(history 1 | sed 's/ *[0-9]* *//')" # Send current command. printf '\e]51;e;F;%s\e\\' \ "$(printf "%s" "$__eat_current_command" | base64)" # Send pre-exec sequence. printf '\e]51;e;G\e\\' # Update title to include the command running. # "${PWD/$HOME/'~'}" converts "/home/akib/foo/" to "~/foo/". # The next one is substituted with '$', or '#' if we're "root". printf '\e]2;%s@%s:%s%s %s\e\\' "$USER" "$HOSTNAME" \ "${PWD/$HOME/'~'}" \ "$(test $UID -eq 0 && echo '#' || echo '$')" \ "$__eat_current_command" } __eat_before_prompt_command () { __eat_exit_status="$?" __eat_inhibit_preexec=yes } __eat_after_prompt_command () { __eat_inhibit_preexec=no } __eat_before_exec () { if test $__eat_inhibit_preexec = no \ && test "$BASH_COMMAND" != __eat_before_prompt_command then __eat_inhibit_preexec=yes __eat_preexec fi } __eat_enable_integration () { __eat_integration_enabled=yes __eat_current_command="" __eat_exit_status=0 __eat_inhibit_preexec=yes local __eat_prompt_start='\e]51;e;B\e\\' local __eat_prompt_end='\e]51;e;C\e\\' local __eat_continuation_start='\e]51;e;D\e\\' local __eat_continuation_end='\e]51;e;E\e\\' PS1="\[$__eat_prompt_start\]$PS1\[$__eat_prompt_end\]" PS2="\[$__eat_continuation_start\]$PS2\[$__eat_continuation_end\]" PROMPT_COMMAND+=(__eat_prompt_command) trap '__eat_before_exec' DEBUG # Wrap 'PROMPT_COMMAND' to avoid it getting trapped in 'DEBUG' trap. # Fun fact: Microsoft doesn't still about know this simple trick. # They ended up using something as silly and pityful as # 'VAR=$PROMPT_COMMAND' to copy a Bash array in VSCode Bash # integration script, which simply won't work ever, and then # complain about Bash in the comments! xD PROMPT_COMMAND+=(__eat_after_prompt_command) PROMPT_COMMAND=(__eat_before_prompt_command "${PROMPT_COMMAND[@]}") # Send the history, for native shell prompt. printf '\e]51;e;I;0;bash;%s;%s\e\\' \ "$(printf "%s" "$HOSTNAME" | base64)" \ "$(printf "%s" "$HISTFILE" | base64)" local REPLY IFS=$';\e' read -r -s -t 10 -d "\\" -a REPLY if test "${REPLY[4]}" != 0 then printf '\e]51;e;I;1;bash;%s\e\\' \ "$(tail -n "${REPLY[4]}" "$HISTFILE" | base64)" fi } _eat_msg () { local msg=$'\e]51;e;M' for _ in $(eval "echo {1..$#}") do msg="$msg;$(printf "%s" "$1" | base64)" shift done printf "%s\e\\" "$msg" } # Enable. if test -z "$__eat_integration_enabled" && \ test "${TERM:0:4}" = "eat-" then __eat_enable_integration else true fi # Local Variables: # mode: sh # sh-shell: bash # End: emacs-eat/integration/zsh000066400000000000000000000063561453707721100160100ustar00rootroot00000000000000# integration/zsh --- Zsh integration # Copyright (C) 2022, 2023 Akib Azmain Turja. # This file is not part of GNU Emacs. # This file 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. # 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. # For a full copy of the GNU General Public License # see . __eat_precmd () { __eat_exit_status="$?" # Send exit status. if test -n "$__eat_current_command" then printf '\e]51;e;H;%i\e\\' "$__eat_exit_status" fi __eat_current_command="" # Inform that a new prompt is going to be printed. printf '\e]51;e;J\e\\' # Send the current working directory, for directory tracking. printf '\e]51;e;A;%s;%s\e\\' "$(printf "%s" "$HOST" | base64)" \ "$(printf "%s" "$PWD" | base64)" # Update title. # "${PWD/$HOME/'~'}" converts "/home/akib/org/" to "~/org/". # The next one is substituted with '%', or '#' if we're "root". printf '\e]2;%s@%s:%s%s\e\\' "$USER" "$HOST" "${PWD/$HOME/~}" \ "$(test $UID -eq 0 && echo '#' || echo '%')" } __eat_preexec () { __eat_current_command="$1" # Send current command. printf '\e]51;e;F;%s\e\\' \ "$(printf "%s" "$__eat_current_command" | base64)" # Send pre-exec sequence. printf '\e]51;e;G\e\\' # Update title to include the command running. # "${PWD/$HOME/~}" converts "/home/akib/foo/" to "~/foo/". # The next one is substituted with '%', or '#' if we're "root". printf '\e]2;%s@%s:%s%s %s\e\\' "$USER" "$HOST" "${PWD/$HOME/~}" \ "$(test $UID -eq 0 && echo '#' || echo '%')" \ "$__eat_current_command" } __eat_enable_integration () { __eat_integration_enabled=yes __eat_current_command="" __eat_exit_status=0 local __eat_prompt_start="$(printf '\e]51;e;B\e\\')" local __eat_prompt_end="$(printf '\e]51;e;C\e\\')" local __eat_continuation_start="$(printf '\e]51;e;D\e\\')" local __eat_continuation_end="$(printf '\e]51;e;E\e\\')" PS1="%{$__eat_prompt_start%}$PS1%{$__eat_prompt_end%}" PS2="%{$__eat_continuation_start%}$PS2%{$__eat_continuation_end%}" # TODO: What to do about RPS1 and friends? autoload -Uz add-zsh-hook add-zsh-hook precmd __eat_precmd add-zsh-hook preexec __eat_preexec # Send the history, for native shell prompt. printf '\e]51;e;I;0;bash;%s;%s\e\\' \ "$(printf "%s" "$HOSTNAME" | base64)" \ "$(printf "%s" "$HISTFILE" | base64)" local REPLY IFS=$';\e' read -r -s -t 10 -d "\\" -A REPLY if test "${REPLY[5]}" != 0 then printf '\e]51;e;I;1;zsh;%s\e\\' \ "$(tail -n "${REPLY[5]}" "$HISTFILE" | base64)" fi } _eat_msg () { local msg=$'\e]51;e;M' for _ in $(eval "echo {1..$#}") do msg="$msg;$(printf "%s" "$1" | base64)" shift done printf "%s\e\\" "$msg" } # Enable. if test -z "$__eat_integration_enabled" && \ test "${TERM:0:4}" = "eat-" then __eat_enable_integration else true fi # Local Variables: # mode: sh # sh-shell: zsh # End: emacs-eat/make-changelog000077500000000000000000000025541453707721100155220ustar00rootroot00000000000000#!/bin/sh ./gitlog-to-changelog \ --ignore-matching='^; ' --ignore-line='^; ' \ --ignore-commits="f3fed64957b4e88cfa1ff2c5ddfb665f249624cc,\ 1b2b7aee26dd611a44226530f9a010ec9bf22add" \ --format='%B' >ChangeLog # Find the years covered by the generated ChangeLog, so that # a proper copyright notice can be output. years=$(sed -n 's/^\([0-9][0-9]*\).*/\1/p' ChangeLog | sort -nu) start_year=$(echo "$years" | head -1) end_year=$(echo "$years" | tail -1) if test "$start_year" = "$end_year"; then year_range=$start_year else year_range=$start_year-$end_year fi copyright_notice=" ;; Local Variables: ;; coding: utf-8 ;; End: Copyright (C) $year_range Akib Azmain Turja. 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 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 ." echo "$copyright_notice" >>ChangeLog emacs-eat/term/000077500000000000000000000000001453707721100136735ustar00rootroot00000000000000emacs-eat/term/eat.el000066400000000000000000000032261453707721100147710ustar00rootroot00000000000000;;; eat.el --- Terminal initialization for Eat -*- lexical-binding: t; -*- ;; Copyright (C) 2022 Akib Azmain Turja. ;; Author: Akib Azmain Turja ;; Created: 2022-12-04 ;; This file is not part of GNU Emacs. ;; This file 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. ;; 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. ;; For a full copy of the GNU General Public License ;; see . ;;; Commentary: ;; Extra support for the Eat terminal emulator. ;;; Code: (require 'term/xterm) (defcustom xterm-eat-extra-capabilities '(modifyOtherKeys reportBackground getSelection setSelection) "List of extra capabilities supported in Eat. Each element of the list enables a feature. The elements can be: `modifyOtherKeys' More key bindings work (e.g., \"\\C-,\") `reportBackground' Eat reports the terminal background color. `getSelection' Eat yanks text from parent Emacs `kill-ring'. `setSelection' Eat saves killed text to parent Emacs `kill-ring'." :type xterm--extra-capabilities-type :group 'eat) (defun terminal-init-eat () "Terminal initialization function for Eat." (let ((xterm-extra-capabilities xterm-eat-extra-capabilities)) (tty-run-terminal-initialization (selected-frame) "xterm"))) (provide 'term/eat) ;;; eat.el ends here emacs-eat/terminfo/000077500000000000000000000000001453707721100145475ustar00rootroot00000000000000emacs-eat/terminfo/65/000077500000000000000000000000001453707721100150015ustar00rootroot00000000000000emacs-eat/terminfo/65/eat-256color000066400000000000000000000042411453707721100170470ustar00rootroot00000000000000'ieat-256color|Emacs Eat with 256 colorsPÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"&ÿÿ1BDHOÿÿQ^ÿÿbfptxÿÿƒˆÿÿ–› ÿÿ¥ª¯´½ÁÈÿÿÑÖÜÿÿÿÿâÿÿÿÿÿÿÿÿÿÿåÿÿéÿÿÿÿÿÿëÿÿðÿÿÿÿÿÿÿÿôøþ "(,ÿÿ1ÿÿ5:?CJÿÿQU[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿaÿÿdmvˆ‘𣬵ÿÿÿÿÿÿÿÿÿÿÿÿ¾Òÿÿÿÿÿÿ×ÚåèêíÿÿÿÿJLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQÿÿ’ÿÿÿÿ–œÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦ÿÿÿÿ­ÿÿÿÿÿÿÿÿ´»ÂÿÿÿÿÉÿÿÐÿÿÿÿÿÿ×ÿÿÿÿÿÿÿÿÿÿÞäêñøÿ&.6>FMT[bjrz‚В𢩰·¾ÆÎÖÞæîöþ "*2:BJRZahoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtÿÿÿÿ„ÿÿÿÿÿÿÿÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ’ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜ÿÿÿÿÿÿœÛ [%i%p1%d;%p2%dr[%i%p1%dG[%i%p1%d;%p2%dH [?25l[?12l[?25h[?12;25h]2;\(0[?1049h[%p1%dX(B(B[?1049l\[3~OBOP[21~OQOROS[15~[17~[18~[19~[20~OH[2~OD[6~[5~OCOA[?1l[?1hE[%p1%dP[%p1%dM[%p1%dB[%p1%d@[%p1%dS[%p1%dL[%p1%dD[%p1%dC[%p1%dT[%p1%dA%p1%c[%p2%{1}%-%db\c8[%i%p1%dd7 M%?%p9%t(0%e(B%;[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m ]2;++,,--..00``aaffggiihhjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~[?7h[?7lOF[3;2~[2;2~[6;2~[5;2~[23~[24~[15;2~[17;2~[18;2~[19;2~[20;2~[21;2~[23;2~[24;2~[15;5~[17;5~[18;5~[19;5~[20;5~[21;5~[23;5~[24;5~[15;6~[17;6~[18;6~[19;6~[20;6~[21;6~[23;6~[24;6~[15;3~[17;3~[18;3~[19;3~[20;3~[21;3~[23;3~[24;3~[%i%d;%dR[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;mrmxxsmxxemacs-eat/terminfo/65/eat-color000066400000000000000000000042431453707721100166140ustar00rootroot00000000000000&i<eat-color|Emacs Eat with eight colorsPÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ÿÿ"&ÿÿ1BDHOÿÿQ^ÿÿbfptxÿÿƒˆÿÿ–› ÿÿ¥ª¯´½ÁÈÿÿÑÖÜÿÿÿÿâÿÿÿÿÿÿÿÿÿÿåÿÿéÿÿÿÿÿÿëÿÿðÿÿÿÿÿÿÿÿôøþ "(,ÿÿ1ÿÿ5:?CJÿÿQU[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿaÿÿdmvˆ‘𣬵ÿÿÿÿÿÿÿÿÿÿÿÿ¾Òÿÿÿÿÿÿ×ÚåèêíÿÿÿÿJLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQÿÿ’ÿÿÿÿ–œÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦ÿÿÿÿ­ÿÿÿÿÿÿÿÿ´»ÂÿÿÿÿÉÿÿÐÿÿÿÿÿÿ×ÿÿÿÿÿÿÿÿÿÿÞäêñøÿ&.6>FMT[bjrz‚В𢩰·¾ÆÎÖÞæîöþ "*2:BJRZahoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtÿÿÿÿ„ÿÿÿÿÿÿÿÿÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ$ÿÿÿÿÿÿ(2 [%i%p1%d;%p2%dr[%i%p1%dG[%i%p1%d;%p2%dH [?25l[?12l[?25h[?12;25h]2;\(0[?1049h[%p1%dX(B(B[?1049l\[3~OBOP[21~OQOROS[15~[17~[18~[19~[20~OH[2~OD[6~[5~OCOA[?1l[?1hE[%p1%dP[%p1%dM[%p1%dB[%p1%d@[%p1%dS[%p1%dL[%p1%dD[%p1%dC[%p1%dT[%p1%dA%p1%c[%p2%{1}%-%db\c8[%i%p1%dd7 M%?%p9%t(0%e(B%;[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m ]2;++,,--..00``aaffggiihhjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~[?7h[?7lOF[3;2~[2;2~[6;2~[5;2~[23~[24~[15;2~[17;2~[18;2~[19;2~[20;2~[21;2~[23;2~[24;2~[15;5~[17;5~[18;5~[19;5~[20;5~[21;5~[23;5~[24;5~[15;6~[17;6~[18;6~[19;6~[20;6~[21;6~[23;6~[24;6~[15;3~[17;3~[18;3~[19;3~[20;3~[21;3~[23;3~[24;3~[%i%d;%dR[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m[3%p1%dm[4%p1%dmrmxxsmxxemacs-eat/terminfo/65/eat-mono000066400000000000000000000037211453707721100164460ustar00rootroot00000000000000"d“eat-mono|Emacs Eat without colorsPÿÿ"&ÿÿ1BDHOÿÿQ^ÿÿbfptxÿÿƒˆÿÿ–› ÿÿ¥ª¯´½ÁÈÿÿÑÖÜÿÿÿÿâÿÿÿÿÿÿÿÿÿÿåÿÿéÿÿÿÿÿÿëÿÿðÿÿÿÿÿÿÿÿôøþ "(,ÿÿ1ÿÿ5:?CJÿÿQU[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿaÿÿdmvˆ‘𣬵ÿÿÿÿÿÿÿÿÿÿÿÿ¾Òÿÿÿÿÿÿ×ÚåèêíÿÿÿÿJLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQÿÿ’ÿÿÿÿ–œÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦ÿÿÿÿ­ÿÿÿÿÿÿÿÿ´»ÂÿÿÿÿÉÿÿÐÿÿÿÿÿÿ×ÿÿÿÿÿÿÿÿÿÿÞäêñøÿ&.6>FMT[bjrz‚В𢩰·¾ÆÎÖÞæîöþ "*2:BJRZahoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‰ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ [%i%p1%d;%p2%dr[%i%p1%dG[%i%p1%d;%p2%dH [?25l[?12l[?25h[?12;25h]2;\(0[?1049h[%p1%dX(B(B[?1049l\[3~OBOP[21~OQOROS[15~[17~[18~[19~[20~OH[2~OD[6~[5~OCOA[?1l[?1hE[%p1%dP[%p1%dM[%p1%dB[%p1%d@[%p1%dS[%p1%dL[%p1%dD[%p1%dC[%p1%dT[%p1%dA%p1%c[%p2%{1}%-%db\c8[%i%p1%dd7 M%?%p9%t(0%e(B%;[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m ]2;++,,--..00``aaffggiihhjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~[?7h[?7lOF[3;2~[2;2~[6;2~[5;2~[23~[24~[15;2~[17;2~[18;2~[19;2~[20;2~[21;2~[23;2~[24;2~[15;5~[17;5~[18;5~[19;5~[20;5~[21;5~[23;5~[24;5~[15;6~[17;6~[18;6~[19;6~[20;6~[21;6~[23;6~[24;6~[15;3~[17;3~[18;3~[19;3~[20;3~[21;3~[23;3~[24;3~[%i%d;%dRrmxxsmxxemacs-eat/terminfo/65/eat-truecolor000066400000000000000000000042501453707721100175120ustar00rootroot00000000000000'ieat-truecolor|Emacs Eat with truecolorPÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"&ÿÿ1BDHOÿÿQ^ÿÿbfptxÿÿƒˆÿÿ–› ÿÿ¥ª¯´½ÁÈÿÿÑÖÜÿÿÿÿâÿÿÿÿÿÿÿÿÿÿåÿÿéÿÿÿÿÿÿëÿÿðÿÿÿÿÿÿÿÿôøþ "(,ÿÿ1ÿÿ5:?CJÿÿQU[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿaÿÿdmvˆ‘𣬵ÿÿÿÿÿÿÿÿÿÿÿÿ¾Òÿÿÿÿÿÿ×ÚåèêíÿÿÿÿJLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQÿÿ’ÿÿÿÿ–œÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦ÿÿÿÿ­ÿÿÿÿÿÿÿÿ´»ÂÿÿÿÿÉÿÿÐÿÿÿÿÿÿ×ÿÿÿÿÿÿÿÿÿÿÞäêñøÿ&.6>FMT[bjrz‚В𢩰·¾ÆÎÖÞæîöþ "*2:BJRZahoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtÿÿÿÿ„ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ’ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜ÿÿÿÿÿÿœÛ [%i%p1%d;%p2%dr[%i%p1%dG[%i%p1%d;%p2%dH [?25l[?12l[?25h[?12;25h]2;\(0[?1049h[%p1%dX(B(B[?1049l\[3~OBOP[21~OQOROS[15~[17~[18~[19~[20~OH[2~OD[6~[5~OCOA[?1l[?1hE[%p1%dP[%p1%dM[%p1%dB[%p1%d@[%p1%dS[%p1%dL[%p1%dD[%p1%dC[%p1%dT[%p1%dA%p1%c[%p2%{1}%-%db\c8[%i%p1%dd7 M%?%p9%t(0%e(B%;[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m ]2;++,,--..00``aaffggiihhjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~[?7h[?7lOF[3;2~[2;2~[6;2~[5;2~[23~[24~[15;2~[17;2~[18;2~[19;2~[20;2~[21;2~[23;2~[24;2~[15;5~[17;5~[18;5~[19;5~[20;5~[21;5~[23;5~[24;5~[15;6~[17;6~[18;6~[19;6~[20;6~[21;6~[23;6~[24;6~[15;3~[17;3~[18;3~[19;3~[20;3~[21;3~[23;3~[24;3~[%i%d;%dR[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;mTcrmxxsmxxemacs-eat/terminfo/e/000077500000000000000000000000001453707721100147735ustar00rootroot00000000000000emacs-eat/terminfo/e/eat-256color000066400000000000000000000042411453707721100170410ustar00rootroot00000000000000'ieat-256color|Emacs Eat with 256 colorsPÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"&ÿÿ1BDHOÿÿQ^ÿÿbfptxÿÿƒˆÿÿ–› ÿÿ¥ª¯´½ÁÈÿÿÑÖÜÿÿÿÿâÿÿÿÿÿÿÿÿÿÿåÿÿéÿÿÿÿÿÿëÿÿðÿÿÿÿÿÿÿÿôøþ "(,ÿÿ1ÿÿ5:?CJÿÿQU[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿaÿÿdmvˆ‘𣬵ÿÿÿÿÿÿÿÿÿÿÿÿ¾Òÿÿÿÿÿÿ×ÚåèêíÿÿÿÿJLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQÿÿ’ÿÿÿÿ–œÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦ÿÿÿÿ­ÿÿÿÿÿÿÿÿ´»ÂÿÿÿÿÉÿÿÐÿÿÿÿÿÿ×ÿÿÿÿÿÿÿÿÿÿÞäêñøÿ&.6>FMT[bjrz‚В𢩰·¾ÆÎÖÞæîöþ "*2:BJRZahoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtÿÿÿÿ„ÿÿÿÿÿÿÿÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ’ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜ÿÿÿÿÿÿœÛ [%i%p1%d;%p2%dr[%i%p1%dG[%i%p1%d;%p2%dH [?25l[?12l[?25h[?12;25h]2;\(0[?1049h[%p1%dX(B(B[?1049l\[3~OBOP[21~OQOROS[15~[17~[18~[19~[20~OH[2~OD[6~[5~OCOA[?1l[?1hE[%p1%dP[%p1%dM[%p1%dB[%p1%d@[%p1%dS[%p1%dL[%p1%dD[%p1%dC[%p1%dT[%p1%dA%p1%c[%p2%{1}%-%db\c8[%i%p1%dd7 M%?%p9%t(0%e(B%;[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m ]2;++,,--..00``aaffggiihhjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~[?7h[?7lOF[3;2~[2;2~[6;2~[5;2~[23~[24~[15;2~[17;2~[18;2~[19;2~[20;2~[21;2~[23;2~[24;2~[15;5~[17;5~[18;5~[19;5~[20;5~[21;5~[23;5~[24;5~[15;6~[17;6~[18;6~[19;6~[20;6~[21;6~[23;6~[24;6~[15;3~[17;3~[18;3~[19;3~[20;3~[21;3~[23;3~[24;3~[%i%d;%dR[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;mrmxxsmxxemacs-eat/terminfo/e/eat-color000066400000000000000000000042431453707721100166060ustar00rootroot00000000000000&i<eat-color|Emacs Eat with eight colorsPÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ÿÿ"&ÿÿ1BDHOÿÿQ^ÿÿbfptxÿÿƒˆÿÿ–› ÿÿ¥ª¯´½ÁÈÿÿÑÖÜÿÿÿÿâÿÿÿÿÿÿÿÿÿÿåÿÿéÿÿÿÿÿÿëÿÿðÿÿÿÿÿÿÿÿôøþ "(,ÿÿ1ÿÿ5:?CJÿÿQU[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿaÿÿdmvˆ‘𣬵ÿÿÿÿÿÿÿÿÿÿÿÿ¾Òÿÿÿÿÿÿ×ÚåèêíÿÿÿÿJLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQÿÿ’ÿÿÿÿ–œÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦ÿÿÿÿ­ÿÿÿÿÿÿÿÿ´»ÂÿÿÿÿÉÿÿÐÿÿÿÿÿÿ×ÿÿÿÿÿÿÿÿÿÿÞäêñøÿ&.6>FMT[bjrz‚В𢩰·¾ÆÎÖÞæîöþ "*2:BJRZahoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtÿÿÿÿ„ÿÿÿÿÿÿÿÿÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ$ÿÿÿÿÿÿ(2 [%i%p1%d;%p2%dr[%i%p1%dG[%i%p1%d;%p2%dH [?25l[?12l[?25h[?12;25h]2;\(0[?1049h[%p1%dX(B(B[?1049l\[3~OBOP[21~OQOROS[15~[17~[18~[19~[20~OH[2~OD[6~[5~OCOA[?1l[?1hE[%p1%dP[%p1%dM[%p1%dB[%p1%d@[%p1%dS[%p1%dL[%p1%dD[%p1%dC[%p1%dT[%p1%dA%p1%c[%p2%{1}%-%db\c8[%i%p1%dd7 M%?%p9%t(0%e(B%;[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m ]2;++,,--..00``aaffggiihhjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~[?7h[?7lOF[3;2~[2;2~[6;2~[5;2~[23~[24~[15;2~[17;2~[18;2~[19;2~[20;2~[21;2~[23;2~[24;2~[15;5~[17;5~[18;5~[19;5~[20;5~[21;5~[23;5~[24;5~[15;6~[17;6~[18;6~[19;6~[20;6~[21;6~[23;6~[24;6~[15;3~[17;3~[18;3~[19;3~[20;3~[21;3~[23;3~[24;3~[%i%d;%dR[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m[3%p1%dm[4%p1%dmrmxxsmxxemacs-eat/terminfo/e/eat-mono000066400000000000000000000037211453707721100164400ustar00rootroot00000000000000"d“eat-mono|Emacs Eat without colorsPÿÿ"&ÿÿ1BDHOÿÿQ^ÿÿbfptxÿÿƒˆÿÿ–› ÿÿ¥ª¯´½ÁÈÿÿÑÖÜÿÿÿÿâÿÿÿÿÿÿÿÿÿÿåÿÿéÿÿÿÿÿÿëÿÿðÿÿÿÿÿÿÿÿôøþ "(,ÿÿ1ÿÿ5:?CJÿÿQU[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿaÿÿdmvˆ‘𣬵ÿÿÿÿÿÿÿÿÿÿÿÿ¾Òÿÿÿÿÿÿ×ÚåèêíÿÿÿÿJLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQÿÿ’ÿÿÿÿ–œÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦ÿÿÿÿ­ÿÿÿÿÿÿÿÿ´»ÂÿÿÿÿÉÿÿÐÿÿÿÿÿÿ×ÿÿÿÿÿÿÿÿÿÿÞäêñøÿ&.6>FMT[bjrz‚В𢩰·¾ÆÎÖÞæîöþ "*2:BJRZahoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‰ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ [%i%p1%d;%p2%dr[%i%p1%dG[%i%p1%d;%p2%dH [?25l[?12l[?25h[?12;25h]2;\(0[?1049h[%p1%dX(B(B[?1049l\[3~OBOP[21~OQOROS[15~[17~[18~[19~[20~OH[2~OD[6~[5~OCOA[?1l[?1hE[%p1%dP[%p1%dM[%p1%dB[%p1%d@[%p1%dS[%p1%dL[%p1%dD[%p1%dC[%p1%dT[%p1%dA%p1%c[%p2%{1}%-%db\c8[%i%p1%dd7 M%?%p9%t(0%e(B%;[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m ]2;++,,--..00``aaffggiihhjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~[?7h[?7lOF[3;2~[2;2~[6;2~[5;2~[23~[24~[15;2~[17;2~[18;2~[19;2~[20;2~[21;2~[23;2~[24;2~[15;5~[17;5~[18;5~[19;5~[20;5~[21;5~[23;5~[24;5~[15;6~[17;6~[18;6~[19;6~[20;6~[21;6~[23;6~[24;6~[15;3~[17;3~[18;3~[19;3~[20;3~[21;3~[23;3~[24;3~[%i%d;%dRrmxxsmxxemacs-eat/terminfo/e/eat-truecolor000066400000000000000000000042501453707721100175040ustar00rootroot00000000000000'ieat-truecolor|Emacs Eat with truecolorPÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"&ÿÿ1BDHOÿÿQ^ÿÿbfptxÿÿƒˆÿÿ–› ÿÿ¥ª¯´½ÁÈÿÿÑÖÜÿÿÿÿâÿÿÿÿÿÿÿÿÿÿåÿÿéÿÿÿÿÿÿëÿÿðÿÿÿÿÿÿÿÿôøþ "(,ÿÿ1ÿÿ5:?CJÿÿQU[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿaÿÿdmvˆ‘𣬵ÿÿÿÿÿÿÿÿÿÿÿÿ¾Òÿÿÿÿÿÿ×ÚåèêíÿÿÿÿJLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQÿÿ’ÿÿÿÿ–œÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦ÿÿÿÿ­ÿÿÿÿÿÿÿÿ´»ÂÿÿÿÿÉÿÿÐÿÿÿÿÿÿ×ÿÿÿÿÿÿÿÿÿÿÞäêñøÿ&.6>FMT[bjrz‚В𢩰·¾ÆÎÖÞæîöþ "*2:BJRZahoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtÿÿÿÿ„ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ’ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜ÿÿÿÿÿÿœÛ [%i%p1%d;%p2%dr[%i%p1%dG[%i%p1%d;%p2%dH [?25l[?12l[?25h[?12;25h]2;\(0[?1049h[%p1%dX(B(B[?1049l\[3~OBOP[21~OQOROS[15~[17~[18~[19~[20~OH[2~OD[6~[5~OCOA[?1l[?1hE[%p1%dP[%p1%dM[%p1%dB[%p1%d@[%p1%dS[%p1%dL[%p1%dD[%p1%dC[%p1%dT[%p1%dA%p1%c[%p2%{1}%-%db\c8[%i%p1%dd7 M%?%p9%t(0%e(B%;[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m ]2;++,,--..00``aaffggiihhjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~[?7h[?7lOF[3;2~[2;2~[6;2~[5;2~[23~[24~[15;2~[17;2~[18;2~[19;2~[20;2~[21;2~[23;2~[24;2~[15;5~[17;5~[18;5~[19;5~[20;5~[21;5~[23;5~[24;5~[15;6~[17;6~[18;6~[19;6~[20;6~[21;6~[23;6~[24;6~[15;3~[17;3~[18;3~[19;3~[20;3~[21;3~[23;3~[24;3~[%i%d;%dR[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;mTcrmxxsmxxemacs-eat/texinfo.tex000066400000000000000000013377471453707721100151500ustar00rootroot00000000000000% texinfo.tex -- TeX macros to handle Texinfo files. % % Load plain if necessary, i.e., if running under initex. \expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi % \def\texinfoversion{2020-02-11.09} % % Copyright 1985, 1986, 1988, 1990-2019 Free Software Foundation, Inc. % % This texinfo.tex file 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 texinfo.tex file 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 . % % As a special exception, when this file is read by TeX when processing % a Texinfo source document, you may use the result without % restriction. This Exception is an additional permission under section 7 % of the GNU General Public License, version 3 ("GPLv3"). % % Please try the latest version of texinfo.tex before submitting bug % reports; you can get the latest version from: % https://ftp.gnu.org/gnu/texinfo/ (the Texinfo release area), or % https://ftpmirror.gnu.org/texinfo/ (same, via a mirror), or % https://www.gnu.org/software/texinfo/ (the Texinfo home page) % The texinfo.tex in any given distribution could well be out % of date, so if that's what you're using, please check. % % Send bug reports to bug-texinfo@gnu.org. Please include including a % complete document in each bug report with which we can reproduce the % problem. Patches are, of course, greatly appreciated. % % To process a Texinfo manual with TeX, it's most reliable to use the % texi2dvi shell script that comes with the distribution. For a simple % manual foo.texi, however, you can get away with this: % tex foo.texi % texindex foo.?? % tex foo.texi % tex foo.texi % dvips foo.dvi -o # or whatever; this makes foo.ps. % The extra TeX runs get the cross-reference information correct. % Sometimes one run after texindex suffices, and sometimes you need more % than two; texi2dvi does it as many times as necessary. % % It is possible to adapt texinfo.tex for other languages, to some % extent. You can get the existing language-specific files from the % full Texinfo distribution. % % The GNU Texinfo home page is https://www.gnu.org/software/texinfo. \message{Loading texinfo [version \texinfoversion]:} % If in a .fmt file, print the version number % and turn on active characters that we couldn't do earlier because % they might have appeared in the input file name. \everyjob{\message{[Texinfo version \texinfoversion]}% \catcode`+=\active \catcode`\_=\active} % LaTeX's \typeout. This ensures that the messages it is used for % are identical in format to the corresponding ones from latex/pdflatex. \def\typeout{\immediate\write17}% \chardef\other=12 % We never want plain's \outer definition of \+ in Texinfo. % For @tex, we can use \tabalign. \let\+ = \relax % Save some plain tex macros whose names we will redefine. \let\ptexb=\b \let\ptexbullet=\bullet \let\ptexc=\c \let\ptexcomma=\, \let\ptexdot=\. \let\ptexdots=\dots \let\ptexend=\end \let\ptexequiv=\equiv \let\ptexexclam=\! \let\ptexfootnote=\footnote \let\ptexgtr=> \let\ptexhat=^ \let\ptexi=\i \let\ptexindent=\indent \let\ptexinsert=\insert \let\ptexlbrace=\{ \let\ptexless=< \let\ptexnewwrite\newwrite \let\ptexnoindent=\noindent \let\ptexplus=+ \let\ptexraggedright=\raggedright \let\ptexrbrace=\} \let\ptexslash=\/ \let\ptexsp=\sp \let\ptexstar=\* \let\ptexsup=\sup \let\ptext=\t \let\ptextop=\top {\catcode`\'=\active \global\let\ptexquoteright'}% active in plain's math mode % If this character appears in an error message or help string, it % starts a new line in the output. \newlinechar = `^^J % Use TeX 3.0's \inputlineno to get the line number, for better error % messages, but if we're using an old version of TeX, don't do anything. % \ifx\inputlineno\thisisundefined \let\linenumber = \empty % Pre-3.0. \else \def\linenumber{l.\the\inputlineno:\space} \fi % Set up fixed words for English if not already set. \ifx\putwordAppendix\undefined \gdef\putwordAppendix{Appendix}\fi \ifx\putwordChapter\undefined \gdef\putwordChapter{Chapter}\fi \ifx\putworderror\undefined \gdef\putworderror{error}\fi \ifx\putwordfile\undefined \gdef\putwordfile{file}\fi \ifx\putwordin\undefined \gdef\putwordin{in}\fi \ifx\putwordIndexIsEmpty\undefined \gdef\putwordIndexIsEmpty{(Index is empty)}\fi \ifx\putwordIndexNonexistent\undefined \gdef\putwordIndexNonexistent{(Index is nonexistent)}\fi \ifx\putwordInfo\undefined \gdef\putwordInfo{Info}\fi \ifx\putwordInstanceVariableof\undefined \gdef\putwordInstanceVariableof{Instance Variable of}\fi \ifx\putwordMethodon\undefined \gdef\putwordMethodon{Method on}\fi \ifx\putwordNoTitle\undefined \gdef\putwordNoTitle{No Title}\fi \ifx\putwordof\undefined \gdef\putwordof{of}\fi \ifx\putwordon\undefined \gdef\putwordon{on}\fi \ifx\putwordpage\undefined \gdef\putwordpage{page}\fi \ifx\putwordsection\undefined \gdef\putwordsection{section}\fi \ifx\putwordSection\undefined \gdef\putwordSection{Section}\fi \ifx\putwordsee\undefined \gdef\putwordsee{see}\fi \ifx\putwordSee\undefined \gdef\putwordSee{See}\fi \ifx\putwordShortTOC\undefined \gdef\putwordShortTOC{Short Contents}\fi \ifx\putwordTOC\undefined \gdef\putwordTOC{Table of Contents}\fi % \ifx\putwordMJan\undefined \gdef\putwordMJan{January}\fi \ifx\putwordMFeb\undefined \gdef\putwordMFeb{February}\fi \ifx\putwordMMar\undefined \gdef\putwordMMar{March}\fi \ifx\putwordMApr\undefined \gdef\putwordMApr{April}\fi \ifx\putwordMMay\undefined \gdef\putwordMMay{May}\fi \ifx\putwordMJun\undefined \gdef\putwordMJun{June}\fi \ifx\putwordMJul\undefined \gdef\putwordMJul{July}\fi \ifx\putwordMAug\undefined \gdef\putwordMAug{August}\fi \ifx\putwordMSep\undefined \gdef\putwordMSep{September}\fi \ifx\putwordMOct\undefined \gdef\putwordMOct{October}\fi \ifx\putwordMNov\undefined \gdef\putwordMNov{November}\fi \ifx\putwordMDec\undefined \gdef\putwordMDec{December}\fi % \ifx\putwordDefmac\undefined \gdef\putwordDefmac{Macro}\fi \ifx\putwordDefspec\undefined \gdef\putwordDefspec{Special Form}\fi \ifx\putwordDefvar\undefined \gdef\putwordDefvar{Variable}\fi \ifx\putwordDefopt\undefined \gdef\putwordDefopt{User Option}\fi \ifx\putwordDeffunc\undefined \gdef\putwordDeffunc{Function}\fi % Give the space character the catcode for a space. \def\spaceisspace{\catcode`\ =10\relax} % Likewise for ^^M, the end of line character. \def\endlineisspace{\catcode13=10\relax} \chardef\dashChar = `\- \chardef\slashChar = `\/ \chardef\underChar = `\_ % Ignore a token. % \def\gobble#1{} % The following is used inside several \edef's. \def\makecsname#1{\expandafter\noexpand\csname#1\endcsname} % Hyphenation fixes. \hyphenation{ Flor-i-da Ghost-script Ghost-view Mac-OS Post-Script ap-pen-dix bit-map bit-maps data-base data-bases eshell fall-ing half-way long-est man-u-script man-u-scripts mini-buf-fer mini-buf-fers over-view par-a-digm par-a-digms rath-er rec-tan-gu-lar ro-bot-ics se-vere-ly set-up spa-ces spell-ing spell-ings stand-alone strong-est time-stamp time-stamps which-ever white-space wide-spread wrap-around } % Sometimes it is convenient to have everything in the transcript file % and nothing on the terminal. We don't just call \tracingall here, % since that produces some useless output on the terminal. We also make % some effort to order the tracing commands to reduce output in the log % file; cf. trace.sty in LaTeX. % \def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}% \def\loggingall{% \tracingstats2 \tracingpages1 \tracinglostchars2 % 2 gives us more in etex \tracingparagraphs1 \tracingoutput1 \tracingmacros2 \tracingrestores1 \showboxbreadth\maxdimen \showboxdepth\maxdimen \ifx\eTeXversion\thisisundefined\else % etex gives us more logging \tracingscantokens1 \tracingifs1 \tracinggroups1 \tracingnesting2 \tracingassigns1 \fi \tracingcommands3 % 3 gives us more in etex \errorcontextlines16 }% % @errormsg{MSG}. Do the index-like expansions on MSG, but if things % aren't perfect, it's not the end of the world, being an error message, % after all. % \def\errormsg{\begingroup \indexnofonts \doerrormsg} \def\doerrormsg#1{\errmessage{#1}} % add check for \lastpenalty to plain's definitions. If the last thing % we did was a \nobreak, we don't want to insert more space. % \def\smallbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\smallskipamount \removelastskip\penalty-50\smallskip\fi\fi} \def\medbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\medskipamount \removelastskip\penalty-100\medskip\fi\fi} \def\bigbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\bigskipamount \removelastskip\penalty-200\bigskip\fi\fi} % Output routine % % For a final copy, take out the rectangles % that mark overfull boxes (in case you have decided % that the text looks ok even though it passes the margin). % \def\finalout{\overfullrule=0pt } \newdimen\outerhsize \newdimen\outervsize % set by the paper size routines \newdimen\topandbottommargin \topandbottommargin=.75in % Output a mark which sets \thischapter, \thissection and \thiscolor. % We dump everything together because we only have one kind of mark. % This works because we only use \botmark / \topmark, not \firstmark. % % A mark contains a subexpression of the \ifcase ... \fi construct. % \get*marks macros below extract the needed part using \ifcase. % % Another complication is to let the user choose whether \thischapter % (\thissection) refers to the chapter (section) in effect at the top % of a page, or that at the bottom of a page. % \domark is called twice inside \chapmacro, to add one % mark before the section break, and one after. % In the second call \prevchapterdefs is the same as \currentchapterdefs, % and \prevsectiondefs is the same as \currentsectiondefs. % Then if the page is not broken at the mark, some of the previous % section appears on the page, and we can get the name of this section % from \firstmark for @everyheadingmarks top. % @everyheadingmarks bottom uses \botmark. % % See page 260 of The TeXbook. \def\domark{% \toks0=\expandafter{\currentchapterdefs}% \toks2=\expandafter{\currentsectiondefs}% \toks4=\expandafter{\prevchapterdefs}% \toks6=\expandafter{\prevsectiondefs}% \toks8=\expandafter{\currentcolordefs}% \mark{% \the\toks0 \the\toks2 % 0: marks for @everyheadingmarks top \noexpand\or \the\toks4 \the\toks6 % 1: for @everyheadingmarks bottom \noexpand\else \the\toks8 % 2: color marks }% } % \gettopheadingmarks, \getbottomheadingmarks, % \getcolormarks - extract needed part of mark. % % \topmark doesn't work for the very first chapter (after the title % page or the contents), so we use \firstmark there -- this gets us % the mark with the chapter defs, unless the user sneaks in, e.g., % @setcolor (or @url, or @link, etc.) between @contents and the very % first @chapter. \def\gettopheadingmarks{% \ifcase0\the\savedtopmark\fi \ifx\thischapter\empty \ifcase0\firstmark\fi \fi } \def\getbottomheadingmarks{\ifcase1\botmark\fi} \def\getcolormarks{\ifcase2\the\savedtopmark\fi} % Avoid "undefined control sequence" errors. \def\currentchapterdefs{} \def\currentsectiondefs{} \def\currentsection{} \def\prevchapterdefs{} \def\prevsectiondefs{} \def\currentcolordefs{} % Margin to add to right of even pages, to left of odd pages. \newdimen\bindingoffset \newdimen\normaloffset \newdimen\txipagewidth \newdimen\txipageheight % Main output routine. % \chardef\PAGE = 255 \newtoks\defaultoutput \defaultoutput = {\savetopmark\onepageout{\pagecontents\PAGE}} \output=\expandafter{\the\defaultoutput} \newbox\headlinebox \newbox\footlinebox % When outputting the double column layout for indices, an output routine % is run several times, which hides the original value of \topmark. This % can lead to a page heading being output and duplicating the chapter heading % of the index. Hence, save the contents of \topmark at the beginning of % the output routine. The saved contents are valid until we actually % \shipout a page. % % (We used to run a short output routine to actually set \topmark and % \firstmark to the right values, but if this was called with an empty page % containing whatsits for writing index entries, the whatsits would be thrown % away and the index auxiliary file would remain empty.) % \newtoks\savedtopmark \newif\iftopmarksaved \topmarksavedtrue \def\savetopmark{% \iftopmarksaved\else \global\savedtopmark=\expandafter{\topmark}% \global\topmarksavedtrue \fi } % \onepageout takes a vbox as an argument. % \shipout a vbox for a single page, adding an optional header, footer % and footnote. This also causes index entries for this page to be written % to the auxiliary files. % \def\onepageout#1{% \hoffset=\normaloffset % \ifodd\pageno \advance\hoffset by \bindingoffset \else \advance\hoffset by -\bindingoffset\fi % % Retrieve the information for the headings from the marks in the page, % and call Plain TeX's \makeheadline and \makefootline, which use the % values in \headline and \footline. % % This is used to check if we are on the first page of a chapter. \ifcase1\the\savedtopmark\fi \let\prevchaptername\thischaptername \ifcase0\firstmark\fi \let\curchaptername\thischaptername % \ifodd\pageno \getoddheadingmarks \else \getevenheadingmarks \fi % \ifx\curchaptername\prevchaptername \let\thischapterheading\thischapter \else % \thischapterheading is the same as \thischapter except it is blank % for the first page of a chapter. This is to prevent the chapter name % being shown twice. \def\thischapterheading{}% \fi % % Common context changes for both heading and footing. % Do this outside of the \shipout so @code etc. will be expanded in % the headline as they should be, not taken literally (outputting ''code). \def\commonheadfootline{\let\hsize=\txipagewidth \texinfochars} % \global\setbox\headlinebox = \vbox{\commonheadfootline \makeheadline}% % \ifodd\pageno \getoddfootingmarks \else \getevenfootingmarks \fi \global\setbox\footlinebox = \vbox{\commonheadfootline \makefootline}% % {% % Set context for writing to auxiliary files like index files. % Have to do this stuff outside the \shipout because we want it to % take effect in \write's, yet the group defined by the \vbox ends % before the \shipout runs. % \atdummies % don't expand commands in the output. \turnoffactive \shipout\vbox{% % Do this early so pdf references go to the beginning of the page. \ifpdfmakepagedest \pdfdest name{\the\pageno} xyz\fi % \unvbox\headlinebox \pagebody{#1}% \ifdim\ht\footlinebox > 0pt % Only leave this space if the footline is nonempty. % (We lessened \vsize for it in \oddfootingyyy.) % The \baselineskip=24pt in plain's \makefootline has no effect. \vskip 24pt \unvbox\footlinebox \fi % }% }% \global\topmarksavedfalse \advancepageno \ifnum\outputpenalty>-20000 \else\dosupereject\fi } \newinsert\margin \dimen\margin=\maxdimen % Main part of page, including any footnotes \def\pagebody#1{\vbox to\txipageheight{\boxmaxdepth=\maxdepth #1}} {\catcode`\@ =11 \gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi % marginal hacks, juha@viisa.uucp (Juha Takala) \ifvoid\margin\else % marginal info is present \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi \dimen@=\dp#1\relax \unvbox#1\relax \ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi \ifr@ggedbottom \kern-\dimen@ \vfil \fi} } % Argument parsing % Parse an argument, then pass it to #1. The argument is the rest of % the input line (except we remove a trailing comment). #1 should be a % macro which expects an ordinary undelimited TeX argument. % For example, \def\foo{\parsearg\fooxxx}. % \def\parsearg{\parseargusing{}} \def\parseargusing#1#2{% \def\argtorun{#2}% \begingroup \obeylines \spaceisspace #1% \parseargline\empty% Insert the \empty token, see \finishparsearg below. } {\obeylines % \gdef\parseargline#1^^M{% \endgroup % End of the group started in \parsearg. \argremovecomment #1\comment\ArgTerm% }% } % First remove any @comment, then any @c comment. Pass the result on to % \argcheckspaces. \def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm} \def\argremovec#1\c#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm} % Each occurrence of `\^^M' or `\^^M' is replaced by a single space. % % \argremovec might leave us with trailing space, e.g., % @end itemize @c foo % This space token undergoes the same procedure and is eventually removed % by \finishparsearg. % \def\argcheckspaces#1\^^M{\argcheckspacesX#1\^^M \^^M} \def\argcheckspacesX#1 \^^M{\argcheckspacesY#1\^^M} \def\argcheckspacesY#1\^^M#2\^^M#3\ArgTerm{% \def\temp{#3}% \ifx\temp\empty % Do not use \next, perhaps the caller of \parsearg uses it; reuse \temp: \let\temp\finishparsearg \else \let\temp\argcheckspaces \fi % Put the space token in: \temp#1 #3\ArgTerm } % If a _delimited_ argument is enclosed in braces, they get stripped; so % to get _exactly_ the rest of the line, we had to prevent such situation. % We prepended an \empty token at the very beginning and we expand it now, % just before passing the control to \argtorun. % (Similarly, we have to think about #3 of \argcheckspacesY above: it is % either the null string, or it ends with \^^M---thus there is no danger % that a pair of braces would be stripped. % % But first, we have to remove the trailing space token. % \def\finishparsearg#1 \ArgTerm{\expandafter\argtorun\expandafter{#1}} % \parseargdef - define a command taking an argument on the line % % \parseargdef\foo{...} % is roughly equivalent to % \def\foo{\parsearg\Xfoo} % \def\Xfoo#1{...} \def\parseargdef#1{% \expandafter \doparseargdef \csname\string#1\endcsname #1% } \def\doparseargdef#1#2{% \def#2{\parsearg#1}% \def#1##1% } % Several utility definitions with active space: { \obeyspaces \gdef\obeyedspace{ } % Make each space character in the input produce a normal interword % space in the output. Don't allow a line break at this space, as this % is used only in environments like @example, where each line of input % should produce a line of output anyway. % \gdef\sepspaces{\obeyspaces\let =\tie} % If an index command is used in an @example environment, any spaces % therein should become regular spaces in the raw index file, not the % expansion of \tie (\leavevmode \penalty \@M \ ). \gdef\unsepspaces{\let =\space} } \def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next} % Define the framework for environments in texinfo.tex. It's used like this: % % \envdef\foo{...} % \def\Efoo{...} % % It's the responsibility of \envdef to insert \begingroup before the % actual body; @end closes the group after calling \Efoo. \envdef also % defines \thisenv, so the current environment is known; @end checks % whether the environment name matches. The \checkenv macro can also be % used to check whether the current environment is the one expected. % % Non-false conditionals (@iftex, @ifset) don't fit into this, so they % are not treated as environments; they don't open a group. (The % implementation of @end takes care not to call \endgroup in this % special case.) % At run-time, environments start with this: \def\startenvironment#1{\begingroup\def\thisenv{#1}} % initialize \let\thisenv\empty % ... but they get defined via ``\envdef\foo{...}'': \long\def\envdef#1#2{\def#1{\startenvironment#1#2}} \def\envparseargdef#1#2{\parseargdef#1{\startenvironment#1#2}} % Check whether we're in the right environment: \def\checkenv#1{% \def\temp{#1}% \ifx\thisenv\temp \else \badenverr \fi } % Environment mismatch, #1 expected: \def\badenverr{% \errhelp = \EMsimple \errmessage{This command can appear only \inenvironment\temp, not \inenvironment\thisenv}% } \def\inenvironment#1{% \ifx#1\empty outside of any environment% \else in environment \expandafter\string#1% \fi } % @end foo executes the definition of \Efoo. % But first, it executes a specialized version of \checkenv % \parseargdef\end{% \if 1\csname iscond.#1\endcsname \else % The general wording of \badenverr may not be ideal. \expandafter\checkenv\csname#1\endcsname \csname E#1\endcsname \endgroup \fi } \newhelp\EMsimple{Press RETURN to continue.} % Be sure we're in horizontal mode when doing a tie, since we make space % equivalent to this in @example-like environments. Otherwise, a space % at the beginning of a line will start with \penalty -- and % since \penalty is valid in vertical mode, we'd end up putting the % penalty on the vertical list instead of in the new paragraph. {\catcode`@ = 11 % Avoid using \@M directly, because that causes trouble % if the definition is written into an index file. \global\let\tiepenalty = \@M \gdef\tie{\leavevmode\penalty\tiepenalty\ } } % @: forces normal size whitespace following. \def\:{\spacefactor=1000 } % @* forces a line break. \def\*{\unskip\hfil\break\hbox{}\ignorespaces} % @/ allows a line break. \let\/=\allowbreak % @. is an end-of-sentence period. \def\.{.\spacefactor=\endofsentencespacefactor\space} % @! is an end-of-sentence bang. \def\!{!\spacefactor=\endofsentencespacefactor\space} % @? is an end-of-sentence query. \def\?{?\spacefactor=\endofsentencespacefactor\space} % @frenchspacing on|off says whether to put extra space after punctuation. % \def\onword{on} \def\offword{off} % \parseargdef\frenchspacing{% \def\temp{#1}% \ifx\temp\onword \plainfrenchspacing \else\ifx\temp\offword \plainnonfrenchspacing \else \errhelp = \EMsimple \errmessage{Unknown @frenchspacing option `\temp', must be on|off}% \fi\fi } % @w prevents a word break. Without the \leavevmode, @w at the % beginning of a paragraph, when TeX is still in vertical mode, would % produce a whole line of output instead of starting the paragraph. \def\w#1{\leavevmode\hbox{#1}} % @group ... @end group forces ... to be all on one page, by enclosing % it in a TeX vbox. We use \vtop instead of \vbox to construct the box % to keep its height that of a normal line. According to the rules for % \topskip (p.114 of the TeXbook), the glue inserted is % max (\topskip - \ht (first item), 0). If that height is large, % therefore, no glue is inserted, and the space between the headline and % the text is small, which looks bad. % % Another complication is that the group might be very large. This can % cause the glue on the previous page to be unduly stretched, because it % does not have much material. In this case, it's better to add an % explicit \vfill so that the extra space is at the bottom. The % threshold for doing this is if the group is more than \vfilllimit % percent of a page (\vfilllimit can be changed inside of @tex). % \newbox\groupbox \def\vfilllimit{0.7} % \envdef\group{% \ifnum\catcode`\^^M=\active \else \errhelp = \groupinvalidhelp \errmessage{@group invalid in context where filling is enabled}% \fi \startsavinginserts % \setbox\groupbox = \vtop\bgroup % Do @comment since we are called inside an environment such as % @example, where each end-of-line in the input causes an % end-of-line in the output. We don't want the end-of-line after % the `@group' to put extra space in the output. Since @group % should appear on a line by itself (according to the Texinfo % manual), we don't worry about eating any user text. \comment } % % The \vtop produces a box with normal height and large depth; thus, TeX puts % \baselineskip glue before it, and (when the next line of text is done) % \lineskip glue after it. Thus, space below is not quite equal to space % above. But it's pretty close. \def\Egroup{% % To get correct interline space between the last line of the group % and the first line afterwards, we have to propagate \prevdepth. \endgraf % Not \par, as it may have been set to \lisppar. \global\dimen1 = \prevdepth \egroup % End the \vtop. \addgroupbox \prevdepth = \dimen1 \checkinserts } \def\addgroupbox{ % \dimen0 is the vertical size of the group's box. \dimen0 = \ht\groupbox \advance\dimen0 by \dp\groupbox % \dimen2 is how much space is left on the page (more or less). \dimen2 = \txipageheight \advance\dimen2 by -\pagetotal % if the group doesn't fit on the current page, and it's a big big % group, force a page break. \ifdim \dimen0 > \dimen2 \ifdim \pagetotal < \vfilllimit\txipageheight \page \fi \fi \box\groupbox } % % TeX puts in an \escapechar (i.e., `@') at the beginning of the help % message, so this ends up printing `@group can only ...'. % \newhelp\groupinvalidhelp{% group can only be used in environments such as @example,^^J% where each line of input produces a line of output.} % @need space-in-mils % forces a page break if there is not space-in-mils remaining. \newdimen\mil \mil=0.001in \parseargdef\need{% % Ensure vertical mode, so we don't make a big box in the middle of a % paragraph. \par % % If the @need value is less than one line space, it's useless. \dimen0 = #1\mil \dimen2 = \ht\strutbox \advance\dimen2 by \dp\strutbox \ifdim\dimen0 > \dimen2 % % Do a \strut just to make the height of this box be normal, so the % normal leading is inserted relative to the preceding line. % And a page break here is fine. \vtop to #1\mil{\strut\vfil}% % % TeX does not even consider page breaks if a penalty added to the % main vertical list is 10000 or more. But in order to see if the % empty box we just added fits on the page, we must make it consider % page breaks. On the other hand, we don't want to actually break the % page after the empty box. So we use a penalty of 9999. % % There is an extremely small chance that TeX will actually break the % page at this \penalty, if there are no other feasible breakpoints in % sight. (If the user is using lots of big @group commands, which % almost-but-not-quite fill up a page, TeX will have a hard time doing % good page breaking, for example.) However, I could not construct an % example where a page broke at this \penalty; if it happens in a real % document, then we can reconsider our strategy. \penalty9999 % % Back up by the size of the box, whether we did a page break or not. \kern -#1\mil % % Do not allow a page break right after this kern. \nobreak \fi } % @br forces paragraph break (and is undocumented). \let\br = \par % @page forces the start of a new page. % \def\page{\par\vfill\supereject} % @exdent text.... % outputs text on separate line in roman font, starting at standard page margin % This records the amount of indent in the innermost environment. % That's how much \exdent should take out. \newskip\exdentamount % This defn is used inside fill environments such as @defun. \parseargdef\exdent{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break} % This defn is used inside nofill environments such as @example. \parseargdef\nofillexdent{{\advance \leftskip by -\exdentamount \leftline{\hskip\leftskip{\rm#1}}}} % @inmargin{WHICH}{TEXT} puts TEXT in the WHICH margin next to the current % paragraph. For more general purposes, use the \margin insertion % class. WHICH is `l' or `r'. Not documented, written for gawk manual. % \newskip\inmarginspacing \inmarginspacing=1cm \def\strutdepth{\dp\strutbox} % \def\doinmargin#1#2{\strut\vadjust{% \nobreak \kern-\strutdepth \vtop to \strutdepth{% \baselineskip=\strutdepth \vss % if you have multiple lines of stuff to put here, you'll need to % make the vbox yourself of the appropriate size. \ifx#1l% \llap{\ignorespaces #2\hskip\inmarginspacing}% \else \rlap{\hskip\hsize \hskip\inmarginspacing \ignorespaces #2}% \fi \null }% }} \def\inleftmargin{\doinmargin l} \def\inrightmargin{\doinmargin r} % % @inmargin{TEXT [, RIGHT-TEXT]} % (if RIGHT-TEXT is given, use TEXT for left page, RIGHT-TEXT for right; % else use TEXT for both). % \def\inmargin#1{\parseinmargin #1,,\finish} \def\parseinmargin#1,#2,#3\finish{% not perfect, but better than nothing. \setbox0 = \hbox{\ignorespaces #2}% \ifdim\wd0 > 0pt \def\lefttext{#1}% have both texts \def\righttext{#2}% \else \def\lefttext{#1}% have only one text \def\righttext{#1}% \fi % \ifodd\pageno \def\temp{\inrightmargin\righttext}% odd page -> outside is right margin \else \def\temp{\inleftmargin\lefttext}% \fi \temp } % @include FILE -- \input text of FILE. % \def\include{\parseargusing\filenamecatcodes\includezzz} \def\includezzz#1{% \pushthisfilestack \def\thisfile{#1}% {% \makevalueexpandable % we want to expand any @value in FILE. \turnoffactive % and allow special characters in the expansion \indexnofonts % Allow `@@' and other weird things in file names. \wlog{texinfo.tex: doing @include of #1^^J}% \edef\temp{\noexpand\input #1 }% % % This trickery is to read FILE outside of a group, in case it makes % definitions, etc. \expandafter }\temp \popthisfilestack } \def\filenamecatcodes{% \catcode`\\=\other \catcode`~=\other \catcode`^=\other \catcode`_=\other \catcode`|=\other \catcode`<=\other \catcode`>=\other \catcode`+=\other \catcode`-=\other \catcode`\`=\other \catcode`\'=\other } \def\pushthisfilestack{% \expandafter\pushthisfilestackX\popthisfilestack\StackTerm } \def\pushthisfilestackX{% \expandafter\pushthisfilestackY\thisfile\StackTerm } \def\pushthisfilestackY #1\StackTerm #2\StackTerm {% \gdef\popthisfilestack{\gdef\thisfile{#1}\gdef\popthisfilestack{#2}}% } \def\popthisfilestack{\errthisfilestackempty} \def\errthisfilestackempty{\errmessage{Internal error: the stack of filenames is empty.}} % \def\thisfile{} % @center line % outputs that line, centered. % \parseargdef\center{% \ifhmode \let\centersub\centerH \else \let\centersub\centerV \fi \centersub{\hfil \ignorespaces#1\unskip \hfil}% \let\centersub\relax % don't let the definition persist, just in case } \def\centerH#1{{% \hfil\break \advance\hsize by -\leftskip \advance\hsize by -\rightskip \line{#1}% \break }} % \newcount\centerpenalty \def\centerV#1{% % The idea here is the same as in \startdefun, \cartouche, etc.: if % @center is the first thing after a section heading, we need to wipe % out the negative parskip inserted by \sectionheading, but still % prevent a page break here. \centerpenalty = \lastpenalty \ifnum\centerpenalty>10000 \vskip\parskip \fi \ifnum\centerpenalty>9999 \penalty\centerpenalty \fi \line{\kern\leftskip #1\kern\rightskip}% } % @sp n outputs n lines of vertical space % \parseargdef\sp{\vskip #1\baselineskip} % @comment ...line which is ignored... % @c is the same as @comment % @ignore ... @end ignore is another way to write a comment \def\c{\begingroup \catcode`\^^M=\active% \catcode`\@=\other \catcode`\{=\other \catcode`\}=\other% \cxxx} {\catcode`\^^M=\active \gdef\cxxx#1^^M{\endgroup}} % \let\comment\c % @paragraphindent NCHARS % We'll use ems for NCHARS, close enough. % NCHARS can also be the word `asis' or `none'. % We cannot feasibly implement @paragraphindent asis, though. % \def\asisword{asis} % no translation, these are keywords \def\noneword{none} % \parseargdef\paragraphindent{% \def\temp{#1}% \ifx\temp\asisword \else \ifx\temp\noneword \defaultparindent = 0pt \else \defaultparindent = #1em \fi \fi \parindent = \defaultparindent } % @exampleindent NCHARS % We'll use ems for NCHARS like @paragraphindent. % It seems @exampleindent asis isn't necessary, but % I preserve it to make it similar to @paragraphindent. \parseargdef\exampleindent{% \def\temp{#1}% \ifx\temp\asisword \else \ifx\temp\noneword \lispnarrowing = 0pt \else \lispnarrowing = #1em \fi \fi } % @firstparagraphindent WORD % If WORD is `none', then suppress indentation of the first paragraph % after a section heading. If WORD is `insert', then do indent at such % paragraphs. % % The paragraph indentation is suppressed or not by calling % \suppressfirstparagraphindent, which the sectioning commands do. % We switch the definition of this back and forth according to WORD. % By default, we suppress indentation. % \def\suppressfirstparagraphindent{\dosuppressfirstparagraphindent} \def\insertword{insert} % \parseargdef\firstparagraphindent{% \def\temp{#1}% \ifx\temp\noneword \let\suppressfirstparagraphindent = \dosuppressfirstparagraphindent \else\ifx\temp\insertword \let\suppressfirstparagraphindent = \relax \else \errhelp = \EMsimple \errmessage{Unknown @firstparagraphindent option `\temp'}% \fi\fi } % Here is how we actually suppress indentation. Redefine \everypar to % \kern backwards by \parindent, and then reset itself to empty. % % We also make \indent itself not actually do anything until the next % paragraph. % \gdef\dosuppressfirstparagraphindent{% \gdef\indent {\restorefirstparagraphindent \indent}% \gdef\noindent{\restorefirstparagraphindent \noindent}% \global\everypar = {\kern -\parindent \restorefirstparagraphindent}% } % \gdef\restorefirstparagraphindent{% \global\let\indent = \ptexindent \global\let\noindent = \ptexnoindent \global\everypar = {}% } % @refill is a no-op. \let\refill=\relax % @setfilename INFO-FILENAME - ignored \let\setfilename=\comment % @bye. \outer\def\bye{\pagealignmacro\tracingstats=1\ptexend} \message{pdf,} % adobe `portable' document format \newcount\tempnum \newcount\lnkcount \newtoks\filename \newcount\filenamelength \newcount\pgn \newtoks\toksA \newtoks\toksB \newtoks\toksC \newtoks\toksD \newbox\boxA \newbox\boxB \newcount\countA \newif\ifpdf \newif\ifpdfmakepagedest % % For LuaTeX % \newif\iftxiuseunicodedestname \txiuseunicodedestnamefalse % For pdfTeX etc. \ifx\luatexversion\thisisundefined \else % Use Unicode destination names \txiuseunicodedestnametrue % Escape PDF strings with converting UTF-16 from UTF-8 \begingroup \catcode`\%=12 \directlua{ function UTF16oct(str) tex.sprint(string.char(0x5c) .. '376' .. string.char(0x5c) .. '377') for c in string.utfvalues(str) do if c < 0x10000 then tex.sprint( string.format(string.char(0x5c) .. string.char(0x25) .. '03o' .. string.char(0x5c) .. string.char(0x25) .. '03o', math.floor(c / 256), math.floor(c % 256))) else c = c - 0x10000 local c_hi = c / 1024 + 0xd800 local c_lo = c % 1024 + 0xdc00 tex.sprint( string.format(string.char(0x5c) .. string.char(0x25) .. '03o' .. string.char(0x5c) .. string.char(0x25) .. '03o' .. string.char(0x5c) .. string.char(0x25) .. '03o' .. string.char(0x5c) .. string.char(0x25) .. '03o', math.floor(c_hi / 256), math.floor(c_hi % 256), math.floor(c_lo / 256), math.floor(c_lo % 256))) end end end } \endgroup \def\pdfescapestrutfsixteen#1{\directlua{UTF16oct('\luaescapestring{#1}')}} % Escape PDF strings without converting \begingroup \directlua{ function PDFescstr(str) for c in string.bytes(str) do if c <= 0x20 or c >= 0x80 or c == 0x28 or c == 0x29 or c == 0x5c then tex.sprint(-2, string.format(string.char(0x5c) .. string.char(0x25) .. '03o', c)) else tex.sprint(-2, string.char(c)) end end end } % The -2 in the arguments here gives all the input to TeX catcode 12 % (other) or 10 (space), preventing undefined control sequence errors. See % https://lists.gnu.org/archive/html/bug-texinfo/2019-08/msg00031.html % \endgroup \def\pdfescapestring#1{\directlua{PDFescstr('\luaescapestring{#1}')}} \ifnum\luatexversion>84 % For LuaTeX >= 0.85 \def\pdfdest{\pdfextension dest} \let\pdfoutput\outputmode \def\pdfliteral{\pdfextension literal} \def\pdfcatalog{\pdfextension catalog} \def\pdftexversion{\numexpr\pdffeedback version\relax} \let\pdfximage\saveimageresource \let\pdfrefximage\useimageresource \let\pdflastximage\lastsavedimageresourceindex \def\pdfendlink{\pdfextension endlink\relax} \def\pdfoutline{\pdfextension outline} \def\pdfstartlink{\pdfextension startlink} \def\pdffontattr{\pdfextension fontattr} \def\pdfobj{\pdfextension obj} \def\pdflastobj{\numexpr\pdffeedback lastobj\relax} \let\pdfpagewidth\pagewidth \let\pdfpageheight\pageheight \edef\pdfhorigin{\pdfvariable horigin} \edef\pdfvorigin{\pdfvariable vorigin} \fi \fi % when pdftex is run in dvi mode, \pdfoutput is defined (so \pdfoutput=1 % can be set). So we test for \relax and 0 as well as being undefined. \ifx\pdfoutput\thisisundefined \else \ifx\pdfoutput\relax \else \ifcase\pdfoutput \else \pdftrue \fi \fi \fi \newif\ifpdforxetex \pdforxetexfalse \ifpdf \pdforxetextrue \fi \ifx\XeTeXrevision\thisisundefined\else \pdforxetextrue \fi % PDF uses PostScript string constants for the names of xref targets, % for display in the outlines, and in other places. Thus, we have to % double any backslashes. Otherwise, a name like "\node" will be % interpreted as a newline (\n), followed by o, d, e. Not good. % % See http://www.ntg.nl/pipermail/ntg-pdftex/2004-July/000654.html and % related messages. The final outcome is that it is up to the TeX user % to double the backslashes and otherwise make the string valid, so % that's what we do. pdftex 1.30.0 (ca.2005) introduced a primitive to % do this reliably, so we use it. % #1 is a control sequence in which to do the replacements, % which we \xdef. \def\txiescapepdf#1{% \ifx\pdfescapestring\thisisundefined % No primitive available; should we give a warning or log? % Many times it won't matter. \xdef#1{#1}% \else % The expandable \pdfescapestring primitive escapes parentheses, % backslashes, and other special chars. \xdef#1{\pdfescapestring{#1}}% \fi } \def\txiescapepdfutfsixteen#1{% \ifx\pdfescapestrutfsixteen\thisisundefined % No UTF-16 converting macro available. \txiescapepdf{#1}% \else \xdef#1{\pdfescapestrutfsixteen{#1}}% \fi } \newhelp\nopdfimagehelp{Texinfo supports .png, .jpg, .jpeg, and .pdf images with PDF output, and none of those formats could be found. (.eps cannot be supported due to the design of the PDF format; use regular TeX (DVI output) for that.)} \ifpdf % % Color manipulation macros using ideas from pdfcolor.tex, % except using rgb instead of cmyk; the latter is said to render as a % very dark gray on-screen and a very dark halftone in print, instead % of actual black. The dark red here is dark enough to print on paper as % nearly black, but still distinguishable for online viewing. We use % black by default, though. \def\rgbDarkRed{0.50 0.09 0.12} \def\rgbBlack{0 0 0} % % rg sets the color for filling (usual text, etc.); % RG sets the color for stroking (thin rules, e.g., normal _'s). \def\pdfsetcolor#1{\pdfliteral{#1 rg #1 RG}} % % Set color, and create a mark which defines \thiscolor accordingly, % so that \makeheadline knows which color to restore. \def\setcolor#1{% \xdef\currentcolordefs{\gdef\noexpand\thiscolor{#1}}% \domark \pdfsetcolor{#1}% } % \def\maincolor{\rgbBlack} \pdfsetcolor{\maincolor} \edef\thiscolor{\maincolor} \def\currentcolordefs{} % \def\makefootline{% \baselineskip24pt \line{\pdfsetcolor{\maincolor}\the\footline}% } % \def\makeheadline{% \vbox to 0pt{% \vskip-22.5pt \line{% \vbox to8.5pt{}% % Extract \thiscolor definition from the marks. \getcolormarks % Typeset the headline with \maincolor, then restore the color. \pdfsetcolor{\maincolor}\the\headline\pdfsetcolor{\thiscolor}% }% \vss }% \nointerlineskip } % % \pdfcatalog{/PageMode /UseOutlines} % % #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto). \def\dopdfimage#1#2#3{% \def\pdfimagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}% \def\pdfimageheight{#3}\setbox2 = \hbox{\ignorespaces #3}% % % pdftex (and the PDF format) support .pdf, .png, .jpg (among % others). Let's try in that order, PDF first since if % someone has a scalable image, presumably better to use that than a % bitmap. \let\pdfimgext=\empty \begingroup \openin 1 #1.pdf \ifeof 1 \openin 1 #1.PDF \ifeof 1 \openin 1 #1.png \ifeof 1 \openin 1 #1.jpg \ifeof 1 \openin 1 #1.jpeg \ifeof 1 \openin 1 #1.JPG \ifeof 1 \errhelp = \nopdfimagehelp \errmessage{Could not find image file #1 for pdf}% \else \gdef\pdfimgext{JPG}% \fi \else \gdef\pdfimgext{jpeg}% \fi \else \gdef\pdfimgext{jpg}% \fi \else \gdef\pdfimgext{png}% \fi \else \gdef\pdfimgext{PDF}% \fi \else \gdef\pdfimgext{pdf}% \fi \closein 1 \endgroup % % without \immediate, ancient pdftex seg faults when the same image is % included twice. (Version 3.14159-pre-1.0-unofficial-20010704.) \ifnum\pdftexversion < 14 \immediate\pdfimage \else \immediate\pdfximage \fi \ifdim \wd0 >0pt width \pdfimagewidth \fi \ifdim \wd2 >0pt height \pdfimageheight \fi \ifnum\pdftexversion<13 #1.\pdfimgext \else {#1.\pdfimgext}% \fi \ifnum\pdftexversion < 14 \else \pdfrefximage \pdflastximage \fi} % \def\setpdfdestname#1{{% % We have to set dummies so commands such as @code, and characters % such as \, aren't expanded when present in a section title. \indexnofonts \makevalueexpandable \turnoffactive \iftxiuseunicodedestname \ifx \declaredencoding \latone % Pass through Latin-1 characters. % LuaTeX with byte wise I/O converts Latin-1 characters to Unicode. \else \ifx \declaredencoding \utfeight % Pass through Unicode characters. \else % Use ASCII approximations in destination names. \passthroughcharsfalse \fi \fi \else % Use ASCII approximations in destination names. \passthroughcharsfalse \fi \def\pdfdestname{#1}% \txiescapepdf\pdfdestname }} % \def\setpdfoutlinetext#1{{% \indexnofonts \makevalueexpandable \turnoffactive \ifx \declaredencoding \latone % The PDF format can use an extended form of Latin-1 in bookmark % strings. See Appendix D of the PDF Reference, Sixth Edition, for % the "PDFDocEncoding". \passthroughcharstrue % Pass through Latin-1 characters. % LuaTeX: Convert to Unicode % pdfTeX: Use Latin-1 as PDFDocEncoding \def\pdfoutlinetext{#1}% \else \ifx \declaredencoding \utfeight \ifx\luatexversion\thisisundefined % For pdfTeX with UTF-8. % TODO: the PDF format can use UTF-16 in bookmark strings, % but the code for this isn't done yet. % Use ASCII approximations. \passthroughcharsfalse \def\pdfoutlinetext{#1}% \else % For LuaTeX with UTF-8. % Pass through Unicode characters for title texts. \passthroughcharstrue \def\pdfoutlinetext{#1}% \fi \else % For non-Latin-1 or non-UTF-8 encodings. % Use ASCII approximations. \passthroughcharsfalse \def\pdfoutlinetext{#1}% \fi \fi % LuaTeX: Convert to UTF-16 % pdfTeX: Use Latin-1 as PDFDocEncoding \txiescapepdfutfsixteen\pdfoutlinetext }} % \def\pdfmkdest#1{% \setpdfdestname{#1}% \safewhatsit{\pdfdest name{\pdfdestname} xyz}% } % % used to mark target names; must be expandable. \def\pdfmkpgn#1{#1} % % by default, use black for everything. \def\urlcolor{\rgbBlack} \def\linkcolor{\rgbBlack} \def\endlink{\setcolor{\maincolor}\pdfendlink} % % Adding outlines to PDF; macros for calculating structure of outlines % come from Petr Olsak \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0% \else \csname#1\endcsname \fi} \def\advancenumber#1{\tempnum=\expnumber{#1}\relax \advance\tempnum by 1 \expandafter\xdef\csname#1\endcsname{\the\tempnum}} % % #1 is the section text, which is what will be displayed in the % outline by the pdf viewer. #2 is the pdf expression for the number % of subentries (or empty, for subsubsections). #3 is the node text, % which might be empty if this toc entry had no corresponding node. % #4 is the page number % \def\dopdfoutline#1#2#3#4{% % Generate a link to the node text if that exists; else, use the % page number. We could generate a destination for the section % text in the case where a section has no node, but it doesn't % seem worth the trouble, since most documents are normally structured. \setpdfoutlinetext{#1} \setpdfdestname{#3} \ifx\pdfdestname\empty \def\pdfdestname{#4}% \fi % \pdfoutline goto name{\pdfmkpgn{\pdfdestname}}#2{\pdfoutlinetext}% } % \def\pdfmakeoutlines{% \begingroup % Read toc silently, to get counts of subentries for \pdfoutline. \def\partentry##1##2##3##4{}% ignore parts in the outlines \def\numchapentry##1##2##3##4{% \def\thischapnum{##2}% \def\thissecnum{0}% \def\thissubsecnum{0}% }% \def\numsecentry##1##2##3##4{% \advancenumber{chap\thischapnum}% \def\thissecnum{##2}% \def\thissubsecnum{0}% }% \def\numsubsecentry##1##2##3##4{% \advancenumber{sec\thissecnum}% \def\thissubsecnum{##2}% }% \def\numsubsubsecentry##1##2##3##4{% \advancenumber{subsec\thissubsecnum}% }% \def\thischapnum{0}% \def\thissecnum{0}% \def\thissubsecnum{0}% % % use \def rather than \let here because we redefine \chapentry et % al. a second time, below. \def\appentry{\numchapentry}% \def\appsecentry{\numsecentry}% \def\appsubsecentry{\numsubsecentry}% \def\appsubsubsecentry{\numsubsubsecentry}% \def\unnchapentry{\numchapentry}% \def\unnsecentry{\numsecentry}% \def\unnsubsecentry{\numsubsecentry}% \def\unnsubsubsecentry{\numsubsubsecentry}% \readdatafile{toc}% % % Read toc second time, this time actually producing the outlines. % The `-' means take the \expnumber as the absolute number of % subentries, which we calculated on our first read of the .toc above. % % We use the node names as the destinations. \def\numchapentry##1##2##3##4{% \dopdfoutline{##1}{count-\expnumber{chap##2}}{##3}{##4}}% \def\numsecentry##1##2##3##4{% \dopdfoutline{##1}{count-\expnumber{sec##2}}{##3}{##4}}% \def\numsubsecentry##1##2##3##4{% \dopdfoutline{##1}{count-\expnumber{subsec##2}}{##3}{##4}}% \def\numsubsubsecentry##1##2##3##4{% count is always zero \dopdfoutline{##1}{}{##3}{##4}}% % % PDF outlines are displayed using system fonts, instead of % document fonts. Therefore we cannot use special characters, % since the encoding is unknown. For example, the eogonek from % Latin 2 (0xea) gets translated to a | character. Info from % Staszek Wawrykiewicz, 19 Jan 2004 04:09:24 +0100. % % TODO this right, we have to translate 8-bit characters to % their "best" equivalent, based on the @documentencoding. Too % much work for too little return. Just use the ASCII equivalents % we use for the index sort strings. % \indexnofonts \setupdatafile % We can have normal brace characters in the PDF outlines, unlike % Texinfo index files. So set that up. \def\{{\lbracecharliteral}% \def\}{\rbracecharliteral}% \catcode`\\=\active \otherbackslash \input \tocreadfilename \endgroup } {\catcode`[=1 \catcode`]=2 \catcode`{=\other \catcode`}=\other \gdef\lbracecharliteral[{]% \gdef\rbracecharliteral[}]% ] % \def\skipspaces#1{\def\PP{#1}\def\D{|}% \ifx\PP\D\let\nextsp\relax \else\let\nextsp\skipspaces \addtokens{\filename}{\PP}% \advance\filenamelength by 1 \fi \nextsp} \def\getfilename#1{% \filenamelength=0 % If we don't expand the argument now, \skipspaces will get % snagged on things like "@value{foo}". \edef\temp{#1}% \expandafter\skipspaces\temp|\relax } \ifnum\pdftexversion < 14 \let \startlink \pdfannotlink \else \let \startlink \pdfstartlink \fi % make a live url in pdf output. \def\pdfurl#1{% \begingroup % it seems we really need yet another set of dummies; have not % tried to figure out what each command should do in the context % of @url. for now, just make @/ a no-op, that's the only one % people have actually reported a problem with. % \normalturnoffactive \def\@{@}% \let\/=\empty \makevalueexpandable % do we want to go so far as to use \indexnofonts instead of just % special-casing \var here? \def\var##1{##1}% % \leavevmode\setcolor{\urlcolor}% \startlink attr{/Border [0 0 0]}% user{/Subtype /Link /A << /S /URI /URI (#1) >>}% \endgroup} % \pdfgettoks - Surround page numbers in #1 with @pdflink. #1 may % be a simple number, or a list of numbers in the case of an index % entry. \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}} \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks} \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks} \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}} \def\maketoks{% \expandafter\poptoks\the\toksA|ENDTOKS|\relax \ifx\first0\adn0 \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3 \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6 \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9 \else \ifnum0=\countA\else\makelink\fi \ifx\first.\let\next=\done\else \let\next=\maketoks \addtokens{\toksB}{\the\toksD} \ifx\first,\addtokens{\toksB}{\space}\fi \fi \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi \next} \def\makelink{\addtokens{\toksB}% {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0} \def\pdflink#1{% \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{#1}} \setcolor{\linkcolor}#1\endlink} \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st} \else % non-pdf mode \let\pdfmkdest = \gobble \let\pdfurl = \gobble \let\endlink = \relax \let\setcolor = \gobble \let\pdfsetcolor = \gobble \let\pdfmakeoutlines = \relax \fi % \ifx\pdfoutput % % For XeTeX % \ifx\XeTeXrevision\thisisundefined \else % % XeTeX version check % \ifnum\strcmp{\the\XeTeXversion\XeTeXrevision}{0.99996}>-1 % TeX Live 2016 contains XeTeX 0.99996 and xdvipdfmx 20160307. % It can use the `dvipdfmx:config' special (from TeX Live SVN r40941). % For avoiding PDF destination name replacement, we use this special % instead of xdvipdfmx's command line option `-C 0x0010'. \special{dvipdfmx:config C 0x0010} % XeTeX 0.99995+ comes with xdvipdfmx 20160307+. % It can handle Unicode destination names for PDF. \txiuseunicodedestnametrue \else % XeTeX < 0.99996 (TeX Live < 2016) cannot use the % `dvipdfmx:config' special. % So for avoiding PDF destination name replacement, % xdvipdfmx's command line option `-C 0x0010' is necessary. % % XeTeX < 0.99995 can not handle Unicode destination names for PDF % because xdvipdfmx 20150315 has a UTF-16 conversion issue. % It is fixed by xdvipdfmx 20160106 (TeX Live SVN r39753). \txiuseunicodedestnamefalse \fi % % Color support % \def\rgbDarkRed{0.50 0.09 0.12} \def\rgbBlack{0 0 0} % \def\pdfsetcolor#1{\special{pdf:scolor [#1]}} % % Set color, and create a mark which defines \thiscolor accordingly, % so that \makeheadline knows which color to restore. \def\setcolor#1{% \xdef\currentcolordefs{\gdef\noexpand\thiscolor{#1}}% \domark \pdfsetcolor{#1}% } % \def\maincolor{\rgbBlack} \pdfsetcolor{\maincolor} \edef\thiscolor{\maincolor} \def\currentcolordefs{} % \def\makefootline{% \baselineskip24pt \line{\pdfsetcolor{\maincolor}\the\footline}% } % \def\makeheadline{% \vbox to 0pt{% \vskip-22.5pt \line{% \vbox to8.5pt{}% % Extract \thiscolor definition from the marks. \getcolormarks % Typeset the headline with \maincolor, then restore the color. \pdfsetcolor{\maincolor}\the\headline\pdfsetcolor{\thiscolor}% }% \vss }% \nointerlineskip } % % PDF outline support % % Emulate pdfTeX primitive \def\pdfdest name#1 xyz{% \special{pdf:dest (#1) [@thispage /XYZ @xpos @ypos null]}% } % \def\setpdfdestname#1{{% % We have to set dummies so commands such as @code, and characters % such as \, aren't expanded when present in a section title. \indexnofonts \makevalueexpandable \turnoffactive \iftxiuseunicodedestname % Pass through Unicode characters. \else % Use ASCII approximations in destination names. \passthroughcharsfalse \fi \def\pdfdestname{#1}% \txiescapepdf\pdfdestname }} % \def\setpdfoutlinetext#1{{% \turnoffactive % Always use Unicode characters in title texts. \def\pdfoutlinetext{#1}% % For XeTeX, xdvipdfmx converts to UTF-16. % So we do not convert. \txiescapepdf\pdfoutlinetext }} % \def\pdfmkdest#1{% \setpdfdestname{#1}% \safewhatsit{\pdfdest name{\pdfdestname} xyz}% } % % by default, use black for everything. \def\urlcolor{\rgbBlack} \def\linkcolor{\rgbBlack} \def\endlink{\setcolor{\maincolor}\pdfendlink} % \def\dopdfoutline#1#2#3#4{% \setpdfoutlinetext{#1} \setpdfdestname{#3} \ifx\pdfdestname\empty \def\pdfdestname{#4}% \fi % \special{pdf:out [-] #2 << /Title (\pdfoutlinetext) /A << /S /GoTo /D (\pdfdestname) >> >> }% } % \def\pdfmakeoutlines{% \begingroup % % For XeTeX, counts of subentries are not necessary. % Therefore, we read toc only once. % % We use node names as destinations. \def\partentry##1##2##3##4{}% ignore parts in the outlines \def\numchapentry##1##2##3##4{% \dopdfoutline{##1}{1}{##3}{##4}}% \def\numsecentry##1##2##3##4{% \dopdfoutline{##1}{2}{##3}{##4}}% \def\numsubsecentry##1##2##3##4{% \dopdfoutline{##1}{3}{##3}{##4}}% \def\numsubsubsecentry##1##2##3##4{% \dopdfoutline{##1}{4}{##3}{##4}}% % \let\appentry\numchapentry% \let\appsecentry\numsecentry% \let\appsubsecentry\numsubsecentry% \let\appsubsubsecentry\numsubsubsecentry% \let\unnchapentry\numchapentry% \let\unnsecentry\numsecentry% \let\unnsubsecentry\numsubsecentry% \let\unnsubsubsecentry\numsubsubsecentry% % % For XeTeX, xdvipdfmx converts strings to UTF-16. % Therefore, the encoding and the language may not be considered. % \indexnofonts \setupdatafile % We can have normal brace characters in the PDF outlines, unlike % Texinfo index files. So set that up. \def\{{\lbracecharliteral}% \def\}{\rbracecharliteral}% \catcode`\\=\active \otherbackslash \input \tocreadfilename \endgroup } {\catcode`[=1 \catcode`]=2 \catcode`{=\other \catcode`}=\other \gdef\lbracecharliteral[{]% \gdef\rbracecharliteral[}]% ] \special{pdf:docview << /PageMode /UseOutlines >> } % ``\special{pdf:tounicode ...}'' is not necessary % because xdvipdfmx converts strings from UTF-8 to UTF-16 without it. % However, due to a UTF-16 conversion issue of xdvipdfmx 20150315, % ``\special{pdf:dest ...}'' cannot handle non-ASCII strings. % It is fixed by xdvipdfmx 20160106 (TeX Live SVN r39753). % \def\skipspaces#1{\def\PP{#1}\def\D{|}% \ifx\PP\D\let\nextsp\relax \else\let\nextsp\skipspaces \addtokens{\filename}{\PP}% \advance\filenamelength by 1 \fi \nextsp} \def\getfilename#1{% \filenamelength=0 % If we don't expand the argument now, \skipspaces will get % snagged on things like "@value{foo}". \edef\temp{#1}% \expandafter\skipspaces\temp|\relax } % make a live url in pdf output. \def\pdfurl#1{% \begingroup % it seems we really need yet another set of dummies; have not % tried to figure out what each command should do in the context % of @url. for now, just make @/ a no-op, that's the only one % people have actually reported a problem with. % \normalturnoffactive \def\@{@}% \let\/=\empty \makevalueexpandable % do we want to go so far as to use \indexnofonts instead of just % special-casing \var here? \def\var##1{##1}% % \leavevmode\setcolor{\urlcolor}% \special{pdf:bann << /Border [0 0 0] /Subtype /Link /A << /S /URI /URI (#1) >> >>}% \endgroup} \def\endlink{\setcolor{\maincolor}\special{pdf:eann}} \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}} \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks} \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks} \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}} \def\maketoks{% \expandafter\poptoks\the\toksA|ENDTOKS|\relax \ifx\first0\adn0 \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3 \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6 \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9 \else \ifnum0=\countA\else\makelink\fi \ifx\first.\let\next=\done\else \let\next=\maketoks \addtokens{\toksB}{\the\toksD} \ifx\first,\addtokens{\toksB}{\space}\fi \fi \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi \next} \def\makelink{\addtokens{\toksB}% {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0} \def\pdflink#1{% \special{pdf:bann << /Border [0 0 0] /Type /Annot /Subtype /Link /A << /S /GoTo /D (#1) >> >>}% \setcolor{\linkcolor}#1\endlink} \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st} % % % @image support % % #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto). \def\doxeteximage#1#2#3{% \def\xeteximagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}% \def\xeteximageheight{#3}\setbox2 = \hbox{\ignorespaces #3}% % % XeTeX (and the PDF format) supports .pdf, .png, .jpg (among % others). Let's try in that order, PDF first since if % someone has a scalable image, presumably better to use that than a % bitmap. \let\xeteximgext=\empty \begingroup \openin 1 #1.pdf \ifeof 1 \openin 1 #1.PDF \ifeof 1 \openin 1 #1.png \ifeof 1 \openin 1 #1.jpg \ifeof 1 \openin 1 #1.jpeg \ifeof 1 \openin 1 #1.JPG \ifeof 1 \errmessage{Could not find image file #1 for XeTeX}% \else \gdef\xeteximgext{JPG}% \fi \else \gdef\xeteximgext{jpeg}% \fi \else \gdef\xeteximgext{jpg}% \fi \else \gdef\xeteximgext{png}% \fi \else \gdef\xeteximgext{PDF}% \fi \else \gdef\xeteximgext{pdf}% \fi \closein 1 \endgroup % \def\xetexpdfext{pdf}% \ifx\xeteximgext\xetexpdfext \XeTeXpdffile "#1".\xeteximgext "" \else \def\xetexpdfext{PDF}% \ifx\xeteximgext\xetexpdfext \XeTeXpdffile "#1".\xeteximgext "" \else \XeTeXpicfile "#1".\xeteximgext "" \fi \fi \ifdim \wd0 >0pt width \xeteximagewidth \fi \ifdim \wd2 >0pt height \xeteximageheight \fi \relax } \fi % \message{fonts,} % Set the baselineskip to #1, and the lineskip and strut size % correspondingly. There is no deep meaning behind these magic numbers % used as factors; they just match (closely enough) what Knuth defined. % \def\lineskipfactor{.08333} \def\strutheightpercent{.70833} \def\strutdepthpercent {.29167} % % can get a sort of poor man's double spacing by redefining this. \def\baselinefactor{1} % \newdimen\textleading \def\setleading#1{% \dimen0 = #1\relax \normalbaselineskip = \baselinefactor\dimen0 \normallineskip = \lineskipfactor\normalbaselineskip \normalbaselines \setbox\strutbox =\hbox{% \vrule width0pt height\strutheightpercent\baselineskip depth \strutdepthpercent \baselineskip }% } % PDF CMaps. See also LaTeX's t1.cmap. % % do nothing with this by default. \expandafter\let\csname cmapOT1\endcsname\gobble \expandafter\let\csname cmapOT1IT\endcsname\gobble \expandafter\let\csname cmapOT1TT\endcsname\gobble % if we are producing pdf, and we have \pdffontattr, then define cmaps. % (\pdffontattr was introduced many years ago, but people still run % older pdftex's; it's easy to conditionalize, so we do.) \ifpdf \ifx\pdffontattr\thisisundefined \else \begingroup \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap %%DocumentNeededResources: ProcSet (CIDInit) %%IncludeResource: ProcSet (CIDInit) %%BeginResource: CMap (TeX-OT1-0) %%Title: (TeX-OT1-0 TeX OT1 0) %%Version: 1.000 %%EndComments /CIDInit /ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo << /Registry (TeX) /Ordering (OT1) /Supplement 0 >> def /CMapName /TeX-OT1-0 def /CMapType 2 def 1 begincodespacerange <00> <7F> endcodespacerange 8 beginbfrange <00> <01> <0393> <09> <0A> <03A8> <23> <26> <0023> <28> <3B> <0028> <3F> <5B> <003F> <5D> <5E> <005D> <61> <7A> <0061> <7B> <7C> <2013> endbfrange 40 beginbfchar <02> <0398> <03> <039B> <04> <039E> <05> <03A0> <06> <03A3> <07> <03D2> <08> <03A6> <0B> <00660066> <0C> <00660069> <0D> <0066006C> <0E> <006600660069> <0F> <00660066006C> <10> <0131> <11> <0237> <12> <0060> <13> <00B4> <14> <02C7> <15> <02D8> <16> <00AF> <17> <02DA> <18> <00B8> <19> <00DF> <1A> <00E6> <1B> <0153> <1C> <00F8> <1D> <00C6> <1E> <0152> <1F> <00D8> <21> <0021> <22> <201D> <27> <2019> <3C> <00A1> <3D> <003D> <3E> <00BF> <5C> <201C> <5F> <02D9> <60> <2018> <7D> <02DD> <7E> <007E> <7F> <00A8> endbfchar endcmap CMapName currentdict /CMap defineresource pop end end %%EndResource %%EOF }\endgroup \expandafter\edef\csname cmapOT1\endcsname#1{% \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% }% % % \cmapOT1IT \begingroup \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap %%DocumentNeededResources: ProcSet (CIDInit) %%IncludeResource: ProcSet (CIDInit) %%BeginResource: CMap (TeX-OT1IT-0) %%Title: (TeX-OT1IT-0 TeX OT1IT 0) %%Version: 1.000 %%EndComments /CIDInit /ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo << /Registry (TeX) /Ordering (OT1IT) /Supplement 0 >> def /CMapName /TeX-OT1IT-0 def /CMapType 2 def 1 begincodespacerange <00> <7F> endcodespacerange 8 beginbfrange <00> <01> <0393> <09> <0A> <03A8> <25> <26> <0025> <28> <3B> <0028> <3F> <5B> <003F> <5D> <5E> <005D> <61> <7A> <0061> <7B> <7C> <2013> endbfrange 42 beginbfchar <02> <0398> <03> <039B> <04> <039E> <05> <03A0> <06> <03A3> <07> <03D2> <08> <03A6> <0B> <00660066> <0C> <00660069> <0D> <0066006C> <0E> <006600660069> <0F> <00660066006C> <10> <0131> <11> <0237> <12> <0060> <13> <00B4> <14> <02C7> <15> <02D8> <16> <00AF> <17> <02DA> <18> <00B8> <19> <00DF> <1A> <00E6> <1B> <0153> <1C> <00F8> <1D> <00C6> <1E> <0152> <1F> <00D8> <21> <0021> <22> <201D> <23> <0023> <24> <00A3> <27> <2019> <3C> <00A1> <3D> <003D> <3E> <00BF> <5C> <201C> <5F> <02D9> <60> <2018> <7D> <02DD> <7E> <007E> <7F> <00A8> endbfchar endcmap CMapName currentdict /CMap defineresource pop end end %%EndResource %%EOF }\endgroup \expandafter\edef\csname cmapOT1IT\endcsname#1{% \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% }% % % \cmapOT1TT \begingroup \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap %%DocumentNeededResources: ProcSet (CIDInit) %%IncludeResource: ProcSet (CIDInit) %%BeginResource: CMap (TeX-OT1TT-0) %%Title: (TeX-OT1TT-0 TeX OT1TT 0) %%Version: 1.000 %%EndComments /CIDInit /ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo << /Registry (TeX) /Ordering (OT1TT) /Supplement 0 >> def /CMapName /TeX-OT1TT-0 def /CMapType 2 def 1 begincodespacerange <00> <7F> endcodespacerange 5 beginbfrange <00> <01> <0393> <09> <0A> <03A8> <21> <26> <0021> <28> <5F> <0028> <61> <7E> <0061> endbfrange 32 beginbfchar <02> <0398> <03> <039B> <04> <039E> <05> <03A0> <06> <03A3> <07> <03D2> <08> <03A6> <0B> <2191> <0C> <2193> <0D> <0027> <0E> <00A1> <0F> <00BF> <10> <0131> <11> <0237> <12> <0060> <13> <00B4> <14> <02C7> <15> <02D8> <16> <00AF> <17> <02DA> <18> <00B8> <19> <00DF> <1A> <00E6> <1B> <0153> <1C> <00F8> <1D> <00C6> <1E> <0152> <1F> <00D8> <20> <2423> <27> <2019> <60> <2018> <7F> <00A8> endbfchar endcmap CMapName currentdict /CMap defineresource pop end end %%EndResource %%EOF }\endgroup \expandafter\edef\csname cmapOT1TT\endcsname#1{% \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% }% \fi\fi % Set the font macro #1 to the font named \fontprefix#2. % #3 is the font's design size, #4 is a scale factor, #5 is the CMap % encoding (only OT1, OT1IT and OT1TT are allowed, or empty to omit). % Example: % #1 = \textrm % #2 = \rmshape % #3 = 10 % #4 = \mainmagstep % #5 = OT1 % \def\setfont#1#2#3#4#5{% \font#1=\fontprefix#2#3 scaled #4 \csname cmap#5\endcsname#1% } % This is what gets called when #5 of \setfont is empty. \let\cmap\gobble % % (end of cmaps) % Use cm as the default font prefix. % To specify the font prefix, you must define \fontprefix % before you read in texinfo.tex. \ifx\fontprefix\thisisundefined \def\fontprefix{cm} \fi % Support font families that don't use the same naming scheme as CM. \def\rmshape{r} \def\rmbshape{bx} % where the normal face is bold \def\bfshape{b} \def\bxshape{bx} \def\ttshape{tt} \def\ttbshape{tt} \def\ttslshape{sltt} \def\itshape{ti} \def\itbshape{bxti} \def\slshape{sl} \def\slbshape{bxsl} \def\sfshape{ss} \def\sfbshape{ss} \def\scshape{csc} \def\scbshape{csc} % Definitions for a main text size of 11pt. (The default in Texinfo.) % \def\definetextfontsizexi{% % Text fonts (11.2pt, magstep1). \def\textnominalsize{11pt} \edef\mainmagstep{\magstephalf} \setfont\textrm\rmshape{10}{\mainmagstep}{OT1} \setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT} \setfont\textbf\bfshape{10}{\mainmagstep}{OT1} \setfont\textit\itshape{10}{\mainmagstep}{OT1IT} \setfont\textsl\slshape{10}{\mainmagstep}{OT1} \setfont\textsf\sfshape{10}{\mainmagstep}{OT1} \setfont\textsc\scshape{10}{\mainmagstep}{OT1} \setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT} \font\texti=cmmi10 scaled \mainmagstep \font\textsy=cmsy10 scaled \mainmagstep \def\textecsize{1095} % A few fonts for @defun names and args. \setfont\defbf\bfshape{10}{\magstep1}{OT1} \setfont\deftt\ttshape{10}{\magstep1}{OT1TT} \setfont\defsl\slshape{10}{\magstep1}{OT1} \setfont\defttsl\ttslshape{10}{\magstep1}{OT1TT} \def\df{\let\ttfont=\deftt \let\bffont = \defbf \let\ttslfont=\defttsl \let\slfont=\defsl \bf} % Fonts for indices, footnotes, small examples (9pt). \def\smallnominalsize{9pt} \setfont\smallrm\rmshape{9}{1000}{OT1} \setfont\smalltt\ttshape{9}{1000}{OT1TT} \setfont\smallbf\bfshape{10}{900}{OT1} \setfont\smallit\itshape{9}{1000}{OT1IT} \setfont\smallsl\slshape{9}{1000}{OT1} \setfont\smallsf\sfshape{9}{1000}{OT1} \setfont\smallsc\scshape{10}{900}{OT1} \setfont\smallttsl\ttslshape{10}{900}{OT1TT} \font\smalli=cmmi9 \font\smallsy=cmsy9 \def\smallecsize{0900} % Fonts for small examples (8pt). \def\smallernominalsize{8pt} \setfont\smallerrm\rmshape{8}{1000}{OT1} \setfont\smallertt\ttshape{8}{1000}{OT1TT} \setfont\smallerbf\bfshape{10}{800}{OT1} \setfont\smallerit\itshape{8}{1000}{OT1IT} \setfont\smallersl\slshape{8}{1000}{OT1} \setfont\smallersf\sfshape{8}{1000}{OT1} \setfont\smallersc\scshape{10}{800}{OT1} \setfont\smallerttsl\ttslshape{10}{800}{OT1TT} \font\smalleri=cmmi8 \font\smallersy=cmsy8 \def\smallerecsize{0800} % Fonts for math mode superscripts (7pt). \def\sevennominalsize{7pt} \setfont\sevenrm\rmshape{7}{1000}{OT1} \setfont\seventt\ttshape{10}{700}{OT1TT} \setfont\sevenbf\bfshape{10}{700}{OT1} \setfont\sevenit\itshape{7}{1000}{OT1IT} \setfont\sevensl\slshape{10}{700}{OT1} \setfont\sevensf\sfshape{10}{700}{OT1} \setfont\sevensc\scshape{10}{700}{OT1} \setfont\seventtsl\ttslshape{10}{700}{OT1TT} \font\seveni=cmmi7 \font\sevensy=cmsy7 \def\sevenecsize{0700} % Fonts for title page (20.4pt): \def\titlenominalsize{20pt} \setfont\titlerm\rmbshape{12}{\magstep3}{OT1} \setfont\titleit\itbshape{10}{\magstep4}{OT1IT} \setfont\titlesl\slbshape{10}{\magstep4}{OT1} \setfont\titlett\ttbshape{12}{\magstep3}{OT1TT} \setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT} \setfont\titlesf\sfbshape{17}{\magstep1}{OT1} \let\titlebf=\titlerm \setfont\titlesc\scbshape{10}{\magstep4}{OT1} \font\titlei=cmmi12 scaled \magstep3 \font\titlesy=cmsy10 scaled \magstep4 \def\titleecsize{2074} % Chapter (and unnumbered) fonts (17.28pt). \def\chapnominalsize{17pt} \setfont\chaprm\rmbshape{12}{\magstep2}{OT1} \setfont\chapit\itbshape{10}{\magstep3}{OT1IT} \setfont\chapsl\slbshape{10}{\magstep3}{OT1} \setfont\chaptt\ttbshape{12}{\magstep2}{OT1TT} \setfont\chapttsl\ttslshape{10}{\magstep3}{OT1TT} \setfont\chapsf\sfbshape{17}{1000}{OT1} \let\chapbf=\chaprm \setfont\chapsc\scbshape{10}{\magstep3}{OT1} \font\chapi=cmmi12 scaled \magstep2 \font\chapsy=cmsy10 scaled \magstep3 \def\chapecsize{1728} % Section fonts (14.4pt). \def\secnominalsize{14pt} \setfont\secrm\rmbshape{12}{\magstep1}{OT1} \setfont\secrmnotbold\rmshape{12}{\magstep1}{OT1} \setfont\secit\itbshape{10}{\magstep2}{OT1IT} \setfont\secsl\slbshape{10}{\magstep2}{OT1} \setfont\sectt\ttbshape{12}{\magstep1}{OT1TT} \setfont\secttsl\ttslshape{10}{\magstep2}{OT1TT} \setfont\secsf\sfbshape{12}{\magstep1}{OT1} \let\secbf\secrm \setfont\secsc\scbshape{10}{\magstep2}{OT1} \font\seci=cmmi12 scaled \magstep1 \font\secsy=cmsy10 scaled \magstep2 \def\sececsize{1440} % Subsection fonts (13.15pt). \def\ssecnominalsize{13pt} \setfont\ssecrm\rmbshape{12}{\magstephalf}{OT1} \setfont\ssecit\itbshape{10}{1315}{OT1IT} \setfont\ssecsl\slbshape{10}{1315}{OT1} \setfont\ssectt\ttbshape{12}{\magstephalf}{OT1TT} \setfont\ssecttsl\ttslshape{10}{1315}{OT1TT} \setfont\ssecsf\sfbshape{12}{\magstephalf}{OT1} \let\ssecbf\ssecrm \setfont\ssecsc\scbshape{10}{1315}{OT1} \font\sseci=cmmi12 scaled \magstephalf \font\ssecsy=cmsy10 scaled 1315 \def\ssececsize{1200} % Reduced fonts for @acronym in text (10pt). \def\reducednominalsize{10pt} \setfont\reducedrm\rmshape{10}{1000}{OT1} \setfont\reducedtt\ttshape{10}{1000}{OT1TT} \setfont\reducedbf\bfshape{10}{1000}{OT1} \setfont\reducedit\itshape{10}{1000}{OT1IT} \setfont\reducedsl\slshape{10}{1000}{OT1} \setfont\reducedsf\sfshape{10}{1000}{OT1} \setfont\reducedsc\scshape{10}{1000}{OT1} \setfont\reducedttsl\ttslshape{10}{1000}{OT1TT} \font\reducedi=cmmi10 \font\reducedsy=cmsy10 \def\reducedecsize{1000} \textleading = 13.2pt % line spacing for 11pt CM \textfonts % reset the current fonts \rm } % end of 11pt text font size definitions, \definetextfontsizexi % Definitions to make the main text be 10pt Computer Modern, with % section, chapter, etc., sizes following suit. This is for the GNU % Press printing of the Emacs 22 manual. Maybe other manuals in the % future. Used with @smallbook, which sets the leading to 12pt. % \def\definetextfontsizex{% % Text fonts (10pt). \def\textnominalsize{10pt} \edef\mainmagstep{1000} \setfont\textrm\rmshape{10}{\mainmagstep}{OT1} \setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT} \setfont\textbf\bfshape{10}{\mainmagstep}{OT1} \setfont\textit\itshape{10}{\mainmagstep}{OT1IT} \setfont\textsl\slshape{10}{\mainmagstep}{OT1} \setfont\textsf\sfshape{10}{\mainmagstep}{OT1} \setfont\textsc\scshape{10}{\mainmagstep}{OT1} \setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT} \font\texti=cmmi10 scaled \mainmagstep \font\textsy=cmsy10 scaled \mainmagstep \def\textecsize{1000} % A few fonts for @defun names and args. \setfont\defbf\bfshape{10}{\magstephalf}{OT1} \setfont\deftt\ttshape{10}{\magstephalf}{OT1TT} \setfont\defsl\slshape{10}{\magstephalf}{OT1} \setfont\defttsl\ttslshape{10}{\magstephalf}{OT1TT} \def\df{\let\ttfont=\deftt \let\bffont = \defbf \let\slfont=\defsl \let\ttslfont=\defttsl \bf} % Fonts for indices, footnotes, small examples (9pt). \def\smallnominalsize{9pt} \setfont\smallrm\rmshape{9}{1000}{OT1} \setfont\smalltt\ttshape{9}{1000}{OT1TT} \setfont\smallbf\bfshape{10}{900}{OT1} \setfont\smallit\itshape{9}{1000}{OT1IT} \setfont\smallsl\slshape{9}{1000}{OT1} \setfont\smallsf\sfshape{9}{1000}{OT1} \setfont\smallsc\scshape{10}{900}{OT1} \setfont\smallttsl\ttslshape{10}{900}{OT1TT} \font\smalli=cmmi9 \font\smallsy=cmsy9 \def\smallecsize{0900} % Fonts for small examples (8pt). \def\smallernominalsize{8pt} \setfont\smallerrm\rmshape{8}{1000}{OT1} \setfont\smallertt\ttshape{8}{1000}{OT1TT} \setfont\smallerbf\bfshape{10}{800}{OT1} \setfont\smallerit\itshape{8}{1000}{OT1IT} \setfont\smallersl\slshape{8}{1000}{OT1} \setfont\smallersf\sfshape{8}{1000}{OT1} \setfont\smallersc\scshape{10}{800}{OT1} \setfont\smallerttsl\ttslshape{10}{800}{OT1TT} \font\smalleri=cmmi8 \font\smallersy=cmsy8 \def\smallerecsize{0800} % Fonts for math mode superscripts (7pt). \def\sevennominalsize{7pt} \setfont\sevenrm\rmshape{7}{1000}{OT1} \setfont\seventt\ttshape{10}{700}{OT1TT} \setfont\sevenbf\bfshape{10}{700}{OT1} \setfont\sevenit\itshape{7}{1000}{OT1IT} \setfont\sevensl\slshape{10}{700}{OT1} \setfont\sevensf\sfshape{10}{700}{OT1} \setfont\sevensc\scshape{10}{700}{OT1} \setfont\seventtsl\ttslshape{10}{700}{OT1TT} \font\seveni=cmmi7 \font\sevensy=cmsy7 \def\sevenecsize{0700} % Fonts for title page (20.4pt): \def\titlenominalsize{20pt} \setfont\titlerm\rmbshape{12}{\magstep3}{OT1} \setfont\titleit\itbshape{10}{\magstep4}{OT1IT} \setfont\titlesl\slbshape{10}{\magstep4}{OT1} \setfont\titlett\ttbshape{12}{\magstep3}{OT1TT} \setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT} \setfont\titlesf\sfbshape{17}{\magstep1}{OT1} \let\titlebf=\titlerm \setfont\titlesc\scbshape{10}{\magstep4}{OT1} \font\titlei=cmmi12 scaled \magstep3 \font\titlesy=cmsy10 scaled \magstep4 \def\titleecsize{2074} % Chapter fonts (14.4pt). \def\chapnominalsize{14pt} \setfont\chaprm\rmbshape{12}{\magstep1}{OT1} \setfont\chapit\itbshape{10}{\magstep2}{OT1IT} \setfont\chapsl\slbshape{10}{\magstep2}{OT1} \setfont\chaptt\ttbshape{12}{\magstep1}{OT1TT} \setfont\chapttsl\ttslshape{10}{\magstep2}{OT1TT} \setfont\chapsf\sfbshape{12}{\magstep1}{OT1} \let\chapbf\chaprm \setfont\chapsc\scbshape{10}{\magstep2}{OT1} \font\chapi=cmmi12 scaled \magstep1 \font\chapsy=cmsy10 scaled \magstep2 \def\chapecsize{1440} % Section fonts (12pt). \def\secnominalsize{12pt} \setfont\secrm\rmbshape{12}{1000}{OT1} \setfont\secit\itbshape{10}{\magstep1}{OT1IT} \setfont\secsl\slbshape{10}{\magstep1}{OT1} \setfont\sectt\ttbshape{12}{1000}{OT1TT} \setfont\secttsl\ttslshape{10}{\magstep1}{OT1TT} \setfont\secsf\sfbshape{12}{1000}{OT1} \let\secbf\secrm \setfont\secsc\scbshape{10}{\magstep1}{OT1} \font\seci=cmmi12 \font\secsy=cmsy10 scaled \magstep1 \def\sececsize{1200} % Subsection fonts (10pt). \def\ssecnominalsize{10pt} \setfont\ssecrm\rmbshape{10}{1000}{OT1} \setfont\ssecit\itbshape{10}{1000}{OT1IT} \setfont\ssecsl\slbshape{10}{1000}{OT1} \setfont\ssectt\ttbshape{10}{1000}{OT1TT} \setfont\ssecttsl\ttslshape{10}{1000}{OT1TT} \setfont\ssecsf\sfbshape{10}{1000}{OT1} \let\ssecbf\ssecrm \setfont\ssecsc\scbshape{10}{1000}{OT1} \font\sseci=cmmi10 \font\ssecsy=cmsy10 \def\ssececsize{1000} % Reduced fonts for @acronym in text (9pt). \def\reducednominalsize{9pt} \setfont\reducedrm\rmshape{9}{1000}{OT1} \setfont\reducedtt\ttshape{9}{1000}{OT1TT} \setfont\reducedbf\bfshape{10}{900}{OT1} \setfont\reducedit\itshape{9}{1000}{OT1IT} \setfont\reducedsl\slshape{9}{1000}{OT1} \setfont\reducedsf\sfshape{9}{1000}{OT1} \setfont\reducedsc\scshape{10}{900}{OT1} \setfont\reducedttsl\ttslshape{10}{900}{OT1TT} \font\reducedi=cmmi9 \font\reducedsy=cmsy9 \def\reducedecsize{0900} \divide\parskip by 2 % reduce space between paragraphs \textleading = 12pt % line spacing for 10pt CM \textfonts % reset the current fonts \rm } % end of 10pt text font size definitions, \definetextfontsizex % Fonts for short table of contents. \setfont\shortcontrm\rmshape{12}{1000}{OT1} \setfont\shortcontbf\bfshape{10}{\magstep1}{OT1} % no cmb12 \setfont\shortcontsl\slshape{12}{1000}{OT1} \setfont\shortconttt\ttshape{12}{1000}{OT1TT} % We provide the user-level command % @fonttextsize 10 % (or 11) to redefine the text font size. pt is assumed. % \def\xiword{11} \def\xword{10} \def\xwordpt{10pt} % \parseargdef\fonttextsize{% \def\textsizearg{#1}% %\wlog{doing @fonttextsize \textsizearg}% % % Set \globaldefs so that documents can use this inside @tex, since % makeinfo 4.8 does not support it, but we need it nonetheless. % \begingroup \globaldefs=1 \ifx\textsizearg\xword \definetextfontsizex \else \ifx\textsizearg\xiword \definetextfontsizexi \else \errhelp=\EMsimple \errmessage{@fonttextsize only supports `10' or `11', not `\textsizearg'} \fi\fi \endgroup } % % Change the current font style to #1, remembering it in \curfontstyle. % For now, we do not accumulate font styles: @b{@i{foo}} prints foo in % italics, not bold italics. % \def\setfontstyle#1{% \def\curfontstyle{#1}% not as a control sequence, because we are \edef'd. \csname #1font\endcsname % change the current font } \def\rm{\fam=0 \setfontstyle{rm}} \def\it{\fam=\itfam \setfontstyle{it}} \def\sl{\fam=\slfam \setfontstyle{sl}} \def\bf{\fam=\bffam \setfontstyle{bf}}\def\bfstylename{bf} \def\tt{\fam=\ttfam \setfontstyle{tt}} % Texinfo sort of supports the sans serif font style, which plain TeX does not. % So we set up a \sf. \newfam\sffam \def\sf{\fam=\sffam \setfontstyle{sf}} % We don't need math for this font style. \def\ttsl{\setfontstyle{ttsl}} % In order for the font changes to affect most math symbols and letters, % we have to define the \textfont of the standard families. % We don't bother to reset \scriptscriptfont; awaiting user need. % \def\resetmathfonts{% \textfont0=\rmfont \textfont1=\ifont \textfont2=\syfont \textfont\itfam=\itfont \textfont\slfam=\slfont \textfont\bffam=\bffont \textfont\ttfam=\ttfont \textfont\sffam=\sffont % % Fonts for superscript. Note that the 7pt fonts are used regardless % of the current font size. \scriptfont0=\sevenrm \scriptfont1=\seveni \scriptfont2=\sevensy \scriptfont\itfam=\sevenit \scriptfont\slfam=\sevensl \scriptfont\bffam=\sevenbf \scriptfont\ttfam=\seventt \scriptfont\sffam=\sevensf } % % The font-changing commands (all called \...fonts) redefine the meanings % of \STYLEfont, instead of just \STYLE. We do this because \STYLE needs % to also set the current \fam for math mode. Our \STYLE (e.g., \rm) % commands hardwire \STYLEfont to set the current font. % % The fonts used for \ifont are for "math italics" (\itfont is for italics % in regular text). \syfont is also used in math mode only. % % Each font-changing command also sets the names \lsize (one size lower) % and \lllsize (three sizes lower). These relative commands are used % in, e.g., the LaTeX logo and acronyms. % % This all needs generalizing, badly. % \def\assignfonts#1{% \expandafter\let\expandafter\rmfont\csname #1rm\endcsname \expandafter\let\expandafter\itfont\csname #1it\endcsname \expandafter\let\expandafter\slfont\csname #1sl\endcsname \expandafter\let\expandafter\bffont\csname #1bf\endcsname \expandafter\let\expandafter\ttfont\csname #1tt\endcsname \expandafter\let\expandafter\smallcaps\csname #1sc\endcsname \expandafter\let\expandafter\sffont \csname #1sf\endcsname \expandafter\let\expandafter\ifont \csname #1i\endcsname \expandafter\let\expandafter\syfont \csname #1sy\endcsname \expandafter\let\expandafter\ttslfont\csname #1ttsl\endcsname } \newif\ifrmisbold % Select smaller font size with the current style. Used to change font size % in, e.g., the LaTeX logo and acronyms. If we are using bold fonts for % normal roman text, also use bold fonts for roman text in the smaller size. \def\switchtolllsize{% \expandafter\assignfonts\expandafter{\lllsize}% \ifrmisbold \let\rmfont\bffont \fi \csname\curfontstyle\endcsname }% \def\switchtolsize{% \expandafter\assignfonts\expandafter{\lsize}% \ifrmisbold \let\rmfont\bffont \fi \csname\curfontstyle\endcsname }% \def\definefontsetatsize#1#2#3#4#5{% \expandafter\def\csname #1fonts\endcsname{% \def\curfontsize{#1}% \def\lsize{#2}\def\lllsize{#3}% \csname rmisbold#5\endcsname \assignfonts{#1}% \resetmathfonts \setleading{#4}% }} \definefontsetatsize{text} {reduced}{smaller}{\textleading}{false} \definefontsetatsize{title} {chap} {subsec} {27pt} {true} \definefontsetatsize{chap} {sec} {text} {19pt} {true} \definefontsetatsize{sec} {subsec} {reduced}{17pt} {true} \definefontsetatsize{ssec} {text} {small} {15pt} {true} \definefontsetatsize{reduced}{small} {smaller}{10.5pt}{false} \definefontsetatsize{small} {smaller}{smaller}{10.5pt}{false} \definefontsetatsize{smaller}{smaller}{smaller}{9.5pt} {false} \def\titlefont#1{{\titlefonts\rm #1}} \let\subsecfonts = \ssecfonts \let\subsubsecfonts = \ssecfonts % Define these just so they can be easily changed for other fonts. \def\angleleft{$\langle$} \def\angleright{$\rangle$} % Set the fonts to use with the @small... environments. \let\smallexamplefonts = \smallfonts % About \smallexamplefonts. If we use \smallfonts (9pt), @smallexample % can fit this many characters: % 8.5x11=86 smallbook=72 a4=90 a5=69 % If we use \scriptfonts (8pt), then we can fit this many characters: % 8.5x11=90+ smallbook=80 a4=90+ a5=77 % For me, subjectively, the few extra characters that fit aren't worth % the additional smallness of 8pt. So I'm making the default 9pt. % % By the way, for comparison, here's what fits with @example (10pt): % 8.5x11=71 smallbook=60 a4=75 a5=58 % --karl, 24jan03. % Set up the default fonts, so we can use them for creating boxes. % \definetextfontsizexi \message{markup,} % Check if we are currently using a typewriter font. Since all the % Computer Modern typewriter fonts have zero interword stretch (and % shrink), and it is reasonable to expect all typewriter fonts to have % this property, we can check that font parameter. % \def\ifmonospace{\ifdim\fontdimen3\font=0pt } % Markup style infrastructure. \defmarkupstylesetup\INITMACRO will % define and register \INITMACRO to be called on markup style changes. % \INITMACRO can check \currentmarkupstyle for the innermost % style. \let\currentmarkupstyle\empty \def\setupmarkupstyle#1{% \def\currentmarkupstyle{#1}% \markupstylesetup } \let\markupstylesetup\empty \def\defmarkupstylesetup#1{% \expandafter\def\expandafter\markupstylesetup \expandafter{\markupstylesetup #1}% \def#1% } % Markup style setup for left and right quotes. \defmarkupstylesetup\markupsetuplq{% \expandafter\let\expandafter \temp \csname markupsetuplq\currentmarkupstyle\endcsname \ifx\temp\relax \markupsetuplqdefault \else \temp \fi } \defmarkupstylesetup\markupsetuprq{% \expandafter\let\expandafter \temp \csname markupsetuprq\currentmarkupstyle\endcsname \ifx\temp\relax \markupsetuprqdefault \else \temp \fi } { \catcode`\'=\active \catcode`\`=\active \gdef\markupsetuplqdefault{\let`\lq} \gdef\markupsetuprqdefault{\let'\rq} \gdef\markupsetcodequoteleft{\let`\codequoteleft} \gdef\markupsetcodequoteright{\let'\codequoteright} } \let\markupsetuplqcode \markupsetcodequoteleft \let\markupsetuprqcode \markupsetcodequoteright % \let\markupsetuplqexample \markupsetcodequoteleft \let\markupsetuprqexample \markupsetcodequoteright % \let\markupsetuplqkbd \markupsetcodequoteleft \let\markupsetuprqkbd \markupsetcodequoteright % \let\markupsetuplqsamp \markupsetcodequoteleft \let\markupsetuprqsamp \markupsetcodequoteright % \let\markupsetuplqverb \markupsetcodequoteleft \let\markupsetuprqverb \markupsetcodequoteright % \let\markupsetuplqverbatim \markupsetcodequoteleft \let\markupsetuprqverbatim \markupsetcodequoteright % Allow an option to not use regular directed right quote/apostrophe % (char 0x27), but instead the undirected quote from cmtt (char 0x0d). % The undirected quote is ugly, so don't make it the default, but it % works for pasting with more pdf viewers (at least evince), the % lilypond developers report. xpdf does work with the regular 0x27. % \def\codequoteright{% \ifmonospace \expandafter\ifx\csname SETtxicodequoteundirected\endcsname\relax \expandafter\ifx\csname SETcodequoteundirected\endcsname\relax '% \else \char'15 \fi \else \char'15 \fi \else '% \fi } % % and a similar option for the left quote char vs. a grave accent. % Modern fonts display ASCII 0x60 as a grave accent, so some people like % the code environments to do likewise. % \def\codequoteleft{% \ifmonospace \expandafter\ifx\csname SETtxicodequotebacktick\endcsname\relax \expandafter\ifx\csname SETcodequotebacktick\endcsname\relax % [Knuth] pp. 380,381,391 % \relax disables Spanish ligatures ?` and !` of \tt font. \relax`% \else \char'22 \fi \else \char'22 \fi \else \relax`% \fi } % Commands to set the quote options. % \parseargdef\codequoteundirected{% \def\temp{#1}% \ifx\temp\onword \expandafter\let\csname SETtxicodequoteundirected\endcsname = t% \else\ifx\temp\offword \expandafter\let\csname SETtxicodequoteundirected\endcsname = \relax \else \errhelp = \EMsimple \errmessage{Unknown @codequoteundirected value `\temp', must be on|off}% \fi\fi } % \parseargdef\codequotebacktick{% \def\temp{#1}% \ifx\temp\onword \expandafter\let\csname SETtxicodequotebacktick\endcsname = t% \else\ifx\temp\offword \expandafter\let\csname SETtxicodequotebacktick\endcsname = \relax \else \errhelp = \EMsimple \errmessage{Unknown @codequotebacktick value `\temp', must be on|off}% \fi\fi } % [Knuth] pp. 380,381,391, disable Spanish ligatures ?` and !` of \tt font. \def\noligaturesquoteleft{\relax\lq} % Count depth in font-changes, for error checks \newcount\fontdepth \fontdepth=0 % Font commands. % #1 is the font command (\sl or \it), #2 is the text to slant. % If we are in a monospaced environment, however, 1) always use \ttsl, % and 2) do not add an italic correction. \def\dosmartslant#1#2{% \ifusingtt {{\ttsl #2}\let\next=\relax}% {\def\next{{#1#2}\futurelet\next\smartitaliccorrection}}% \next } \def\smartslanted{\dosmartslant\sl} \def\smartitalic{\dosmartslant\it} % Output an italic correction unless \next (presumed to be the following % character) is such as not to need one. \def\smartitaliccorrection{% \ifx\next,% \else\ifx\next-% \else\ifx\next.% \else\ifx\next\.% \else\ifx\next\comma% \else\ptexslash \fi\fi\fi\fi\fi \aftersmartic } % Unconditional use \ttsl, and no ic. @var is set to this for defuns. \def\ttslanted#1{{\ttsl #1}} % @cite is like \smartslanted except unconditionally use \sl. We never want % ttsl for book titles, do we? \def\cite#1{{\sl #1}\futurelet\next\smartitaliccorrection} \def\aftersmartic{} \def\var#1{% \let\saveaftersmartic = \aftersmartic \def\aftersmartic{\null\let\aftersmartic=\saveaftersmartic}% \smartslanted{#1}% } \let\i=\smartitalic \let\slanted=\smartslanted \let\dfn=\smartslanted \let\emph=\smartitalic % Explicit font changes: @r, @sc, undocumented @ii. \def\r#1{{\rm #1}} % roman font \def\sc#1{{\smallcaps#1}} % smallcaps font \def\ii#1{{\it #1}} % italic font % @b, explicit bold. Also @strong. \def\b#1{{\bf #1}} \let\strong=\b % @sansserif, explicit sans. \def\sansserif#1{{\sf #1}} % We can't just use \exhyphenpenalty, because that only has effect at % the end of a paragraph. Restore normal hyphenation at the end of the % group within which \nohyphenation is presumably called. % \def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation} \def\restorehyphenation{\hyphenchar\font = `- } % Set sfcode to normal for the chars that usually have another value. % Can't use plain's \frenchspacing because it uses the `\x notation, and % sometimes \x has an active definition that messes things up. % \catcode`@=11 \def\plainfrenchspacing{% \sfcode`\.=\@m \sfcode`\?=\@m \sfcode`\!=\@m \sfcode`\:=\@m \sfcode`\;=\@m \sfcode`\,=\@m \def\endofsentencespacefactor{1000}% for @. and friends } \def\plainnonfrenchspacing{% \sfcode`\.3000\sfcode`\?3000\sfcode`\!3000 \sfcode`\:2000\sfcode`\;1500\sfcode`\,1250 \def\endofsentencespacefactor{3000}% for @. and friends } \catcode`@=\other \def\endofsentencespacefactor{3000}% default % @t, explicit typewriter. \def\t#1{% {\tt \plainfrenchspacing #1}% \null } % @samp. \def\samp#1{{\setupmarkupstyle{samp}\lq\tclose{#1}\rq\null}} % @indicateurl is \samp, that is, with quotes. \let\indicateurl=\samp % @code (and similar) prints in typewriter, but with spaces the same % size as normal in the surrounding text, without hyphenation, etc. % This is a subroutine for that. \def\tclose#1{% {% % Change normal interword space to be same as for the current font. \spaceskip = \fontdimen2\font % % Switch to typewriter. \tt % % But `\ ' produces the large typewriter interword space. \def\ {{\spaceskip = 0pt{} }}% % % Turn off hyphenation. \nohyphenation % \plainfrenchspacing #1% }% \null % reset spacefactor to 1000 } % We *must* turn on hyphenation at `-' and `_' in @code. % (But see \codedashfinish below.) % Otherwise, it is too hard to avoid overfull hboxes % in the Emacs manual, the Library manual, etc. % % Unfortunately, TeX uses one parameter (\hyphenchar) to control % both hyphenation at - and hyphenation within words. % We must therefore turn them both off (\tclose does that) % and arrange explicitly to hyphenate at a dash. -- rms. { \catcode`\-=\active \catcode`\_=\active \catcode`\'=\active \catcode`\`=\active \global\let'=\rq \global\let`=\lq % default definitions % \global\def\code{\begingroup \setupmarkupstyle{code}% % The following should really be moved into \setupmarkupstyle handlers. \catcode\dashChar=\active \catcode\underChar=\active \ifallowcodebreaks \let-\codedash \let_\codeunder \else \let-\normaldash \let_\realunder \fi % Given -foo (with a single dash), we do not want to allow a break % after the hyphen. \global\let\codedashprev=\codedash % \codex } % \gdef\codedash{\futurelet\next\codedashfinish} \gdef\codedashfinish{% \normaldash % always output the dash character itself. % % Now, output a discretionary to allow a line break, unless % (a) the next character is a -, or % (b) the preceding character is a -. % E.g., given --posix, we do not want to allow a break after either -. % Given --foo-bar, we do want to allow a break between the - and the b. \ifx\next\codedash \else \ifx\codedashprev\codedash \else \discretionary{}{}{}\fi \fi % we need the space after the = for the case when \next itself is a % space token; it would get swallowed otherwise. As in @code{- a}. \global\let\codedashprev= \next } } \def\normaldash{-} % \def\codex #1{\tclose{#1}\endgroup} \def\codeunder{% % this is all so @math{@code{var_name}+1} can work. In math mode, _ % is "active" (mathcode"8000) and \normalunderscore (or \char95, etc.) % will therefore expand the active definition of _, which is us % (inside @code that is), therefore an endless loop. \ifusingtt{\ifmmode \mathchar"075F % class 0=ordinary, family 7=ttfam, pos 0x5F=_. \else\normalunderscore \fi \discretionary{}{}{}}% {\_}% } % An additional complication: the above will allow breaks after, e.g., % each of the four underscores in __typeof__. This is bad. % @allowcodebreaks provides a document-level way to turn breaking at - % and _ on and off. % \newif\ifallowcodebreaks \allowcodebreakstrue \def\keywordtrue{true} \def\keywordfalse{false} \parseargdef\allowcodebreaks{% \def\txiarg{#1}% \ifx\txiarg\keywordtrue \allowcodebreakstrue \else\ifx\txiarg\keywordfalse \allowcodebreaksfalse \else \errhelp = \EMsimple \errmessage{Unknown @allowcodebreaks option `\txiarg', must be true|false}% \fi\fi } % For @command, @env, @file, @option quotes seem unnecessary, % so use \code rather than \samp. \let\command=\code \let\env=\code \let\file=\code \let\option=\code % @uref (abbreviation for `urlref') aka @url takes an optional % (comma-separated) second argument specifying the text to display and % an optional third arg as text to display instead of (rather than in % addition to) the url itself. First (mandatory) arg is the url. % TeX-only option to allow changing PDF output to show only the second % arg (if given), and not the url (which is then just the link target). \newif\ifurefurlonlylink % The main macro is \urefbreak, which allows breaking at expected % places within the url. (There used to be another version, which % didn't support automatic breaking.) \def\urefbreak{\begingroup \urefcatcodes \dourefbreak} \let\uref=\urefbreak % \def\dourefbreak#1{\urefbreakfinish #1,,,\finish} \def\urefbreakfinish#1,#2,#3,#4\finish{% doesn't work in @example \unsepspaces \pdfurl{#1}% \setbox0 = \hbox{\ignorespaces #3}% \ifdim\wd0 > 0pt \unhbox0 % third arg given, show only that \else \setbox0 = \hbox{\ignorespaces #2}% look for second arg \ifdim\wd0 > 0pt \ifpdf % For pdfTeX and LuaTeX \ifurefurlonlylink % PDF plus option to not display url, show just arg \unhbox0 \else % PDF, normally display both arg and url for consistency, % visibility, if the pdf is eventually used to print, etc. \unhbox0\ (\urefcode{#1})% \fi \else \ifx\XeTeXrevision\thisisundefined \unhbox0\ (\urefcode{#1})% DVI, always show arg and url \else % For XeTeX \ifurefurlonlylink % PDF plus option to not display url, show just arg \unhbox0 \else % PDF, normally display both arg and url for consistency, % visibility, if the pdf is eventually used to print, etc. \unhbox0\ (\urefcode{#1})% \fi \fi \fi \else \urefcode{#1}% only url given, so show it \fi \fi \endlink \endgroup} % Allow line breaks around only a few characters (only). \def\urefcatcodes{% \catcode`\&=\active \catcode`\.=\active \catcode`\#=\active \catcode`\?=\active \catcode`\/=\active } { \urefcatcodes % \global\def\urefcode{\begingroup \setupmarkupstyle{code}% \urefcatcodes \let&\urefcodeamp \let.\urefcodedot \let#\urefcodehash \let?\urefcodequest \let/\urefcodeslash \codex } % % By default, they are just regular characters. \global\def&{\normalamp} \global\def.{\normaldot} \global\def#{\normalhash} \global\def?{\normalquest} \global\def/{\normalslash} } \def\urefcodeamp{\urefprebreak \&\urefpostbreak} \def\urefcodedot{\urefprebreak .\urefpostbreak} \def\urefcodehash{\urefprebreak \#\urefpostbreak} \def\urefcodequest{\urefprebreak ?\urefpostbreak} \def\urefcodeslash{\futurelet\next\urefcodeslashfinish} { \catcode`\/=\active \global\def\urefcodeslashfinish{% \urefprebreak \slashChar % Allow line break only after the final / in a sequence of % slashes, to avoid line break between the slashes in http://. \ifx\next/\else \urefpostbreak \fi } } % By default we'll break after the special characters, but some people like to % break before the special chars, so allow that. Also allow no breaking at % all, for manual control. % \parseargdef\urefbreakstyle{% \def\txiarg{#1}% \ifx\txiarg\wordnone \def\urefprebreak{\nobreak}\def\urefpostbreak{\nobreak} \else\ifx\txiarg\wordbefore \def\urefprebreak{\urefallowbreak}\def\urefpostbreak{\nobreak} \else\ifx\txiarg\wordafter \def\urefprebreak{\nobreak}\def\urefpostbreak{\urefallowbreak} \else \errhelp = \EMsimple \errmessage{Unknown @urefbreakstyle setting `\txiarg'}% \fi\fi\fi } \def\wordafter{after} \def\wordbefore{before} \def\wordnone{none} % Allow a ragged right output to aid breaking long URL's. There can % be a break at the \allowbreak with no extra glue (if the existing stretch in % the line is sufficent), a break at the \penalty100 with extra glue added % at the end of the line, or no break at all here. % Changing the value of the penalty and/or the amount of stretch affects how % preferrable one choice is over the other. \def\urefallowbreak{% \allowbreak \hskip 0pt plus 2 em\relax \penalty300 \hskip 0pt plus -2 em\relax } \urefbreakstyle after % @url synonym for @uref, since that's how everyone uses it. % \let\url=\uref % rms does not like angle brackets --karl, 17may97. % So now @email is just like @uref, unless we are pdf. % %\def\email#1{\angleleft{\tt #1}\angleright} \ifpdforxetex \def\email#1{\doemail#1,,\finish} \def\doemail#1,#2,#3\finish{\begingroup \unsepspaces \pdfurl{mailto:#1}% \setbox0 = \hbox{\ignorespaces #2}% \ifdim\wd0>0pt\unhbox0\else\code{#1}\fi \endlink \endgroup} \else \let\email=\uref \fi % @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always), % `example' (@kbd uses ttsl only inside of @example and friends), % or `code' (@kbd uses normal tty font always). \parseargdef\kbdinputstyle{% \def\txiarg{#1}% \ifx\txiarg\worddistinct \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}% \else\ifx\txiarg\wordexample \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}% \else\ifx\txiarg\wordcode \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}% \else \errhelp = \EMsimple \errmessage{Unknown @kbdinputstyle setting `\txiarg'}% \fi\fi\fi } \def\worddistinct{distinct} \def\wordexample{example} \def\wordcode{code} % Default is `distinct'. \kbdinputstyle distinct % @kbd is like @code, except that if the argument is just one @key command, % then @kbd has no effect. \def\kbd#1{{\def\look{#1}\expandafter\kbdsub\look??\par}} \def\xkey{\key} \def\kbdsub#1#2#3\par{% \def\one{#1}\def\three{#3}\def\threex{??}% \ifx\one\xkey\ifx\threex\three \key{#2}% \else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi \else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi } % definition of @key that produces a lozenge. Doesn't adjust to text size. %\setfont\keyrm\rmshape{8}{1000}{OT1} %\font\keysy=cmsy9 %\def\key#1{{\keyrm\textfont2=\keysy \leavevmode\hbox{% % \raise0.4pt\hbox{\angleleft}\kern-.08em\vtop{% % \vbox{\hrule\kern-0.4pt % \hbox{\raise0.4pt\hbox{\vphantom{\angleleft}}#1}}% % \kern-0.4pt\hrule}% % \kern-.06em\raise0.4pt\hbox{\angleright}}}} % definition of @key with no lozenge. If the current font is already % monospace, don't change it; that way, we respect @kbdinputstyle. But % if it isn't monospace, then use \tt. % \def\key#1{{\setupmarkupstyle{key}% \nohyphenation \ifmonospace\else\tt\fi #1}\null} % @clicksequence{File @click{} Open ...} \def\clicksequence#1{\begingroup #1\endgroup} % @clickstyle @arrow (by default) \parseargdef\clickstyle{\def\click{#1}} \def\click{\arrow} % Typeset a dimension, e.g., `in' or `pt'. The only reason for the % argument is to make the input look right: @dmn{pt} instead of @dmn{}pt. % \def\dmn#1{\thinspace #1} % @acronym for "FBI", "NATO", and the like. % We print this one point size smaller, since it's intended for % all-uppercase. % \def\acronym#1{\doacronym #1,,\finish} \def\doacronym#1,#2,#3\finish{% {\switchtolsize #1}% \def\temp{#2}% \ifx\temp\empty \else \space ({\unsepspaces \ignorespaces \temp \unskip})% \fi \null % reset \spacefactor=1000 } % @abbr for "Comput. J." and the like. % No font change, but don't do end-of-sentence spacing. % \def\abbr#1{\doabbr #1,,\finish} \def\doabbr#1,#2,#3\finish{% {\plainfrenchspacing #1}% \def\temp{#2}% \ifx\temp\empty \else \space ({\unsepspaces \ignorespaces \temp \unskip})% \fi \null % reset \spacefactor=1000 } % @asis just yields its argument. Used with @table, for example. % \def\asis#1{#1} % @math outputs its argument in math mode. % % One complication: _ usually means subscripts, but it could also mean % an actual _ character, as in @math{@var{some_variable} + 1}. So make % _ active, and distinguish by seeing if the current family is \slfam, % which is what @var uses. { \catcode`\_ = \active \gdef\mathunderscore{% \catcode`\_=\active \def_{\ifnum\fam=\slfam \_\else\sb\fi}% } } % Another complication: we want \\ (and @\) to output a math (or tt) \. % FYI, plain.tex uses \\ as a temporary control sequence (for no % particular reason), but this is not advertised and we don't care. % % The \mathchar is class=0=ordinary, family=7=ttfam, position=5C=\. \def\mathbackslash{\ifnum\fam=\ttfam \mathchar"075C \else\backslash \fi} % \def\math{% \ifmmode\else % only go into math if not in math mode already \tex \mathunderscore \let\\ = \mathbackslash \mathactive % make the texinfo accent commands work in math mode \let\"=\ddot \let\'=\acute \let\==\bar \let\^=\hat \let\`=\grave \let\u=\breve \let\v=\check \let\~=\tilde \let\dotaccent=\dot % have to provide another name for sup operator \let\mathopsup=\sup $\expandafter\finishmath\fi } \def\finishmath#1{#1$\endgroup} % Close the group opened by \tex. % Some active characters (such as <) are spaced differently in math. % We have to reset their definitions in case the @math was an argument % to a command which sets the catcodes (such as @item or @section). % { \catcode`^ = \active \catcode`< = \active \catcode`> = \active \catcode`+ = \active \catcode`' = \active \gdef\mathactive{% \let^ = \ptexhat \let< = \ptexless \let> = \ptexgtr \let+ = \ptexplus \let' = \ptexquoteright } } % for @sub and @sup, if in math mode, just do a normal sub/superscript. % If in text, use math to place as sub/superscript, but switch % into text mode, with smaller fonts. This is a different font than the % one used for real math sub/superscripts (8pt vs. 7pt), but let's not % fix it (significant additions to font machinery) until someone notices. % \def\sub{\ifmmode \expandafter\sb \else \expandafter\finishsub\fi} \def\finishsub#1{$\sb{\hbox{\switchtolllsize #1}}$}% % \def\sup{\ifmmode \expandafter\ptexsp \else \expandafter\finishsup\fi} \def\finishsup#1{$\ptexsp{\hbox{\switchtolllsize #1}}$}% % @inlinefmt{FMTNAME,PROCESSED-TEXT} and @inlineraw{FMTNAME,RAW-TEXT}. % Ignore unless FMTNAME == tex; then it is like @iftex and @tex, % except specified as a normal braced arg, so no newlines to worry about. % \def\outfmtnametex{tex} % \long\def\inlinefmt#1{\doinlinefmt #1,\finish} \long\def\doinlinefmt#1,#2,\finish{% \def\inlinefmtname{#1}% \ifx\inlinefmtname\outfmtnametex \ignorespaces #2\fi } % % @inlinefmtifelse{FMTNAME,THEN-TEXT,ELSE-TEXT} expands THEN-TEXT if % FMTNAME is tex, else ELSE-TEXT. \long\def\inlinefmtifelse#1{\doinlinefmtifelse #1,,,\finish} \long\def\doinlinefmtifelse#1,#2,#3,#4,\finish{% \def\inlinefmtname{#1}% \ifx\inlinefmtname\outfmtnametex \ignorespaces #2\else \ignorespaces #3\fi } % % For raw, must switch into @tex before parsing the argument, to avoid % setting catcodes prematurely. Doing it this way means that, for % example, @inlineraw{html, foo{bar} gets a parse error instead of being % ignored. But this isn't important because if people want a literal % *right* brace they would have to use a command anyway, so they may as % well use a command to get a left brace too. We could re-use the % delimiter character idea from \verb, but it seems like overkill. % \long\def\inlineraw{\tex \doinlineraw} \long\def\doinlineraw#1{\doinlinerawtwo #1,\finish} \def\doinlinerawtwo#1,#2,\finish{% \def\inlinerawname{#1}% \ifx\inlinerawname\outfmtnametex \ignorespaces #2\fi \endgroup % close group opened by \tex. } % @inlineifset{VAR, TEXT} expands TEXT if VAR is @set. % \long\def\inlineifset#1{\doinlineifset #1,\finish} \long\def\doinlineifset#1,#2,\finish{% \def\inlinevarname{#1}% \expandafter\ifx\csname SET\inlinevarname\endcsname\relax \else\ignorespaces#2\fi } % @inlineifclear{VAR, TEXT} expands TEXT if VAR is not @set. % \long\def\inlineifclear#1{\doinlineifclear #1,\finish} \long\def\doinlineifclear#1,#2,\finish{% \def\inlinevarname{#1}% \expandafter\ifx\csname SET\inlinevarname\endcsname\relax \ignorespaces#2\fi } \message{glyphs,} % and logos. % @@ prints an @, as does @atchar{}. \def\@{\char64 } \let\atchar=\@ % @{ @} @lbracechar{} @rbracechar{} all generate brace characters. \def\lbracechar{{\ifmonospace\char123\else\ensuremath\lbrace\fi}} \def\rbracechar{{\ifmonospace\char125\else\ensuremath\rbrace\fi}} \let\{=\lbracechar \let\}=\rbracechar % @comma{} to avoid , parsing problems. \let\comma = , % Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent % Others are defined by plain TeX: @` @' @" @^ @~ @= @u @v @H. \let\, = \ptexc \let\dotaccent = \ptexdot \def\ringaccent#1{{\accent23 #1}} \let\tieaccent = \ptext \let\ubaraccent = \ptexb \let\udotaccent = \d % Other special characters: @questiondown @exclamdown @ordf @ordm % Plain TeX defines: @AA @AE @O @OE @L (plus lowercase versions) @ss. \def\questiondown{?`} \def\exclamdown{!`} \def\ordf{\leavevmode\raise1ex\hbox{\switchtolllsize \underbar{a}}} \def\ordm{\leavevmode\raise1ex\hbox{\switchtolllsize \underbar{o}}} % Dotless i and dotless j, used for accents. \def\imacro{i} \def\jmacro{j} \def\dotless#1{% \def\temp{#1}% \ifx\temp\imacro \ifmmode\imath \else\ptexi \fi \else\ifx\temp\jmacro \ifmmode\jmath \else\j \fi \else \errmessage{@dotless can be used only with i or j}% \fi\fi } % The \TeX{} logo, as in plain, but resetting the spacing so that a % period following counts as ending a sentence. (Idea found in latex.) % \edef\TeX{\TeX \spacefactor=1000 } % @LaTeX{} logo. Not quite the same results as the definition in % latex.ltx, since we use a different font for the raised A; it's most % convenient for us to use an explicitly smaller font, rather than using % the \scriptstyle font (since we don't reset \scriptstyle and % \scriptscriptstyle). % \def\LaTeX{% L\kern-.36em {\setbox0=\hbox{T}% \vbox to \ht0{\hbox{% \ifx\textnominalsize\xwordpt % for 10pt running text, lllsize (8pt) is too small for the A in LaTeX. % Revert to plain's \scriptsize, which is 7pt. \count255=\the\fam $\fam\count255 \scriptstyle A$% \else % For 11pt, we can use our lllsize. \switchtolllsize A% \fi }% \vss }}% \kern-.15em \TeX } % Some math mode symbols. Define \ensuremath to switch into math mode % unless we are already there. Expansion tricks may not be needed here, % but safer, and can't hurt. \def\ensuremath{\ifmmode \expandafter\asis \else\expandafter\ensuredmath \fi} \def\ensuredmath#1{$\relax#1$} % \def\bullet{\ensuremath\ptexbullet} \def\geq{\ensuremath\ge} \def\leq{\ensuremath\le} \def\minus{\ensuremath-} % @dots{} outputs an ellipsis using the current font. % We do .5em per period so that it has the same spacing in the cm % typewriter fonts as three actual period characters; on the other hand, % in other typewriter fonts three periods are wider than 1.5em. So do % whichever is larger. % \def\dots{% \leavevmode \setbox0=\hbox{...}% get width of three periods \ifdim\wd0 > 1.5em \dimen0 = \wd0 \else \dimen0 = 1.5em \fi \hbox to \dimen0{% \hskip 0pt plus.25fil .\hskip 0pt plus1fil .\hskip 0pt plus1fil .\hskip 0pt plus.5fil }% } % @enddots{} is an end-of-sentence ellipsis. % \def\enddots{% \dots \spacefactor=\endofsentencespacefactor } % @point{}, @result{}, @expansion{}, @print{}, @equiv{}. % % Since these characters are used in examples, they should be an even number of % \tt widths. Each \tt character is 1en, so two makes it 1em. % \def\point{$\star$} \def\arrow{\leavevmode\raise.05ex\hbox to 1em{\hfil$\rightarrow$\hfil}} \def\result{\leavevmode\raise.05ex\hbox to 1em{\hfil$\Rightarrow$\hfil}} \def\expansion{\leavevmode\hbox to 1em{\hfil$\mapsto$\hfil}} \def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}} \def\equiv{\leavevmode\hbox to 1em{\hfil$\ptexequiv$\hfil}} % The @error{} command. % Adapted from the TeXbook's \boxit. % \newbox\errorbox % {\ttfont \global\dimen0 = 3em}% Width of the box. \dimen2 = .55pt % Thickness of rules % The text. (`r' is open on the right, `e' somewhat less so on the left.) \setbox0 = \hbox{\kern-.75pt \reducedsf \putworderror\kern-1.5pt} % \setbox\errorbox=\hbox to \dimen0{\hfil \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right. \advance\hsize by -2\dimen2 % Rules. \vbox{% \hrule height\dimen2 \hbox{\vrule width\dimen2 \kern3pt % Space to left of text. \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below. \kern3pt\vrule width\dimen2}% Space to right. \hrule height\dimen2} \hfil} % \def\error{\leavevmode\lower.7ex\copy\errorbox} % @pounds{} is a sterling sign, which Knuth put in the CM italic font. % \def\pounds{{\it\$}} % @euro{} comes from a separate font, depending on the current style. % We use the free feym* fonts from the eurosym package by Henrik % Theiling, which support regular, slanted, bold and bold slanted (and % "outlined" (blackboard board, sort of) versions, which we don't need). % It is available from http://www.ctan.org/tex-archive/fonts/eurosym. % % Although only regular is the truly official Euro symbol, we ignore % that. The Euro is designed to be slightly taller than the regular % font height. % % feymr - regular % feymo - slanted % feybr - bold % feybo - bold slanted % % There is no good (free) typewriter version, to my knowledge. % A feymr10 euro is ~7.3pt wide, while a normal cmtt10 char is ~5.25pt wide. % Hmm. % % Also doesn't work in math. Do we need to do math with euro symbols? % Hope not. % % \def\euro{{\eurofont e}} \def\eurofont{% % We set the font at each command, rather than predefining it in % \textfonts and the other font-switching commands, so that % installations which never need the symbol don't have to have the % font installed. % % There is only one designed size (nominal 10pt), so we always scale % that to the current nominal size. % % By the way, simply using "at 1em" works for cmr10 and the like, but % does not work for cmbx10 and other extended/shrunken fonts. % \def\eurosize{\csname\curfontsize nominalsize\endcsname}% % \ifx\curfontstyle\bfstylename % bold: \font\thiseurofont = \ifusingit{feybo10}{feybr10} at \eurosize \else % regular: \font\thiseurofont = \ifusingit{feymo10}{feymr10} at \eurosize \fi \thiseurofont } % Glyphs from the EC fonts. We don't use \let for the aliases, because % sometimes we redefine the original macro, and the alias should reflect % the redefinition. % % Use LaTeX names for the Icelandic letters. \def\DH{{\ecfont \char"D0}} % Eth \def\dh{{\ecfont \char"F0}} % eth \def\TH{{\ecfont \char"DE}} % Thorn \def\th{{\ecfont \char"FE}} % thorn % \def\guillemetleft{{\ecfont \char"13}} \def\guillemotleft{\guillemetleft} \def\guillemetright{{\ecfont \char"14}} \def\guillemotright{\guillemetright} \def\guilsinglleft{{\ecfont \char"0E}} \def\guilsinglright{{\ecfont \char"0F}} \def\quotedblbase{{\ecfont \char"12}} \def\quotesinglbase{{\ecfont \char"0D}} % % This positioning is not perfect (see the ogonek LaTeX package), but % we have the precomposed glyphs for the most common cases. We put the % tests to use those glyphs in the single \ogonek macro so we have fewer % dummy definitions to worry about for index entries, etc. % % ogonek is also used with other letters in Lithuanian (IOU), but using % the precomposed glyphs for those is not so easy since they aren't in % the same EC font. \def\ogonek#1{{% \def\temp{#1}% \ifx\temp\macrocharA\Aogonek \else\ifx\temp\macrochara\aogonek \else\ifx\temp\macrocharE\Eogonek \else\ifx\temp\macrochare\eogonek \else \ecfont \setbox0=\hbox{#1}% \ifdim\ht0=1ex\accent"0C #1% \else\ooalign{\unhbox0\crcr\hidewidth\char"0C \hidewidth}% \fi \fi\fi\fi\fi }% } \def\Aogonek{{\ecfont \char"81}}\def\macrocharA{A} \def\aogonek{{\ecfont \char"A1}}\def\macrochara{a} \def\Eogonek{{\ecfont \char"86}}\def\macrocharE{E} \def\eogonek{{\ecfont \char"A6}}\def\macrochare{e} % % Use the European Computer Modern fonts (cm-super in outline format) % for non-CM glyphs. That is ec* for regular text and tc* for the text % companion symbols (LaTeX TS1 encoding). Both are part of the ec % package and follow the same conventions. % \def\ecfont{\etcfont{e}} \def\tcfont{\etcfont{t}} % \def\etcfont#1{% % We can't distinguish serif/sans and italic/slanted, but this % is used for crude hacks anyway (like adding French and German % quotes to documents typeset with CM, where we lose kerning), so % hopefully nobody will notice/care. \edef\ecsize{\csname\curfontsize ecsize\endcsname}% \edef\nominalsize{\csname\curfontsize nominalsize\endcsname}% \ifmonospace % typewriter: \font\thisecfont = #1ctt\ecsize \space at \nominalsize \else \ifx\curfontstyle\bfstylename % bold: \font\thisecfont = #1cb\ifusingit{i}{x}\ecsize \space at \nominalsize \else % regular: \font\thisecfont = #1c\ifusingit{ti}{rm}\ecsize \space at \nominalsize \fi \fi \thisecfont } % @registeredsymbol - R in a circle. The font for the R should really % be smaller yet, but lllsize is the best we can do for now. % Adapted from the plain.tex definition of \copyright. % \def\registeredsymbol{% $^{{\ooalign{\hfil\raise.07ex\hbox{\switchtolllsize R}% \hfil\crcr\Orb}}% }$% } % @textdegree - the normal degrees sign. % \def\textdegree{$^\circ$} % Laurent Siebenmann reports \Orb undefined with: % Textures 1.7.7 (preloaded format=plain 93.10.14) (68K) 16 APR 2004 02:38 % so we'll define it if necessary. % \ifx\Orb\thisisundefined \def\Orb{\mathhexbox20D} \fi % Quotes. \chardef\quotedblleft="5C \chardef\quotedblright=`\" \chardef\quoteleft=`\` \chardef\quoteright=`\' \message{page headings,} \newskip\titlepagetopglue \titlepagetopglue = 1.5in \newskip\titlepagebottomglue \titlepagebottomglue = 2pc % First the title page. Must do @settitle before @titlepage. \newif\ifseenauthor \newif\iffinishedtitlepage % @setcontentsaftertitlepage used to do an implicit @contents or % @shortcontents after @end titlepage, but it is now obsolete. \def\setcontentsaftertitlepage{% \errmessage{@setcontentsaftertitlepage has been removed as a Texinfo command; move your @contents command if you want the contents after the title page.}}% \def\setshortcontentsaftertitlepage{% \errmessage{@setshortcontentsaftertitlepage has been removed as a Texinfo command; move your @shortcontents and @contents commands if you want the contents after the title page.}}% \parseargdef\shorttitlepage{% \begingroup \hbox{}\vskip 1.5in \chaprm \centerline{#1}% \endgroup\page\hbox{}\page} \envdef\titlepage{% % Open one extra group, as we want to close it in the middle of \Etitlepage. \begingroup \parindent=0pt \textfonts % Leave some space at the very top of the page. \vglue\titlepagetopglue % No rule at page bottom unless we print one at the top with @title. \finishedtitlepagetrue % % Most title ``pages'' are actually two pages long, with space % at the top of the second. We don't want the ragged left on the second. \let\oldpage = \page \def\page{% \iffinishedtitlepage\else \finishtitlepage \fi \let\page = \oldpage \page \null }% } \def\Etitlepage{% \iffinishedtitlepage\else \finishtitlepage \fi % It is important to do the page break before ending the group, % because the headline and footline are only empty inside the group. % If we use the new definition of \page, we always get a blank page % after the title page, which we certainly don't want. \oldpage \endgroup % % Need this before the \...aftertitlepage checks so that if they are % in effect the toc pages will come out with page numbers. \HEADINGSon } \def\finishtitlepage{% \vskip4pt \hrule height 2pt width \hsize \vskip\titlepagebottomglue \finishedtitlepagetrue } % Settings used for typesetting titles: no hyphenation, no indentation, % don't worry much about spacing, ragged right. This should be used % inside a \vbox, and fonts need to be set appropriately first. \par should % be specified before the end of the \vbox, since a vbox is a group. % \def\raggedtitlesettings{% \rm \hyphenpenalty=10000 \parindent=0pt \tolerance=5000 \ptexraggedright } % Macros to be used within @titlepage: \let\subtitlerm=\rmfont \def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines} \parseargdef\title{% \checkenv\titlepage \vbox{\titlefonts \raggedtitlesettings #1\par}% % print a rule at the page bottom also. \finishedtitlepagefalse \vskip4pt \hrule height 4pt width \hsize \vskip4pt } \parseargdef\subtitle{% \checkenv\titlepage {\subtitlefont \rightline{#1}}% } % @author should come last, but may come many times. % It can also be used inside @quotation. % \parseargdef\author{% \def\temp{\quotation}% \ifx\thisenv\temp \def\quotationauthor{#1}% printed in \Equotation. \else \checkenv\titlepage \ifseenauthor\else \vskip 0pt plus 1filll \seenauthortrue \fi {\secfonts\rm \leftline{#1}}% \fi } % Set up page headings and footings. \let\thispage=\folio \newtoks\evenheadline % headline on even pages \newtoks\oddheadline % headline on odd pages \newtoks\evenfootline % footline on even pages \newtoks\oddfootline % footline on odd pages % Now make \makeheadline and \makefootline in Plain TeX use those variables \headline={{\textfonts\rm \ifodd\pageno \the\oddheadline \else \the\evenheadline \fi}} \footline={{\textfonts\rm \ifodd\pageno \the\oddfootline \else \the\evenfootline \fi}\HEADINGShook} \let\HEADINGShook=\relax % Commands to set those variables. % For example, this is what @headings on does % @evenheading @thistitle|@thispage|@thischapter % @oddheading @thischapter|@thispage|@thistitle % @evenfooting @thisfile|| % @oddfooting ||@thisfile \def\evenheading{\parsearg\evenheadingxxx} \def\evenheadingxxx #1{\evenheadingyyy #1\|\|\|\|\finish} \def\evenheadingyyy #1\|#2\|#3\|#4\finish{% \global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} \def\oddheading{\parsearg\oddheadingxxx} \def\oddheadingxxx #1{\oddheadingyyy #1\|\|\|\|\finish} \def\oddheadingyyy #1\|#2\|#3\|#4\finish{% \global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} \parseargdef\everyheading{\oddheadingxxx{#1}\evenheadingxxx{#1}}% \def\evenfooting{\parsearg\evenfootingxxx} \def\evenfootingxxx #1{\evenfootingyyy #1\|\|\|\|\finish} \def\evenfootingyyy #1\|#2\|#3\|#4\finish{% \global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} \def\oddfooting{\parsearg\oddfootingxxx} \def\oddfootingxxx #1{\oddfootingyyy #1\|\|\|\|\finish} \def\oddfootingyyy #1\|#2\|#3\|#4\finish{% \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}% % % Leave some space for the footline. Hopefully ok to assume % @evenfooting will not be used by itself. \global\advance\txipageheight by -12pt \global\advance\vsize by -12pt } \parseargdef\everyfooting{\oddfootingxxx{#1}\evenfootingxxx{#1}} % @evenheadingmarks top \thischapter <- chapter at the top of a page % @evenheadingmarks bottom \thischapter <- chapter at the bottom of a page % % The same set of arguments for: % % @oddheadingmarks % @evenfootingmarks % @oddfootingmarks % @everyheadingmarks % @everyfootingmarks % These define \getoddheadingmarks, \getevenheadingmarks, % \getoddfootingmarks, and \getevenfootingmarks, each to one of % \gettopheadingmarks, \getbottomheadingmarks. % \def\evenheadingmarks{\headingmarks{even}{heading}} \def\oddheadingmarks{\headingmarks{odd}{heading}} \def\evenfootingmarks{\headingmarks{even}{footing}} \def\oddfootingmarks{\headingmarks{odd}{footing}} \parseargdef\everyheadingmarks{\headingmarks{even}{heading}{#1} \headingmarks{odd}{heading}{#1} } \parseargdef\everyfootingmarks{\headingmarks{even}{footing}{#1} \headingmarks{odd}{footing}{#1} } % #1 = even/odd, #2 = heading/footing, #3 = top/bottom. \def\headingmarks#1#2#3 {% \expandafter\let\expandafter\temp \csname get#3headingmarks\endcsname \global\expandafter\let\csname get#1#2marks\endcsname \temp } \everyheadingmarks bottom \everyfootingmarks bottom % @headings double turns headings on for double-sided printing. % @headings single turns headings on for single-sided printing. % @headings off turns them off. % @headings on same as @headings double, retained for compatibility. % @headings after turns on double-sided headings after this page. % @headings doubleafter turns on double-sided headings after this page. % @headings singleafter turns on single-sided headings after this page. % By default, they are off at the start of a document, % and turned `on' after @end titlepage. \parseargdef\headings{\csname HEADINGS#1\endcsname} \def\headingsoff{% non-global headings elimination \evenheadline={\hfil}\evenfootline={\hfil}% \oddheadline={\hfil}\oddfootline={\hfil}% } \def\HEADINGSoff{{\globaldefs=1 \headingsoff}} % global setting \HEADINGSoff % it's the default % When we turn headings on, set the page number to 1. % For double-sided printing, put current file name in lower left corner, % chapter name on inside top of right hand pages, document % title on inside top of left hand pages, and page numbers on outside top % edge of all pages. \def\HEADINGSdouble{% \global\pageno=1 \global\evenfootline={\hfil} \global\oddfootline={\hfil} \global\evenheadline={\line{\folio\hfil\thistitle}} \global\oddheadline={\line{\thischapterheading\hfil\folio}} \global\let\contentsalignmacro = \chapoddpage } \let\contentsalignmacro = \chappager % For single-sided printing, chapter title goes across top left of page, % page number on top right. \def\HEADINGSsingle{% \global\pageno=1 \global\evenfootline={\hfil} \global\oddfootline={\hfil} \global\evenheadline={\line{\thischapterheading\hfil\folio}} \global\oddheadline={\line{\thischapterheading\hfil\folio}} \global\let\contentsalignmacro = \chappager } \def\HEADINGSon{\HEADINGSdouble} \def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex} \let\HEADINGSdoubleafter=\HEADINGSafter \def\HEADINGSdoublex{% \global\evenfootline={\hfil} \global\oddfootline={\hfil} \global\evenheadline={\line{\folio\hfil\thistitle}} \global\oddheadline={\line{\thischapterheading\hfil\folio}} \global\let\contentsalignmacro = \chapoddpage } \def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex} \def\HEADINGSsinglex{% \global\evenfootline={\hfil} \global\oddfootline={\hfil} \global\evenheadline={\line{\thischapterheading\hfil\folio}} \global\oddheadline={\line{\thischapterheading\hfil\folio}} \global\let\contentsalignmacro = \chappager } % Subroutines used in generating headings % This produces Day Month Year style of output. % Only define if not already defined, in case a txi-??.tex file has set % up a different format (e.g., txi-cs.tex does this). \ifx\today\thisisundefined \def\today{% \number\day\space \ifcase\month \or\putwordMJan\or\putwordMFeb\or\putwordMMar\or\putwordMApr \or\putwordMMay\or\putwordMJun\or\putwordMJul\or\putwordMAug \or\putwordMSep\or\putwordMOct\or\putwordMNov\or\putwordMDec \fi \space\number\year} \fi % @settitle line... specifies the title of the document, for headings. % It generates no output of its own. \def\thistitle{\putwordNoTitle} \def\settitle{\parsearg{\gdef\thistitle}} \message{tables,} % Tables -- @table, @ftable, @vtable, @item(x). % default indentation of table text \newdimen\tableindent \tableindent=.8in % default indentation of @itemize and @enumerate text \newdimen\itemindent \itemindent=.3in % margin between end of table item and start of table text. \newdimen\itemmargin \itemmargin=.1in % used internally for \itemindent minus \itemmargin \newdimen\itemmax % Note @table, @ftable, and @vtable define @item, @itemx, etc., with % these defs. % They also define \itemindex % to index the item name in whatever manner is desired (perhaps none). \newif\ifitemxneedsnegativevskip \def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi} \def\internalBitem{\smallbreak \parsearg\itemzzz} \def\internalBitemx{\itemxpar \parsearg\itemzzz} \def\itemzzz #1{\begingroup % \advance\hsize by -\rightskip \advance\hsize by -\tableindent \setbox0=\hbox{\itemindicate{#1}}% \itemindex{#1}% \nobreak % This prevents a break before @itemx. % % If the item text does not fit in the space we have, put it on a line % by itself, and do not allow a page break either before or after that % line. We do not start a paragraph here because then if the next % command is, e.g., @kindex, the whatsit would get put into the % horizontal list on a line by itself, resulting in extra blank space. \ifdim \wd0>\itemmax % % Make this a paragraph so we get the \parskip glue and wrapping, % but leave it ragged-right. \begingroup \advance\leftskip by-\tableindent \advance\hsize by\tableindent \advance\rightskip by0pt plus1fil\relax \leavevmode\unhbox0\par \endgroup % % We're going to be starting a paragraph, but we don't want the % \parskip glue -- logically it's part of the @item we just started. \nobreak \vskip-\parskip % % Stop a page break at the \parskip glue coming up. However, if % what follows is an environment such as @example, there will be no % \parskip glue; then the negative vskip we just inserted would % cause the example and the item to crash together. So we use this % bizarre value of 10001 as a signal to \aboveenvbreak to insert % \parskip glue after all. Section titles are handled this way also. % \penalty 10001 \endgroup \itemxneedsnegativevskipfalse \else % The item text fits into the space. Start a paragraph, so that the % following text (if any) will end up on the same line. \noindent % Do this with kerns and \unhbox so that if there is a footnote in % the item text, it can migrate to the main vertical list and % eventually be printed. \nobreak\kern-\tableindent \dimen0 = \itemmax \advance\dimen0 by \itemmargin \advance\dimen0 by -\wd0 \unhbox0 \nobreak\kern\dimen0 \endgroup \itemxneedsnegativevskiptrue \fi } \def\item{\errmessage{@item while not in a list environment}} \def\itemx{\errmessage{@itemx while not in a list environment}} % @table, @ftable, @vtable. \envdef\table{% \let\itemindex\gobble \tablecheck{table}% } \envdef\ftable{% \def\itemindex ##1{\doind {fn}{\code{##1}}}% \tablecheck{ftable}% } \envdef\vtable{% \def\itemindex ##1{\doind {vr}{\code{##1}}}% \tablecheck{vtable}% } \def\tablecheck#1{% \ifnum \the\catcode`\^^M=\active \endgroup \errmessage{This command won't work in this context; perhaps the problem is that we are \inenvironment\thisenv}% \def\next{\doignore{#1}}% \else \let\next\tablex \fi \next } \def\tablex#1{% \def\itemindicate{#1}% \parsearg\tabley } \def\tabley#1{% {% \makevalueexpandable \edef\temp{\noexpand\tablez #1\space\space\space}% \expandafter }\temp \endtablez } \def\tablez #1 #2 #3 #4\endtablez{% \aboveenvbreak \ifnum 0#1>0 \advance \leftskip by #1\mil \fi \ifnum 0#2>0 \tableindent=#2\mil \fi \ifnum 0#3>0 \advance \rightskip by #3\mil \fi \itemmax=\tableindent \advance \itemmax by -\itemmargin \advance \leftskip by \tableindent \exdentamount=\tableindent \parindent = 0pt \parskip = \smallskipamount \ifdim \parskip=0pt \parskip=2pt \fi \let\item = \internalBitem \let\itemx = \internalBitemx } \def\Etable{\endgraf\afterenvbreak} \let\Eftable\Etable \let\Evtable\Etable \let\Eitemize\Etable \let\Eenumerate\Etable % This is the counter used by @enumerate, which is really @itemize \newcount \itemno \envdef\itemize{\parsearg\doitemize} \def\doitemize#1{% \aboveenvbreak \itemmax=\itemindent \advance\itemmax by -\itemmargin \advance\leftskip by \itemindent \exdentamount=\itemindent \parindent=0pt \parskip=\smallskipamount \ifdim\parskip=0pt \parskip=2pt \fi % % Try typesetting the item mark so that if the document erroneously says % something like @itemize @samp (intending @table), there's an error % right away at the @itemize. It's not the best error message in the % world, but it's better than leaving it to the @item. This means if % the user wants an empty mark, they have to say @w{} not just @w. \def\itemcontents{#1}% \setbox0 = \hbox{\itemcontents}% % % @itemize with no arg is equivalent to @itemize @bullet. \ifx\itemcontents\empty\def\itemcontents{\bullet}\fi % \let\item=\itemizeitem } % Definition of @item while inside @itemize and @enumerate. % \def\itemizeitem{% \advance\itemno by 1 % for enumerations {\let\par=\endgraf \smallbreak}% reasonable place to break {% % If the document has an @itemize directly after a section title, a % \nobreak will be last on the list, and \sectionheading will have % done a \vskip-\parskip. In that case, we don't want to zero % parskip, or the item text will crash with the heading. On the % other hand, when there is normal text preceding the item (as there % usually is), we do want to zero parskip, or there would be too much % space. In that case, we won't have a \nobreak before. At least % that's the theory. \ifnum\lastpenalty<10000 \parskip=0in \fi \noindent \hbox to 0pt{\hss \itemcontents \kern\itemmargin}% % \ifinner\else \vadjust{\penalty 1200}% not good to break after first line of item. \fi % We can be in inner vertical mode in a footnote, although an % @itemize looks awful there. }% \flushcr } % \splitoff TOKENS\endmark defines \first to be the first token in % TOKENS, and \rest to be the remainder. % \def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}% % Allow an optional argument of an uppercase letter, lowercase letter, % or number, to specify the first label in the enumerated list. No % argument is the same as `1'. % \envparseargdef\enumerate{\enumeratey #1 \endenumeratey} \def\enumeratey #1 #2\endenumeratey{% % If we were given no argument, pretend we were given `1'. \def\thearg{#1}% \ifx\thearg\empty \def\thearg{1}\fi % % Detect if the argument is a single token. If so, it might be a % letter. Otherwise, the only valid thing it can be is a number. % (We will always have one token, because of the test we just made. % This is a good thing, since \splitoff doesn't work given nothing at % all -- the first parameter is undelimited.) \expandafter\splitoff\thearg\endmark \ifx\rest\empty % Only one token in the argument. It could still be anything. % A ``lowercase letter'' is one whose \lccode is nonzero. % An ``uppercase letter'' is one whose \lccode is both nonzero, and % not equal to itself. % Otherwise, we assume it's a number. % % We need the \relax at the end of the \ifnum lines to stop TeX from % continuing to look for a . % \ifnum\lccode\expandafter`\thearg=0\relax \numericenumerate % a number (we hope) \else % It's a letter. \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax \lowercaseenumerate % lowercase letter \else \uppercaseenumerate % uppercase letter \fi \fi \else % Multiple tokens in the argument. We hope it's a number. \numericenumerate \fi } % An @enumerate whose labels are integers. The starting integer is % given in \thearg. % \def\numericenumerate{% \itemno = \thearg \startenumeration{\the\itemno}% } % The starting (lowercase) letter is in \thearg. \def\lowercaseenumerate{% \itemno = \expandafter`\thearg \startenumeration{% % Be sure we're not beyond the end of the alphabet. \ifnum\itemno=0 \errmessage{No more lowercase letters in @enumerate; get a bigger alphabet}% \fi \char\lccode\itemno }% } % The starting (uppercase) letter is in \thearg. \def\uppercaseenumerate{% \itemno = \expandafter`\thearg \startenumeration{% % Be sure we're not beyond the end of the alphabet. \ifnum\itemno=0 \errmessage{No more uppercase letters in @enumerate; get a bigger alphabet} \fi \char\uccode\itemno }% } % Call \doitemize, adding a period to the first argument and supplying the % common last two arguments. Also subtract one from the initial value in % \itemno, since @item increments \itemno. % \def\startenumeration#1{% \advance\itemno by -1 \doitemize{#1.}\flushcr } % @alphaenumerate and @capsenumerate are abbreviations for giving an arg % to @enumerate. % \def\alphaenumerate{\enumerate{a}} \def\capsenumerate{\enumerate{A}} \def\Ealphaenumerate{\Eenumerate} \def\Ecapsenumerate{\Eenumerate} % @multitable macros % Amy Hendrickson, 8/18/94, 3/6/96 % % @multitable ... @end multitable will make as many columns as desired. % Contents of each column will wrap at width given in preamble. Width % can be specified either with sample text given in a template line, % or in percent of \hsize, the current width of text on page. % Table can continue over pages but will only break between lines. % To make preamble: % % Either define widths of columns in terms of percent of \hsize: % @multitable @columnfractions .25 .3 .45 % @item ... % % Numbers following @columnfractions are the percent of the total % current hsize to be used for each column. You may use as many % columns as desired. % Or use a template: % @multitable {Column 1 template} {Column 2 template} {Column 3 template} % @item ... % using the widest term desired in each column. % Each new table line starts with @item, each subsequent new column % starts with @tab. Empty columns may be produced by supplying @tab's % with nothing between them for as many times as empty columns are needed, % ie, @tab@tab@tab will produce two empty columns. % @item, @tab do not need to be on their own lines, but it will not hurt % if they are. % Sample multitable: % @multitable {Column 1 template} {Column 2 template} {Column 3 template} % @item first col stuff @tab second col stuff @tab third col % @item % first col stuff % @tab % second col stuff % @tab % third col % @item first col stuff @tab second col stuff % @tab Many paragraphs of text may be used in any column. % % They will wrap at the width determined by the template. % @item@tab@tab This will be in third column. % @end multitable % Default dimensions may be reset by user. % @multitableparskip is vertical space between paragraphs in table. % @multitableparindent is paragraph indent in table. % @multitablecolmargin is horizontal space to be left between columns. % @multitablelinespace is space to leave between table items, baseline % to baseline. % 0pt means it depends on current normal line spacing. % \newskip\multitableparskip \newskip\multitableparindent \newdimen\multitablecolspace \newskip\multitablelinespace \multitableparskip=0pt \multitableparindent=6pt \multitablecolspace=12pt \multitablelinespace=0pt % Macros used to set up halign preamble: % \let\endsetuptable\relax \def\xendsetuptable{\endsetuptable} \let\columnfractions\relax \def\xcolumnfractions{\columnfractions} \newif\ifsetpercent % #1 is the @columnfraction, usually a decimal number like .5, but might % be just 1. We just use it, whatever it is. % \def\pickupwholefraction#1 {% \global\advance\colcount by 1 \expandafter\xdef\csname col\the\colcount\endcsname{#1\hsize}% \setuptable } \newcount\colcount \def\setuptable#1{% \def\firstarg{#1}% \ifx\firstarg\xendsetuptable \let\go = \relax \else \ifx\firstarg\xcolumnfractions \global\setpercenttrue \else \ifsetpercent \let\go\pickupwholefraction \else \global\advance\colcount by 1 \setbox0=\hbox{#1\unskip\space}% Add a normal word space as a % separator; typically that is always in the input, anyway. \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}% \fi \fi \ifx\go\pickupwholefraction % Put the argument back for the \pickupwholefraction call, so % we'll always have a period there to be parsed. \def\go{\pickupwholefraction#1}% \else \let\go = \setuptable \fi% \fi \go } % multitable-only commands. % % @headitem starts a heading row, which we typeset in bold. Assignments % have to be global since we are inside the implicit group of an % alignment entry. \everycr below resets \everytab so we don't have to % undo it ourselves. \def\headitemfont{\b}% for people to use in the template row; not changeable \def\headitem{% \checkenv\multitable \crcr \gdef\headitemcrhook{\nobreak}% attempt to avoid page break after headings \global\everytab={\bf}% can't use \headitemfont since the parsing differs \the\everytab % for the first item }% % % default for tables with no headings. \let\headitemcrhook=\relax % % A \tab used to include \hskip1sp. But then the space in a template % line is not enough. That is bad. So let's go back to just `&' until % we again encounter the problem the 1sp was intended to solve. % --karl, nathan@acm.org, 20apr99. \def\tab{\checkenv\multitable &\the\everytab}% % @multitable ... @end multitable definitions: % \newtoks\everytab % insert after every tab. % \envdef\multitable{% \vskip\parskip \startsavinginserts % % @item within a multitable starts a normal row. % We use \def instead of \let so that if one of the multitable entries % contains an @itemize, we don't choke on the \item (seen as \crcr aka % \endtemplate) expanding \doitemize. \def\item{\crcr}% % \tolerance=9500 \hbadness=9500 \setmultitablespacing \parskip=\multitableparskip \parindent=\multitableparindent \overfullrule=0pt \global\colcount=0 % \everycr = {% \noalign{% \global\everytab={}% Reset from possible headitem. \global\colcount=0 % Reset the column counter. % % Check for saved footnotes, etc.: \checkinserts % % Perhaps a \nobreak, then reset: \headitemcrhook \global\let\headitemcrhook=\relax }% }% % \parsearg\domultitable } \def\domultitable#1{% % To parse everything between @multitable and @item: \setuptable#1 \endsetuptable % % This preamble sets up a generic column definition, which will % be used as many times as user calls for columns. % \vtop will set a single line and will also let text wrap and % continue for many paragraphs if desired. \halign\bgroup &% \global\advance\colcount by 1 \multistrut \vtop{% % Use the current \colcount to find the correct column width: \hsize=\expandafter\csname col\the\colcount\endcsname % % In order to keep entries from bumping into each other % we will add a \leftskip of \multitablecolspace to all columns after % the first one. % % If a template has been used, we will add \multitablecolspace % to the width of each template entry. % % If the user has set preamble in terms of percent of \hsize we will % use that dimension as the width of the column, and the \leftskip % will keep entries from bumping into each other. Table will start at % left margin and final column will justify at right margin. % % Make sure we don't inherit \rightskip from the outer environment. \rightskip=0pt \ifnum\colcount=1 % The first column will be indented with the surrounding text. \advance\hsize by\leftskip \else \ifsetpercent \else % If user has not set preamble in terms of percent of \hsize % we will advance \hsize by \multitablecolspace. \advance\hsize by \multitablecolspace \fi % In either case we will make \leftskip=\multitablecolspace: \leftskip=\multitablecolspace \fi % Ignoring space at the beginning and end avoids an occasional spurious % blank line, when TeX decides to break the line at the space before the % box from the multistrut, so the strut ends up on a line by itself. % For example: % @multitable @columnfractions .11 .89 % @item @code{#} % @tab Legal holiday which is valid in major parts of the whole country. % Is automatically provided with highlighting sequences respectively % marking characters. \noindent\ignorespaces##\unskip\multistrut }\cr } \def\Emultitable{% \crcr \egroup % end the \halign \global\setpercentfalse } \def\setmultitablespacing{% \def\multistrut{\strut}% just use the standard line spacing % % Compute \multitablelinespace (if not defined by user) for use in % \multitableparskip calculation. We used define \multistrut based on % this, but (ironically) that caused the spacing to be off. % See bug-texinfo report from Werner Lemberg, 31 Oct 2004 12:52:20 +0100. \ifdim\multitablelinespace=0pt \setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip \global\advance\multitablelinespace by-\ht0 \fi % Test to see if parskip is larger than space between lines of % table. If not, do nothing. % If so, set to same dimension as multitablelinespace. \ifdim\multitableparskip>\multitablelinespace \global\multitableparskip=\multitablelinespace \global\advance\multitableparskip-7pt % to keep parskip somewhat smaller % than skip between lines in the table. \fi% \ifdim\multitableparskip=0pt \global\multitableparskip=\multitablelinespace \global\advance\multitableparskip-7pt % to keep parskip somewhat smaller % than skip between lines in the table. \fi} \message{conditionals,} % @iftex, @ifnotdocbook, @ifnothtml, @ifnotinfo, @ifnotplaintext, % @ifnotxml always succeed. They currently do nothing; we don't % attempt to check whether the conditionals are properly nested. But we % have to remember that they are conditionals, so that @end doesn't % attempt to close an environment group. % \def\makecond#1{% \expandafter\let\csname #1\endcsname = \relax \expandafter\let\csname iscond.#1\endcsname = 1 } \makecond{iftex} \makecond{ifnotdocbook} \makecond{ifnothtml} \makecond{ifnotinfo} \makecond{ifnotplaintext} \makecond{ifnotxml} % Ignore @ignore, @ifhtml, @ifinfo, and the like. % \def\direntry{\doignore{direntry}} \def\documentdescription{\doignore{documentdescription}} \def\docbook{\doignore{docbook}} \def\html{\doignore{html}} \def\ifdocbook{\doignore{ifdocbook}} \def\ifhtml{\doignore{ifhtml}} \def\ifinfo{\doignore{ifinfo}} \def\ifnottex{\doignore{ifnottex}} \def\ifplaintext{\doignore{ifplaintext}} \def\ifxml{\doignore{ifxml}} \def\ignore{\doignore{ignore}} \def\menu{\doignore{menu}} \def\xml{\doignore{xml}} % Ignore text until a line `@end #1', keeping track of nested conditionals. % % A count to remember the depth of nesting. \newcount\doignorecount \def\doignore#1{\begingroup % Scan in ``verbatim'' mode: \obeylines \catcode`\@ = \other \catcode`\{ = \other \catcode`\} = \other % % Make sure that spaces turn into tokens that match what \doignoretext wants. \spaceisspace % % Count number of #1's that we've seen. \doignorecount = 0 % % Swallow text until we reach the matching `@end #1'. \dodoignore{#1}% } { \catcode`_=11 % We want to use \_STOP_ which cannot appear in texinfo source. \obeylines % % \gdef\dodoignore#1{% % #1 contains the command name as a string, e.g., `ifinfo'. % % Define a command to find the next `@end #1'. \long\def\doignoretext##1^^M@end #1{% \doignoretextyyy##1^^M@#1\_STOP_}% % % And this command to find another #1 command, at the beginning of a % line. (Otherwise, we would consider a line `@c @ifset', for % example, to count as an @ifset for nesting.) \long\def\doignoretextyyy##1^^M@#1##2\_STOP_{\doignoreyyy{##2}\_STOP_}% % % And now expand that command. \doignoretext ^^M% }% } \def\doignoreyyy#1{% \def\temp{#1}% \ifx\temp\empty % Nothing found. \let\next\doignoretextzzz \else % Found a nested condition, ... \advance\doignorecount by 1 \let\next\doignoretextyyy % ..., look for another. % If we're here, #1 ends with ^^M\ifinfo (for example). \fi \next #1% the token \_STOP_ is present just after this macro. } % We have to swallow the remaining "\_STOP_". % \def\doignoretextzzz#1{% \ifnum\doignorecount = 0 % We have just found the outermost @end. \let\next\enddoignore \else % Still inside a nested condition. \advance\doignorecount by -1 \let\next\doignoretext % Look for the next @end. \fi \next } % Finish off ignored text. { \obeylines% % Ignore anything after the last `@end #1'; this matters in verbatim % environments, where otherwise the newline after an ignored conditional % would result in a blank line in the output. \gdef\enddoignore#1^^M{\endgroup\ignorespaces}% } % @set VAR sets the variable VAR to an empty value. % @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE. % % Since we want to separate VAR from REST-OF-LINE (which might be % empty), we can't just use \parsearg; we have to insert a space of our % own to delimit the rest of the line, and then take it out again if we % didn't need it. % We rely on the fact that \parsearg sets \catcode`\ =10. % \parseargdef\set{\setyyy#1 \endsetyyy} \def\setyyy#1 #2\endsetyyy{% {% \makevalueexpandable \def\temp{#2}% \edef\next{\gdef\makecsname{SET#1}}% \ifx\temp\empty \next{}% \else \setzzz#2\endsetzzz \fi }% } % Remove the trailing space \setxxx inserted. \def\setzzz#1 \endsetzzz{\next{#1}} % @clear VAR clears (i.e., unsets) the variable VAR. % \parseargdef\clear{% {% \makevalueexpandable \global\expandafter\let\csname SET#1\endcsname=\relax }% } % @value{foo} gets the text saved in variable foo. \def\value{\begingroup\makevalueexpandable\valuexxx} \def\valuexxx#1{\expandablevalue{#1}\endgroup} { \catcode`\-=\active \catcode`\_=\active % \gdef\makevalueexpandable{% \let\value = \expandablevalue % We don't want these characters active, ... \catcode`\-=\other \catcode`\_=\other % ..., but we might end up with active ones in the argument if % we're called from @code, as @code{@value{foo-bar_}}, though. % So \let them to their normal equivalents. \let-\normaldash \let_\normalunderscore } } \def\expandablevalue#1{% \expandafter\ifx\csname SET#1\endcsname\relax {[No value for ``#1'']}% \message{Variable `#1', used in @value, is not set.}% \else \csname SET#1\endcsname \fi } % Like \expandablevalue, but completely expandable (the \message in the % definition above operates at the execution level of TeX). Used when % writing to auxiliary files, due to the expansion that \write does. % If flag is undefined, pass through an unexpanded @value command: maybe it % will be set by the time it is read back in. % % NB flag names containing - or _ may not work here. \def\dummyvalue#1{% \expandafter\ifx\csname SET#1\endcsname\relax \string\value{#1}% \else \csname SET#1\endcsname \fi } % Used for @value's in index entries to form the sort key: expand the @value % if possible, otherwise sort late. \def\indexnofontsvalue#1{% \expandafter\ifx\csname SET#1\endcsname\relax ZZZZZZZ% \else \csname SET#1\endcsname \fi } % @ifset VAR ... @end ifset reads the `...' iff VAR has been defined % with @set. % % To get the special treatment we need for `@end ifset,' we call % \makecond and then redefine. % \makecond{ifset} \def\ifset{\parsearg{\doifset{\let\next=\ifsetfail}}} \def\doifset#1#2{% {% \makevalueexpandable \let\next=\empty \expandafter\ifx\csname SET#2\endcsname\relax #1% If not set, redefine \next. \fi \expandafter }\next } \def\ifsetfail{\doignore{ifset}} % @ifclear VAR ... @end executes the `...' iff VAR has never been % defined with @set, or has been undefined with @clear. % % The `\else' inside the `\doifset' parameter is a trick to reuse the % above code: if the variable is not set, do nothing, if it is set, % then redefine \next to \ifclearfail. % \makecond{ifclear} \def\ifclear{\parsearg{\doifset{\else \let\next=\ifclearfail}}} \def\ifclearfail{\doignore{ifclear}} % @ifcommandisdefined CMD ... @end executes the `...' if CMD (written % without the @) is in fact defined. We can only feasibly check at the % TeX level, so something like `mathcode' is going to considered % defined even though it is not a Texinfo command. % \makecond{ifcommanddefined} \def\ifcommanddefined{\parsearg{\doifcmddefined{\let\next=\ifcmddefinedfail}}} % \def\doifcmddefined#1#2{{% \makevalueexpandable \let\next=\empty \expandafter\ifx\csname #2\endcsname\relax #1% If not defined, \let\next as above. \fi \expandafter }\next } \def\ifcmddefinedfail{\doignore{ifcommanddefined}} % @ifcommandnotdefined CMD ... handled similar to @ifclear above. \makecond{ifcommandnotdefined} \def\ifcommandnotdefined{% \parsearg{\doifcmddefined{\else \let\next=\ifcmdnotdefinedfail}}} \def\ifcmdnotdefinedfail{\doignore{ifcommandnotdefined}} % Set the `txicommandconditionals' variable, so documents have a way to % test if the @ifcommand...defined conditionals are available. \set txicommandconditionals % @dircategory CATEGORY -- specify a category of the dir file % which this file should belong to. Ignore this in TeX. \let\dircategory=\comment % @defininfoenclose. \let\definfoenclose=\comment \message{indexing,} % Index generation facilities % Define \newwrite to be identical to plain tex's \newwrite % except not \outer, so it can be used within macros and \if's. \edef\newwrite{\makecsname{ptexnewwrite}} % \newindex {foo} defines an index named IX. % It automatically defines \IXindex such that % \IXindex ...rest of line... puts an entry in the index IX. % It also defines \IXindfile to be the number of the output channel for % the file that accumulates this index. The file's extension is IX. % The name of an index should be no more than 2 characters long % for the sake of vms. % \def\newindex#1{% \expandafter\chardef\csname#1indfile\endcsname=0 \expandafter\xdef\csname#1index\endcsname{% % Define @#1index \noexpand\doindex{#1}} } % @defindex foo == \newindex{foo} % \def\defindex{\parsearg\newindex} % Define @defcodeindex, like @defindex except put all entries in @code. % \def\defcodeindex{\parsearg\newcodeindex} % \def\newcodeindex#1{% \expandafter\chardef\csname#1indfile\endcsname=0 \expandafter\xdef\csname#1index\endcsname{% \noexpand\docodeindex{#1}}% } % The default indices: \newindex{cp}% concepts, \newcodeindex{fn}% functions, \newcodeindex{vr}% variables, \newcodeindex{tp}% types, \newcodeindex{ky}% keys \newcodeindex{pg}% and programs. % @synindex foo bar makes index foo feed into index bar. % Do this instead of @defindex foo if you don't want it as a separate index. % % @syncodeindex foo bar similar, but put all entries made for index foo % inside @code. % \def\synindex#1 #2 {\dosynindex\doindex{#1}{#2}} \def\syncodeindex#1 #2 {\dosynindex\docodeindex{#1}{#2}} % #1 is \doindex or \docodeindex, #2 the index getting redefined (foo), % #3 the target index (bar). \def\dosynindex#1#2#3{% \requireopenindexfile{#3}% % redefine \fooindfile: \expandafter\let\expandafter\temp\expandafter=\csname#3indfile\endcsname \expandafter\let\csname#2indfile\endcsname=\temp % redefine \fooindex: \expandafter\xdef\csname#2index\endcsname{\noexpand#1{#3}}% } % Define \doindex, the driver for all index macros. % Argument #1 is generated by the calling \fooindex macro, % and it is the two-letter name of the index. \def\doindex#1{\edef\indexname{#1}\parsearg\doindexxxx} \def\doindexxxx #1{\doind{\indexname}{#1}} % like the previous two, but they put @code around the argument. \def\docodeindex#1{\edef\indexname{#1}\parsearg\docodeindexxxx} \def\docodeindexxxx #1{\doind{\indexname}{\code{#1}}} % Used for the aux, toc and index files to prevent expansion of Texinfo % commands. % \def\atdummies{% \definedummyletter\@% \definedummyletter\ % \definedummyletter\{% \definedummyletter\}% \definedummyletter\&% % % Do the redefinitions. \definedummies \otherbackslash } % \definedummyword defines \#1 as \string\#1\space, thus effectively % preventing its expansion. This is used only for control words, % not control letters, because the \space would be incorrect for % control characters, but is needed to separate the control word % from whatever follows. % % These can be used both for control words that take an argument and % those that do not. If it is followed by {arg} in the input, then % that will dutifully get written to the index (or wherever). % % For control letters, we have \definedummyletter, which omits the % space. % \def\definedummyword #1{\def#1{\string#1\space}}% \def\definedummyletter#1{\def#1{\string#1}}% \let\definedummyaccent\definedummyletter % Called from \atdummies to prevent the expansion of commands. % \def\definedummies{% % \let\commondummyword\definedummyword \let\commondummyletter\definedummyletter \let\commondummyaccent\definedummyaccent \commondummiesnofonts % \definedummyletter\_% \definedummyletter\-% % % Non-English letters. \definedummyword\AA \definedummyword\AE \definedummyword\DH \definedummyword\L \definedummyword\O \definedummyword\OE \definedummyword\TH \definedummyword\aa \definedummyword\ae \definedummyword\dh \definedummyword\exclamdown \definedummyword\l \definedummyword\o \definedummyword\oe \definedummyword\ordf \definedummyword\ordm \definedummyword\questiondown \definedummyword\ss \definedummyword\th % % Although these internal commands shouldn't show up, sometimes they do. \definedummyword\bf \definedummyword\gtr \definedummyword\hat \definedummyword\less \definedummyword\sf \definedummyword\sl \definedummyword\tclose \definedummyword\tt % \definedummyword\LaTeX \definedummyword\TeX % % Assorted special characters. \definedummyword\ampchar \definedummyword\atchar \definedummyword\arrow \definedummyword\backslashchar \definedummyword\bullet \definedummyword\comma \definedummyword\copyright \definedummyword\registeredsymbol \definedummyword\dots \definedummyword\enddots \definedummyword\entrybreak \definedummyword\equiv \definedummyword\error \definedummyword\euro \definedummyword\expansion \definedummyword\geq \definedummyword\guillemetleft \definedummyword\guillemetright \definedummyword\guilsinglleft \definedummyword\guilsinglright \definedummyword\lbracechar \definedummyword\leq \definedummyword\mathopsup \definedummyword\minus \definedummyword\ogonek \definedummyword\pounds \definedummyword\point \definedummyword\print \definedummyword\quotedblbase \definedummyword\quotedblleft \definedummyword\quotedblright \definedummyword\quoteleft \definedummyword\quoteright \definedummyword\quotesinglbase \definedummyword\rbracechar \definedummyword\result \definedummyword\sub \definedummyword\sup \definedummyword\textdegree % \definedummyword\subentry % % We want to disable all macros so that they are not expanded by \write. \macrolist \let\value\dummyvalue % \normalturnoffactive } % \commondummiesnofonts: common to \definedummies and \indexnofonts. % Define \commondummyletter, \commondummyaccent and \commondummyword before % using. Used for accents, font commands, and various control letters. % \def\commondummiesnofonts{% % Control letters and accents. \commondummyletter\!% \commondummyaccent\"% \commondummyaccent\'% \commondummyletter\*% \commondummyaccent\,% \commondummyletter\.% \commondummyletter\/% \commondummyletter\:% \commondummyaccent\=% \commondummyletter\?% \commondummyaccent\^% \commondummyaccent\`% \commondummyaccent\~% \commondummyword\u \commondummyword\v \commondummyword\H \commondummyword\dotaccent \commondummyword\ogonek \commondummyword\ringaccent \commondummyword\tieaccent \commondummyword\ubaraccent \commondummyword\udotaccent \commondummyword\dotless % % Texinfo font commands. \commondummyword\b \commondummyword\i \commondummyword\r \commondummyword\sansserif \commondummyword\sc \commondummyword\slanted \commondummyword\t % % Commands that take arguments. \commondummyword\abbr \commondummyword\acronym \commondummyword\anchor \commondummyword\cite \commondummyword\code \commondummyword\command \commondummyword\dfn \commondummyword\dmn \commondummyword\email \commondummyword\emph \commondummyword\env \commondummyword\file \commondummyword\image \commondummyword\indicateurl \commondummyword\inforef \commondummyword\kbd \commondummyword\key \commondummyword\math \commondummyword\option \commondummyword\pxref \commondummyword\ref \commondummyword\samp \commondummyword\strong \commondummyword\tie \commondummyword\U \commondummyword\uref \commondummyword\url \commondummyword\var \commondummyword\verb \commondummyword\w \commondummyword\xref } \let\indexlbrace\relax \let\indexrbrace\relax \let\indexatchar\relax \let\indexbackslash\relax {\catcode`\@=0 \catcode`\\=13 @gdef@backslashdisappear{@def\{}} } { \catcode`\<=13 \catcode`\-=13 \catcode`\`=13 \gdef\indexnonalnumdisappear{% \expandafter\ifx\csname SETtxiindexlquoteignore\endcsname\relax\else % @set txiindexlquoteignore makes us ignore left quotes in the sort term. % (Introduced for FSFS 2nd ed.) \let`=\empty \fi % \expandafter\ifx\csname SETtxiindexbackslashignore\endcsname\relax\else \backslashdisappear \fi % \expandafter\ifx\csname SETtxiindexhyphenignore\endcsname\relax\else \def-{}% \fi \expandafter\ifx\csname SETtxiindexlessthanignore\endcsname\relax\else \def<{}% \fi \expandafter\ifx\csname SETtxiindexatsignignore\endcsname\relax\else \def\@{}% \fi } \gdef\indexnonalnumreappear{% \let-\normaldash \let<\normalless } } % \indexnofonts is used when outputting the strings to sort the index % by, and when constructing control sequence names. It eliminates all % control sequences and just writes whatever the best ASCII sort string % would be for a given command (usually its argument). % \def\indexnofonts{% % Accent commands should become @asis. \def\commondummyaccent##1{\let##1\asis}% % We can just ignore other control letters. \def\commondummyletter##1{\let##1\empty}% % All control words become @asis by default; overrides below. \let\commondummyword\commondummyaccent \commondummiesnofonts % % Don't no-op \tt, since it isn't a user-level command % and is used in the definitions of the active chars like <, >, |, etc. % Likewise with the other plain tex font commands. %\let\tt=\asis % \def\ { }% \def\@{@}% \def\_{\normalunderscore}% \def\-{}% @- shouldn't affect sorting % \uccode`\1=`\{ \uppercase{\def\{{1}}% \uccode`\1=`\} \uppercase{\def\}{1}}% \let\lbracechar\{% \let\rbracechar\}% % % Non-English letters. \def\AA{AA}% \def\AE{AE}% \def\DH{DZZ}% \def\L{L}% \def\OE{OE}% \def\O{O}% \def\TH{TH}% \def\aa{aa}% \def\ae{ae}% \def\dh{dzz}% \def\exclamdown{!}% \def\l{l}% \def\oe{oe}% \def\ordf{a}% \def\ordm{o}% \def\o{o}% \def\questiondown{?}% \def\ss{ss}% \def\th{th}% % \def\LaTeX{LaTeX}% \def\TeX{TeX}% % % Assorted special characters. \defglyph gives the control sequence a % definition that removes the {} that follows its use. \defglyph\atchar{@}% \defglyph\arrow{->}% \defglyph\bullet{bullet}% \defglyph\comma{,}% \defglyph\copyright{copyright}% \defglyph\dots{...}% \defglyph\enddots{...}% \defglyph\equiv{==}% \defglyph\error{error}% \defglyph\euro{euro}% \defglyph\expansion{==>}% \defglyph\geq{>=}% \defglyph\guillemetleft{<<}% \defglyph\guillemetright{>>}% \defglyph\guilsinglleft{<}% \defglyph\guilsinglright{>}% \defglyph\leq{<=}% \defglyph\lbracechar{\{}% \defglyph\minus{-}% \defglyph\point{.}% \defglyph\pounds{pounds}% \defglyph\print{-|}% \defglyph\quotedblbase{"}% \defglyph\quotedblleft{"}% \defglyph\quotedblright{"}% \defglyph\quoteleft{`}% \defglyph\quoteright{'}% \defglyph\quotesinglbase{,}% \defglyph\rbracechar{\}}% \defglyph\registeredsymbol{R}% \defglyph\result{=>}% \defglyph\textdegree{o}% % % We need to get rid of all macros, leaving only the arguments (if present). % Of course this is not nearly correct, but it is the best we can do for now. % makeinfo does not expand macros in the argument to @deffn, which ends up % writing an index entry, and texindex isn't prepared for an index sort entry % that starts with \. % % Since macro invocations are followed by braces, we can just redefine them % to take a single TeX argument. The case of a macro invocation that % goes to end-of-line is not handled. % \macrolist \let\value\indexnofontsvalue } \def\defglyph#1#2{\def#1##1{#2}} % see above % #1 is the index name, #2 is the entry text. \def\doind#1#2{% \iflinks {% % \requireopenindexfile{#1}% \edef\writeto{\csname#1indfile\endcsname}% % \def\indextext{#2}% \safewhatsit\doindwrite }% \fi } % Check if an index file has been opened, and if not, open it. \def\requireopenindexfile#1{% \ifnum\csname #1indfile\endcsname=0 \expandafter\newwrite \csname#1indfile\endcsname \edef\suffix{#1}% % A .fls suffix would conflict with the file extension for the output % of -recorder, so use .f1s instead. \ifx\suffix\indexisfl\def\suffix{f1}\fi % Open the file \immediate\openout\csname#1indfile\endcsname \jobname.\suffix % Using \immediate above here prevents an object entering into the current % box, which could confound checks such as those in \safewhatsit for % preceding skips. \typeout{Writing index file \jobname.\suffix}% \fi} \def\indexisfl{fl} % Definition for writing index entry sort key. { \catcode`\-=13 \gdef\indexwritesortas{% \begingroup \indexnonalnumreappear \indexwritesortasxxx} \gdef\indexwritesortasxxx#1{% \xdef\indexsortkey{#1}\endgroup} } \def\indexwriteseealso#1{ \gdef\pagenumbertext{\string\seealso{#1}}% } \def\indexwriteseeentry#1{ \gdef\pagenumbertext{\string\seeentry{#1}}% } % The default definitions \def\sortas#1{}% \def\seealso#1{\i{\putwordSeeAlso}\ #1}% for sorted index file only \def\putwordSeeAlso{See also} \def\seeentry#1{\i{\putwordSee}\ #1}% for sorted index file only % Given index entry text like "aaa @subentry bbb @sortas{ZZZ}": % * Set \bracedtext to "{aaa}{bbb}" % * Set \fullindexsortkey to "aaa @subentry ZZZ" % * If @seealso occurs, set \pagenumbertext % \def\splitindexentry#1{% \gdef\fullindexsortkey{}% \xdef\bracedtext{}% \def\sep{}% \def\seealso##1{}% \def\seeentry##1{}% \expandafter\doindexsegment#1\subentry\finish\subentry } % append the results from the next segment \def\doindexsegment#1\subentry{% \def\segment{#1}% \ifx\segment\isfinish \else % % Fully expand the segment, throwing away any @sortas directives, and % trim spaces. \edef\trimmed{\segment}% \edef\trimmed{\expandafter\eatspaces\expandafter{\trimmed}}% % \xdef\bracedtext{\bracedtext{\trimmed}}% % % Get the string to sort by. Process the segment with all % font commands turned off. \bgroup \let\sortas\indexwritesortas \let\seealso\indexwriteseealso \let\seeentry\indexwriteseeentry \indexnofonts % The braces around the commands are recognized by texindex. \def\lbracechar{{\string\indexlbrace}}% \def\rbracechar{{\string\indexrbrace}}% \let\{=\lbracechar \let\}=\rbracechar \def\@{{\string\indexatchar}}% \def\atchar##1{\@}% \def\backslashchar{{\string\indexbackslash}}% \uccode`\~=`\\ \uppercase{\let~\backslashchar}% % \let\indexsortkey\empty \global\let\pagenumbertext\empty % Execute the segment and throw away the typeset output. This executes % any @sortas or @seealso commands in this segment. \setbox\dummybox = \hbox{\segment}% \ifx\indexsortkey\empty{% \indexnonalnumdisappear \xdef\trimmed{\segment}% \xdef\trimmed{\expandafter\eatspaces\expandafter{\trimmed}}% \xdef\indexsortkey{\trimmed}% \ifx\indexsortkey\empty\xdef\indexsortkey{ }\fi }\fi % % Append to \fullindexsortkey. \edef\tmp{\gdef\noexpand\fullindexsortkey{% \fullindexsortkey\sep\indexsortkey}}% \tmp \egroup \def\sep{\subentry}% % \expandafter\doindexsegment \fi } \def\isfinish{\finish}% \newbox\dummybox % used above \let\subentry\relax % Use \ instead of @ in index files. To support old texi2dvi and texindex. % This works without changing the escape character used in the toc or aux % files because the index entries are fully expanded here, and \string uses % the current value of \escapechar. \def\escapeisbackslash{\escapechar=`\\} % Use \ in index files by default. texi2dvi didn't support @ as the escape % character (as it checked for "\entry" in the files, and not "@entry"). When % the new version of texi2dvi has had a chance to become more prevalent, then % the escape character can change back to @ again. This should be an easy % change to make now because both @ and \ are only used as escape characters in % index files, never standing for themselves. % \set txiindexescapeisbackslash % Write the entry in \indextext to the index file. % \def\doindwrite{% \maybemarginindex % \atdummies % \expandafter\ifx\csname SETtxiindexescapeisbackslash\endcsname\relax\else \escapeisbackslash \fi % % For texindex which always views { and } as separators. \def\{{\lbracechar{}}% \def\}{\rbracechar{}}% \uccode`\~=`\\ \uppercase{\def~{\backslashchar{}}}% % % Split the entry into primary entry and any subentries, and get the index % sort key. \splitindexentry\indextext % % Set up the complete index entry, with both the sort key and % the original text, including any font commands. We write % three arguments to \entry to the .?? file (four in the % subentry case), texindex reduces to two when writing the .??s % sorted result. % \edef\temp{% \write\writeto{% \string\entry{\fullindexsortkey}% {\ifx\pagenumbertext\empty\noexpand\folio\else\pagenumbertext\fi}% \bracedtext}% }% \temp } % Put the index entry in the margin if desired (undocumented). \def\maybemarginindex{% \ifx\SETmarginindex\relax\else \insert\margin{\hbox{\vrule height8pt depth3pt width0pt \relax\indextext}}% \fi } \let\SETmarginindex=\relax % Take care of unwanted page breaks/skips around a whatsit: % % If a skip is the last thing on the list now, preserve it % by backing up by \lastskip, doing the \write, then inserting % the skip again. Otherwise, the whatsit generated by the % \write or \pdfdest will make \lastskip zero. The result is that % sequences like this: % @end defun % @tindex whatever % @defun ... % will have extra space inserted, because the \medbreak in the % start of the @defun won't see the skip inserted by the @end of % the previous defun. % % But don't do any of this if we're not in vertical mode. We % don't want to do a \vskip and prematurely end a paragraph. % % Avoid page breaks due to these extra skips, too. % % But wait, there is a catch there: % We'll have to check whether \lastskip is zero skip. \ifdim is not % sufficient for this purpose, as it ignores stretch and shrink parts % of the skip. The only way seems to be to check the textual % representation of the skip. % % The following is almost like \def\zeroskipmacro{0.0pt} except that % the ``p'' and ``t'' characters have catcode \other, not 11 (letter). % \edef\zeroskipmacro{\expandafter\the\csname z@skip\endcsname} % \newskip\whatsitskip \newcount\whatsitpenalty % % ..., ready, GO: % \def\safewhatsit#1{\ifhmode #1% \else % \lastskip and \lastpenalty cannot both be nonzero simultaneously. \whatsitskip = \lastskip \edef\lastskipmacro{\the\lastskip}% \whatsitpenalty = \lastpenalty % % If \lastskip is nonzero, that means the last item was a % skip. And since a skip is discardable, that means this % -\whatsitskip glue we're inserting is preceded by a % non-discardable item, therefore it is not a potential % breakpoint, therefore no \nobreak needed. \ifx\lastskipmacro\zeroskipmacro \else \vskip-\whatsitskip \fi % #1% % \ifx\lastskipmacro\zeroskipmacro % If \lastskip was zero, perhaps the last item was a penalty, and % perhaps it was >=10000, e.g., a \nobreak. In that case, we want % to re-insert the same penalty (values >10000 are used for various % signals); since we just inserted a non-discardable item, any % following glue (such as a \parskip) would be a breakpoint. For example: % @deffn deffn-whatever % @vindex index-whatever % Description. % would allow a break between the index-whatever whatsit % and the "Description." paragraph. \ifnum\whatsitpenalty>9999 \penalty\whatsitpenalty \fi \else % On the other hand, if we had a nonzero \lastskip, % this make-up glue would be preceded by a non-discardable item % (the whatsit from the \write), so we must insert a \nobreak. \nobreak\vskip\whatsitskip \fi \fi} % The index entry written in the file actually looks like % \entry {sortstring}{page}{topic} % or % \entry {sortstring}{page}{topic}{subtopic} % The texindex program reads in these files and writes files % containing these kinds of lines: % \initial {c} % before the first topic whose initial is c % \entry {topic}{pagelist} % for a topic that is used without subtopics % \primary {topic} % \entry {topic}{} % for the beginning of a topic that is used with subtopics % \secondary {subtopic}{pagelist} % for each subtopic. % \secondary {subtopic}{} % for a subtopic with sub-subtopics % \tertiary {subtopic}{subsubtopic}{pagelist} % for each sub-subtopic. % Define the user-accessible indexing commands % @findex, @vindex, @kindex, @cindex. \def\findex {\fnindex} \def\kindex {\kyindex} \def\cindex {\cpindex} \def\vindex {\vrindex} \def\tindex {\tpindex} \def\pindex {\pgindex} % Define the macros used in formatting output of the sorted index material. % @printindex causes a particular index (the ??s file) to get printed. % It does not print any chapter heading (usually an @unnumbered). % \parseargdef\printindex{\begingroup \dobreak \chapheadingskip{10000}% % \smallfonts \rm \tolerance = 9500 \plainfrenchspacing \everypar = {}% don't want the \kern\-parindent from indentation suppression. % % See comment in \requireopenindexfile. \def\indexname{#1}\ifx\indexname\indexisfl\def\indexname{f1}\fi % % See if the index file exists and is nonempty. \openin 1 \jobname.\indexname s \ifeof 1 % \enddoublecolumns gets confused if there is no text in the index, % and it loses the chapter title and the aux file entries for the % index. The easiest way to prevent this problem is to make sure % there is some text. \putwordIndexNonexistent \typeout{No file \jobname.\indexname s.}% \else % If the index file exists but is empty, then \openin leaves \ifeof % false. We have to make TeX try to read something from the file, so % it can discover if there is anything in it. \read 1 to \thisline \ifeof 1 \putwordIndexIsEmpty \else \expandafter\printindexzz\thisline\relax\relax\finish% \fi \fi \closein 1 \endgroup} % If the index file starts with a backslash, forgo reading the index % file altogether. If somebody upgrades texinfo.tex they may still have % old index files using \ as the escape character. Reading this would % at best lead to typesetting garbage, at worst a TeX syntax error. \def\printindexzz#1#2\finish{% \expandafter\ifx\csname SETtxiindexescapeisbackslash\endcsname\relax \uccode`\~=`\\ \uppercase{\if\noexpand~}\noexpand#1 \expandafter\ifx\csname SETtxiskipindexfileswithbackslash\endcsname\relax \errmessage{% ERROR: A sorted index file in an obsolete format was skipped. To fix this problem, please upgrade your version of 'texi2dvi' or 'texi2pdf' to that at . If you are using an old version of 'texindex' (part of the Texinfo distribution), you may also need to upgrade to a newer version (at least 6.0). You may be able to typeset the index if you run 'texindex \jobname.\indexname' yourself. You could also try setting the 'txiindexescapeisbackslash' flag by running a command like 'texi2dvi -t "@set txiindexescapeisbackslash" \jobname.texi'. If you do this, Texinfo will try to use index files in the old format. If you continue to have problems, deleting the index files and starting again might help (with 'rm \jobname.?? \jobname.??s')% }% \else (Skipped sorted index file in obsolete format) \fi \else \begindoublecolumns \input \jobname.\indexname s \enddoublecolumns \fi \else \begindoublecolumns \catcode`\\=0\relax \catcode`\@=12\relax \input \jobname.\indexname s \enddoublecolumns \fi } % These macros are used by the sorted index file itself. % Change them to control the appearance of the index. {\catcode`\/=13 \catcode`\-=13 \catcode`\^=13 \catcode`\~=13 \catcode`\_=13 \catcode`\|=13 \catcode`\<=13 \catcode`\>=13 \catcode`\+=13 \catcode`\"=13 \catcode`\$=3 \gdef\initialglyphs{% % special control sequences used in the index sort key \let\indexlbrace\{% \let\indexrbrace\}% \let\indexatchar\@% \def\indexbackslash{\math{\backslash}}% % % Some changes for non-alphabetic characters. Using the glyphs from the % math fonts looks more consistent than the typewriter font used elsewhere % for these characters. \uccode`\~=`\\ \uppercase{\def~{\math{\backslash}}} % % In case @\ is used for backslash \uppercase{\let\\=~} % Can't get bold backslash so don't use bold forward slash \catcode`\/=13 \def/{{\secrmnotbold \normalslash}}% \def-{{\normaldash\normaldash}}% en dash `--' \def^{{\chapbf \normalcaret}}% \def~{{\chapbf \normaltilde}}% \def\_{% \leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em }% \def|{$\vert$}% \def<{$\less$}% \def>{$\gtr$}% \def+{$\normalplus$}% }} \def\initial{% \bgroup \initialglyphs \initialx } \def\initialx#1{% % Remove any glue we may have, we'll be inserting our own. \removelastskip % % We like breaks before the index initials, so insert a bonus. % The glue before the bonus allows a little bit of space at the % bottom of a column to reduce an increase in inter-line spacing. \nobreak \vskip 0pt plus 5\baselineskip \penalty -300 \vskip 0pt plus -5\baselineskip % % Typeset the initial. Making this add up to a whole number of % baselineskips increases the chance of the dots lining up from column % to column. It still won't often be perfect, because of the stretch % we need before each entry, but it's better. % % No shrink because it confuses \balancecolumns. \vskip 1.67\baselineskip plus 1\baselineskip \leftline{\secfonts \kern-0.05em \secbf #1}% % \secfonts is inside the argument of \leftline so that the change of % \baselineskip will not affect any glue inserted before the vbox that % \leftline creates. % Do our best not to break after the initial. \nobreak \vskip .33\baselineskip plus .1\baselineskip \egroup % \initialglyphs } \newdimen\entryrightmargin \entryrightmargin=0pt % \entry typesets a paragraph consisting of the text (#1), dot leaders, and % then page number (#2) flushed to the right margin. It is used for index % and table of contents entries. The paragraph is indented by \leftskip. % \def\entry{% \begingroup % % Start a new paragraph if necessary, so our assignments below can't % affect previous text. \par % % No extra space above this paragraph. \parskip = 0in % % When reading the text of entry, convert explicit line breaks % from @* into spaces. The user might give these in long section % titles, for instance. \def\*{\unskip\space\ignorespaces}% \def\entrybreak{\hfil\break}% An undocumented command % % Swallow the left brace of the text (first parameter): \afterassignment\doentry \let\temp = } \def\entrybreak{\unskip\space\ignorespaces}% \def\doentry{% % Save the text of the entry \global\setbox\boxA=\hbox\bgroup \bgroup % Instead of the swallowed brace. \noindent \aftergroup\finishentry % And now comes the text of the entry. % Not absorbing as a macro argument reduces the chance of problems % with catcodes occurring. } {\catcode`\@=11 \gdef\finishentry#1{% \egroup % end box A \dimen@ = \wd\boxA % Length of text of entry \global\setbox\boxA=\hbox\bgroup \unhbox\boxA % #1 is the page number. % % Get the width of the page numbers, and only use % leaders if they are present. \global\setbox\boxB = \hbox{#1}% \ifdim\wd\boxB = 0pt \null\nobreak\hfill\ % \else % \null\nobreak\indexdotfill % Have leaders before the page number. % \ifpdforxetex \pdfgettoks#1.% \hskip\skip\thinshrinkable\the\toksA \else \hskip\skip\thinshrinkable #1% \fi \fi \egroup % end \boxA \ifdim\wd\boxB = 0pt \noindent\unhbox\boxA\par \nobreak \else\bgroup % We want the text of the entries to be aligned to the left, and the % page numbers to be aligned to the right. % \parindent = 0pt \advance\leftskip by 0pt plus 1fil \advance\leftskip by 0pt plus -1fill \rightskip = 0pt plus -1fil \advance\rightskip by 0pt plus 1fill % Cause last line, which could consist of page numbers on their own % if the list of page numbers is long, to be aligned to the right. \parfillskip=0pt plus -1fill % \advance\rightskip by \entryrightmargin % Determine how far we can stretch into the margin. % This allows, e.g., "Appendix H GNU Free Documentation License" to % fit on one line in @letterpaper format. \ifdim\entryrightmargin>2.1em \dimen@i=2.1em \else \dimen@i=0em \fi \advance \parfillskip by 0pt minus 1\dimen@i % \dimen@ii = \hsize \advance\dimen@ii by -1\leftskip \advance\dimen@ii by -1\entryrightmargin \advance\dimen@ii by 1\dimen@i \ifdim\wd\boxA > \dimen@ii % If the entry doesn't fit in one line \ifdim\dimen@ > 0.8\dimen@ii % due to long index text % Try to split the text roughly evenly. \dimen@ will be the length of % the first line. \dimen@ = 0.7\dimen@ \dimen@ii = \hsize \ifnum\dimen@>\dimen@ii % If the entry is too long (for example, if it needs more than % two lines), use all the space in the first line. \dimen@ = \dimen@ii \fi \advance\leftskip by 0pt plus 1fill % ragged right \advance \dimen@ by 1\rightskip \parshape = 2 0pt \dimen@ 0em \dimen@ii % Ideally we'd add a finite glue at the end of the first line only, % instead of using \parshape with explicit line lengths, but TeX % doesn't seem to provide a way to do such a thing. % % Indent all lines but the first one. \advance\leftskip by 1em \advance\parindent by -1em \fi\fi \indent % start paragraph \unhbox\boxA % % Do not prefer a separate line ending with a hyphen to fewer lines. \finalhyphendemerits = 0 % % Word spacing - no stretch \spaceskip=\fontdimen2\font minus \fontdimen4\font % \linepenalty=1000 % Discourage line breaks. \hyphenpenalty=5000 % Discourage hyphenation. % \par % format the paragraph \egroup % The \vbox \fi \endgroup }} \newskip\thinshrinkable \skip\thinshrinkable=.15em minus .15em % Like plain.tex's \dotfill, except uses up at least 1 em. % The filll stretch here overpowers both the fil and fill stretch to push % the page number to the right. \def\indexdotfill{\cleaders \hbox{$\mathsurround=0pt \mkern1.5mu.\mkern1.5mu$}\hskip 1em plus 1filll} \def\primary #1{\line{#1\hfil}} \def\secondary{\indententry{0.5cm}} \def\tertiary{\indententry{1cm}} \def\indententry#1#2#3{% \bgroup \leftskip=#1 \entry{#2}{#3}% \egroup } % Define two-column mode, which we use to typeset indexes. % Adapted from the TeXbook, page 416, which is to say, % the manmac.tex format used to print the TeXbook itself. \catcode`\@=11 % private names \newbox\partialpage \newdimen\doublecolumnhsize \def\begindoublecolumns{\begingroup % ended by \enddoublecolumns % If not much space left on page, start a new page. \ifdim\pagetotal>0.8\vsize\vfill\eject\fi % % Grab any single-column material above us. \output = {% \savetopmark % \global\setbox\partialpage = \vbox{% % Unvbox the main output page. \unvbox\PAGE \kern-\topskip \kern\baselineskip }% }% \eject % run that output routine to set \partialpage % % Use the double-column output routine for subsequent pages. \output = {\doublecolumnout}% % % Change the page size parameters. We could do this once outside this % routine, in each of @smallbook, @afourpaper, and the default 8.5x11 % format, but then we repeat the same computation. Repeating a couple % of assignments once per index is clearly meaningless for the % execution time, so we may as well do it in one place. % % First we halve the line length, less a little for the gutter between % the columns. We compute the gutter based on the line length, so it % changes automatically with the paper format. The magic constant % below is chosen so that the gutter has the same value (well, +-<1pt) % as it did when we hard-coded it. % % We put the result in a separate register, \doublecolumhsize, so we % can restore it in \pagesofar, after \hsize itself has (potentially) % been clobbered. % \doublecolumnhsize = \hsize \advance\doublecolumnhsize by -.04154\hsize \divide\doublecolumnhsize by 2 \hsize = \doublecolumnhsize % % Get the available space for the double columns -- the normal % (undoubled) page height minus any material left over from the % previous page. \advance\vsize by -\ht\partialpage \vsize = 2\vsize % % For the benefit of balancing columns \advance\baselineskip by 0pt plus 0.5pt } % The double-column output routine for all double-column pages except % the last, which is done by \balancecolumns. % \def\doublecolumnout{% % \savetopmark \splittopskip=\topskip \splitmaxdepth=\maxdepth \dimen@ = \vsize \divide\dimen@ by 2 % % box0 will be the left-hand column, box2 the right. \setbox0=\vsplit\PAGE to\dimen@ \setbox2=\vsplit\PAGE to\dimen@ \global\advance\vsize by 2\ht\partialpage \onepageout\pagesofar % empty except for the first time we are called \unvbox\PAGE \penalty\outputpenalty } % % Re-output the contents of the output page -- any previous material, % followed by the two boxes we just split, in box0 and box2. \def\pagesofar{% \unvbox\partialpage % \hsize = \doublecolumnhsize \wd0=\hsize \wd2=\hsize \hbox to\txipagewidth{\box0\hfil\box2}% } % Finished with double columns. \def\enddoublecolumns{% % The following penalty ensures that the page builder is exercised % _before_ we change the output routine. This is necessary in the % following situation: % % The last section of the index consists only of a single entry. % Before this section, \pagetotal is less than \pagegoal, so no % break occurs before the last section starts. However, the last % section, consisting of \initial and the single \entry, does not % fit on the page and has to be broken off. Without the following % penalty the page builder will not be exercised until \eject % below, and by that time we'll already have changed the output % routine to the \balancecolumns version, so the next-to-last % double-column page will be processed with \balancecolumns, which % is wrong: The two columns will go to the main vertical list, with % the broken-off section in the recent contributions. As soon as % the output routine finishes, TeX starts reconsidering the page % break. The two columns and the broken-off section both fit on the % page, because the two columns now take up only half of the page % goal. When TeX sees \eject from below which follows the final % section, it invokes the new output routine that we've set after % \balancecolumns below; \onepageout will try to fit the two columns % and the final section into the vbox of \txipageheight (see % \pagebody), causing an overfull box. % % Note that glue won't work here, because glue does not exercise the % page builder, unlike penalties (see The TeXbook, pp. 280-281). \penalty0 % \output = {% % Split the last of the double-column material. \savetopmark \balancecolumns }% \eject % call the \output just set \ifdim\pagetotal=0pt % Having called \balancecolumns once, we do not % want to call it again. Therefore, reset \output to its normal % definition right away. \global\output=\expandafter{\the\defaultoutput} % \endgroup % started in \begindoublecolumns % Leave the double-column material on the current page, no automatic % page break. \box\balancedcolumns % % \pagegoal was set to the doubled \vsize above, since we restarted % the current page. We're now back to normal single-column % typesetting, so reset \pagegoal to the normal \vsize. \global\vsize = \txipageheight % \pagegoal = \txipageheight % \else % We had some left-over material. This might happen when \doublecolumnout % is called in \balancecolumns. Try again. \expandafter\enddoublecolumns \fi } \newbox\balancedcolumns \setbox\balancedcolumns=\vbox{shouldnt see this}% % % Only called for the last of the double column material. \doublecolumnout % does the others. \def\balancecolumns{% \setbox0 = \vbox{\unvbox\PAGE}% like \box255 but more efficient, see p.120. \dimen@ = \ht0 \ifdim\dimen@<7\baselineskip % Don't split a short final column in two. \setbox2=\vbox{}% \global\setbox\balancedcolumns=\vbox{\pagesofar}% \else % double the leading vertical space \advance\dimen@ by \topskip \advance\dimen@ by-\baselineskip \divide\dimen@ by 2 % target to split to \dimen@ii = \dimen@ \splittopskip = \topskip % Loop until left column is at least as high as the right column. {% \vbadness = 10000 \loop \global\setbox3 = \copy0 \global\setbox1 = \vsplit3 to \dimen@ \ifdim\ht1<\ht3 \global\advance\dimen@ by 1pt \repeat }% % Now the left column is in box 1, and the right column in box 3. % % Check whether the left column has come out higher than the page itself. % (Note that we have doubled \vsize for the double columns, so % the actual height of the page is 0.5\vsize). \ifdim2\ht1>\vsize % It appears that we have been called upon to balance too much material. % Output some of it with \doublecolumnout, leaving the rest on the page. \setbox\PAGE=\box0 \doublecolumnout \else % Compare the heights of the two columns. \ifdim4\ht1>5\ht3 % Column heights are too different, so don't make their bottoms % flush with each other. \setbox2=\vbox to \ht1 {\unvbox3\vfill}% \setbox0=\vbox to \ht1 {\unvbox1\vfill}% \else % Make column bottoms flush with each other. \setbox2=\vbox to\ht1{\unvbox3\unskip}% \setbox0=\vbox to\ht1{\unvbox1\unskip}% \fi \global\setbox\balancedcolumns=\vbox{\pagesofar}% \fi \fi % } \catcode`\@ = \other \message{sectioning,} % Chapters, sections, etc. % Let's start with @part. \outer\parseargdef\part{\partzzz{#1}} \def\partzzz#1{% \chapoddpage \null \vskip.3\vsize % move it down on the page a bit \begingroup \noindent \titlefonts\rm #1\par % the text \let\lastnode=\empty % no node to associate with \writetocentry{part}{#1}{}% but put it in the toc \headingsoff % no headline or footline on the part page % This outputs a mark at the end of the page that clears \thischapter % and \thissection, as is done in \startcontents. \let\pchapsepmacro\relax \chapmacro{}{Yomitfromtoc}{}% \chapoddpage \endgroup } % \unnumberedno is an oxymoron. But we count the unnumbered % sections so that we can refer to them unambiguously in the pdf % outlines by their "section number". We avoid collisions with chapter % numbers by starting them at 10000. (If a document ever has 10000 % chapters, we're in trouble anyway, I'm sure.) \newcount\unnumberedno \unnumberedno = 10000 \newcount\chapno \newcount\secno \secno=0 \newcount\subsecno \subsecno=0 \newcount\subsubsecno \subsubsecno=0 % This counter is funny since it counts through charcodes of letters A, B, ... \newcount\appendixno \appendixno = `\@ % % \def\appendixletter{\char\the\appendixno} % We do the following ugly conditional instead of the above simple % construct for the sake of pdftex, which needs the actual % letter in the expansion, not just typeset. % \def\appendixletter{% \ifnum\appendixno=`A A% \else\ifnum\appendixno=`B B% \else\ifnum\appendixno=`C C% \else\ifnum\appendixno=`D D% \else\ifnum\appendixno=`E E% \else\ifnum\appendixno=`F F% \else\ifnum\appendixno=`G G% \else\ifnum\appendixno=`H H% \else\ifnum\appendixno=`I I% \else\ifnum\appendixno=`J J% \else\ifnum\appendixno=`K K% \else\ifnum\appendixno=`L L% \else\ifnum\appendixno=`M M% \else\ifnum\appendixno=`N N% \else\ifnum\appendixno=`O O% \else\ifnum\appendixno=`P P% \else\ifnum\appendixno=`Q Q% \else\ifnum\appendixno=`R R% \else\ifnum\appendixno=`S S% \else\ifnum\appendixno=`T T% \else\ifnum\appendixno=`U U% \else\ifnum\appendixno=`V V% \else\ifnum\appendixno=`W W% \else\ifnum\appendixno=`X X% \else\ifnum\appendixno=`Y Y% \else\ifnum\appendixno=`Z Z% % The \the is necessary, despite appearances, because \appendixletter is % expanded while writing the .toc file. \char\appendixno is not % expandable, thus it is written literally, thus all appendixes come out % with the same letter (or @) in the toc without it. \else\char\the\appendixno \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi} % Each @chapter defines these (using marks) as the number+name, number % and name of the chapter. Page headings and footings can use % these. @section does likewise. \def\thischapter{} \def\thischapternum{} \def\thischaptername{} \def\thissection{} \def\thissectionnum{} \def\thissectionname{} \newcount\absseclevel % used to calculate proper heading level \newcount\secbase\secbase=0 % @raisesections/@lowersections modify this count % @raisesections: treat @section as chapter, @subsection as section, etc. \def\raisesections{\global\advance\secbase by -1} % @lowersections: treat @chapter as section, @section as subsection, etc. \def\lowersections{\global\advance\secbase by 1} % we only have subsub. \chardef\maxseclevel = 3 % % A numbered section within an unnumbered changes to unnumbered too. % To achieve this, remember the "biggest" unnum. sec. we are currently in: \chardef\unnlevel = \maxseclevel % % Trace whether the current chapter is an appendix or not: % \chapheadtype is "N" or "A", unnumbered chapters are ignored. \def\chapheadtype{N} % Choose a heading macro % #1 is heading type % #2 is heading level % #3 is text for heading \def\genhead#1#2#3{% % Compute the abs. sec. level: \absseclevel=#2 \advance\absseclevel by \secbase % Make sure \absseclevel doesn't fall outside the range: \ifnum \absseclevel < 0 \absseclevel = 0 \else \ifnum \absseclevel > 3 \absseclevel = 3 \fi \fi % The heading type: \def\headtype{#1}% \if \headtype U% \ifnum \absseclevel < \unnlevel \chardef\unnlevel = \absseclevel \fi \else % Check for appendix sections: \ifnum \absseclevel = 0 \edef\chapheadtype{\headtype}% \else \if \headtype A\if \chapheadtype N% \errmessage{@appendix... within a non-appendix chapter}% \fi\fi \fi % Check for numbered within unnumbered: \ifnum \absseclevel > \unnlevel \def\headtype{U}% \else \chardef\unnlevel = 3 \fi \fi % Now print the heading: \if \headtype U% \ifcase\absseclevel \unnumberedzzz{#3}% \or \unnumberedseczzz{#3}% \or \unnumberedsubseczzz{#3}% \or \unnumberedsubsubseczzz{#3}% \fi \else \if \headtype A% \ifcase\absseclevel \appendixzzz{#3}% \or \appendixsectionzzz{#3}% \or \appendixsubseczzz{#3}% \or \appendixsubsubseczzz{#3}% \fi \else \ifcase\absseclevel \chapterzzz{#3}% \or \seczzz{#3}% \or \numberedsubseczzz{#3}% \or \numberedsubsubseczzz{#3}% \fi \fi \fi \suppressfirstparagraphindent } % an interface: \def\numhead{\genhead N} \def\apphead{\genhead A} \def\unnmhead{\genhead U} % @chapter, @appendix, @unnumbered. Increment top-level counter, reset % all lower-level sectioning counters to zero. % % Also set \chaplevelprefix, which we prepend to @float sequence numbers % (e.g., figures), q.v. By default (before any chapter), that is empty. \let\chaplevelprefix = \empty % \outer\parseargdef\chapter{\numhead0{#1}} % normally numhead0 calls chapterzzz \def\chapterzzz#1{% % section resetting is \global in case the chapter is in a group, such % as an @include file. \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 \global\advance\chapno by 1 % % Used for \float. \gdef\chaplevelprefix{\the\chapno.}% \resetallfloatnos % % \putwordChapter can contain complex things in translations. \toks0=\expandafter{\putwordChapter}% \message{\the\toks0 \space \the\chapno}% % % Write the actual heading. \chapmacro{#1}{Ynumbered}{\the\chapno}% % % So @section and the like are numbered underneath this chapter. \global\let\section = \numberedsec \global\let\subsection = \numberedsubsec \global\let\subsubsection = \numberedsubsubsec } \outer\parseargdef\appendix{\apphead0{#1}} % normally calls appendixzzz % \def\appendixzzz#1{% \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 \global\advance\appendixno by 1 \gdef\chaplevelprefix{\appendixletter.}% \resetallfloatnos % % \putwordAppendix can contain complex things in translations. \toks0=\expandafter{\putwordAppendix}% \message{\the\toks0 \space \appendixletter}% % \chapmacro{#1}{Yappendix}{\appendixletter}% % \global\let\section = \appendixsec \global\let\subsection = \appendixsubsec \global\let\subsubsection = \appendixsubsubsec } % normally unnmhead0 calls unnumberedzzz: \outer\parseargdef\unnumbered{\unnmhead0{#1}} \def\unnumberedzzz#1{% \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 \global\advance\unnumberedno by 1 % % Since an unnumbered has no number, no prefix for figures. \global\let\chaplevelprefix = \empty \resetallfloatnos % % This used to be simply \message{#1}, but TeX fully expands the % argument to \message. Therefore, if #1 contained @-commands, TeX % expanded them. For example, in `@unnumbered The @cite{Book}', TeX % expanded @cite (which turns out to cause errors because \cite is meant % to be executed, not expanded). % % Anyway, we don't want the fully-expanded definition of @cite to appear % as a result of the \message, we just want `@cite' itself. We use % \the to achieve this: TeX expands \the only once, % simply yielding the contents of . (We also do this for % the toc entries.) \toks0 = {#1}% \message{(\the\toks0)}% % \chapmacro{#1}{Ynothing}{\the\unnumberedno}% % \global\let\section = \unnumberedsec \global\let\subsection = \unnumberedsubsec \global\let\subsubsection = \unnumberedsubsubsec } % @centerchap is like @unnumbered, but the heading is centered. \outer\parseargdef\centerchap{% \let\centerparametersmaybe = \centerparameters \unnmhead0{#1}% \let\centerparametersmaybe = \relax } % @top is like @unnumbered. \let\top\unnumbered % Sections. % \outer\parseargdef\numberedsec{\numhead1{#1}} % normally calls seczzz \def\seczzz#1{% \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 \sectionheading{#1}{sec}{Ynumbered}{\the\chapno.\the\secno}% } % normally calls appendixsectionzzz: \outer\parseargdef\appendixsection{\apphead1{#1}} \def\appendixsectionzzz#1{% \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 \sectionheading{#1}{sec}{Yappendix}{\appendixletter.\the\secno}% } \let\appendixsec\appendixsection % normally calls unnumberedseczzz: \outer\parseargdef\unnumberedsec{\unnmhead1{#1}} \def\unnumberedseczzz#1{% \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}% } % Subsections. % % normally calls numberedsubseczzz: \outer\parseargdef\numberedsubsec{\numhead2{#1}} \def\numberedsubseczzz#1{% \global\subsubsecno=0 \global\advance\subsecno by 1 \sectionheading{#1}{subsec}{Ynumbered}{\the\chapno.\the\secno.\the\subsecno}% } % normally calls appendixsubseczzz: \outer\parseargdef\appendixsubsec{\apphead2{#1}} \def\appendixsubseczzz#1{% \global\subsubsecno=0 \global\advance\subsecno by 1 \sectionheading{#1}{subsec}{Yappendix}% {\appendixletter.\the\secno.\the\subsecno}% } % normally calls unnumberedsubseczzz: \outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}} \def\unnumberedsubseczzz#1{% \global\subsubsecno=0 \global\advance\subsecno by 1 \sectionheading{#1}{subsec}{Ynothing}% {\the\unnumberedno.\the\secno.\the\subsecno}% } % Subsubsections. % % normally numberedsubsubseczzz: \outer\parseargdef\numberedsubsubsec{\numhead3{#1}} \def\numberedsubsubseczzz#1{% \global\advance\subsubsecno by 1 \sectionheading{#1}{subsubsec}{Ynumbered}% {\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno}% } % normally appendixsubsubseczzz: \outer\parseargdef\appendixsubsubsec{\apphead3{#1}} \def\appendixsubsubseczzz#1{% \global\advance\subsubsecno by 1 \sectionheading{#1}{subsubsec}{Yappendix}% {\appendixletter.\the\secno.\the\subsecno.\the\subsubsecno}% } % normally unnumberedsubsubseczzz: \outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}} \def\unnumberedsubsubseczzz#1{% \global\advance\subsubsecno by 1 \sectionheading{#1}{subsubsec}{Ynothing}% {\the\unnumberedno.\the\secno.\the\subsecno.\the\subsubsecno}% } % These macros control what the section commands do, according % to what kind of chapter we are in (ordinary, appendix, or unnumbered). % Define them by default for a numbered chapter. \let\section = \numberedsec \let\subsection = \numberedsubsec \let\subsubsection = \numberedsubsubsec % Define @majorheading, @heading and @subheading \def\majorheading{% {\advance\chapheadingskip by 10pt \chapbreak }% \parsearg\chapheadingzzz } \def\chapheading{\chapbreak \parsearg\chapheadingzzz} \def\chapheadingzzz#1{% \vbox{\chapfonts \raggedtitlesettings #1\par}% \nobreak\bigskip \nobreak \suppressfirstparagraphindent } % @heading, @subheading, @subsubheading. \parseargdef\heading{\sectionheading{#1}{sec}{Yomitfromtoc}{} \suppressfirstparagraphindent} \parseargdef\subheading{\sectionheading{#1}{subsec}{Yomitfromtoc}{} \suppressfirstparagraphindent} \parseargdef\subsubheading{\sectionheading{#1}{subsubsec}{Yomitfromtoc}{} \suppressfirstparagraphindent} % These macros generate a chapter, section, etc. heading only % (including whitespace, linebreaking, etc. around it), % given all the information in convenient, parsed form. % Args are the skip and penalty (usually negative) \def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi} % Parameter controlling skip before chapter headings (if needed) \newskip\chapheadingskip % Define plain chapter starts, and page on/off switching for it. \def\chapbreak{\dobreak \chapheadingskip {-4000}} % Start a new page \def\chappager{\par\vfill\supereject} % \chapoddpage - start on an odd page for a new chapter % Because \domark is called before \chapoddpage, the filler page will % get the headings for the next chapter, which is wrong. But we don't % care -- we just disable all headings on the filler page. \def\chapoddpage{% \chappager \ifodd\pageno \else \begingroup \headingsoff \null \chappager \endgroup \fi } \parseargdef\setchapternewpage{\csname CHAPPAG#1\endcsname} \def\CHAPPAGoff{% \global\let\contentsalignmacro = \chappager \global\let\pchapsepmacro=\chapbreak \global\let\pagealignmacro=\chappager} \def\CHAPPAGon{% \global\let\contentsalignmacro = \chappager \global\let\pchapsepmacro=\chappager \global\let\pagealignmacro=\chappager \global\def\HEADINGSon{\HEADINGSsingle}} \def\CHAPPAGodd{% \global\let\contentsalignmacro = \chapoddpage \global\let\pchapsepmacro=\chapoddpage \global\let\pagealignmacro=\chapoddpage \global\def\HEADINGSon{\HEADINGSdouble}} \CHAPPAGon % \chapmacro - Chapter opening. % % #1 is the text, #2 is the section type (Ynumbered, Ynothing, % Yappendix, Yomitfromtoc), #3 the chapter number. % Not used for @heading series. % % To test against our argument. \def\Ynothingkeyword{Ynothing} \def\Yappendixkeyword{Yappendix} \def\Yomitfromtockeyword{Yomitfromtoc} % \def\chapmacro#1#2#3{% \expandafter\ifx\thisenv\titlepage\else \checkenv{}% chapters, etc., should not start inside an environment. \fi % Insert the first mark before the heading break (see notes for \domark). \let\prevchapterdefs=\currentchapterdefs \let\prevsectiondefs=\currentsectiondefs \gdef\currentsectiondefs{\gdef\thissectionname{}\gdef\thissectionnum{}% \gdef\thissection{}}% % \def\temptype{#2}% \ifx\temptype\Ynothingkeyword \gdef\currentchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}% \gdef\thischapter{\thischaptername}}% \else\ifx\temptype\Yomitfromtockeyword \gdef\currentchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}% \gdef\thischapter{}}% \else\ifx\temptype\Yappendixkeyword \toks0={#1}% \xdef\currentchapterdefs{% \gdef\noexpand\thischaptername{\the\toks0}% \gdef\noexpand\thischapternum{\appendixletter}% % \noexpand\putwordAppendix avoids expanding indigestible % commands in some of the translations. \gdef\noexpand\thischapter{\noexpand\putwordAppendix{} \noexpand\thischapternum: \noexpand\thischaptername}% }% \else \toks0={#1}% \xdef\currentchapterdefs{% \gdef\noexpand\thischaptername{\the\toks0}% \gdef\noexpand\thischapternum{\the\chapno}% % \noexpand\putwordChapter avoids expanding indigestible % commands in some of the translations. \gdef\noexpand\thischapter{\noexpand\putwordChapter{} \noexpand\thischapternum: \noexpand\thischaptername}% }% \fi\fi\fi % % Output the mark. Pass it through \safewhatsit, to take care of % the preceding space. \safewhatsit\domark % % Insert the chapter heading break. \pchapsepmacro % % Now the second mark, after the heading break. No break points % between here and the heading. \let\prevchapterdefs=\currentchapterdefs \let\prevsectiondefs=\currentsectiondefs \domark % {% \chapfonts \rm \let\footnote=\errfootnoteheading % give better error message % % Have to define \currentsection before calling \donoderef, because the % xref code eventually uses it. On the other hand, it has to be called % after \pchapsepmacro, or the headline will change too soon. \gdef\currentsection{#1}% % % Only insert the separating space if we have a chapter/appendix % number, and don't print the unnumbered ``number''. \ifx\temptype\Ynothingkeyword \setbox0 = \hbox{}% \def\toctype{unnchap}% \else\ifx\temptype\Yomitfromtockeyword \setbox0 = \hbox{}% contents like unnumbered, but no toc entry \def\toctype{omit}% \else\ifx\temptype\Yappendixkeyword \setbox0 = \hbox{\putwordAppendix{} #3\enspace}% \def\toctype{app}% \else \setbox0 = \hbox{#3\enspace}% \def\toctype{numchap}% \fi\fi\fi % % Write the toc entry for this chapter. Must come before the % \donoderef, because we include the current node name in the toc % entry, and \donoderef resets it to empty. \writetocentry{\toctype}{#1}{#3}% % % For pdftex, we have to write out the node definition (aka, make % the pdfdest) after any page break, but before the actual text has % been typeset. If the destination for the pdf outline is after the % text, then jumping from the outline may wind up with the text not % being visible, for instance under high magnification. \donoderef{#2}% % % Typeset the actual heading. \nobreak % Avoid page breaks at the interline glue. \vbox{\raggedtitlesettings \hangindent=\wd0 \centerparametersmaybe \unhbox0 #1\par}% }% \nobreak\bigskip % no page break after a chapter title \nobreak } % @centerchap -- centered and unnumbered. \let\centerparametersmaybe = \relax \def\centerparameters{% \advance\rightskip by 3\rightskip \leftskip = \rightskip \parfillskip = 0pt } % Section titles. These macros combine the section number parts and % call the generic \sectionheading to do the printing. % \newskip\secheadingskip \def\secheadingbreak{\dobreak \secheadingskip{-1000}} % Subsection titles. \newskip\subsecheadingskip \def\subsecheadingbreak{\dobreak \subsecheadingskip{-500}} % Subsubsection titles. \def\subsubsecheadingskip{\subsecheadingskip} \def\subsubsecheadingbreak{\subsecheadingbreak} % Print any size, any type, section title. % % #1 is the text of the title, % #2 is the section level (sec/subsec/subsubsec), % #3 is the section type (Ynumbered, Ynothing, Yappendix, Yomitfromtoc), % #4 is the section number. % \def\seckeyword{sec} % \def\sectionheading#1#2#3#4{% {% \def\sectionlevel{#2}% \def\temptype{#3}% % % It is ok for the @heading series commands to appear inside an % environment (it's been historically allowed, though the logic is % dubious), but not the others. \ifx\temptype\Yomitfromtockeyword\else \checkenv{}% non-@*heading should not be in an environment. \fi \let\footnote=\errfootnoteheading % % Switch to the right set of fonts. \csname #2fonts\endcsname \rm % % Insert first mark before the heading break (see notes for \domark). \let\prevsectiondefs=\currentsectiondefs \ifx\temptype\Ynothingkeyword \ifx\sectionlevel\seckeyword \gdef\currentsectiondefs{\gdef\thissectionname{#1}\gdef\thissectionnum{}% \gdef\thissection{\thissectionname}}% \fi \else\ifx\temptype\Yomitfromtockeyword % Don't redefine \thissection. \else\ifx\temptype\Yappendixkeyword \ifx\sectionlevel\seckeyword \toks0={#1}% \xdef\currentsectiondefs{% \gdef\noexpand\thissectionname{\the\toks0}% \gdef\noexpand\thissectionnum{#4}% % \noexpand\putwordSection avoids expanding indigestible % commands in some of the translations. \gdef\noexpand\thissection{\noexpand\putwordSection{} \noexpand\thissectionnum: \noexpand\thissectionname}% }% \fi \else \ifx\sectionlevel\seckeyword \toks0={#1}% \xdef\currentsectiondefs{% \gdef\noexpand\thissectionname{\the\toks0}% \gdef\noexpand\thissectionnum{#4}% % \noexpand\putwordSection avoids expanding indigestible % commands in some of the translations. \gdef\noexpand\thissection{\noexpand\putwordSection{} \noexpand\thissectionnum: \noexpand\thissectionname}% }% \fi \fi\fi\fi % % Go into vertical mode. Usually we'll already be there, but we % don't want the following whatsit to end up in a preceding paragraph % if the document didn't happen to have a blank line. \par % % Output the mark. Pass it through \safewhatsit, to take care of % the preceding space. \safewhatsit\domark % % Insert space above the heading. \csname #2headingbreak\endcsname % % Now the second mark, after the heading break. No break points % between here and the heading. \global\let\prevsectiondefs=\currentsectiondefs \domark % % Only insert the space after the number if we have a section number. \ifx\temptype\Ynothingkeyword \setbox0 = \hbox{}% \def\toctype{unn}% \gdef\currentsection{#1}% \else\ifx\temptype\Yomitfromtockeyword % for @headings -- no section number, don't include in toc, % and don't redefine \currentsection. \setbox0 = \hbox{}% \def\toctype{omit}% \let\sectionlevel=\empty \else\ifx\temptype\Yappendixkeyword \setbox0 = \hbox{#4\enspace}% \def\toctype{app}% \gdef\currentsection{#1}% \else \setbox0 = \hbox{#4\enspace}% \def\toctype{num}% \gdef\currentsection{#1}% \fi\fi\fi % % Write the toc entry (before \donoderef). See comments in \chapmacro. \writetocentry{\toctype\sectionlevel}{#1}{#4}% % % Write the node reference (= pdf destination for pdftex). % Again, see comments in \chapmacro. \donoderef{#3}% % % Interline glue will be inserted when the vbox is completed. % That glue will be a valid breakpoint for the page, since it'll be % preceded by a whatsit (usually from the \donoderef, or from the % \writetocentry if there was no node). We don't want to allow that % break, since then the whatsits could end up on page n while the % section is on page n+1, thus toc/etc. are wrong. Debian bug 276000. \nobreak % % Output the actual section heading. \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \ptexraggedright \hangindent=\wd0 % zero if no section number \unhbox0 #1}% }% % Add extra space after the heading -- half of whatever came above it. % Don't allow stretch, though. \kern .5 \csname #2headingskip\endcsname % % Do not let the kern be a potential breakpoint, as it would be if it % was followed by glue. \nobreak % % We'll almost certainly start a paragraph next, so don't let that % glue accumulate. (Not a breakpoint because it's preceded by a % discardable item.) However, when a paragraph is not started next % (\startdefun, \cartouche, \center, etc.), this needs to be wiped out % or the negative glue will cause weirdly wrong output, typically % obscuring the section heading with something else. \vskip-\parskip % % This is so the last item on the main vertical list is a known % \penalty > 10000, so \startdefun, etc., can recognize the situation % and do the needful. \penalty 10001 } \message{toc,} % Table of contents. \newwrite\tocfile % Write an entry to the toc file, opening it if necessary. % Called from @chapter, etc. % % Example usage: \writetocentry{sec}{Section Name}{\the\chapno.\the\secno} % We append the current node name (if any) and page number as additional % arguments for the \{chap,sec,...}entry macros which will eventually % read this. The node name is used in the pdf outlines as the % destination to jump to. % % We open the .toc file for writing here instead of at @setfilename (or % any other fixed time) so that @contents can be anywhere in the document. % But if #1 is `omit', then we don't do anything. This is used for the % table of contents chapter openings themselves. % \newif\iftocfileopened \def\omitkeyword{omit}% % \def\writetocentry#1#2#3{% \edef\writetoctype{#1}% \ifx\writetoctype\omitkeyword \else \iftocfileopened\else \immediate\openout\tocfile = \jobname.toc \global\tocfileopenedtrue \fi % \iflinks {\atdummies \edef\temp{% \write\tocfile{@#1entry{#2}{#3}{\lastnode}{\noexpand\folio}}}% \temp }% \fi \fi % % Tell \shipout to create a pdf destination on each page, if we're % writing pdf. These are used in the table of contents. We can't % just write one on every page because the title pages are numbered % 1 and 2 (the page numbers aren't printed), and so are the first % two pages of the document. Thus, we'd have two destinations named % `1', and two named `2'. \ifpdforxetex \global\pdfmakepagedesttrue \fi } % These characters do not print properly in the Computer Modern roman % fonts, so we must take special care. This is more or less redundant % with the Texinfo input format setup at the end of this file. % \def\activecatcodes{% \catcode`\"=\active \catcode`\$=\active \catcode`\<=\active \catcode`\>=\active \catcode`\\=\active \catcode`\^=\active \catcode`\_=\active \catcode`\|=\active \catcode`\~=\active } % Read the toc file, which is essentially Texinfo input. \def\readtocfile{% \setupdatafile \activecatcodes \input \tocreadfilename } \newskip\contentsrightmargin \contentsrightmargin=1in \newcount\savepageno \newcount\lastnegativepageno \lastnegativepageno = -1 % Prepare to read what we've written to \tocfile. % \def\startcontents#1{% % If @setchapternewpage on, and @headings double, the contents should % start on an odd page, unlike chapters. Thus, we maintain % \contentsalignmacro in parallel with \pagealignmacro. % From: Torbjorn Granlund \contentsalignmacro \immediate\closeout\tocfile % % Don't need to put `Contents' or `Short Contents' in the headline. % It is abundantly clear what they are. \chapmacro{#1}{Yomitfromtoc}{}% % \savepageno = \pageno \begingroup % Set up to handle contents files properly. \raggedbottom % Worry more about breakpoints than the bottom. \entryrightmargin=\contentsrightmargin % Don't use the full line length. % % Roman numerals for page numbers. \ifnum \pageno>0 \global\pageno = \lastnegativepageno \fi } % redefined for the two-volume lispref. We always output on % \jobname.toc even if this is redefined. % \def\tocreadfilename{\jobname.toc} % Normal (long) toc. % \def\contents{% \startcontents{\putwordTOC}% \openin 1 \tocreadfilename\space \ifeof 1 \else \readtocfile \fi \vfill \eject \contentsalignmacro % in case @setchapternewpage odd is in effect \ifeof 1 \else \pdfmakeoutlines \fi \closein 1 \endgroup \lastnegativepageno = \pageno \global\pageno = \savepageno } % And just the chapters. \def\summarycontents{% \startcontents{\putwordShortTOC}% % \let\partentry = \shortpartentry \let\numchapentry = \shortchapentry \let\appentry = \shortchapentry \let\unnchapentry = \shortunnchapentry % We want a true roman here for the page numbers. \secfonts \let\rm=\shortcontrm \let\bf=\shortcontbf \let\sl=\shortcontsl \let\tt=\shortconttt \rm \hyphenpenalty = 10000 \advance\baselineskip by 1pt % Open it up a little. \def\numsecentry##1##2##3##4{} \let\appsecentry = \numsecentry \let\unnsecentry = \numsecentry \let\numsubsecentry = \numsecentry \let\appsubsecentry = \numsecentry \let\unnsubsecentry = \numsecentry \let\numsubsubsecentry = \numsecentry \let\appsubsubsecentry = \numsecentry \let\unnsubsubsecentry = \numsecentry \openin 1 \tocreadfilename\space \ifeof 1 \else \readtocfile \fi \closein 1 \vfill \eject \contentsalignmacro % in case @setchapternewpage odd is in effect \endgroup \lastnegativepageno = \pageno \global\pageno = \savepageno } \let\shortcontents = \summarycontents % Typeset the label for a chapter or appendix for the short contents. % The arg is, e.g., `A' for an appendix, or `3' for a chapter. % \def\shortchaplabel#1{% % This space should be enough, since a single number is .5em, and the % widest letter (M) is 1em, at least in the Computer Modern fonts. % But use \hss just in case. % (This space doesn't include the extra space that gets added after % the label; that gets put in by \shortchapentry above.) % % We'd like to right-justify chapter numbers, but that looks strange % with appendix letters. And right-justifying numbers and % left-justifying letters looks strange when there is less than 10 % chapters. Have to read the whole toc once to know how many chapters % there are before deciding ... \hbox to 1em{#1\hss}% } % These macros generate individual entries in the table of contents. % The first argument is the chapter or section name. % The last argument is the page number. % The arguments in between are the chapter number, section number, ... % Parts, in the main contents. Replace the part number, which doesn't % exist, with an empty box. Let's hope all the numbers have the same width. % Also ignore the page number, which is conventionally not printed. \def\numeralbox{\setbox0=\hbox{8}\hbox to \wd0{\hfil}} \def\partentry#1#2#3#4{% % Add stretch and a bonus for breaking the page before the part heading. % This reduces the chance of the page being broken immediately after the % part heading, before a following chapter heading. \vskip 0pt plus 5\baselineskip \penalty-300 \vskip 0pt plus -5\baselineskip \dochapentry{\numeralbox\labelspace#1}{}% } % % Parts, in the short toc. \def\shortpartentry#1#2#3#4{% \penalty-300 \vskip.5\baselineskip plus.15\baselineskip minus.1\baselineskip \shortchapentry{{\bf #1}}{\numeralbox}{}{}% } % Chapters, in the main contents. \def\numchapentry#1#2#3#4{\dochapentry{#2\labelspace#1}{#4}} % Chapters, in the short toc. % See comments in \dochapentry re vbox and related settings. \def\shortchapentry#1#2#3#4{% \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#4\egroup}% } % Appendices, in the main contents. % Need the word Appendix, and a fixed-size box. % \def\appendixbox#1{% % We use M since it's probably the widest letter. \setbox0 = \hbox{\putwordAppendix{} M}% \hbox to \wd0{\putwordAppendix{} #1\hss}} % \def\appentry#1#2#3#4{\dochapentry{\appendixbox{#2}\hskip.7em#1}{#4}} % Unnumbered chapters. \def\unnchapentry#1#2#3#4{\dochapentry{#1}{#4}} \def\shortunnchapentry#1#2#3#4{\tocentry{#1}{\doshortpageno\bgroup#4\egroup}} % Sections. \def\numsecentry#1#2#3#4{\dosecentry{#2\labelspace#1}{#4}} \let\appsecentry=\numsecentry \def\unnsecentry#1#2#3#4{\dosecentry{#1}{#4}} % Subsections. \def\numsubsecentry#1#2#3#4{\dosubsecentry{#2\labelspace#1}{#4}} \let\appsubsecentry=\numsubsecentry \def\unnsubsecentry#1#2#3#4{\dosubsecentry{#1}{#4}} % And subsubsections. \def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#2\labelspace#1}{#4}} \let\appsubsubsecentry=\numsubsubsecentry \def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#4}} % This parameter controls the indentation of the various levels. % Same as \defaultparindent. \newdimen\tocindent \tocindent = 15pt % Now for the actual typesetting. In all these, #1 is the text and #2 is the % page number. % % If the toc has to be broken over pages, we want it to be at chapters % if at all possible; hence the \penalty. \def\dochapentry#1#2{% \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip \begingroup % Move the page numbers slightly to the right \advance\entryrightmargin by -0.05em \chapentryfonts \tocentry{#1}{\dopageno\bgroup#2\egroup}% \endgroup \nobreak\vskip .25\baselineskip plus.1\baselineskip } \def\dosecentry#1#2{\begingroup \secentryfonts \leftskip=\tocindent \tocentry{#1}{\dopageno\bgroup#2\egroup}% \endgroup} \def\dosubsecentry#1#2{\begingroup \subsecentryfonts \leftskip=2\tocindent \tocentry{#1}{\dopageno\bgroup#2\egroup}% \endgroup} \def\dosubsubsecentry#1#2{\begingroup \subsubsecentryfonts \leftskip=3\tocindent \tocentry{#1}{\dopageno\bgroup#2\egroup}% \endgroup} % We use the same \entry macro as for the index entries. \let\tocentry = \entry % Space between chapter (or whatever) number and the title. \def\labelspace{\hskip1em \relax} \def\dopageno#1{{\rm #1}} \def\doshortpageno#1{{\rm #1}} \def\chapentryfonts{\secfonts \rm} \def\secentryfonts{\textfonts} \def\subsecentryfonts{\textfonts} \def\subsubsecentryfonts{\textfonts} \message{environments,} % @foo ... @end foo. % @tex ... @end tex escapes into raw TeX temporarily. % One exception: @ is still an escape character, so that @end tex works. % But \@ or @@ will get a plain @ character. \envdef\tex{% \setupmarkupstyle{tex}% \catcode `\\=0 \catcode `\{=1 \catcode `\}=2 \catcode `\$=3 \catcode `\&=4 \catcode `\#=6 \catcode `\^=7 \catcode `\_=8 \catcode `\~=\active \let~=\tie \catcode `\%=14 \catcode `\+=\other \catcode `\"=\other \catcode `\|=\other \catcode `\<=\other \catcode `\>=\other \catcode `\`=\other \catcode `\'=\other % % ' is active in math mode (mathcode"8000). So reset it, and all our % other math active characters (just in case), to plain's definitions. \mathactive % % Inverse of the list at the beginning of the file. \let\b=\ptexb \let\bullet=\ptexbullet \let\c=\ptexc \let\,=\ptexcomma \let\.=\ptexdot \let\dots=\ptexdots \let\equiv=\ptexequiv \let\!=\ptexexclam \let\i=\ptexi \let\indent=\ptexindent \let\noindent=\ptexnoindent \let\{=\ptexlbrace \let\+=\tabalign \let\}=\ptexrbrace \let\/=\ptexslash \let\sp=\ptexsp \let\*=\ptexstar %\let\sup=\ptexsup % do not redefine, we want @sup to work in math mode \let\t=\ptext \expandafter \let\csname top\endcsname=\ptextop % we've made it outer \let\frenchspacing=\plainfrenchspacing % \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}% \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}% \def\@{@}% } % There is no need to define \Etex. % Define @lisp ... @end lisp. % @lisp environment forms a group so it can rebind things, % including the definition of @end lisp (which normally is erroneous). % Amount to narrow the margins by for @lisp. \newskip\lispnarrowing \lispnarrowing=0.4in % This is the definition that ^^M gets inside @lisp, @example, and other % such environments. \null is better than a space, since it doesn't % have any width. \def\lisppar{\null\endgraf} % This space is always present above and below environments. \newskip\envskipamount \envskipamount = 0pt % Make spacing and below environment symmetrical. We use \parskip here % to help in doing that, since in @example-like environments \parskip % is reset to zero; thus the \afterenvbreak inserts no space -- but the % start of the next paragraph will insert \parskip. % \def\aboveenvbreak{{% % =10000 instead of <10000 because of a special case in \itemzzz and % \sectionheading, q.v. \ifnum \lastpenalty=10000 \else \advance\envskipamount by \parskip \endgraf \ifdim\lastskip<\envskipamount \removelastskip \ifnum\lastpenalty<10000 % Penalize breaking before the environment, because preceding text % often leads into it. \penalty100 \fi \vskip\envskipamount \fi \fi }} \def\afterenvbreak{{% % =10000 instead of <10000 because of a special case in \itemzzz and % \sectionheading, q.v. \ifnum \lastpenalty=10000 \else \advance\envskipamount by \parskip \endgraf \ifdim\lastskip<\envskipamount \removelastskip % it's not a good place to break if the last penalty was \nobreak % or better ... \ifnum\lastpenalty<10000 \penalty-50 \fi \vskip\envskipamount \fi \fi }} % \nonarrowing is a flag. If "set", @lisp etc don't narrow margins; it will % also clear it, so that its embedded environments do the narrowing again. \let\nonarrowing=\relax % @cartouche ... @end cartouche: draw rectangle w/rounded corners around % environment contents. % \def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth \def\ctr{{\hskip 6pt\circle\char'010}} \def\cbl{{\circle\char'012\hskip -6pt}} \def\cbr{{\hskip 6pt\circle\char'011}} \def\carttop{\hbox to \cartouter{\hskip\lskip \ctl\leaders\hrule height\circthick\hfil\ctr \hskip\rskip}} \def\cartbot{\hbox to \cartouter{\hskip\lskip \cbl\leaders\hrule height\circthick\hfil\cbr \hskip\rskip}} % \newskip\lskip\newskip\rskip % only require the font if @cartouche is actually used \def\cartouchefontdefs{% \font\circle=lcircle10\relax \circthick=\fontdimen8\circle } \newdimen\circthick \newdimen\cartouter\newdimen\cartinner \newskip\normbskip\newskip\normpskip\newskip\normlskip \envdef\cartouche{% \cartouchefontdefs \ifhmode\par\fi % can't be in the midst of a paragraph. \startsavinginserts \lskip=\leftskip \rskip=\rightskip \leftskip=0pt\rightskip=0pt % we want these *outside*. \cartinner=\hsize \advance\cartinner by-\lskip \advance\cartinner by-\rskip \cartouter=\hsize \advance\cartouter by 18.4pt % allow for 3pt kerns on either % side, and for 6pt waste from % each corner char, and rule thickness \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip % % If this cartouche directly follows a sectioning command, we need the % \parskip glue (backspaced over by default) or the cartouche can % collide with the section heading. \ifnum\lastpenalty>10000 \vskip\parskip \penalty\lastpenalty \fi % \setbox\groupbox=\vbox\bgroup \baselineskip=0pt\parskip=0pt\lineskip=0pt \carttop \hbox\bgroup \hskip\lskip \vrule\kern3pt \vbox\bgroup \kern3pt \hsize=\cartinner \baselineskip=\normbskip \lineskip=\normlskip \parskip=\normpskip \vskip -\parskip \comment % For explanation, see the end of def\group. } \def\Ecartouche{% \ifhmode\par\fi \kern3pt \egroup \kern3pt\vrule \hskip\rskip \egroup \cartbot \egroup \addgroupbox \checkinserts } % This macro is called at the beginning of all the @example variants, % inside a group. \newdimen\nonfillparindent \def\nonfillstart{% \aboveenvbreak \ifdim\hfuzz < 12pt \hfuzz = 12pt \fi % Don't be fussy \sepspaces % Make spaces be word-separators rather than space tokens. \let\par = \lisppar % don't ignore blank lines \obeylines % each line of input is a line of output \parskip = 0pt % Turn off paragraph indentation but redefine \indent to emulate % the normal \indent. \nonfillparindent=\parindent \parindent = 0pt \let\indent\nonfillindent % \emergencystretch = 0pt % don't try to avoid overfull boxes \ifx\nonarrowing\relax \advance \leftskip by \lispnarrowing \exdentamount=\lispnarrowing \else \let\nonarrowing = \relax \fi \let\exdent=\nofillexdent } \begingroup \obeyspaces % We want to swallow spaces (but not other tokens) after the fake % @indent in our nonfill-environments, where spaces are normally % active and set to @tie, resulting in them not being ignored after % @indent. \gdef\nonfillindent{\futurelet\temp\nonfillindentcheck}% \gdef\nonfillindentcheck{% \ifx\temp % \expandafter\nonfillindentgobble% \else% \leavevmode\nonfillindentbox% \fi% }% \endgroup \def\nonfillindentgobble#1{\nonfillindent} \def\nonfillindentbox{\hbox to \nonfillparindent{\hss}} % If you want all examples etc. small: @set dispenvsize small. % If you want even small examples the full size: @set dispenvsize nosmall. % This affects the following displayed environments: % @example, @display, @format, @lisp % \def\smallword{small} \def\nosmallword{nosmall} \let\SETdispenvsize\relax \def\setnormaldispenv{% \ifx\SETdispenvsize\smallword % end paragraph for sake of leading, in case document has no blank % line. This is redundant with what happens in \aboveenvbreak, but % we need to do it before changing the fonts, and it's inconvenient % to change the fonts afterward. \ifnum \lastpenalty=10000 \else \endgraf \fi \smallexamplefonts \rm \fi } \def\setsmalldispenv{% \ifx\SETdispenvsize\nosmallword \else \ifnum \lastpenalty=10000 \else \endgraf \fi \smallexamplefonts \rm \fi } % We often define two environments, @foo and @smallfoo. % Let's do it in one command. #1 is the env name, #2 the definition. \def\makedispenvdef#1#2{% \expandafter\envdef\csname#1\endcsname {\setnormaldispenv #2}% \expandafter\envdef\csname small#1\endcsname {\setsmalldispenv #2}% \expandafter\let\csname E#1\endcsname \afterenvbreak \expandafter\let\csname Esmall#1\endcsname \afterenvbreak } % Define two environment synonyms (#1 and #2) for an environment. \def\maketwodispenvdef#1#2#3{% \makedispenvdef{#1}{#3}% \makedispenvdef{#2}{#3}% } % % @lisp: indented, narrowed, typewriter font; % @example: same as @lisp. % % @smallexample and @smalllisp: use smaller fonts. % Originally contributed by Pavel@xerox. % \maketwodispenvdef{lisp}{example}{% \nonfillstart \tt\setupmarkupstyle{example}% \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special. \gobble % eat return } % @display/@smalldisplay: same as @lisp except keep current font. % \makedispenvdef{display}{% \nonfillstart \gobble } % @format/@smallformat: same as @display except don't narrow margins. % \makedispenvdef{format}{% \let\nonarrowing = t% \nonfillstart \gobble } % @flushleft: same as @format, but doesn't obey \SETdispenvsize. \envdef\flushleft{% \let\nonarrowing = t% \nonfillstart \gobble } \let\Eflushleft = \afterenvbreak % @flushright. % \envdef\flushright{% \let\nonarrowing = t% \nonfillstart \advance\leftskip by 0pt plus 1fill\relax \gobble } \let\Eflushright = \afterenvbreak % @raggedright does more-or-less normal line breaking but no right % justification. From plain.tex. \envdef\raggedright{% \rightskip0pt plus2.4em \spaceskip.3333em \xspaceskip.5em\relax } \let\Eraggedright\par \envdef\raggedleft{% \parindent=0pt \leftskip0pt plus2em \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt \hbadness=10000 % Last line will usually be underfull, so turn off % badness reporting. } \let\Eraggedleft\par \envdef\raggedcenter{% \parindent=0pt \rightskip0pt plus1em \leftskip0pt plus1em \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt \hbadness=10000 % Last line will usually be underfull, so turn off % badness reporting. } \let\Eraggedcenter\par % @quotation does normal linebreaking (hence we can't use \nonfillstart) % and narrows the margins. We keep \parskip nonzero in general, since % we're doing normal filling. So, when using \aboveenvbreak and % \afterenvbreak, temporarily make \parskip 0. % \makedispenvdef{quotation}{\quotationstart} % \def\quotationstart{% \indentedblockstart % same as \indentedblock, but increase right margin too. \ifx\nonarrowing\relax \advance\rightskip by \lispnarrowing \fi \parsearg\quotationlabel } % We have retained a nonzero parskip for the environment, since we're % doing normal filling. % \def\Equotation{% \par \ifx\quotationauthor\thisisundefined\else % indent a bit. \leftline{\kern 2\leftskip \sl ---\quotationauthor}% \fi {\parskip=0pt \afterenvbreak}% } \def\Esmallquotation{\Equotation} % If we're given an argument, typeset it in bold with a colon after. \def\quotationlabel#1{% \def\temp{#1}% \ifx\temp\empty \else {\bf #1: }% \fi } % @indentedblock is like @quotation, but indents only on the left and % has no optional argument. % \makedispenvdef{indentedblock}{\indentedblockstart} % \def\indentedblockstart{% {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip \parindent=0pt % % @cartouche defines \nonarrowing to inhibit narrowing at next level down. \ifx\nonarrowing\relax \advance\leftskip by \lispnarrowing \exdentamount = \lispnarrowing \else \let\nonarrowing = \relax \fi } % Keep a nonzero parskip for the environment, since we're doing normal filling. % \def\Eindentedblock{% \par {\parskip=0pt \afterenvbreak}% } \def\Esmallindentedblock{\Eindentedblock} % LaTeX-like @verbatim...@end verbatim and @verb{...} % If we want to allow any as delimiter, % we need the curly braces so that makeinfo sees the @verb command, eg: % `@verbx...x' would look like the '@verbx' command. --janneke@gnu.org % % [Knuth]: Donald Ervin Knuth, 1996. The TeXbook. % % [Knuth] p.344; only we need to do the other characters Texinfo sets % active too. Otherwise, they get lost as the first character on a % verbatim line. \def\dospecials{% \do\ \do\\\do\{\do\}\do\$\do\&% \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~% \do\<\do\>\do\|\do\@\do+\do\"% % Don't do the quotes -- if we do, @set txicodequoteundirected and % @set txicodequotebacktick will not have effect on @verb and % @verbatim, and ?` and !` ligatures won't get disabled. %\do\`\do\'% } % % [Knuth] p. 380 \def\uncatcodespecials{% \def\do##1{\catcode`##1=\other}\dospecials} % % Setup for the @verb command. % % Eight spaces for a tab \begingroup \catcode`\^^I=\active \gdef\tabeightspaces{\catcode`\^^I=\active\def^^I{\ \ \ \ \ \ \ \ }} \endgroup % \def\setupverb{% \tt % easiest (and conventionally used) font for verbatim \def\par{\leavevmode\endgraf}% \setupmarkupstyle{verb}% \tabeightspaces % Respect line breaks, % print special symbols as themselves, and % make each space count % must do in this order: \obeylines \uncatcodespecials \sepspaces } % Setup for the @verbatim environment % % Real tab expansion. \newdimen\tabw \setbox0=\hbox{\tt\space} \tabw=8\wd0 % tab amount % % We typeset each line of the verbatim in an \hbox, so we can handle % tabs. The \global is in case the verbatim line starts with an accent, % or some other command that starts with a begin-group. Otherwise, the % entire \verbbox would disappear at the corresponding end-group, before % it is typeset. Meanwhile, we can't have nested verbatim commands % (can we?), so the \global won't be overwriting itself. \newbox\verbbox \def\starttabbox{\global\setbox\verbbox=\hbox\bgroup} % \begingroup \catcode`\^^I=\active \gdef\tabexpand{% \catcode`\^^I=\active \def^^I{\leavevmode\egroup \dimen\verbbox=\wd\verbbox % the width so far, or since the previous tab \divide\dimen\verbbox by\tabw \multiply\dimen\verbbox by\tabw % compute previous multiple of \tabw \advance\dimen\verbbox by\tabw % advance to next multiple of \tabw \wd\verbbox=\dimen\verbbox \box\verbbox \starttabbox }% } \endgroup % start the verbatim environment. \def\setupverbatim{% \let\nonarrowing = t% \nonfillstart \tt % easiest (and conventionally used) font for verbatim % The \leavevmode here is for blank lines. Otherwise, we would % never \starttabbox and the \egroup would end verbatim mode. \def\par{\leavevmode\egroup\box\verbbox\endgraf}% \tabexpand \setupmarkupstyle{verbatim}% % Respect line breaks, % print special symbols as themselves, and % make each space count. % Must do in this order: \obeylines \uncatcodespecials \sepspaces \everypar{\starttabbox}% } % Do the @verb magic: verbatim text is quoted by unique % delimiter characters. Before first delimiter expect a % right brace, after last delimiter expect closing brace: % % \def\doverb'{'#1'}'{#1} % % [Knuth] p. 382; only eat outer {} \begingroup \catcode`[=1\catcode`]=2\catcode`\{=\other\catcode`\}=\other \gdef\doverb{#1[\def\next##1#1}[##1\endgroup]\next] \endgroup % \def\verb{\begingroup\setupverb\doverb} % % % Do the @verbatim magic: define the macro \doverbatim so that % the (first) argument ends when '@end verbatim' is reached, ie: % % \def\doverbatim#1@end verbatim{#1} % % For Texinfo it's a lot easier than for LaTeX, % because texinfo's \verbatim doesn't stop at '\end{verbatim}': % we need not redefine '\', '{' and '}'. % % Inspired by LaTeX's verbatim command set [latex.ltx] % \begingroup \catcode`\ =\active \obeylines % % ignore everything up to the first ^^M, that's the newline at the end % of the @verbatim input line itself. Otherwise we get an extra blank % line in the output. \xdef\doverbatim#1^^M#2@end verbatim{#2\noexpand\end\gobble verbatim}% % We really want {...\end verbatim} in the body of the macro, but % without the active space; thus we have to use \xdef and \gobble. \endgroup % \envdef\verbatim{% \setupverbatim\doverbatim } \let\Everbatim = \afterenvbreak % @verbatiminclude FILE - insert text of file in verbatim environment. % \def\verbatiminclude{\parseargusing\filenamecatcodes\doverbatiminclude} % \def\doverbatiminclude#1{% {% \makevalueexpandable \setupverbatim {% \indexnofonts % Allow `@@' and other weird things in file names. \wlog{texinfo.tex: doing @verbatiminclude of #1^^J}% \edef\tmp{\noexpand\input #1 } \expandafter }\tmp \afterenvbreak }% } % @copying ... @end copying. % Save the text away for @insertcopying later. % % We save the uninterpreted tokens, rather than creating a box. % Saving the text in a box would be much easier, but then all the % typesetting commands (@smallbook, font changes, etc.) have to be done % beforehand -- and a) we want @copying to be done first in the source % file; b) letting users define the frontmatter in as flexible order as % possible is desirable. % \def\copying{\checkenv{}\begingroup\scanargctxt\docopying} \def\docopying#1@end copying{\endgroup\def\copyingtext{#1}} % \def\insertcopying{% \begingroup \parindent = 0pt % paragraph indentation looks wrong on title page \scanexp\copyingtext \endgroup } \message{defuns,} % @defun etc. \newskip\defbodyindent \defbodyindent=.4in \newskip\defargsindent \defargsindent=50pt \newskip\deflastargmargin \deflastargmargin=18pt \newcount\defunpenalty % Start the processing of @deffn: \def\startdefun{% \ifnum\lastpenalty<10000 \medbreak \defunpenalty=10003 % Will keep this @deffn together with the % following @def command, see below. \else % If there are two @def commands in a row, we'll have a \nobreak, % which is there to keep the function description together with its % header. But if there's nothing but headers, we need to allow a % break somewhere. Check specifically for penalty 10002, inserted % by \printdefunline, instead of 10000, since the sectioning % commands also insert a nobreak penalty, and we don't want to allow % a break between a section heading and a defun. % % As a further refinement, we avoid "club" headers by signalling % with penalty of 10003 after the very first @deffn in the % sequence (see above), and penalty of 10002 after any following % @def command. \ifnum\lastpenalty=10002 \penalty2000 \else \defunpenalty=10002 \fi % % Similarly, after a section heading, do not allow a break. % But do insert the glue. \medskip % preceded by discardable penalty, so not a breakpoint \fi % \parindent=0in \advance\leftskip by \defbodyindent \exdentamount=\defbodyindent } \def\dodefunx#1{% % First, check whether we are in the right environment: \checkenv#1% % % As above, allow line break if we have multiple x headers in a row. % It's not a great place, though. \ifnum\lastpenalty=10002 \penalty3000 \else \defunpenalty=10002 \fi % % And now, it's time to reuse the body of the original defun: \expandafter\gobbledefun#1% } \def\gobbledefun#1\startdefun{} % \printdefunline \deffnheader{text} % \def\printdefunline#1#2{% \begingroup % call \deffnheader: #1#2 \endheader % common ending: \interlinepenalty = 10000 \advance\rightskip by 0pt plus 1fil\relax \endgraf \nobreak\vskip -\parskip \penalty\defunpenalty % signal to \startdefun and \dodefunx % Some of the @defun-type tags do not enable magic parentheses, % rendering the following check redundant. But we don't optimize. \checkparencounts \endgroup } \def\Edefun{\endgraf\medbreak} % \makedefun{deffn} creates \deffn, \deffnx and \Edeffn; % the only thing remaining is to define \deffnheader. % \def\makedefun#1{% \expandafter\let\csname E#1\endcsname = \Edefun \edef\temp{\noexpand\domakedefun \makecsname{#1}\makecsname{#1x}\makecsname{#1header}}% \temp } % \domakedefun \deffn \deffnx \deffnheader { (defn. of \deffnheader) } % % Define \deffn and \deffnx, without parameters. % \deffnheader has to be defined explicitly. % \def\domakedefun#1#2#3{% \envdef#1{% \startdefun \doingtypefnfalse % distinguish typed functions from all else \parseargusing\activeparens{\printdefunline#3}% }% \def#2{\dodefunx#1}% \def#3% } \newif\ifdoingtypefn % doing typed function? \newif\ifrettypeownline % typeset return type on its own line? % @deftypefnnewline on|off says whether the return type of typed functions % are printed on their own line. This affects @deftypefn, @deftypefun, % @deftypeop, and @deftypemethod. % \parseargdef\deftypefnnewline{% \def\temp{#1}% \ifx\temp\onword \expandafter\let\csname SETtxideftypefnnl\endcsname = \empty \else\ifx\temp\offword \expandafter\let\csname SETtxideftypefnnl\endcsname = \relax \else \errhelp = \EMsimple \errmessage{Unknown @txideftypefnnl value `\temp', must be on|off}% \fi\fi } % \dosubind {index}{topic}{subtopic} % % If SUBTOPIC is present, precede it with a space, and call \doind. % (At some time during the 20th century, this made a two-level entry in an % index such as the operation index. Nobody seemed to notice the change in % behaviour though.) \def\dosubind#1#2#3{% \def\thirdarg{#3}% \ifx\thirdarg\empty \doind{#1}{#2}% \else \doind{#1}{#2\space#3}% \fi } % Untyped functions: % @deffn category name args \makedefun{deffn}{\deffngeneral{}} % @deffn category class name args \makedefun{defop}#1 {\defopon{#1\ \putwordon}} % \defopon {category on}class name args \def\defopon#1#2 {\deffngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } % \deffngeneral {subind}category name args % \def\deffngeneral#1#2 #3 #4\endheader{% \dosubind{fn}{\code{#3}}{#1}% \defname{#2}{}{#3}\magicamp\defunargs{#4\unskip}% } % Typed functions: % @deftypefn category type name args \makedefun{deftypefn}{\deftypefngeneral{}} % @deftypeop category class type name args \makedefun{deftypeop}#1 {\deftypeopon{#1\ \putwordon}} % \deftypeopon {category on}class type name args \def\deftypeopon#1#2 {\deftypefngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } % \deftypefngeneral {subind}category type name args % \def\deftypefngeneral#1#2 #3 #4 #5\endheader{% \dosubind{fn}{\code{#4}}{#1}% \doingtypefntrue \defname{#2}{#3}{#4}\defunargs{#5\unskip}% } % Typed variables: % @deftypevr category type var args \makedefun{deftypevr}{\deftypecvgeneral{}} % @deftypecv category class type var args \makedefun{deftypecv}#1 {\deftypecvof{#1\ \putwordof}} % \deftypecvof {category of}class type var args \def\deftypecvof#1#2 {\deftypecvgeneral{\putwordof\ \code{#2}}{#1\ \code{#2}} } % \deftypecvgeneral {subind}category type var args % \def\deftypecvgeneral#1#2 #3 #4 #5\endheader{% \dosubind{vr}{\code{#4}}{#1}% \defname{#2}{#3}{#4}\defunargs{#5\unskip}% } % Untyped variables: % @defvr category var args \makedefun{defvr}#1 {\deftypevrheader{#1} {} } % @defcv category class var args \makedefun{defcv}#1 {\defcvof{#1\ \putwordof}} % \defcvof {category of}class var args \def\defcvof#1#2 {\deftypecvof{#1}#2 {} } % Types: % @deftp category name args \makedefun{deftp}#1 #2 #3\endheader{% \doind{tp}{\code{#2}}% \defname{#1}{}{#2}\defunargs{#3\unskip}% } % Remaining @defun-like shortcuts: \makedefun{defun}{\deffnheader{\putwordDeffunc} } \makedefun{defmac}{\deffnheader{\putwordDefmac} } \makedefun{defspec}{\deffnheader{\putwordDefspec} } \makedefun{deftypefun}{\deftypefnheader{\putwordDeffunc} } \makedefun{defvar}{\defvrheader{\putwordDefvar} } \makedefun{defopt}{\defvrheader{\putwordDefopt} } \makedefun{deftypevar}{\deftypevrheader{\putwordDefvar} } \makedefun{defmethod}{\defopon\putwordMethodon} \makedefun{deftypemethod}{\deftypeopon\putwordMethodon} \makedefun{defivar}{\defcvof\putwordInstanceVariableof} \makedefun{deftypeivar}{\deftypecvof\putwordInstanceVariableof} % \defname, which formats the name of the @def (not the args). % #1 is the category, such as "Function". % #2 is the return type, if any. % #3 is the function name. % % We are followed by (but not passed) the arguments, if any. % \def\defname#1#2#3{% \par % Get the values of \leftskip and \rightskip as they were outside the @def... \advance\leftskip by -\defbodyindent % % Determine if we are typesetting the return type of a typed function % on a line by itself. \rettypeownlinefalse \ifdoingtypefn % doing a typed function specifically? % then check user option for putting return type on its own line: \expandafter\ifx\csname SETtxideftypefnnl\endcsname\relax \else \rettypeownlinetrue \fi \fi % % How we'll format the category name. Putting it in brackets helps % distinguish it from the body text that may end up on the next line % just below it. \def\temp{#1}% \setbox0=\hbox{\kern\deflastargmargin \ifx\temp\empty\else [\rm\temp]\fi} % % Figure out line sizes for the paragraph shape. We'll always have at % least two. \tempnum = 2 % % The first line needs space for \box0; but if \rightskip is nonzero, % we need only space for the part of \box0 which exceeds it: \dimen0=\hsize \advance\dimen0 by -\wd0 \advance\dimen0 by \rightskip % % If doing a return type on its own line, we'll have another line. \ifrettypeownline \advance\tempnum by 1 \def\maybeshapeline{0in \hsize}% \else \def\maybeshapeline{}% \fi % % The continuations: \dimen2=\hsize \advance\dimen2 by -\defargsindent % % The final paragraph shape: \parshape \tempnum 0in \dimen0 \maybeshapeline \defargsindent \dimen2 % % Put the category name at the right margin. \noindent \hbox to 0pt{% \hfil\box0 \kern-\hsize % \hsize has to be shortened this way: \kern\leftskip % Intentionally do not respect \rightskip, since we need the space. }% % % Allow all lines to be underfull without complaint: \tolerance=10000 \hbadness=10000 \exdentamount=\defbodyindent {% % defun fonts. We use typewriter by default (used to be bold) because: % . we're printing identifiers, they should be in tt in principle. % . in languages with many accents, such as Czech or French, it's % common to leave accents off identifiers. The result looks ok in % tt, but exceedingly strange in rm. % . we don't want -- and --- to be treated as ligatures. % . this still does not fix the ?` and !` ligatures, but so far no % one has made identifiers using them :). \df \tt \def\temp{#2}% text of the return type \ifx\temp\empty\else \tclose{\temp}% typeset the return type \ifrettypeownline % put return type on its own line; prohibit line break following: \hfil\vadjust{\nobreak}\break \else \space % type on same line, so just followed by a space \fi \fi % no return type #3% output function name }% {\rm\enskip}% hskip 0.5 em of \rmfont % \boldbrax % arguments will be output next, if any. } % Print arguments in slanted roman (not ttsl), inconsistently with using % tt for the name. This is because literal text is sometimes needed in % the argument list (groff manual), and ttsl and tt are not very % distinguishable. Prevent hyphenation at `-' chars. % \def\defunargs#1{% % use sl by default (not ttsl), % tt for the names. \df \sl \hyphenchar\font=0 % % On the other hand, if an argument has two dashes (for instance), we % want a way to get ttsl. We used to recommend @var for that, so % leave the code in, but it's strange for @var to lead to typewriter. % Nowadays we recommend @code, since the difference between a ttsl hyphen % and a tt hyphen is pretty tiny. @code also disables ?` !`. \def\var##1{{\setupmarkupstyle{var}\ttslanted{##1}}}% #1% \sl\hyphenchar\font=45 } % We want ()&[] to print specially on the defun line. % \def\activeparens{% \catcode`\(=\active \catcode`\)=\active \catcode`\[=\active \catcode`\]=\active \catcode`\&=\active } % Make control sequences which act like normal parenthesis chars. \let\lparen = ( \let\rparen = ) % Be sure that we always have a definition for `(', etc. For example, % if the fn name has parens in it, \boldbrax will not be in effect yet, % so TeX would otherwise complain about undefined control sequence. { \activeparens \global\let(=\lparen \global\let)=\rparen \global\let[=\lbrack \global\let]=\rbrack \global\let& = \& \gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb} \gdef\magicamp{\let&=\amprm} } \let\ampchar\& \newcount\parencount % If we encounter &foo, then turn on ()-hacking afterwards \newif\ifampseen \def\amprm#1 {\ampseentrue{\bf\ }} \def\parenfont{% \ifampseen % At the first level, print parens in roman, % otherwise use the default font. \ifnum \parencount=1 \rm \fi \else % The \sf parens (in \boldbrax) actually are a little bolder than % the contained text. This is especially needed for [ and ] . \sf \fi } \def\infirstlevel#1{% \ifampseen \ifnum\parencount=1 #1% \fi \fi } \def\bfafterword#1 {#1 \bf} \def\opnr{% \global\advance\parencount by 1 {\parenfont(}% \infirstlevel \bfafterword } \def\clnr{% {\parenfont)}% \infirstlevel \sl \global\advance\parencount by -1 } \newcount\brackcount \def\lbrb{% \global\advance\brackcount by 1 {\bf[}% } \def\rbrb{% {\bf]}% \global\advance\brackcount by -1 } \def\checkparencounts{% \ifnum\parencount=0 \else \badparencount \fi \ifnum\brackcount=0 \else \badbrackcount \fi } % these should not use \errmessage; the glibc manual, at least, actually % has such constructs (when documenting function pointers). \def\badparencount{% \message{Warning: unbalanced parentheses in @def...}% \global\parencount=0 } \def\badbrackcount{% \message{Warning: unbalanced square brackets in @def...}% \global\brackcount=0 } \message{macros,} % @macro. % To do this right we need a feature of e-TeX, \scantokens, % which we arrange to emulate with a temporary file in ordinary TeX. \ifx\eTeXversion\thisisundefined \newwrite\macscribble \def\scantokens#1{% \toks0={#1}% \immediate\openout\macscribble=\jobname.tmp \immediate\write\macscribble{\the\toks0}% \immediate\closeout\macscribble \input \jobname.tmp } \fi % Used at the time of macro expansion. % Argument is macro body with arguments substituted \def\scanmacro#1{% \newlinechar`\^^M \def\xeatspaces{\eatspaces}% % % Process the macro body under the current catcode regime. \scantokens{#1@comment}% % % The \comment is to remove the \newlinechar added by \scantokens, and % can be noticed by \parsearg. Note \c isn't used because this means cedilla % in math mode. } % Used for copying and captions \def\scanexp#1{% \expandafter\scanmacro\expandafter{#1}% } \newcount\paramno % Count of parameters \newtoks\macname % Macro name \newif\ifrecursive % Is it recursive? % List of all defined macros in the form % \commondummyword\macro1\commondummyword\macro2... % Currently is also contains all @aliases; the list can be split % if there is a need. \def\macrolist{} % Add the macro to \macrolist \def\addtomacrolist#1{\expandafter \addtomacrolistxxx \csname#1\endcsname} \def\addtomacrolistxxx#1{% \toks0 = \expandafter{\macrolist\commondummyword#1}% \xdef\macrolist{\the\toks0}% } % Utility routines. % This does \let #1 = #2, with \csnames; that is, % \let \csname#1\endcsname = \csname#2\endcsname % (except of course we have to play expansion games). % \def\cslet#1#2{% \expandafter\let \csname#1\expandafter\endcsname \csname#2\endcsname } % Trim leading and trailing spaces off a string. % Concepts from aro-bend problem 15 (see CTAN). {\catcode`\@=11 \gdef\eatspaces #1{\expandafter\trim@\expandafter{#1 }} \gdef\trim@ #1{\trim@@ @#1 @ #1 @ @@} \gdef\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @} \def\unbrace#1{#1} \unbrace{\gdef\trim@@@ #1 } #2@{#1} } % Trim a single trailing ^^M off a string. {\catcode`\^^M=\other \catcode`\Q=3% \gdef\eatcr #1{\eatcra #1Q^^MQ}% \gdef\eatcra#1^^MQ{\eatcrb#1Q}% \gdef\eatcrb#1Q#2Q{#1}% } % Macro bodies are absorbed as an argument in a context where % all characters are catcode 10, 11 or 12, except \ which is active % (as in normal texinfo). It is necessary to change the definition of \ % to recognize macro arguments; this is the job of \mbodybackslash. % % Non-ASCII encodings make 8-bit characters active, so un-activate % them to avoid their expansion. Must do this non-globally, to % confine the change to the current group. % % It's necessary to have hard CRs when the macro is executed. This is % done by making ^^M (\endlinechar) catcode 12 when reading the macro % body, and then making it the \newlinechar in \scanmacro. % \def\scanctxt{% used as subroutine \catcode`\"=\other \catcode`\+=\other \catcode`\<=\other \catcode`\>=\other \catcode`\^=\other \catcode`\_=\other \catcode`\|=\other \catcode`\~=\other \passthroughcharstrue } \def\scanargctxt{% used for copying and captions, not macros. \scanctxt \catcode`\@=\other \catcode`\\=\other \catcode`\^^M=\other } \def\macrobodyctxt{% used for @macro definitions \scanctxt \catcode`\ =\other \catcode`\@=\other \catcode`\{=\other \catcode`\}=\other \catcode`\^^M=\other \usembodybackslash } % Used when scanning braced macro arguments. Note, however, that catcode % changes here are ineffectual if the macro invocation was nested inside % an argument to another Texinfo command. \def\macroargctxt{% \scanctxt \catcode`\ =\active \catcode`\@=\other \catcode`\^^M=\other \catcode`\\=\active } \def\macrolineargctxt{% used for whole-line arguments without braces \scanctxt \catcode`\@=\other \catcode`\{=\other \catcode`\}=\other } % \mbodybackslash is the definition of \ in @macro bodies. % It maps \foo\ => \csname macarg.foo\endcsname => #N % where N is the macro parameter number. % We define \csname macarg.\endcsname to be \realbackslash, so % \\ in macro replacement text gets you a backslash. % {\catcode`@=0 @catcode`@\=@active @gdef@usembodybackslash{@let\=@mbodybackslash} @gdef@mbodybackslash#1\{@csname macarg.#1@endcsname} } \expandafter\def\csname macarg.\endcsname{\realbackslash} \def\margbackslash#1{\char`\#1 } \def\macro{\recursivefalse\parsearg\macroxxx} \def\rmacro{\recursivetrue\parsearg\macroxxx} \def\macroxxx#1{% \getargs{#1}% now \macname is the macname and \argl the arglist \ifx\argl\empty % no arguments \paramno=0\relax \else \expandafter\parsemargdef \argl;% \if\paramno>256\relax \ifx\eTeXversion\thisisundefined \errhelp = \EMsimple \errmessage{You need eTeX to compile a file with macros with more than 256 arguments} \fi \fi \fi \if1\csname ismacro.\the\macname\endcsname \message{Warning: redefining \the\macname}% \else \expandafter\ifx\csname \the\macname\endcsname \relax \else \errmessage{Macro name \the\macname\space already defined}\fi \global\cslet{macsave.\the\macname}{\the\macname}% \global\expandafter\let\csname ismacro.\the\macname\endcsname=1% \addtomacrolist{\the\macname}% \fi \begingroup \macrobodyctxt \ifrecursive \expandafter\parsermacbody \else \expandafter\parsemacbody \fi} \parseargdef\unmacro{% \if1\csname ismacro.#1\endcsname \global\cslet{#1}{macsave.#1}% \global\expandafter\let \csname ismacro.#1\endcsname=0% % Remove the macro name from \macrolist: \begingroup \expandafter\let\csname#1\endcsname \relax \let\commondummyword\unmacrodo \xdef\macrolist{\macrolist}% \endgroup \else \errmessage{Macro #1 not defined}% \fi } % Called by \do from \dounmacro on each macro. The idea is to omit any % macro definitions that have been changed to \relax. % \def\unmacrodo#1{% \ifx #1\relax % remove this \else \noexpand\commondummyword \noexpand#1% \fi } % \getargs -- Parse the arguments to a @macro line. Set \macname to % the name of the macro, and \argl to the braced argument list. \def\getargs#1{\getargsxxx#1{}} \def\getargsxxx#1#{\getmacname #1 \relax\getmacargs} \def\getmacname#1 #2\relax{\macname={#1}} \def\getmacargs#1{\def\argl{#1}} % This made use of the feature that if the last token of a % is #, then the preceding argument is delimited by % an opening brace, and that opening brace is not consumed. % Parse the optional {params} list to @macro or @rmacro. % Set \paramno to the number of arguments, % and \paramlist to a parameter text for the macro (e.g. #1,#2,#3 for a % three-param macro.) Define \macarg.BLAH for each BLAH in the params % list to some hook where the argument is to be expanded. If there are % less than 10 arguments that hook is to be replaced by ##N where N % is the position in that list, that is to say the macro arguments are to be % defined `a la TeX in the macro body. % % That gets used by \mbodybackslash (above). % % If there are 10 or more arguments, a different technique is used: see % \parsemmanyargdef. % \def\parsemargdef#1;{% \paramno=0\def\paramlist{}% \let\hash\relax % \hash is redefined to `#' later to get it into definitions \let\xeatspaces\relax \parsemargdefxxx#1,;,% \ifnum\paramno<10\relax\else \paramno0\relax \parsemmanyargdef@@#1,;,% 10 or more arguments \fi } \def\parsemargdefxxx#1,{% \if#1;\let\next=\relax \else \let\next=\parsemargdefxxx \advance\paramno by 1 \expandafter\edef\csname macarg.\eatspaces{#1}\endcsname {\xeatspaces{\hash\the\paramno}}% \edef\paramlist{\paramlist\hash\the\paramno,}% \fi\next} % \parsemacbody, \parsermacbody % % Read recursive and nonrecursive macro bodies. (They're different since % rec and nonrec macros end differently.) % % We are in \macrobodyctxt, and the \xdef causes backslashshes in the macro % body to be transformed. % Set \macrobody to the body of the macro, and call \defmacro. % {\catcode`\ =\other\long\gdef\parsemacbody#1@end macro{% \xdef\macrobody{\eatcr{#1}}\endgroup\defmacro}}% {\catcode`\ =\other\long\gdef\parsermacbody#1@end rmacro{% \xdef\macrobody{\eatcr{#1}}\endgroup\defmacro}}% % Make @ a letter, so that we can make private-to-Texinfo macro names. \edef\texiatcatcode{\the\catcode`\@} \catcode `@=11\relax %%%%%%%%%%%%%% Code for > 10 arguments only %%%%%%%%%%%%%%%%%% % If there are 10 or more arguments, a different technique is used, where the % hook remains in the body, and when macro is to be expanded the body is % processed again to replace the arguments. % % In that case, the hook is \the\toks N-1, and we simply set \toks N-1 to the % argument N value and then \edef the body (nothing else will expand because of % the catcode regime under which the body was input). % % If you compile with TeX (not eTeX), and you have macros with 10 or more % arguments, no macro can have more than 256 arguments (else error). % % In case that there are 10 or more arguments we parse again the arguments % list to set new definitions for the \macarg.BLAH macros corresponding to % each BLAH argument. It was anyhow needed to parse already once this list % in order to count the arguments, and as macros with at most 9 arguments % are by far more frequent than macro with 10 or more arguments, defining % twice the \macarg.BLAH macros does not cost too much processing power. \def\parsemmanyargdef@@#1,{% \if#1;\let\next=\relax \else \let\next=\parsemmanyargdef@@ \edef\tempb{\eatspaces{#1}}% \expandafter\def\expandafter\tempa \expandafter{\csname macarg.\tempb\endcsname}% % Note that we need some extra \noexpand\noexpand, this is because we % don't want \the to be expanded in the \parsermacbody as it uses an % \xdef . \expandafter\edef\tempa {\noexpand\noexpand\noexpand\the\toks\the\paramno}% \advance\paramno by 1\relax \fi\next} \let\endargs@\relax \let\nil@\relax \def\nilm@{\nil@}% \long\def\nillm@{\nil@}% % This macro is expanded during the Texinfo macro expansion, not during its % definition. It gets all the arguments' values and assigns them to macros % macarg.ARGNAME % % #1 is the macro name % #2 is the list of argument names % #3 is the list of argument values \def\getargvals@#1#2#3{% \def\macargdeflist@{}% \def\saveparamlist@{#2}% Need to keep a copy for parameter expansion. \def\paramlist{#2,\nil@}% \def\macroname{#1}% \begingroup \macroargctxt \def\argvaluelist{#3,\nil@}% \def\@tempa{#3}% \ifx\@tempa\empty \setemptyargvalues@ \else \getargvals@@ \fi } \def\getargvals@@{% \ifx\paramlist\nilm@ % Some sanity check needed here that \argvaluelist is also empty. \ifx\argvaluelist\nillm@ \else \errhelp = \EMsimple \errmessage{Too many arguments in macro `\macroname'!}% \fi \let\next\macargexpandinbody@ \else \ifx\argvaluelist\nillm@ % No more arguments values passed to macro. Set remaining named-arg % macros to empty. \let\next\setemptyargvalues@ \else % pop current arg name into \@tempb \def\@tempa##1{\pop@{\@tempb}{\paramlist}##1\endargs@}% \expandafter\@tempa\expandafter{\paramlist}% % pop current argument value into \@tempc \def\@tempa##1{\longpop@{\@tempc}{\argvaluelist}##1\endargs@}% \expandafter\@tempa\expandafter{\argvaluelist}% % Here \@tempb is the current arg name and \@tempc is the current arg value. % First place the new argument macro definition into \@tempd \expandafter\macname\expandafter{\@tempc}% \expandafter\let\csname macarg.\@tempb\endcsname\relax \expandafter\def\expandafter\@tempe\expandafter{% \csname macarg.\@tempb\endcsname}% \edef\@tempd{\long\def\@tempe{\the\macname}}% \push@\@tempd\macargdeflist@ \let\next\getargvals@@ \fi \fi \next } \def\push@#1#2{% \expandafter\expandafter\expandafter\def \expandafter\expandafter\expandafter#2% \expandafter\expandafter\expandafter{% \expandafter#1#2}% } % Replace arguments by their values in the macro body, and place the result % in macro \@tempa. % \def\macvalstoargs@{% % To do this we use the property that token registers that are \the'ed % within an \edef expand only once. So we are going to place all argument % values into respective token registers. % % First we save the token context, and initialize argument numbering. \begingroup \paramno0\relax % Then, for each argument number #N, we place the corresponding argument % value into a new token list register \toks#N \expandafter\putargsintokens@\saveparamlist@,;,% % Then, we expand the body so that argument are replaced by their % values. The trick for values not to be expanded themselves is that they % are within tokens and that tokens expand only once in an \edef . \edef\@tempc{\csname mac.\macroname .body\endcsname}% % Now we restore the token stack pointer to free the token list registers % which we have used, but we make sure that expanded body is saved after % group. \expandafter \endgroup \expandafter\def\expandafter\@tempa\expandafter{\@tempc}% } % Define the named-macro outside of this group and then close this group. % \def\macargexpandinbody@{% \expandafter \endgroup \macargdeflist@ % First the replace in body the macro arguments by their values, the result % is in \@tempa . \macvalstoargs@ % Then we point at the \norecurse or \gobble (for recursive) macro value % with \@tempb . \expandafter\let\expandafter\@tempb\csname mac.\macroname .recurse\endcsname % Depending on whether it is recursive or not, we need some tailing % \egroup . \ifx\@tempb\gobble \let\@tempc\relax \else \let\@tempc\egroup \fi % And now we do the real job: \edef\@tempd{\noexpand\@tempb{\macroname}\noexpand\scanmacro{\@tempa}\@tempc}% \@tempd } \def\putargsintokens@#1,{% \if#1;\let\next\relax \else \let\next\putargsintokens@ % First we allocate the new token list register, and give it a temporary % alias \@tempb . \toksdef\@tempb\the\paramno % Then we place the argument value into that token list register. \expandafter\let\expandafter\@tempa\csname macarg.#1\endcsname \expandafter\@tempb\expandafter{\@tempa}% \advance\paramno by 1\relax \fi \next } % Trailing missing arguments are set to empty. % \def\setemptyargvalues@{% \ifx\paramlist\nilm@ \let\next\macargexpandinbody@ \else \expandafter\setemptyargvaluesparser@\paramlist\endargs@ \let\next\setemptyargvalues@ \fi \next } \def\setemptyargvaluesparser@#1,#2\endargs@{% \expandafter\def\expandafter\@tempa\expandafter{% \expandafter\def\csname macarg.#1\endcsname{}}% \push@\@tempa\macargdeflist@ \def\paramlist{#2}% } % #1 is the element target macro % #2 is the list macro % #3,#4\endargs@ is the list value \def\pop@#1#2#3,#4\endargs@{% \def#1{#3}% \def#2{#4}% } \long\def\longpop@#1#2#3,#4\endargs@{% \long\def#1{#3}% \long\def#2{#4}% } %%%%%%%%%%%%%% End of code for > 10 arguments %%%%%%%%%%%%%%%%%% % This defines a Texinfo @macro or @rmacro, called by \parsemacbody. % \macrobody has the body of the macro in it, with placeholders for % its parameters, looking like "\xeatspaces{\hash 1}". % \paramno is the number of parameters % \paramlist is a TeX parameter text, e.g. "#1,#2,#3," % There are four cases: macros of zero, one, up to nine, and many arguments. % \xdef is used so that macro definitions will survive the file % they're defined in: @include reads the file inside a group. % \def\defmacro{% \let\hash=##% convert placeholders to macro parameter chars \ifnum\paramno=1 \def\xeatspaces##1{##1}% % This removes the pair of braces around the argument. We don't % use \eatspaces, because this can cause ends of lines to be lost % when the argument to \eatspaces is read, leading to line-based % commands like "@itemize" not being read correctly. \else \let\xeatspaces\relax % suppress expansion \fi \ifcase\paramno % 0 \expandafter\xdef\csname\the\macname\endcsname{% \bgroup \noexpand\spaceisspace \noexpand\endlineisspace \noexpand\expandafter % skip any whitespace after the macro name. \expandafter\noexpand\csname\the\macname @@@\endcsname}% \expandafter\xdef\csname\the\macname @@@\endcsname{% \egroup \noexpand\scanmacro{\macrobody}}% \or % 1 \expandafter\xdef\csname\the\macname\endcsname{% \bgroup \noexpand\braceorline \expandafter\noexpand\csname\the\macname @@@\endcsname}% \expandafter\xdef\csname\the\macname @@@\endcsname##1{% \egroup \noexpand\scanmacro{\macrobody}% }% \else % at most 9 \ifnum\paramno<10\relax % @MACNAME sets the context for reading the macro argument % @MACNAME@@ gets the argument, processes backslashes and appends a % comma. % @MACNAME@@@ removes braces surrounding the argument list. % @MACNAME@@@@ scans the macro body with arguments substituted. \expandafter\xdef\csname\the\macname\endcsname{% \bgroup \noexpand\expandafter % This \expandafter skip any spaces after the \noexpand\macroargctxt % macro before we change the catcode of space. \noexpand\expandafter \expandafter\noexpand\csname\the\macname @@\endcsname}% \expandafter\xdef\csname\the\macname @@\endcsname##1{% \noexpand\passargtomacro \expandafter\noexpand\csname\the\macname @@@\endcsname{##1,}}% \expandafter\xdef\csname\the\macname @@@\endcsname##1{% \expandafter\noexpand\csname\the\macname @@@@\endcsname ##1}% \expandafter\expandafter \expandafter\xdef \expandafter\expandafter \csname\the\macname @@@@\endcsname\paramlist{% \egroup\noexpand\scanmacro{\macrobody}}% \else % 10 or more: \expandafter\xdef\csname\the\macname\endcsname{% \noexpand\getargvals@{\the\macname}{\argl}% }% \global\expandafter\let\csname mac.\the\macname .body\endcsname\macrobody \global\expandafter\let\csname mac.\the\macname .recurse\endcsname\gobble \fi \fi} \catcode `\@\texiatcatcode\relax % end private-to-Texinfo catcodes \def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % {\catcode`\@=0 \catcode`\\=13 % We need to manipulate \ so use @ as escape @catcode`@_=11 % private names @catcode`@!=11 % used as argument separator % \passargtomacro#1#2 - % Call #1 with a list of tokens #2, with any doubled backslashes in #2 % compressed to one. % % This implementation works by expansion, and not execution (so we cannot use % \def or similar). This reduces the risk of this failing in contexts where % complete expansion is done with no execution (for example, in writing out to % an auxiliary file for an index entry). % % State is kept in the input stream: the argument passed to % @look_ahead, @gobble_and_check_finish and @add_segment is % % THE_MACRO ARG_RESULT ! {PENDING_BS} NEXT_TOKEN (... rest of input) % % where: % THE_MACRO - name of the macro we want to call % ARG_RESULT - argument list we build to pass to that macro % PENDING_BS - either a backslash or nothing % NEXT_TOKEN - used to look ahead in the input stream to see what's coming next @gdef@passargtomacro#1#2{% @add_segment #1!{}@relax#2\@_finish\% } @gdef@_finish{@_finishx} @global@let@_finishx@relax % #1 - THE_MACRO ARG_RESULT % #2 - PENDING_BS % #3 - NEXT_TOKEN % #4 used to look ahead % % If the next token is not a backslash, process the rest of the argument; % otherwise, remove the next token. @gdef@look_ahead#1!#2#3#4{% @ifx#4\% @expandafter@gobble_and_check_finish @else @expandafter@add_segment @fi#1!{#2}#4#4% } % #1 - THE_MACRO ARG_RESULT % #2 - PENDING_BS % #3 - NEXT_TOKEN % #4 should be a backslash, which is gobbled. % #5 looks ahead % % Double backslash found. Add a single backslash, and look ahead. @gdef@gobble_and_check_finish#1!#2#3#4#5{% @add_segment#1\!{}#5#5% } @gdef@is_fi{@fi} % #1 - THE_MACRO ARG_RESULT % #2 - PENDING_BS % #3 - NEXT_TOKEN % #4 is input stream until next backslash % % Input stream is either at the start of the argument, or just after a % backslash sequence, either a lone backslash, or a doubled backslash. % NEXT_TOKEN contains the first token in the input stream: if it is \finish, % finish; otherwise, append to ARG_RESULT the segment of the argument up until % the next backslash. PENDING_BACKSLASH contains a backslash to represent % a backslash just before the start of the input stream that has not been % added to ARG_RESULT. @gdef@add_segment#1!#2#3#4\{% @ifx#3@_finish @call_the_macro#1!% @else % append the pending backslash to the result, followed by the next segment @expandafter@is_fi@look_ahead#1#2#4!{\}@fi % this @fi is discarded by @look_ahead. % we can't get rid of it with \expandafter because we don't know how % long #4 is. } % #1 - THE_MACRO % #2 - ARG_RESULT % #3 discards the res of the conditional in @add_segment, and @is_fi ends the % conditional. @gdef@call_the_macro#1#2!#3@fi{@is_fi #1{#2}} } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % \braceorline MAC is used for a one-argument macro MAC. It checks % whether the next non-whitespace character is a {. It sets the context % for reading the argument (slightly different in the two cases). Then, % to read the argument, in the whole-line case, it then calls the regular % \parsearg MAC; in the lbrace case, it calls \passargtomacro MAC. % \def\braceorline#1{\let\macnamexxx=#1\futurelet\nchar\braceorlinexxx} \def\braceorlinexxx{% \ifx\nchar\bgroup \macroargctxt \expandafter\passargtomacro \else \macrolineargctxt\expandafter\parsearg \fi \macnamexxx} % @alias. % We need some trickery to remove the optional spaces around the equal % sign. Make them active and then expand them all to nothing. % \def\alias{\parseargusing\obeyspaces\aliasxxx} \def\aliasxxx #1{\aliasyyy#1\relax} \def\aliasyyy #1=#2\relax{% {% \expandafter\let\obeyedspace=\empty \addtomacrolist{#1}% \xdef\next{\global\let\makecsname{#1}=\makecsname{#2}}% }% \next } \message{cross references,} \newwrite\auxfile \newif\ifhavexrefs % True if xref values are known. \newif\ifwarnedxrefs % True if we warned once that they aren't known. % @inforef is relatively simple. \def\inforef #1{\inforefzzz #1,,,,**} \def\inforefzzz #1,#2,#3,#4**{% \putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}}, node \samp{\ignorespaces#1{}}} % @node's only job in TeX is to define \lastnode, which is used in % cross-references. The @node line might or might not have commas, and % might or might not have spaces before the first comma, like: % @node foo , bar , ... % We don't want such trailing spaces in the node name. % \parseargdef\node{\checkenv{}\donode #1 ,\finishnodeparse} % % also remove a trailing comma, in case of something like this: % @node Help-Cross, , , Cross-refs \def\donode#1 ,#2\finishnodeparse{\dodonode #1,\finishnodeparse} \def\dodonode#1,#2\finishnodeparse{\gdef\lastnode{#1}\omittopnode} % Used so that the @top node doesn't have to be wrapped in an @ifnottex % conditional. % \doignore goes to more effort to skip nested conditionals but we don't need % that here. \def\omittopnode{% \ifx\lastnode\wordTop \expandafter\ignorenode\fi } \def\wordTop{Top} % Until the next @node or @bye command, divert output to a box that is not % output. \def\ignorenode{\setbox\dummybox\vbox\bgroup\def\node{\egroup\node}% \ignorenodebye } {\let\bye\relax \gdef\ignorenodebye{\let\bye\ignorenodebyedef} \gdef\ignorenodebyedef{\egroup(`Top' node ignored)\bye}} % The redefinition of \bye here is because it is declared \outer \let\lastnode=\empty % Write a cross-reference definition for the current node. #1 is the % type (Ynumbered, Yappendix, Ynothing). % \def\donoderef#1{% \ifx\lastnode\empty\else \setref{\lastnode}{#1}% \global\let\lastnode=\empty \fi } % @anchor{NAME} -- define xref target at arbitrary point. % \newcount\savesfregister % \def\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi} \def\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi} \def\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces} % \setref{NAME}{SNT} defines a cross-reference point NAME (a node or an % anchor), which consists of three parts: % 1) NAME-title - the current sectioning name taken from \currentsection, % or the anchor name. % 2) NAME-snt - section number and type, passed as the SNT arg, or % empty for anchors. % 3) NAME-pg - the page number. % % This is called from \donoderef, \anchor, and \dofloat. In the case of % floats, there is an additional part, which is not written here: % 4) NAME-lof - the text as it should appear in a @listoffloats. % \def\setref#1#2{% \pdfmkdest{#1}% \iflinks {% \requireauxfile \atdummies % preserve commands, but don't expand them % match definition in \xrdef, \refx, \xrefX. \def\value##1{##1}% \edef\writexrdef##1##2{% \write\auxfile{@xrdef{#1-% #1 of \setref, expanded by the \edef ##1}{##2}}% these are parameters of \writexrdef }% \toks0 = \expandafter{\currentsection}% \immediate \writexrdef{title}{\the\toks0 }% \immediate \writexrdef{snt}{\csname #2\endcsname}% \Ynumbered etc. \safewhatsit{\writexrdef{pg}{\folio}}% will be written later, at \shipout }% \fi } % @xrefautosectiontitle on|off says whether @section(ing) names are used % automatically in xrefs, if the third arg is not explicitly specified. % This was provided as a "secret" @set xref-automatic-section-title % variable, now it's official. % \parseargdef\xrefautomaticsectiontitle{% \def\temp{#1}% \ifx\temp\onword \expandafter\let\csname SETxref-automatic-section-title\endcsname = \empty \else\ifx\temp\offword \expandafter\let\csname SETxref-automatic-section-title\endcsname = \relax \else \errhelp = \EMsimple \errmessage{Unknown @xrefautomaticsectiontitle value `\temp', must be on|off}% \fi\fi } % % @xref, @pxref, and @ref generate cross-references. For \xrefX, #1 is % the node name, #2 the name of the Info cross-reference, #3 the printed % node name, #4 the name of the Info file, #5 the name of the printed % manual. All but the node name can be omitted. % \def\pxref{\putwordsee{} \xrefXX} \def\xref{\putwordSee{} \xrefXX} \def\ref{\xrefXX} \def\xrefXX#1{\def\xrefXXarg{#1}\futurelet\tokenafterxref\xrefXXX} \def\xrefXXX{\expandafter\xrefX\expandafter[\xrefXXarg,,,,,,,]} % \newbox\toprefbox \newbox\printedrefnamebox \newbox\infofilenamebox \newbox\printedmanualbox % \def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup \unsepspaces % % Get args without leading/trailing spaces. \def\printedrefname{\ignorespaces #3}% \setbox\printedrefnamebox = \hbox{\printedrefname\unskip}% % \def\infofilename{\ignorespaces #4}% \setbox\infofilenamebox = \hbox{\infofilename\unskip}% % \def\printedmanual{\ignorespaces #5}% \setbox\printedmanualbox = \hbox{\printedmanual\unskip}% % % If the printed reference name (arg #3) was not explicitly given in % the @xref, figure out what we want to use. \ifdim \wd\printedrefnamebox = 0pt % No printed node name was explicitly given. \expandafter\ifx\csname SETxref-automatic-section-title\endcsname \relax % Not auto section-title: use node name inside the square brackets. \def\printedrefname{\ignorespaces #1}% \else % Auto section-title: use chapter/section title inside % the square brackets if we have it. \ifdim \wd\printedmanualbox > 0pt % It is in another manual, so we don't have it; use node name. \def\printedrefname{\ignorespaces #1}% \else \ifhavexrefs % We (should) know the real title if we have the xref values. \def\printedrefname{\refx{#1-title}{}}% \else % Otherwise just copy the Info node name. \def\printedrefname{\ignorespaces #1}% \fi% \fi \fi \fi % % Make link in pdf output. \ifpdf % For pdfTeX and LuaTeX {\indexnofonts \makevalueexpandable \turnoffactive % This expands tokens, so do it after making catcode changes, so _ % etc. don't get their TeX definitions. This ignores all spaces in % #4, including (wrongly) those in the middle of the filename. \getfilename{#4}% % % This (wrongly) does not take account of leading or trailing % spaces in #1, which should be ignored. \setpdfdestname{#1}% % \ifx\pdfdestname\empty \def\pdfdestname{Top}% no empty targets \fi % \leavevmode \startlink attr{/Border [0 0 0]}% \ifnum\filenamelength>0 goto file{\the\filename.pdf} name{\pdfdestname}% \else goto name{\pdfmkpgn{\pdfdestname}}% \fi }% \setcolor{\linkcolor}% \else \ifx\XeTeXrevision\thisisundefined \else % For XeTeX {\indexnofonts \makevalueexpandable \turnoffactive % This expands tokens, so do it after making catcode changes, so _ % etc. don't get their TeX definitions. This ignores all spaces in % #4, including (wrongly) those in the middle of the filename. \getfilename{#4}% % % This (wrongly) does not take account of leading or trailing % spaces in #1, which should be ignored. \setpdfdestname{#1}% % \ifx\pdfdestname\empty \def\pdfdestname{Top}% no empty targets \fi % \leavevmode \ifnum\filenamelength>0 % With default settings, % XeTeX (xdvipdfmx) replaces link destination names with integers. % In this case, the replaced destination names of % remote PDFs are no longer known. In order to avoid a replacement, % you can use xdvipdfmx's command line option `-C 0x0010'. % If you use XeTeX 0.99996+ (TeX Live 2016+), % this command line option is no longer necessary % because we can use the `dvipdfmx:config' special. \special{pdf:bann << /Border [0 0 0] /Type /Annot /Subtype /Link /A << /S /GoToR /F (\the\filename.pdf) /D (\pdfdestname) >> >>}% \else \special{pdf:bann << /Border [0 0 0] /Type /Annot /Subtype /Link /A << /S /GoTo /D (\pdfdestname) >> >>}% \fi }% \setcolor{\linkcolor}% \fi \fi {% % Have to otherify everything special to allow the \csname to % include an _ in the xref name, etc. \indexnofonts \turnoffactive \def\value##1{##1}% \expandafter\global\expandafter\let\expandafter\Xthisreftitle \csname XR#1-title\endcsname }% % % Float references are printed completely differently: "Figure 1.2" % instead of "[somenode], p.3". \iffloat distinguishes them by % \Xthisreftitle being set to a magic string. \iffloat\Xthisreftitle % If the user specified the print name (third arg) to the ref, % print it instead of our usual "Figure 1.2". \ifdim\wd\printedrefnamebox = 0pt \refx{#1-snt}{}% \else \printedrefname \fi % % If the user also gave the printed manual name (fifth arg), append % "in MANUALNAME". \ifdim \wd\printedmanualbox > 0pt \space \putwordin{} \cite{\printedmanual}% \fi \else % node/anchor (non-float) references. % % If we use \unhbox to print the node names, TeX does not insert % empty discretionaries after hyphens, which means that it will not % find a line break at a hyphen in a node names. Since some manuals % are best written with fairly long node names, containing hyphens, % this is a loss. Therefore, we give the text of the node name % again, so it is as if TeX is seeing it for the first time. % \ifdim \wd\printedmanualbox > 0pt % Cross-manual reference with a printed manual name. % \crossmanualxref{\cite{\printedmanual\unskip}}% % \else\ifdim \wd\infofilenamebox > 0pt % Cross-manual reference with only an info filename (arg 4), no % printed manual name (arg 5). This is essentially the same as % the case above; we output the filename, since we have nothing else. % \crossmanualxref{\code{\infofilename\unskip}}% % \else % Reference within this manual. % % Only output a following space if the -snt ref is nonempty; for % @unnumbered and @anchor, it won't be. \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}% \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi % % output the `[mynode]' via the macro below so it can be overridden. \xrefprintnodename\printedrefname % % But we always want a comma and a space: ,\space % % output the `page 3'. \turnoffactive \putwordpage\tie\refx{#1-pg}{}% % Add a , if xref followed by a space \if\space\noexpand\tokenafterxref ,% \else\ifx\ \tokenafterxref ,% @TAB \else\ifx\*\tokenafterxref ,% @* \else\ifx\ \tokenafterxref ,% @SPACE \else\ifx\ \tokenafterxref ,% @NL \else\ifx\tie\tokenafterxref ,% @tie \fi\fi\fi\fi\fi\fi \fi\fi \fi \endlink \endgroup} % Output a cross-manual xref to #1. Used just above (twice). % % Only include the text "Section ``foo'' in" if the foo is neither % missing or Top. Thus, @xref{,,,foo,The Foo Manual} outputs simply % "see The Foo Manual", the idea being to refer to the whole manual. % % But, this being TeX, we can't easily compare our node name against the % string "Top" while ignoring the possible spaces before and after in % the input. By adding the arbitrary 7sp below, we make it much less % likely that a real node name would have the same width as "Top" (e.g., % in a monospaced font). Hopefully it will never happen in practice. % % For the same basic reason, we retypeset the "Top" at every % reference, since the current font is indeterminate. % \def\crossmanualxref#1{% \setbox\toprefbox = \hbox{Top\kern7sp}% \setbox2 = \hbox{\ignorespaces \printedrefname \unskip \kern7sp}% \ifdim \wd2 > 7sp % nonempty? \ifdim \wd2 = \wd\toprefbox \else % same as Top? \putwordSection{} ``\printedrefname'' \putwordin{}\space \fi \fi #1% } % This macro is called from \xrefX for the `[nodename]' part of xref % output. It's a separate macro only so it can be changed more easily, % since square brackets don't work well in some documents. Particularly % one that Bob is working on :). % \def\xrefprintnodename#1{[#1]} % Things referred to by \setref. % \def\Ynothing{} \def\Yomitfromtoc{} \def\Ynumbered{% \ifnum\secno=0 \putwordChapter@tie \the\chapno \else \ifnum\subsecno=0 \putwordSection@tie \the\chapno.\the\secno \else \ifnum\subsubsecno=0 \putwordSection@tie \the\chapno.\the\secno.\the\subsecno \else \putwordSection@tie \the\chapno.\the\secno.\the\subsecno.\the\subsubsecno \fi\fi\fi } \def\Yappendix{% \ifnum\secno=0 \putwordAppendix@tie @char\the\appendixno{}% \else \ifnum\subsecno=0 \putwordSection@tie @char\the\appendixno.\the\secno \else \ifnum\subsubsecno=0 \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno \else \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno \fi\fi\fi } % \refx{NAME}{SUFFIX} - reference a cross-reference string named NAME. SUFFIX % is output afterwards if non-empty. \def\refx#1#2{% \requireauxfile {% \indexnofonts \turnoffactive \def\value##1{##1}% \expandafter\global\expandafter\let\expandafter\thisrefX \csname XR#1\endcsname }% \ifx\thisrefX\relax % If not defined, say something at least. \angleleft un\-de\-fined\angleright \iflinks \ifhavexrefs {\toks0 = {#1}% avoid expansion of possibly-complex value \message{\linenumber Undefined cross reference `\the\toks0'.}}% \else \ifwarnedxrefs\else \global\warnedxrefstrue \message{Cross reference values unknown; you must run TeX again.}% \fi \fi \fi \else % It's defined, so just use it. \thisrefX \fi #2% Output the suffix in any case. } % This is the macro invoked by entries in the aux file. Define a control % sequence for a cross-reference target (we prepend XR to the control sequence % name to avoid collisions). The value is the page number. If this is a float % type, we have more work to do. % \def\xrdef#1#2{% {% Expand the node or anchor name to remove control sequences. % \turnoffactive stops 8-bit characters being changed to commands % like @'e. \refx does the same to retrieve the value in the definition. \indexnofonts \turnoffactive \def\value##1{##1}% \xdef\safexrefname{#1}% }% % \bgroup \expandafter\gdef\csname XR\safexrefname\endcsname{#2}% \egroup % We put the \gdef inside a group to avoid the definitions building up on % TeX's save stack, which can cause it to run out of space for aux files with % thousands of lines. \gdef doesn't use the save stack, but \csname does % when it defines an unknown control sequence as \relax. % % Was that xref control sequence that we just defined for a float? \expandafter\iffloat\csname XR\safexrefname\endcsname % it was a float, and we have the (safe) float type in \iffloattype. \expandafter\let\expandafter\floatlist \csname floatlist\iffloattype\endcsname % % Is this the first time we've seen this float type? \expandafter\ifx\floatlist\relax \toks0 = {\do}% yes, so just \do \else % had it before, so preserve previous elements in list. \toks0 = \expandafter{\floatlist\do}% \fi % % Remember this xref in the control sequence \floatlistFLOATTYPE, % for later use in \listoffloats. \expandafter\xdef\csname floatlist\iffloattype\endcsname{\the\toks0 {\safexrefname}}% \fi } % If working on a large document in chapters, it is convenient to % be able to disable indexing, cross-referencing, and contents, for test runs. % This is done with @novalidate at the beginning of the file. % \newif\iflinks \linkstrue % by default we want the aux files. \let\novalidate = \linksfalse % Used when writing to the aux file, or when using data from it. \def\requireauxfile{% \iflinks \tryauxfile % Open the new aux file. TeX will close it automatically at exit. \immediate\openout\auxfile=\jobname.aux \fi \global\let\requireauxfile=\relax % Only do this once. } % Read the last existing aux file, if any. No error if none exists. % \def\tryauxfile{% \openin 1 \jobname.aux \ifeof 1 \else \readdatafile{aux}% \global\havexrefstrue \fi \closein 1 } \def\setupdatafile{% \catcode`\^^@=\other \catcode`\^^A=\other \catcode`\^^B=\other \catcode`\^^C=\other \catcode`\^^D=\other \catcode`\^^E=\other \catcode`\^^F=\other \catcode`\^^G=\other \catcode`\^^H=\other \catcode`\^^K=\other \catcode`\^^L=\other \catcode`\^^N=\other \catcode`\^^P=\other \catcode`\^^Q=\other \catcode`\^^R=\other \catcode`\^^S=\other \catcode`\^^T=\other \catcode`\^^U=\other \catcode`\^^V=\other \catcode`\^^W=\other \catcode`\^^X=\other \catcode`\^^Z=\other \catcode`\^^[=\other \catcode`\^^\=\other \catcode`\^^]=\other \catcode`\^^^=\other \catcode`\^^_=\other \catcode`\^=\other % % Special characters. Should be turned off anyway, but... \catcode`\~=\other \catcode`\[=\other \catcode`\]=\other \catcode`\"=\other \catcode`\_=\other \catcode`\|=\other \catcode`\<=\other \catcode`\>=\other \catcode`\$=\other \catcode`\#=\other \catcode`\&=\other \catcode`\%=\other \catcode`+=\other % avoid \+ for paranoia even though we've turned it off % \catcode`\\=\active % % @ is our escape character in .aux files, and we need braces. \catcode`\{=1 \catcode`\}=2 \catcode`\@=0 } \def\readdatafile#1{% \begingroup \setupdatafile \input\jobname.#1 \endgroup} \message{insertions,} % including footnotes. \newcount \footnoteno % The trailing space in the following definition for supereject is % vital for proper filling; pages come out unaligned when you do a % pagealignmacro call if that space before the closing brace is % removed. (Generally, numeric constants should always be followed by a % space to prevent strange expansion errors.) \def\supereject{\par\penalty -20000\footnoteno =0 } % @footnotestyle is meaningful for Info output only. \let\footnotestyle=\comment {\catcode `\@=11 % % Auto-number footnotes. Otherwise like plain. \gdef\footnote{% \global\advance\footnoteno by \@ne \edef\thisfootno{$^{\the\footnoteno}$}% % % In case the footnote comes at the end of a sentence, preserve the % extra spacing after we do the footnote number. \let\@sf\empty \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\ptexslash\fi % % Remove inadvertent blank space before typesetting the footnote number. \unskip \thisfootno\@sf \dofootnote }% % Don't bother with the trickery in plain.tex to not require the % footnote text as a parameter. Our footnotes don't need to be so general. % % Oh yes, they do; otherwise, @ifset (and anything else that uses % \parseargline) fails inside footnotes because the tokens are fixed when % the footnote is read. --karl, 16nov96. % \gdef\dofootnote{% \insert\footins\bgroup % % Nested footnotes are not supported in TeX, that would take a lot % more work. (\startsavinginserts does not suffice.) \let\footnote=\errfootnotenest % % We want to typeset this text as a normal paragraph, even if the % footnote reference occurs in (for example) a display environment. % So reset some parameters. \hsize=\txipagewidth \interlinepenalty\interfootnotelinepenalty \splittopskip\ht\strutbox % top baseline for broken footnotes \splitmaxdepth\dp\strutbox \floatingpenalty\@MM \leftskip\z@skip \rightskip\z@skip \spaceskip\z@skip \xspaceskip\z@skip \parindent\defaultparindent % \smallfonts \rm % % Because we use hanging indentation in footnotes, a @noindent appears % to exdent this text, so make it be a no-op. makeinfo does not use % hanging indentation so @noindent can still be needed within footnote % text after an @example or the like (not that this is good style). \let\noindent = \relax % % Hang the footnote text off the number. Use \everypar in case the % footnote extends for more than one paragraph. \everypar = {\hang}% \textindent{\thisfootno}% % % Don't crash into the line above the footnote text. Since this % expands into a box, it must come within the paragraph, lest it % provide a place where TeX can split the footnote. \footstrut % % Invoke rest of plain TeX footnote routine. \futurelet\next\fo@t } }%end \catcode `\@=11 \def\errfootnotenest{% \errhelp=\EMsimple \errmessage{Nested footnotes not supported in texinfo.tex, even though they work in makeinfo; sorry} } \def\errfootnoteheading{% \errhelp=\EMsimple \errmessage{Footnotes in chapters, sections, etc., are not supported} } % In case a @footnote appears in a vbox, save the footnote text and create % the real \insert just after the vbox finished. Otherwise, the insertion % would be lost. % Similarly, if a @footnote appears inside an alignment, save the footnote % text to a box and make the \insert when a row of the table is finished. % And the same can be done for other insert classes. --kasal, 16nov03. % % Replace the \insert primitive by a cheating macro. % Deeper inside, just make sure that the saved insertions are not spilled % out prematurely. % \def\startsavinginserts{% \ifx \insert\ptexinsert \let\insert\saveinsert \else \let\checkinserts\relax \fi } % This \insert replacement works for both \insert\footins{foo} and % \insert\footins\bgroup foo\egroup, but it doesn't work for \insert27{foo}. % \def\saveinsert#1{% \edef\next{\noexpand\savetobox \makeSAVEname#1}% \afterassignment\next % swallow the left brace \let\temp = } \def\makeSAVEname#1{\makecsname{SAVE\expandafter\gobble\string#1}} \def\savetobox#1{\global\setbox#1 = \vbox\bgroup \unvbox#1} \def\checksaveins#1{\ifvoid#1\else \placesaveins#1\fi} \def\placesaveins#1{% \ptexinsert \csname\expandafter\gobblesave\string#1\endcsname {\box#1}% } % eat @SAVE -- beware, all of them have catcode \other: { \def\dospecials{\do S\do A\do V\do E} \uncatcodespecials % ;-) \gdef\gobblesave @SAVE{} } % initialization: \def\newsaveins #1{% \edef\next{\noexpand\newsaveinsX \makeSAVEname#1}% \next } \def\newsaveinsX #1{% \csname newbox\endcsname #1% \expandafter\def\expandafter\checkinserts\expandafter{\checkinserts \checksaveins #1}% } % initialize: \let\checkinserts\empty \newsaveins\footins \newsaveins\margin % @image. We use the macros from epsf.tex to support this. % If epsf.tex is not installed and @image is used, we complain. % % Check for and read epsf.tex up front. If we read it only at @image % time, we might be inside a group, and then its definitions would get % undone and the next image would fail. \openin 1 = epsf.tex \ifeof 1 \else % Do not bother showing banner with epsf.tex v2.7k (available in % doc/epsf.tex and on ctan). \def\epsfannounce{\toks0 = }% \input epsf.tex \fi \closein 1 % % We will only complain once about lack of epsf.tex. \newif\ifwarnednoepsf \newhelp\noepsfhelp{epsf.tex must be installed for images to work. It is also included in the Texinfo distribution, or you can get it from https://ctan.org/texarchive/macros/texinfo/texinfo/doc/epsf.tex.} % \def\image#1{% \ifx\epsfbox\thisisundefined \ifwarnednoepsf \else \errhelp = \noepsfhelp \errmessage{epsf.tex not found, images will be ignored}% \global\warnednoepsftrue \fi \else \imagexxx #1,,,,,\finish \fi } % % Arguments to @image: % #1 is (mandatory) image filename; we tack on .eps extension. % #2 is (optional) width, #3 is (optional) height. % #4 is (ignored optional) html alt text. % #5 is (ignored optional) extension. % #6 is just the usual extra ignored arg for parsing stuff. \newif\ifimagevmode \def\imagexxx#1,#2,#3,#4,#5,#6\finish{\begingroup \catcode`\^^M = 5 % in case we're inside an example \normalturnoffactive % allow _ et al. in names \def\xprocessmacroarg{\eatspaces}% in case we are being used via a macro % If the image is by itself, center it. \ifvmode \imagevmodetrue \else \ifx\centersub\centerV % for @center @image, we need a vbox so we can have our vertical space \imagevmodetrue \vbox\bgroup % vbox has better behavior than vtop herev \fi\fi % \ifimagevmode \nobreak\medskip % Usually we'll have text after the image which will insert % \parskip glue, so insert it here too to equalize the space % above and below. \nobreak\vskip\parskip \nobreak \fi % % Leave vertical mode so that indentation from an enclosing % environment such as @quotation is respected. % However, if we're at the top level, we don't want the % normal paragraph indentation. % On the other hand, if we are in the case of @center @image, we don't % want to start a paragraph, which will create a hsize-width box and % eradicate the centering. \ifx\centersub\centerV\else \noindent \fi % % Output the image. \ifpdf % For pdfTeX and LuaTeX <= 0.80 \dopdfimage{#1}{#2}{#3}% \else \ifx\XeTeXrevision\thisisundefined % For epsf.tex % \epsfbox itself resets \epsf?size at each figure. \setbox0 = \hbox{\ignorespaces #2}% \ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi \setbox0 = \hbox{\ignorespaces #3}% \ifdim\wd0 > 0pt \epsfysize=#3\relax \fi \epsfbox{#1.eps}% \else % For XeTeX \doxeteximage{#1}{#2}{#3}% \fi \fi % \ifimagevmode \medskip % space after a standalone image \fi \ifx\centersub\centerV \egroup \fi \endgroup} % @float FLOATTYPE,LABEL,LOC ... @end float for displayed figures, tables, % etc. We don't actually implement floating yet, we always include the % float "here". But it seemed the best name for the future. % \envparseargdef\float{\eatcommaspace\eatcommaspace\dofloat#1, , ,\finish} % There may be a space before second and/or third parameter; delete it. \def\eatcommaspace#1, {#1,} % #1 is the optional FLOATTYPE, the text label for this float, typically % "Figure", "Table", "Example", etc. Can't contain commas. If omitted, % this float will not be numbered and cannot be referred to. % % #2 is the optional xref label. Also must be present for the float to % be referable. % % #3 is the optional positioning argument; for now, it is ignored. It % will somehow specify the positions allowed to float to (here, top, bottom). % % We keep a separate counter for each FLOATTYPE, which we reset at each % chapter-level command. \let\resetallfloatnos=\empty % \def\dofloat#1,#2,#3,#4\finish{% \let\thiscaption=\empty \let\thisshortcaption=\empty % % don't lose footnotes inside @float. % % BEWARE: when the floats start float, we have to issue warning whenever an % insert appears inside a float which could possibly float. --kasal, 26may04 % \startsavinginserts % % We can't be used inside a paragraph. \par % \vtop\bgroup \def\floattype{#1}% \def\floatlabel{#2}% \def\floatloc{#3}% we do nothing with this yet. % \ifx\floattype\empty \let\safefloattype=\empty \else {% % the floattype might have accents or other special characters, % but we need to use it in a control sequence name. \indexnofonts \turnoffactive \xdef\safefloattype{\floattype}% }% \fi % % If label is given but no type, we handle that as the empty type. \ifx\floatlabel\empty \else % We want each FLOATTYPE to be numbered separately (Figure 1, % Table 1, Figure 2, ...). (And if no label, no number.) % \expandafter\getfloatno\csname\safefloattype floatno\endcsname \global\advance\floatno by 1 % {% % This magic value for \currentsection is output by \setref as the % XREFLABEL-title value. \xrefX uses it to distinguish float % labels (which have a completely different output format) from % node and anchor labels. And \xrdef uses it to construct the % lists of floats. % \edef\currentsection{\floatmagic=\safefloattype}% \setref{\floatlabel}{Yfloat}% }% \fi % % start with \parskip glue, I guess. \vskip\parskip % % Don't suppress indentation if a float happens to start a section. \restorefirstparagraphindent } % we have these possibilities: % @float Foo,lbl & @caption{Cap}: Foo 1.1: Cap % @float Foo,lbl & no caption: Foo 1.1 % @float Foo & @caption{Cap}: Foo: Cap % @float Foo & no caption: Foo % @float ,lbl & Caption{Cap}: 1.1: Cap % @float ,lbl & no caption: 1.1 % @float & @caption{Cap}: Cap % @float & no caption: % \def\Efloat{% \let\floatident = \empty % % In all cases, if we have a float type, it comes first. \ifx\floattype\empty \else \def\floatident{\floattype}\fi % % If we have an xref label, the number comes next. \ifx\floatlabel\empty \else \ifx\floattype\empty \else % if also had float type, need tie first. \appendtomacro\floatident{\tie}% \fi % the number. \appendtomacro\floatident{\chaplevelprefix\the\floatno}% \fi % % Start the printed caption with what we've constructed in % \floatident, but keep it separate; we need \floatident again. \let\captionline = \floatident % \ifx\thiscaption\empty \else \ifx\floatident\empty \else \appendtomacro\captionline{: }% had ident, so need a colon between \fi % % caption text. \appendtomacro\captionline{\scanexp\thiscaption}% \fi % % If we have anything to print, print it, with space before. % Eventually this needs to become an \insert. \ifx\captionline\empty \else \vskip.5\parskip \captionline % % Space below caption. \vskip\parskip \fi % % If have an xref label, write the list of floats info. Do this % after the caption, to avoid chance of it being a breakpoint. \ifx\floatlabel\empty \else % Write the text that goes in the lof to the aux file as % \floatlabel-lof. Besides \floatident, we include the short % caption if specified, else the full caption if specified, else nothing. {% \requireauxfile \atdummies % \ifx\thisshortcaption\empty \def\gtemp{\thiscaption}% \else \def\gtemp{\thisshortcaption}% \fi \immediate\write\auxfile{@xrdef{\floatlabel-lof}{\floatident \ifx\gtemp\empty \else : \gtemp \fi}}% }% \fi \egroup % end of \vtop % \checkinserts } % Append the tokens #2 to the definition of macro #1, not expanding either. % \def\appendtomacro#1#2{% \expandafter\def\expandafter#1\expandafter{#1#2}% } % @caption, @shortcaption % \def\caption{\docaption\thiscaption} \def\shortcaption{\docaption\thisshortcaption} \def\docaption{\checkenv\float \bgroup\scanargctxt\defcaption} \def\defcaption#1#2{\egroup \def#1{#2}} % The parameter is the control sequence identifying the counter we are % going to use. Create it if it doesn't exist and assign it to \floatno. \def\getfloatno#1{% \ifx#1\relax % Haven't seen this figure type before. \csname newcount\endcsname #1% % % Remember to reset this floatno at the next chap. \expandafter\gdef\expandafter\resetallfloatnos \expandafter{\resetallfloatnos #1=0 }% \fi \let\floatno#1% } % \setref calls this to get the XREFLABEL-snt value. We want an @xref % to the FLOATLABEL to expand to "Figure 3.1". We call \setref when we % first read the @float command. % \def\Yfloat{\floattype@tie \chaplevelprefix\the\floatno}% % Magic string used for the XREFLABEL-title value, so \xrefX can % distinguish floats from other xref types. \def\floatmagic{!!float!!} % #1 is the control sequence we are passed; we expand into a conditional % which is true if #1 represents a float ref. That is, the magic % \currentsection value which we \setref above. % \def\iffloat#1{\expandafter\doiffloat#1==\finish} % % #1 is (maybe) the \floatmagic string. If so, #2 will be the % (safe) float type for this float. We set \iffloattype to #2. % \def\doiffloat#1=#2=#3\finish{% \def\temp{#1}% \def\iffloattype{#2}% \ifx\temp\floatmagic } % @listoffloats FLOATTYPE - print a list of floats like a table of contents. % \parseargdef\listoffloats{% \def\floattype{#1}% floattype {% % the floattype might have accents or other special characters, % but we need to use it in a control sequence name. \indexnofonts \turnoffactive \xdef\safefloattype{\floattype}% }% % % \xrdef saves the floats as a \do-list in \floatlistSAFEFLOATTYPE. \expandafter\ifx\csname floatlist\safefloattype\endcsname \relax \ifhavexrefs % if the user said @listoffloats foo but never @float foo. \message{\linenumber No `\safefloattype' floats to list.}% \fi \else \begingroup \leftskip=\tocindent % indent these entries like a toc \let\do=\listoffloatsdo \csname floatlist\safefloattype\endcsname \endgroup \fi } % This is called on each entry in a list of floats. We're passed the % xref label, in the form LABEL-title, which is how we save it in the % aux file. We strip off the -title and look up \XRLABEL-lof, which % has the text we're supposed to typeset here. % % Figures without xref labels will not be included in the list (since % they won't appear in the aux file). % \def\listoffloatsdo#1{\listoffloatsdoentry#1\finish} \def\listoffloatsdoentry#1-title\finish{{% % Can't fully expand XR#1-lof because it can contain anything. Just % pass the control sequence. On the other hand, XR#1-pg is just the % page number, and we want to fully expand that so we can get a link % in pdf output. \toksA = \expandafter{\csname XR#1-lof\endcsname}% % % use the same \entry macro we use to generate the TOC and index. \edef\writeentry{\noexpand\entry{\the\toksA}{\csname XR#1-pg\endcsname}}% \writeentry }} \message{localization,} % For single-language documents, @documentlanguage is usually given very % early, just after @documentencoding. Single argument is the language % (de) or locale (de_DE) abbreviation. % { \catcode`\_ = \active \globaldefs=1 \parseargdef\documentlanguage{% \tex % read txi-??.tex file in plain TeX. % Read the file by the name they passed if it exists. \let_ = \normalunderscore % normal _ character for filename test \openin 1 txi-#1.tex \ifeof 1 \documentlanguagetrywithoutunderscore #1_\finish \else \globaldefs = 1 % everything in the txi-LL files needs to persist \input txi-#1.tex \fi \closein 1 \endgroup % end raw TeX } % % If they passed de_DE, and txi-de_DE.tex doesn't exist, % try txi-de.tex. % \gdef\documentlanguagetrywithoutunderscore#1_#2\finish{% \openin 1 txi-#1.tex \ifeof 1 \errhelp = \nolanghelp \errmessage{Cannot read language file txi-#1.tex}% \else \globaldefs = 1 % everything in the txi-LL files needs to persist \input txi-#1.tex \fi \closein 1 } }% end of special _ catcode % \newhelp\nolanghelp{The given language definition file cannot be found or is empty. Maybe you need to install it? Putting it in the current directory should work if nowhere else does.} % This macro is called from txi-??.tex files; the first argument is the % \language name to set (without the "\lang@" prefix), the second and % third args are \{left,right}hyphenmin. % % The language names to pass are determined when the format is built. % See the etex.log file created at that time, e.g., % /usr/local/texlive/2008/texmf-var/web2c/pdftex/etex.log. % % With TeX Live 2008, etex now includes hyphenation patterns for all % available languages. This means we can support hyphenation in % Texinfo, at least to some extent. (This still doesn't solve the % accented characters problem.) % \catcode`@=11 \def\txisetlanguage#1#2#3{% % do not set the language if the name is undefined in the current TeX. \expandafter\ifx\csname lang@#1\endcsname \relax \message{no patterns for #1}% \else \global\language = \csname lang@#1\endcsname \fi % but there is no harm in adjusting the hyphenmin values regardless. \global\lefthyphenmin = #2\relax \global\righthyphenmin = #3\relax } % XeTeX and LuaTeX can handle Unicode natively. % Their default I/O uses UTF-8 sequences instead of a byte-wise operation. % Other TeX engines' I/O (pdfTeX, etc.) is byte-wise. % \newif\iftxinativeunicodecapable \newif\iftxiusebytewiseio \ifx\XeTeXrevision\thisisundefined \ifx\luatexversion\thisisundefined \txinativeunicodecapablefalse \txiusebytewiseiotrue \else \txinativeunicodecapabletrue \txiusebytewiseiofalse \fi \else \txinativeunicodecapabletrue \txiusebytewiseiofalse \fi % Set I/O by bytes instead of UTF-8 sequence for XeTeX and LuaTex % for non-UTF-8 (byte-wise) encodings. % \def\setbytewiseio{% \ifx\XeTeXrevision\thisisundefined \else \XeTeXdefaultencoding "bytes" % For subsequent files to be read \XeTeXinputencoding "bytes" % For document root file % Unfortunately, there seems to be no corresponding XeTeX command for % output encoding. This is a problem for auxiliary index and TOC files. % The only solution would be perhaps to write out @U{...} sequences in % place of non-ASCII characters. \fi \ifx\luatexversion\thisisundefined \else \directlua{ local utf8_char, byte, gsub = unicode.utf8.char, string.byte, string.gsub local function convert_char (char) return utf8_char(byte(char)) end local function convert_line (line) return gsub(line, ".", convert_char) end callback.register("process_input_buffer", convert_line) local function convert_line_out (line) local line_out = "" for c in string.utfvalues(line) do line_out = line_out .. string.char(c) end return line_out end callback.register("process_output_buffer", convert_line_out) } \fi \txiusebytewiseiotrue } % Helpers for encodings. % Set the catcode of characters 128 through 255 to the specified number. % \def\setnonasciicharscatcode#1{% \count255=128 \loop\ifnum\count255<256 \global\catcode\count255=#1\relax \advance\count255 by 1 \repeat } \def\setnonasciicharscatcodenonglobal#1{% \count255=128 \loop\ifnum\count255<256 \catcode\count255=#1\relax \advance\count255 by 1 \repeat } % @documentencoding sets the definition of non-ASCII characters % according to the specified encoding. % \def\documentencoding{\parseargusing\filenamecatcodes\documentencodingzzz} \def\documentencodingzzz#1{% % % Encoding being declared for the document. \def\declaredencoding{\csname #1.enc\endcsname}% % % Supported encodings: names converted to tokens in order to be able % to compare them with \ifx. \def\ascii{\csname US-ASCII.enc\endcsname}% \def\latnine{\csname ISO-8859-15.enc\endcsname}% \def\latone{\csname ISO-8859-1.enc\endcsname}% \def\lattwo{\csname ISO-8859-2.enc\endcsname}% \def\utfeight{\csname UTF-8.enc\endcsname}% % \ifx \declaredencoding \ascii \asciichardefs % \else \ifx \declaredencoding \lattwo \iftxinativeunicodecapable \setbytewiseio \fi \setnonasciicharscatcode\active \lattwochardefs % \else \ifx \declaredencoding \latone \iftxinativeunicodecapable \setbytewiseio \fi \setnonasciicharscatcode\active \latonechardefs % \else \ifx \declaredencoding \latnine \iftxinativeunicodecapable \setbytewiseio \fi \setnonasciicharscatcode\active \latninechardefs % \else \ifx \declaredencoding \utfeight \iftxinativeunicodecapable % For native Unicode handling (XeTeX and LuaTeX) \nativeunicodechardefs \else % For treating UTF-8 as byte sequences (TeX, eTeX and pdfTeX) \setnonasciicharscatcode\active % since we already invoked \utfeightchardefs at the top level % (below), do not re-invoke it, otherwise our check for duplicated % definitions gets triggered. Making non-ascii chars active is % sufficient. \fi % \else \message{Ignoring unknown document encoding: #1.}% % \fi % utfeight \fi % latnine \fi % latone \fi % lattwo \fi % ascii % \ifx\XeTeXrevision\thisisundefined \else \ifx \declaredencoding \utfeight \else \ifx \declaredencoding \ascii \else \message{Warning: XeTeX with non-UTF-8 encodings cannot handle % non-ASCII characters in auxiliary files.}% \fi \fi \fi } % emacs-page % A message to be logged when using a character that isn't available % the default font encoding (OT1). % \def\missingcharmsg#1{\message{Character missing, sorry: #1.}} % Take account of \c (plain) vs. \, (Texinfo) difference. \def\cedilla#1{\ifx\c\ptexc\c{#1}\else\,{#1}\fi} % First, make active non-ASCII characters in order for them to be % correctly categorized when TeX reads the replacement text of % macros containing the character definitions. \setnonasciicharscatcode\active % \def\gdefchar#1#2{% \gdef#1{% \ifpassthroughchars \string#1% \else #2% \fi }} % Latin1 (ISO-8859-1) character definitions. \def\latonechardefs{% \gdefchar^^a0{\tie} \gdefchar^^a1{\exclamdown} \gdefchar^^a2{{\tcfont \char162}} % cent \gdefchar^^a3{\pounds{}} \gdefchar^^a4{{\tcfont \char164}} % currency \gdefchar^^a5{{\tcfont \char165}} % yen \gdefchar^^a6{{\tcfont \char166}} % broken bar \gdefchar^^a7{\S} \gdefchar^^a8{\"{}} \gdefchar^^a9{\copyright{}} \gdefchar^^aa{\ordf} \gdefchar^^ab{\guillemetleft{}} \gdefchar^^ac{\ensuremath\lnot} \gdefchar^^ad{\-} \gdefchar^^ae{\registeredsymbol{}} \gdefchar^^af{\={}} % \gdefchar^^b0{\textdegree} \gdefchar^^b1{$\pm$} \gdefchar^^b2{$^2$} \gdefchar^^b3{$^3$} \gdefchar^^b4{\'{}} \gdefchar^^b5{$\mu$} \gdefchar^^b6{\P} \gdefchar^^b7{\ensuremath\cdot} \gdefchar^^b8{\cedilla\ } \gdefchar^^b9{$^1$} \gdefchar^^ba{\ordm} \gdefchar^^bb{\guillemetright{}} \gdefchar^^bc{$1\over4$} \gdefchar^^bd{$1\over2$} \gdefchar^^be{$3\over4$} \gdefchar^^bf{\questiondown} % \gdefchar^^c0{\`A} \gdefchar^^c1{\'A} \gdefchar^^c2{\^A} \gdefchar^^c3{\~A} \gdefchar^^c4{\"A} \gdefchar^^c5{\ringaccent A} \gdefchar^^c6{\AE} \gdefchar^^c7{\cedilla C} \gdefchar^^c8{\`E} \gdefchar^^c9{\'E} \gdefchar^^ca{\^E} \gdefchar^^cb{\"E} \gdefchar^^cc{\`I} \gdefchar^^cd{\'I} \gdefchar^^ce{\^I} \gdefchar^^cf{\"I} % \gdefchar^^d0{\DH} \gdefchar^^d1{\~N} \gdefchar^^d2{\`O} \gdefchar^^d3{\'O} \gdefchar^^d4{\^O} \gdefchar^^d5{\~O} \gdefchar^^d6{\"O} \gdefchar^^d7{$\times$} \gdefchar^^d8{\O} \gdefchar^^d9{\`U} \gdefchar^^da{\'U} \gdefchar^^db{\^U} \gdefchar^^dc{\"U} \gdefchar^^dd{\'Y} \gdefchar^^de{\TH} \gdefchar^^df{\ss} % \gdefchar^^e0{\`a} \gdefchar^^e1{\'a} \gdefchar^^e2{\^a} \gdefchar^^e3{\~a} \gdefchar^^e4{\"a} \gdefchar^^e5{\ringaccent a} \gdefchar^^e6{\ae} \gdefchar^^e7{\cedilla c} \gdefchar^^e8{\`e} \gdefchar^^e9{\'e} \gdefchar^^ea{\^e} \gdefchar^^eb{\"e} \gdefchar^^ec{\`{\dotless i}} \gdefchar^^ed{\'{\dotless i}} \gdefchar^^ee{\^{\dotless i}} \gdefchar^^ef{\"{\dotless i}} % \gdefchar^^f0{\dh} \gdefchar^^f1{\~n} \gdefchar^^f2{\`o} \gdefchar^^f3{\'o} \gdefchar^^f4{\^o} \gdefchar^^f5{\~o} \gdefchar^^f6{\"o} \gdefchar^^f7{$\div$} \gdefchar^^f8{\o} \gdefchar^^f9{\`u} \gdefchar^^fa{\'u} \gdefchar^^fb{\^u} \gdefchar^^fc{\"u} \gdefchar^^fd{\'y} \gdefchar^^fe{\th} \gdefchar^^ff{\"y} } % Latin9 (ISO-8859-15) encoding character definitions. \def\latninechardefs{% % Encoding is almost identical to Latin1. \latonechardefs % \gdefchar^^a4{\euro{}} \gdefchar^^a6{\v S} \gdefchar^^a8{\v s} \gdefchar^^b4{\v Z} \gdefchar^^b8{\v z} \gdefchar^^bc{\OE} \gdefchar^^bd{\oe} \gdefchar^^be{\"Y} } % Latin2 (ISO-8859-2) character definitions. \def\lattwochardefs{% \gdefchar^^a0{\tie} \gdefchar^^a1{\ogonek{A}} \gdefchar^^a2{\u{}} \gdefchar^^a3{\L} \gdefchar^^a4{\missingcharmsg{CURRENCY SIGN}} \gdefchar^^a5{\v L} \gdefchar^^a6{\'S} \gdefchar^^a7{\S} \gdefchar^^a8{\"{}} \gdefchar^^a9{\v S} \gdefchar^^aa{\cedilla S} \gdefchar^^ab{\v T} \gdefchar^^ac{\'Z} \gdefchar^^ad{\-} \gdefchar^^ae{\v Z} \gdefchar^^af{\dotaccent Z} % \gdefchar^^b0{\textdegree{}} \gdefchar^^b1{\ogonek{a}} \gdefchar^^b2{\ogonek{ }} \gdefchar^^b3{\l} \gdefchar^^b4{\'{}} \gdefchar^^b5{\v l} \gdefchar^^b6{\'s} \gdefchar^^b7{\v{}} \gdefchar^^b8{\cedilla\ } \gdefchar^^b9{\v s} \gdefchar^^ba{\cedilla s} \gdefchar^^bb{\v t} \gdefchar^^bc{\'z} \gdefchar^^bd{\H{}} \gdefchar^^be{\v z} \gdefchar^^bf{\dotaccent z} % \gdefchar^^c0{\'R} \gdefchar^^c1{\'A} \gdefchar^^c2{\^A} \gdefchar^^c3{\u A} \gdefchar^^c4{\"A} \gdefchar^^c5{\'L} \gdefchar^^c6{\'C} \gdefchar^^c7{\cedilla C} \gdefchar^^c8{\v C} \gdefchar^^c9{\'E} \gdefchar^^ca{\ogonek{E}} \gdefchar^^cb{\"E} \gdefchar^^cc{\v E} \gdefchar^^cd{\'I} \gdefchar^^ce{\^I} \gdefchar^^cf{\v D} % \gdefchar^^d0{\DH} \gdefchar^^d1{\'N} \gdefchar^^d2{\v N} \gdefchar^^d3{\'O} \gdefchar^^d4{\^O} \gdefchar^^d5{\H O} \gdefchar^^d6{\"O} \gdefchar^^d7{$\times$} \gdefchar^^d8{\v R} \gdefchar^^d9{\ringaccent U} \gdefchar^^da{\'U} \gdefchar^^db{\H U} \gdefchar^^dc{\"U} \gdefchar^^dd{\'Y} \gdefchar^^de{\cedilla T} \gdefchar^^df{\ss} % \gdefchar^^e0{\'r} \gdefchar^^e1{\'a} \gdefchar^^e2{\^a} \gdefchar^^e3{\u a} \gdefchar^^e4{\"a} \gdefchar^^e5{\'l} \gdefchar^^e6{\'c} \gdefchar^^e7{\cedilla c} \gdefchar^^e8{\v c} \gdefchar^^e9{\'e} \gdefchar^^ea{\ogonek{e}} \gdefchar^^eb{\"e} \gdefchar^^ec{\v e} \gdefchar^^ed{\'{\dotless{i}}} \gdefchar^^ee{\^{\dotless{i}}} \gdefchar^^ef{\v d} % \gdefchar^^f0{\dh} \gdefchar^^f1{\'n} \gdefchar^^f2{\v n} \gdefchar^^f3{\'o} \gdefchar^^f4{\^o} \gdefchar^^f5{\H o} \gdefchar^^f6{\"o} \gdefchar^^f7{$\div$} \gdefchar^^f8{\v r} \gdefchar^^f9{\ringaccent u} \gdefchar^^fa{\'u} \gdefchar^^fb{\H u} \gdefchar^^fc{\"u} \gdefchar^^fd{\'y} \gdefchar^^fe{\cedilla t} \gdefchar^^ff{\dotaccent{}} } % UTF-8 character definitions. % % This code to support UTF-8 is based on LaTeX's utf8.def, with some % changes for Texinfo conventions. It is included here under the GPL by % permission from Frank Mittelbach and the LaTeX team. % \newcount\countUTFx \newcount\countUTFy \newcount\countUTFz \gdef\UTFviiiTwoOctets#1#2{\expandafter \UTFviiiDefined\csname u8:#1\string #2\endcsname} % \gdef\UTFviiiThreeOctets#1#2#3{\expandafter \UTFviiiDefined\csname u8:#1\string #2\string #3\endcsname} % \gdef\UTFviiiFourOctets#1#2#3#4{\expandafter \UTFviiiDefined\csname u8:#1\string #2\string #3\string #4\endcsname} \gdef\UTFviiiDefined#1{% \ifx #1\relax \message{\linenumber Unicode char \string #1 not defined for Texinfo}% \else \expandafter #1% \fi } % Give non-ASCII bytes the active definitions for processing UTF-8 sequences \begingroup \catcode`\~13 \catcode`\$12 \catcode`\"12 % Loop from \countUTFx to \countUTFy, performing \UTFviiiTmp % substituting ~ and $ with a character token of that value. \def\UTFviiiLoop{% \global\catcode\countUTFx\active \uccode`\~\countUTFx \uccode`\$\countUTFx \uppercase\expandafter{\UTFviiiTmp}% \advance\countUTFx by 1 \ifnum\countUTFx < \countUTFy \expandafter\UTFviiiLoop \fi} % For bytes other than the first in a UTF-8 sequence. Not expected to % be expanded except when writing to auxiliary files. \countUTFx = "80 \countUTFy = "C2 \def\UTFviiiTmp{% \gdef~{% \ifpassthroughchars $\fi}}% \UTFviiiLoop \countUTFx = "C2 \countUTFy = "E0 \def\UTFviiiTmp{% \gdef~{% \ifpassthroughchars $% \else\expandafter\UTFviiiTwoOctets\expandafter$\fi}}% \UTFviiiLoop \countUTFx = "E0 \countUTFy = "F0 \def\UTFviiiTmp{% \gdef~{% \ifpassthroughchars $% \else\expandafter\UTFviiiThreeOctets\expandafter$\fi}}% \UTFviiiLoop \countUTFx = "F0 \countUTFy = "F4 \def\UTFviiiTmp{% \gdef~{% \ifpassthroughchars $% \else\expandafter\UTFviiiFourOctets\expandafter$\fi }}% \UTFviiiLoop \endgroup \def\globallet{\global\let} % save some \expandafter's below % @U{xxxx} to produce U+xxxx, if we support it. \def\U#1{% \expandafter\ifx\csname uni:#1\endcsname \relax \iftxinativeunicodecapable % All Unicode characters can be used if native Unicode handling is % active. However, if the font does not have the glyph, % letters are missing. \begingroup \uccode`\.="#1\relax \uppercase{.} \endgroup \else \errhelp = \EMsimple \errmessage{Unicode character U+#1 not supported, sorry}% \fi \else \csname uni:#1\endcsname \fi } % These macros are used here to construct the name of a control % sequence to be defined. \def\UTFviiiTwoOctetsName#1#2{% \csname u8:#1\string #2\endcsname}% \def\UTFviiiThreeOctetsName#1#2#3{% \csname u8:#1\string #2\string #3\endcsname}% \def\UTFviiiFourOctetsName#1#2#3#4{% \csname u8:#1\string #2\string #3\string #4\endcsname}% % For UTF-8 byte sequences (TeX, e-TeX and pdfTeX), % provide a definition macro to replace a Unicode character; % this gets used by the @U command % \begingroup \catcode`\"=12 \catcode`\<=12 \catcode`\.=12 \catcode`\,=12 \catcode`\;=12 \catcode`\!=12 \catcode`\~=13 \gdef\DeclareUnicodeCharacterUTFviii#1#2{% \countUTFz = "#1\relax \begingroup \parseXMLCharref % Give \u8:... its definition. The sequence of seven \expandafter's % expands after the \gdef three times, e.g. % % 1. \UTFviiTwoOctetsName B1 B2 % 2. \csname u8:B1 \string B2 \endcsname % 3. \u8: B1 B2 (a single control sequence token) % \expandafter\expandafter \expandafter\expandafter \expandafter\expandafter \expandafter\gdef \UTFviiiTmp{#2}% % \expandafter\ifx\csname uni:#1\endcsname \relax \else \message{Internal error, already defined: #1}% \fi % % define an additional control sequence for this code point. \expandafter\globallet\csname uni:#1\endcsname \UTFviiiTmp \endgroup} % % Given the value in \countUTFz as a Unicode code point, set \UTFviiiTmp % to the corresponding UTF-8 sequence. \gdef\parseXMLCharref{% \ifnum\countUTFz < "A0\relax \errhelp = \EMsimple \errmessage{Cannot define Unicode char value < 00A0}% \else\ifnum\countUTFz < "800\relax \parseUTFviiiA,% \parseUTFviiiB C\UTFviiiTwoOctetsName.,% \else\ifnum\countUTFz < "10000\relax \parseUTFviiiA;% \parseUTFviiiA,% \parseUTFviiiB E\UTFviiiThreeOctetsName.{,;}% \else \parseUTFviiiA;% \parseUTFviiiA,% \parseUTFviiiA!% \parseUTFviiiB F\UTFviiiFourOctetsName.{!,;}% \fi\fi\fi } % Extract a byte from the end of the UTF-8 representation of \countUTFx. % It must be a non-initial byte in the sequence. % Change \uccode of #1 for it to be used in \parseUTFviiiB as one % of the bytes. \gdef\parseUTFviiiA#1{% \countUTFx = \countUTFz \divide\countUTFz by 64 \countUTFy = \countUTFz % Save to be the future value of \countUTFz. \multiply\countUTFz by 64 % \countUTFz is now \countUTFx with the last 5 bits cleared. Subtract % in order to get the last five bits. \advance\countUTFx by -\countUTFz % Convert this to the byte in the UTF-8 sequence. \advance\countUTFx by 128 \uccode `#1\countUTFx \countUTFz = \countUTFy} % Used to put a UTF-8 byte sequence into \UTFviiiTmp % #1 is the increment for \countUTFz to yield a the first byte of the UTF-8 % sequence. % #2 is one of the \UTFviii*OctetsName macros. % #3 is always a full stop (.) % #4 is a template for the other bytes in the sequence. The values for these % bytes is substituted in here with \uppercase using the \uccode's. \gdef\parseUTFviiiB#1#2#3#4{% \advance\countUTFz by "#10\relax \uccode `#3\countUTFz \uppercase{\gdef\UTFviiiTmp{#2#3#4}}} \endgroup % For native Unicode handling (XeTeX and LuaTeX), % provide a definition macro that sets a catcode to `other' non-globally % \def\DeclareUnicodeCharacterNativeOther#1#2{% \catcode"#1=\other } % https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_M % U+0000..U+007F = https://en.wikipedia.org/wiki/Basic_Latin_(Unicode_block) % U+0080..U+00FF = https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block) % U+0100..U+017F = https://en.wikipedia.org/wiki/Latin_Extended-A % U+0180..U+024F = https://en.wikipedia.org/wiki/Latin_Extended-B % % Many of our renditions are less than wonderful, and all the missing % characters are available somewhere. Loading the necessary fonts % awaits user request. We can't truly support Unicode without % reimplementing everything that's been done in LaTeX for many years, % plus probably using luatex or xetex, and who knows what else. % We won't be doing that here in this simple file. But we can try to at % least make most of the characters not bomb out. % \def\unicodechardefs{% \DeclareUnicodeCharacter{00A0}{\tie}% \DeclareUnicodeCharacter{00A1}{\exclamdown}% \DeclareUnicodeCharacter{00A2}{{\tcfont \char162}}% 0242=cent \DeclareUnicodeCharacter{00A3}{\pounds{}}% \DeclareUnicodeCharacter{00A4}{{\tcfont \char164}}% 0244=currency \DeclareUnicodeCharacter{00A5}{{\tcfont \char165}}% 0245=yen \DeclareUnicodeCharacter{00A6}{{\tcfont \char166}}% 0246=brokenbar \DeclareUnicodeCharacter{00A7}{\S}% \DeclareUnicodeCharacter{00A8}{\"{ }}% \DeclareUnicodeCharacter{00A9}{\copyright{}}% \DeclareUnicodeCharacter{00AA}{\ordf}% \DeclareUnicodeCharacter{00AB}{\guillemetleft{}}% \DeclareUnicodeCharacter{00AC}{\ensuremath\lnot}% \DeclareUnicodeCharacter{00AD}{\-}% \DeclareUnicodeCharacter{00AE}{\registeredsymbol{}}% \DeclareUnicodeCharacter{00AF}{\={ }}% % \DeclareUnicodeCharacter{00B0}{\ringaccent{ }}% \DeclareUnicodeCharacter{00B1}{\ensuremath\pm}% \DeclareUnicodeCharacter{00B2}{$^2$}% \DeclareUnicodeCharacter{00B3}{$^3$}% \DeclareUnicodeCharacter{00B4}{\'{ }}% \DeclareUnicodeCharacter{00B5}{$\mu$}% \DeclareUnicodeCharacter{00B6}{\P}% \DeclareUnicodeCharacter{00B7}{\ensuremath\cdot}% \DeclareUnicodeCharacter{00B8}{\cedilla{ }}% \DeclareUnicodeCharacter{00B9}{$^1$}% \DeclareUnicodeCharacter{00BA}{\ordm}% \DeclareUnicodeCharacter{00BB}{\guillemetright{}}% \DeclareUnicodeCharacter{00BC}{$1\over4$}% \DeclareUnicodeCharacter{00BD}{$1\over2$}% \DeclareUnicodeCharacter{00BE}{$3\over4$}% \DeclareUnicodeCharacter{00BF}{\questiondown}% % \DeclareUnicodeCharacter{00C0}{\`A}% \DeclareUnicodeCharacter{00C1}{\'A}% \DeclareUnicodeCharacter{00C2}{\^A}% \DeclareUnicodeCharacter{00C3}{\~A}% \DeclareUnicodeCharacter{00C4}{\"A}% \DeclareUnicodeCharacter{00C5}{\AA}% \DeclareUnicodeCharacter{00C6}{\AE}% \DeclareUnicodeCharacter{00C7}{\cedilla{C}}% \DeclareUnicodeCharacter{00C8}{\`E}% \DeclareUnicodeCharacter{00C9}{\'E}% \DeclareUnicodeCharacter{00CA}{\^E}% \DeclareUnicodeCharacter{00CB}{\"E}% \DeclareUnicodeCharacter{00CC}{\`I}% \DeclareUnicodeCharacter{00CD}{\'I}% \DeclareUnicodeCharacter{00CE}{\^I}% \DeclareUnicodeCharacter{00CF}{\"I}% % \DeclareUnicodeCharacter{00D0}{\DH}% \DeclareUnicodeCharacter{00D1}{\~N}% \DeclareUnicodeCharacter{00D2}{\`O}% \DeclareUnicodeCharacter{00D3}{\'O}% \DeclareUnicodeCharacter{00D4}{\^O}% \DeclareUnicodeCharacter{00D5}{\~O}% \DeclareUnicodeCharacter{00D6}{\"O}% \DeclareUnicodeCharacter{00D7}{\ensuremath\times}% \DeclareUnicodeCharacter{00D8}{\O}% \DeclareUnicodeCharacter{00D9}{\`U}% \DeclareUnicodeCharacter{00DA}{\'U}% \DeclareUnicodeCharacter{00DB}{\^U}% \DeclareUnicodeCharacter{00DC}{\"U}% \DeclareUnicodeCharacter{00DD}{\'Y}% \DeclareUnicodeCharacter{00DE}{\TH}% \DeclareUnicodeCharacter{00DF}{\ss}% % \DeclareUnicodeCharacter{00E0}{\`a}% \DeclareUnicodeCharacter{00E1}{\'a}% \DeclareUnicodeCharacter{00E2}{\^a}% \DeclareUnicodeCharacter{00E3}{\~a}% \DeclareUnicodeCharacter{00E4}{\"a}% \DeclareUnicodeCharacter{00E5}{\aa}% \DeclareUnicodeCharacter{00E6}{\ae}% \DeclareUnicodeCharacter{00E7}{\cedilla{c}}% \DeclareUnicodeCharacter{00E8}{\`e}% \DeclareUnicodeCharacter{00E9}{\'e}% \DeclareUnicodeCharacter{00EA}{\^e}% \DeclareUnicodeCharacter{00EB}{\"e}% \DeclareUnicodeCharacter{00EC}{\`{\dotless{i}}}% \DeclareUnicodeCharacter{00ED}{\'{\dotless{i}}}% \DeclareUnicodeCharacter{00EE}{\^{\dotless{i}}}% \DeclareUnicodeCharacter{00EF}{\"{\dotless{i}}}% % \DeclareUnicodeCharacter{00F0}{\dh}% \DeclareUnicodeCharacter{00F1}{\~n}% \DeclareUnicodeCharacter{00F2}{\`o}% \DeclareUnicodeCharacter{00F3}{\'o}% \DeclareUnicodeCharacter{00F4}{\^o}% \DeclareUnicodeCharacter{00F5}{\~o}% \DeclareUnicodeCharacter{00F6}{\"o}% \DeclareUnicodeCharacter{00F7}{\ensuremath\div}% \DeclareUnicodeCharacter{00F8}{\o}% \DeclareUnicodeCharacter{00F9}{\`u}% \DeclareUnicodeCharacter{00FA}{\'u}% \DeclareUnicodeCharacter{00FB}{\^u}% \DeclareUnicodeCharacter{00FC}{\"u}% \DeclareUnicodeCharacter{00FD}{\'y}% \DeclareUnicodeCharacter{00FE}{\th}% \DeclareUnicodeCharacter{00FF}{\"y}% % \DeclareUnicodeCharacter{0100}{\=A}% \DeclareUnicodeCharacter{0101}{\=a}% \DeclareUnicodeCharacter{0102}{\u{A}}% \DeclareUnicodeCharacter{0103}{\u{a}}% \DeclareUnicodeCharacter{0104}{\ogonek{A}}% \DeclareUnicodeCharacter{0105}{\ogonek{a}}% \DeclareUnicodeCharacter{0106}{\'C}% \DeclareUnicodeCharacter{0107}{\'c}% \DeclareUnicodeCharacter{0108}{\^C}% \DeclareUnicodeCharacter{0109}{\^c}% \DeclareUnicodeCharacter{010A}{\dotaccent{C}}% \DeclareUnicodeCharacter{010B}{\dotaccent{c}}% \DeclareUnicodeCharacter{010C}{\v{C}}% \DeclareUnicodeCharacter{010D}{\v{c}}% \DeclareUnicodeCharacter{010E}{\v{D}}% \DeclareUnicodeCharacter{010F}{d'}% % \DeclareUnicodeCharacter{0110}{\DH}% \DeclareUnicodeCharacter{0111}{\dh}% \DeclareUnicodeCharacter{0112}{\=E}% \DeclareUnicodeCharacter{0113}{\=e}% \DeclareUnicodeCharacter{0114}{\u{E}}% \DeclareUnicodeCharacter{0115}{\u{e}}% \DeclareUnicodeCharacter{0116}{\dotaccent{E}}% \DeclareUnicodeCharacter{0117}{\dotaccent{e}}% \DeclareUnicodeCharacter{0118}{\ogonek{E}}% \DeclareUnicodeCharacter{0119}{\ogonek{e}}% \DeclareUnicodeCharacter{011A}{\v{E}}% \DeclareUnicodeCharacter{011B}{\v{e}}% \DeclareUnicodeCharacter{011C}{\^G}% \DeclareUnicodeCharacter{011D}{\^g}% \DeclareUnicodeCharacter{011E}{\u{G}}% \DeclareUnicodeCharacter{011F}{\u{g}}% % \DeclareUnicodeCharacter{0120}{\dotaccent{G}}% \DeclareUnicodeCharacter{0121}{\dotaccent{g}}% \DeclareUnicodeCharacter{0122}{\cedilla{G}}% \DeclareUnicodeCharacter{0123}{\cedilla{g}}% \DeclareUnicodeCharacter{0124}{\^H}% \DeclareUnicodeCharacter{0125}{\^h}% \DeclareUnicodeCharacter{0126}{\missingcharmsg{H WITH STROKE}}% \DeclareUnicodeCharacter{0127}{\missingcharmsg{h WITH STROKE}}% \DeclareUnicodeCharacter{0128}{\~I}% \DeclareUnicodeCharacter{0129}{\~{\dotless{i}}}% \DeclareUnicodeCharacter{012A}{\=I}% \DeclareUnicodeCharacter{012B}{\={\dotless{i}}}% \DeclareUnicodeCharacter{012C}{\u{I}}% \DeclareUnicodeCharacter{012D}{\u{\dotless{i}}}% \DeclareUnicodeCharacter{012E}{\ogonek{I}}% \DeclareUnicodeCharacter{012F}{\ogonek{i}}% % \DeclareUnicodeCharacter{0130}{\dotaccent{I}}% \DeclareUnicodeCharacter{0131}{\dotless{i}}% \DeclareUnicodeCharacter{0132}{IJ}% \DeclareUnicodeCharacter{0133}{ij}% \DeclareUnicodeCharacter{0134}{\^J}% \DeclareUnicodeCharacter{0135}{\^{\dotless{j}}}% \DeclareUnicodeCharacter{0136}{\cedilla{K}}% \DeclareUnicodeCharacter{0137}{\cedilla{k}}% \DeclareUnicodeCharacter{0138}{\ensuremath\kappa}% \DeclareUnicodeCharacter{0139}{\'L}% \DeclareUnicodeCharacter{013A}{\'l}% \DeclareUnicodeCharacter{013B}{\cedilla{L}}% \DeclareUnicodeCharacter{013C}{\cedilla{l}}% \DeclareUnicodeCharacter{013D}{L'}% should kern \DeclareUnicodeCharacter{013E}{l'}% should kern \DeclareUnicodeCharacter{013F}{L\U{00B7}}% % \DeclareUnicodeCharacter{0140}{l\U{00B7}}% \DeclareUnicodeCharacter{0141}{\L}% \DeclareUnicodeCharacter{0142}{\l}% \DeclareUnicodeCharacter{0143}{\'N}% \DeclareUnicodeCharacter{0144}{\'n}% \DeclareUnicodeCharacter{0145}{\cedilla{N}}% \DeclareUnicodeCharacter{0146}{\cedilla{n}}% \DeclareUnicodeCharacter{0147}{\v{N}}% \DeclareUnicodeCharacter{0148}{\v{n}}% \DeclareUnicodeCharacter{0149}{'n}% \DeclareUnicodeCharacter{014A}{\missingcharmsg{ENG}}% \DeclareUnicodeCharacter{014B}{\missingcharmsg{eng}}% \DeclareUnicodeCharacter{014C}{\=O}% \DeclareUnicodeCharacter{014D}{\=o}% \DeclareUnicodeCharacter{014E}{\u{O}}% \DeclareUnicodeCharacter{014F}{\u{o}}% % \DeclareUnicodeCharacter{0150}{\H{O}}% \DeclareUnicodeCharacter{0151}{\H{o}}% \DeclareUnicodeCharacter{0152}{\OE}% \DeclareUnicodeCharacter{0153}{\oe}% \DeclareUnicodeCharacter{0154}{\'R}% \DeclareUnicodeCharacter{0155}{\'r}% \DeclareUnicodeCharacter{0156}{\cedilla{R}}% \DeclareUnicodeCharacter{0157}{\cedilla{r}}% \DeclareUnicodeCharacter{0158}{\v{R}}% \DeclareUnicodeCharacter{0159}{\v{r}}% \DeclareUnicodeCharacter{015A}{\'S}% \DeclareUnicodeCharacter{015B}{\'s}% \DeclareUnicodeCharacter{015C}{\^S}% \DeclareUnicodeCharacter{015D}{\^s}% \DeclareUnicodeCharacter{015E}{\cedilla{S}}% \DeclareUnicodeCharacter{015F}{\cedilla{s}}% % \DeclareUnicodeCharacter{0160}{\v{S}}% \DeclareUnicodeCharacter{0161}{\v{s}}% \DeclareUnicodeCharacter{0162}{\cedilla{T}}% \DeclareUnicodeCharacter{0163}{\cedilla{t}}% \DeclareUnicodeCharacter{0164}{\v{T}}% \DeclareUnicodeCharacter{0165}{\v{t}}% \DeclareUnicodeCharacter{0166}{\missingcharmsg{H WITH STROKE}}% \DeclareUnicodeCharacter{0167}{\missingcharmsg{h WITH STROKE}}% \DeclareUnicodeCharacter{0168}{\~U}% \DeclareUnicodeCharacter{0169}{\~u}% \DeclareUnicodeCharacter{016A}{\=U}% \DeclareUnicodeCharacter{016B}{\=u}% \DeclareUnicodeCharacter{016C}{\u{U}}% \DeclareUnicodeCharacter{016D}{\u{u}}% \DeclareUnicodeCharacter{016E}{\ringaccent{U}}% \DeclareUnicodeCharacter{016F}{\ringaccent{u}}% % \DeclareUnicodeCharacter{0170}{\H{U}}% \DeclareUnicodeCharacter{0171}{\H{u}}% \DeclareUnicodeCharacter{0172}{\ogonek{U}}% \DeclareUnicodeCharacter{0173}{\ogonek{u}}% \DeclareUnicodeCharacter{0174}{\^W}% \DeclareUnicodeCharacter{0175}{\^w}% \DeclareUnicodeCharacter{0176}{\^Y}% \DeclareUnicodeCharacter{0177}{\^y}% \DeclareUnicodeCharacter{0178}{\"Y}% \DeclareUnicodeCharacter{0179}{\'Z}% \DeclareUnicodeCharacter{017A}{\'z}% \DeclareUnicodeCharacter{017B}{\dotaccent{Z}}% \DeclareUnicodeCharacter{017C}{\dotaccent{z}}% \DeclareUnicodeCharacter{017D}{\v{Z}}% \DeclareUnicodeCharacter{017E}{\v{z}}% \DeclareUnicodeCharacter{017F}{\missingcharmsg{LONG S}}% % \DeclareUnicodeCharacter{01C4}{D\v{Z}}% \DeclareUnicodeCharacter{01C5}{D\v{z}}% \DeclareUnicodeCharacter{01C6}{d\v{z}}% \DeclareUnicodeCharacter{01C7}{LJ}% \DeclareUnicodeCharacter{01C8}{Lj}% \DeclareUnicodeCharacter{01C9}{lj}% \DeclareUnicodeCharacter{01CA}{NJ}% \DeclareUnicodeCharacter{01CB}{Nj}% \DeclareUnicodeCharacter{01CC}{nj}% \DeclareUnicodeCharacter{01CD}{\v{A}}% \DeclareUnicodeCharacter{01CE}{\v{a}}% \DeclareUnicodeCharacter{01CF}{\v{I}}% % \DeclareUnicodeCharacter{01D0}{\v{\dotless{i}}}% \DeclareUnicodeCharacter{01D1}{\v{O}}% \DeclareUnicodeCharacter{01D2}{\v{o}}% \DeclareUnicodeCharacter{01D3}{\v{U}}% \DeclareUnicodeCharacter{01D4}{\v{u}}% % \DeclareUnicodeCharacter{01E2}{\={\AE}}% \DeclareUnicodeCharacter{01E3}{\={\ae}}% \DeclareUnicodeCharacter{01E6}{\v{G}}% \DeclareUnicodeCharacter{01E7}{\v{g}}% \DeclareUnicodeCharacter{01E8}{\v{K}}% \DeclareUnicodeCharacter{01E9}{\v{k}}% % \DeclareUnicodeCharacter{01F0}{\v{\dotless{j}}}% \DeclareUnicodeCharacter{01F1}{DZ}% \DeclareUnicodeCharacter{01F2}{Dz}% \DeclareUnicodeCharacter{01F3}{dz}% \DeclareUnicodeCharacter{01F4}{\'G}% \DeclareUnicodeCharacter{01F5}{\'g}% \DeclareUnicodeCharacter{01F8}{\`N}% \DeclareUnicodeCharacter{01F9}{\`n}% \DeclareUnicodeCharacter{01FC}{\'{\AE}}% \DeclareUnicodeCharacter{01FD}{\'{\ae}}% \DeclareUnicodeCharacter{01FE}{\'{\O}}% \DeclareUnicodeCharacter{01FF}{\'{\o}}% % \DeclareUnicodeCharacter{021E}{\v{H}}% \DeclareUnicodeCharacter{021F}{\v{h}}% % \DeclareUnicodeCharacter{0226}{\dotaccent{A}}% \DeclareUnicodeCharacter{0227}{\dotaccent{a}}% \DeclareUnicodeCharacter{0228}{\cedilla{E}}% \DeclareUnicodeCharacter{0229}{\cedilla{e}}% \DeclareUnicodeCharacter{022E}{\dotaccent{O}}% \DeclareUnicodeCharacter{022F}{\dotaccent{o}}% % \DeclareUnicodeCharacter{0232}{\=Y}% \DeclareUnicodeCharacter{0233}{\=y}% \DeclareUnicodeCharacter{0237}{\dotless{j}}% % \DeclareUnicodeCharacter{02DB}{\ogonek{ }}% % % Greek letters upper case \DeclareUnicodeCharacter{0391}{{\it A}}% \DeclareUnicodeCharacter{0392}{{\it B}}% \DeclareUnicodeCharacter{0393}{\ensuremath{\mit\Gamma}}% \DeclareUnicodeCharacter{0394}{\ensuremath{\mit\Delta}}% \DeclareUnicodeCharacter{0395}{{\it E}}% \DeclareUnicodeCharacter{0396}{{\it Z}}% \DeclareUnicodeCharacter{0397}{{\it H}}% \DeclareUnicodeCharacter{0398}{\ensuremath{\mit\Theta}}% \DeclareUnicodeCharacter{0399}{{\it I}}% \DeclareUnicodeCharacter{039A}{{\it K}}% \DeclareUnicodeCharacter{039B}{\ensuremath{\mit\Lambda}}% \DeclareUnicodeCharacter{039C}{{\it M}}% \DeclareUnicodeCharacter{039D}{{\it N}}% \DeclareUnicodeCharacter{039E}{\ensuremath{\mit\Xi}}% \DeclareUnicodeCharacter{039F}{{\it O}}% \DeclareUnicodeCharacter{03A0}{\ensuremath{\mit\Pi}}% \DeclareUnicodeCharacter{03A1}{{\it P}}% %\DeclareUnicodeCharacter{03A2}{} % none - corresponds to final sigma \DeclareUnicodeCharacter{03A3}{\ensuremath{\mit\Sigma}}% \DeclareUnicodeCharacter{03A4}{{\it T}}% \DeclareUnicodeCharacter{03A5}{\ensuremath{\mit\Upsilon}}% \DeclareUnicodeCharacter{03A6}{\ensuremath{\mit\Phi}}% \DeclareUnicodeCharacter{03A7}{{\it X}}% \DeclareUnicodeCharacter{03A8}{\ensuremath{\mit\Psi}}% \DeclareUnicodeCharacter{03A9}{\ensuremath{\mit\Omega}}% % % Vowels with accents \DeclareUnicodeCharacter{0390}{\ensuremath{\ddot{\acute\iota}}}% \DeclareUnicodeCharacter{03AC}{\ensuremath{\acute\alpha}}% \DeclareUnicodeCharacter{03AD}{\ensuremath{\acute\epsilon}}% \DeclareUnicodeCharacter{03AE}{\ensuremath{\acute\eta}}% \DeclareUnicodeCharacter{03AF}{\ensuremath{\acute\iota}}% \DeclareUnicodeCharacter{03B0}{\ensuremath{\acute{\ddot\upsilon}}}% % % Standalone accent \DeclareUnicodeCharacter{0384}{\ensuremath{\acute{\ }}}% % % Greek letters lower case \DeclareUnicodeCharacter{03B1}{\ensuremath\alpha}% \DeclareUnicodeCharacter{03B2}{\ensuremath\beta}% \DeclareUnicodeCharacter{03B3}{\ensuremath\gamma}% \DeclareUnicodeCharacter{03B4}{\ensuremath\delta}% \DeclareUnicodeCharacter{03B5}{\ensuremath\epsilon}% \DeclareUnicodeCharacter{03B6}{\ensuremath\zeta}% \DeclareUnicodeCharacter{03B7}{\ensuremath\eta}% \DeclareUnicodeCharacter{03B8}{\ensuremath\theta}% \DeclareUnicodeCharacter{03B9}{\ensuremath\iota}% \DeclareUnicodeCharacter{03BA}{\ensuremath\kappa}% \DeclareUnicodeCharacter{03BB}{\ensuremath\lambda}% \DeclareUnicodeCharacter{03BC}{\ensuremath\mu}% \DeclareUnicodeCharacter{03BD}{\ensuremath\nu}% \DeclareUnicodeCharacter{03BE}{\ensuremath\xi}% \DeclareUnicodeCharacter{03BF}{{\it o}}% omicron \DeclareUnicodeCharacter{03C0}{\ensuremath\pi}% \DeclareUnicodeCharacter{03C1}{\ensuremath\rho}% \DeclareUnicodeCharacter{03C2}{\ensuremath\varsigma}% \DeclareUnicodeCharacter{03C3}{\ensuremath\sigma}% \DeclareUnicodeCharacter{03C4}{\ensuremath\tau}% \DeclareUnicodeCharacter{03C5}{\ensuremath\upsilon}% \DeclareUnicodeCharacter{03C6}{\ensuremath\phi}% \DeclareUnicodeCharacter{03C7}{\ensuremath\chi}% \DeclareUnicodeCharacter{03C8}{\ensuremath\psi}% \DeclareUnicodeCharacter{03C9}{\ensuremath\omega}% % % More Greek vowels with accents \DeclareUnicodeCharacter{03CA}{\ensuremath{\ddot\iota}}% \DeclareUnicodeCharacter{03CB}{\ensuremath{\ddot\upsilon}}% \DeclareUnicodeCharacter{03CC}{\ensuremath{\acute o}}% \DeclareUnicodeCharacter{03CD}{\ensuremath{\acute\upsilon}}% \DeclareUnicodeCharacter{03CE}{\ensuremath{\acute\omega}}% % % Variant Greek letters \DeclareUnicodeCharacter{03D1}{\ensuremath\vartheta}% \DeclareUnicodeCharacter{03D6}{\ensuremath\varpi}% \DeclareUnicodeCharacter{03F1}{\ensuremath\varrho}% % \DeclareUnicodeCharacter{1E02}{\dotaccent{B}}% \DeclareUnicodeCharacter{1E03}{\dotaccent{b}}% \DeclareUnicodeCharacter{1E04}{\udotaccent{B}}% \DeclareUnicodeCharacter{1E05}{\udotaccent{b}}% \DeclareUnicodeCharacter{1E06}{\ubaraccent{B}}% \DeclareUnicodeCharacter{1E07}{\ubaraccent{b}}% \DeclareUnicodeCharacter{1E0A}{\dotaccent{D}}% \DeclareUnicodeCharacter{1E0B}{\dotaccent{d}}% \DeclareUnicodeCharacter{1E0C}{\udotaccent{D}}% \DeclareUnicodeCharacter{1E0D}{\udotaccent{d}}% \DeclareUnicodeCharacter{1E0E}{\ubaraccent{D}}% \DeclareUnicodeCharacter{1E0F}{\ubaraccent{d}}% % \DeclareUnicodeCharacter{1E1E}{\dotaccent{F}}% \DeclareUnicodeCharacter{1E1F}{\dotaccent{f}}% % \DeclareUnicodeCharacter{1E20}{\=G}% \DeclareUnicodeCharacter{1E21}{\=g}% \DeclareUnicodeCharacter{1E22}{\dotaccent{H}}% \DeclareUnicodeCharacter{1E23}{\dotaccent{h}}% \DeclareUnicodeCharacter{1E24}{\udotaccent{H}}% \DeclareUnicodeCharacter{1E25}{\udotaccent{h}}% \DeclareUnicodeCharacter{1E26}{\"H}% \DeclareUnicodeCharacter{1E27}{\"h}% % \DeclareUnicodeCharacter{1E30}{\'K}% \DeclareUnicodeCharacter{1E31}{\'k}% \DeclareUnicodeCharacter{1E32}{\udotaccent{K}}% \DeclareUnicodeCharacter{1E33}{\udotaccent{k}}% \DeclareUnicodeCharacter{1E34}{\ubaraccent{K}}% \DeclareUnicodeCharacter{1E35}{\ubaraccent{k}}% \DeclareUnicodeCharacter{1E36}{\udotaccent{L}}% \DeclareUnicodeCharacter{1E37}{\udotaccent{l}}% \DeclareUnicodeCharacter{1E3A}{\ubaraccent{L}}% \DeclareUnicodeCharacter{1E3B}{\ubaraccent{l}}% \DeclareUnicodeCharacter{1E3E}{\'M}% \DeclareUnicodeCharacter{1E3F}{\'m}% % \DeclareUnicodeCharacter{1E40}{\dotaccent{M}}% \DeclareUnicodeCharacter{1E41}{\dotaccent{m}}% \DeclareUnicodeCharacter{1E42}{\udotaccent{M}}% \DeclareUnicodeCharacter{1E43}{\udotaccent{m}}% \DeclareUnicodeCharacter{1E44}{\dotaccent{N}}% \DeclareUnicodeCharacter{1E45}{\dotaccent{n}}% \DeclareUnicodeCharacter{1E46}{\udotaccent{N}}% \DeclareUnicodeCharacter{1E47}{\udotaccent{n}}% \DeclareUnicodeCharacter{1E48}{\ubaraccent{N}}% \DeclareUnicodeCharacter{1E49}{\ubaraccent{n}}% % \DeclareUnicodeCharacter{1E54}{\'P}% \DeclareUnicodeCharacter{1E55}{\'p}% \DeclareUnicodeCharacter{1E56}{\dotaccent{P}}% \DeclareUnicodeCharacter{1E57}{\dotaccent{p}}% \DeclareUnicodeCharacter{1E58}{\dotaccent{R}}% \DeclareUnicodeCharacter{1E59}{\dotaccent{r}}% \DeclareUnicodeCharacter{1E5A}{\udotaccent{R}}% \DeclareUnicodeCharacter{1E5B}{\udotaccent{r}}% \DeclareUnicodeCharacter{1E5E}{\ubaraccent{R}}% \DeclareUnicodeCharacter{1E5F}{\ubaraccent{r}}% % \DeclareUnicodeCharacter{1E60}{\dotaccent{S}}% \DeclareUnicodeCharacter{1E61}{\dotaccent{s}}% \DeclareUnicodeCharacter{1E62}{\udotaccent{S}}% \DeclareUnicodeCharacter{1E63}{\udotaccent{s}}% \DeclareUnicodeCharacter{1E6A}{\dotaccent{T}}% \DeclareUnicodeCharacter{1E6B}{\dotaccent{t}}% \DeclareUnicodeCharacter{1E6C}{\udotaccent{T}}% \DeclareUnicodeCharacter{1E6D}{\udotaccent{t}}% \DeclareUnicodeCharacter{1E6E}{\ubaraccent{T}}% \DeclareUnicodeCharacter{1E6F}{\ubaraccent{t}}% % \DeclareUnicodeCharacter{1E7C}{\~V}% \DeclareUnicodeCharacter{1E7D}{\~v}% \DeclareUnicodeCharacter{1E7E}{\udotaccent{V}}% \DeclareUnicodeCharacter{1E7F}{\udotaccent{v}}% % \DeclareUnicodeCharacter{1E80}{\`W}% \DeclareUnicodeCharacter{1E81}{\`w}% \DeclareUnicodeCharacter{1E82}{\'W}% \DeclareUnicodeCharacter{1E83}{\'w}% \DeclareUnicodeCharacter{1E84}{\"W}% \DeclareUnicodeCharacter{1E85}{\"w}% \DeclareUnicodeCharacter{1E86}{\dotaccent{W}}% \DeclareUnicodeCharacter{1E87}{\dotaccent{w}}% \DeclareUnicodeCharacter{1E88}{\udotaccent{W}}% \DeclareUnicodeCharacter{1E89}{\udotaccent{w}}% \DeclareUnicodeCharacter{1E8A}{\dotaccent{X}}% \DeclareUnicodeCharacter{1E8B}{\dotaccent{x}}% \DeclareUnicodeCharacter{1E8C}{\"X}% \DeclareUnicodeCharacter{1E8D}{\"x}% \DeclareUnicodeCharacter{1E8E}{\dotaccent{Y}}% \DeclareUnicodeCharacter{1E8F}{\dotaccent{y}}% % \DeclareUnicodeCharacter{1E90}{\^Z}% \DeclareUnicodeCharacter{1E91}{\^z}% \DeclareUnicodeCharacter{1E92}{\udotaccent{Z}}% \DeclareUnicodeCharacter{1E93}{\udotaccent{z}}% \DeclareUnicodeCharacter{1E94}{\ubaraccent{Z}}% \DeclareUnicodeCharacter{1E95}{\ubaraccent{z}}% \DeclareUnicodeCharacter{1E96}{\ubaraccent{h}}% \DeclareUnicodeCharacter{1E97}{\"t}% \DeclareUnicodeCharacter{1E98}{\ringaccent{w}}% \DeclareUnicodeCharacter{1E99}{\ringaccent{y}}% % \DeclareUnicodeCharacter{1EA0}{\udotaccent{A}}% \DeclareUnicodeCharacter{1EA1}{\udotaccent{a}}% % \DeclareUnicodeCharacter{1EB8}{\udotaccent{E}}% \DeclareUnicodeCharacter{1EB9}{\udotaccent{e}}% \DeclareUnicodeCharacter{1EBC}{\~E}% \DeclareUnicodeCharacter{1EBD}{\~e}% % \DeclareUnicodeCharacter{1ECA}{\udotaccent{I}}% \DeclareUnicodeCharacter{1ECB}{\udotaccent{i}}% \DeclareUnicodeCharacter{1ECC}{\udotaccent{O}}% \DeclareUnicodeCharacter{1ECD}{\udotaccent{o}}% % \DeclareUnicodeCharacter{1EE4}{\udotaccent{U}}% \DeclareUnicodeCharacter{1EE5}{\udotaccent{u}}% % \DeclareUnicodeCharacter{1EF2}{\`Y}% \DeclareUnicodeCharacter{1EF3}{\`y}% \DeclareUnicodeCharacter{1EF4}{\udotaccent{Y}}% % \DeclareUnicodeCharacter{1EF8}{\~Y}% \DeclareUnicodeCharacter{1EF9}{\~y}% % % Punctuation \DeclareUnicodeCharacter{2013}{--}% \DeclareUnicodeCharacter{2014}{---}% \DeclareUnicodeCharacter{2018}{\quoteleft{}}% \DeclareUnicodeCharacter{2019}{\quoteright{}}% \DeclareUnicodeCharacter{201A}{\quotesinglbase{}}% \DeclareUnicodeCharacter{201C}{\quotedblleft{}}% \DeclareUnicodeCharacter{201D}{\quotedblright{}}% \DeclareUnicodeCharacter{201E}{\quotedblbase{}}% \DeclareUnicodeCharacter{2020}{\ensuremath\dagger}% \DeclareUnicodeCharacter{2021}{\ensuremath\ddagger}% \DeclareUnicodeCharacter{2022}{\bullet{}}% \DeclareUnicodeCharacter{202F}{\thinspace}% \DeclareUnicodeCharacter{2026}{\dots{}}% \DeclareUnicodeCharacter{2039}{\guilsinglleft{}}% \DeclareUnicodeCharacter{203A}{\guilsinglright{}}% % \DeclareUnicodeCharacter{20AC}{\euro{}}% % \DeclareUnicodeCharacter{2192}{\expansion{}}% \DeclareUnicodeCharacter{21D2}{\result{}}% % % Mathematical symbols \DeclareUnicodeCharacter{2200}{\ensuremath\forall}% \DeclareUnicodeCharacter{2203}{\ensuremath\exists}% \DeclareUnicodeCharacter{2208}{\ensuremath\in}% \DeclareUnicodeCharacter{2212}{\minus{}}% \DeclareUnicodeCharacter{2217}{\ast}% \DeclareUnicodeCharacter{221E}{\ensuremath\infty}% \DeclareUnicodeCharacter{2225}{\ensuremath\parallel}% \DeclareUnicodeCharacter{2227}{\ensuremath\wedge}% \DeclareUnicodeCharacter{2229}{\ensuremath\cap}% \DeclareUnicodeCharacter{2261}{\equiv{}}% \DeclareUnicodeCharacter{2264}{\ensuremath\leq}% \DeclareUnicodeCharacter{2265}{\ensuremath\geq}% \DeclareUnicodeCharacter{2282}{\ensuremath\subset}% \DeclareUnicodeCharacter{2287}{\ensuremath\supseteq}% % \DeclareUnicodeCharacter{2016}{\ensuremath\Vert}% \DeclareUnicodeCharacter{2032}{\ensuremath\prime}% \DeclareUnicodeCharacter{210F}{\ensuremath\hbar}% \DeclareUnicodeCharacter{2111}{\ensuremath\Im}% \DeclareUnicodeCharacter{2113}{\ensuremath\ell}% \DeclareUnicodeCharacter{2118}{\ensuremath\wp}% \DeclareUnicodeCharacter{211C}{\ensuremath\Re}% \DeclareUnicodeCharacter{2135}{\ensuremath\aleph}% \DeclareUnicodeCharacter{2190}{\ensuremath\leftarrow}% \DeclareUnicodeCharacter{2191}{\ensuremath\uparrow}% \DeclareUnicodeCharacter{2193}{\ensuremath\downarrow}% \DeclareUnicodeCharacter{2194}{\ensuremath\leftrightarrow}% \DeclareUnicodeCharacter{2195}{\ensuremath\updownarrow}% \DeclareUnicodeCharacter{2196}{\ensuremath\nwarrow}% \DeclareUnicodeCharacter{2197}{\ensuremath\nearrow}% \DeclareUnicodeCharacter{2198}{\ensuremath\searrow}% \DeclareUnicodeCharacter{2199}{\ensuremath\swarrow}% \DeclareUnicodeCharacter{21A6}{\ensuremath\mapsto}% \DeclareUnicodeCharacter{21A9}{\ensuremath\hookleftarrow}% \DeclareUnicodeCharacter{21AA}{\ensuremath\hookrightarrow}% \DeclareUnicodeCharacter{21BC}{\ensuremath\leftharpoonup}% \DeclareUnicodeCharacter{21BD}{\ensuremath\leftharpoondown}% \DeclareUnicodeCharacter{21C0}{\ensuremath\rightharpoonup}% \DeclareUnicodeCharacter{21C1}{\ensuremath\rightharpoondown}% \DeclareUnicodeCharacter{21CC}{\ensuremath\rightleftharpoons}% \DeclareUnicodeCharacter{21D0}{\ensuremath\Leftarrow}% \DeclareUnicodeCharacter{21D1}{\ensuremath\Uparrow}% \DeclareUnicodeCharacter{21D3}{\ensuremath\Downarrow}% \DeclareUnicodeCharacter{21D4}{\ensuremath\Leftrightarrow}% \DeclareUnicodeCharacter{21D5}{\ensuremath\Updownarrow}% \DeclareUnicodeCharacter{2202}{\ensuremath\partial}% \DeclareUnicodeCharacter{2205}{\ensuremath\emptyset}% \DeclareUnicodeCharacter{2207}{\ensuremath\nabla}% \DeclareUnicodeCharacter{2209}{\ensuremath\notin}% \DeclareUnicodeCharacter{220B}{\ensuremath\owns}% \DeclareUnicodeCharacter{220F}{\ensuremath\prod}% \DeclareUnicodeCharacter{2210}{\ensuremath\coprod}% \DeclareUnicodeCharacter{2211}{\ensuremath\sum}% \DeclareUnicodeCharacter{2213}{\ensuremath\mp}% \DeclareUnicodeCharacter{2218}{\ensuremath\circ}% \DeclareUnicodeCharacter{221A}{\ensuremath\surd}% \DeclareUnicodeCharacter{221D}{\ensuremath\propto}% \DeclareUnicodeCharacter{2220}{\ensuremath\angle}% \DeclareUnicodeCharacter{2223}{\ensuremath\mid}% \DeclareUnicodeCharacter{2228}{\ensuremath\vee}% \DeclareUnicodeCharacter{222A}{\ensuremath\cup}% \DeclareUnicodeCharacter{222B}{\ensuremath\smallint}% \DeclareUnicodeCharacter{222E}{\ensuremath\oint}% \DeclareUnicodeCharacter{223C}{\ensuremath\sim}% \DeclareUnicodeCharacter{2240}{\ensuremath\wr}% \DeclareUnicodeCharacter{2243}{\ensuremath\simeq}% \DeclareUnicodeCharacter{2245}{\ensuremath\cong}% \DeclareUnicodeCharacter{2248}{\ensuremath\approx}% \DeclareUnicodeCharacter{224D}{\ensuremath\asymp}% \DeclareUnicodeCharacter{2250}{\ensuremath\doteq}% \DeclareUnicodeCharacter{2260}{\ensuremath\neq}% \DeclareUnicodeCharacter{226A}{\ensuremath\ll}% \DeclareUnicodeCharacter{226B}{\ensuremath\gg}% \DeclareUnicodeCharacter{227A}{\ensuremath\prec}% \DeclareUnicodeCharacter{227B}{\ensuremath\succ}% \DeclareUnicodeCharacter{2283}{\ensuremath\supset}% \DeclareUnicodeCharacter{2286}{\ensuremath\subseteq}% \DeclareUnicodeCharacter{228E}{\ensuremath\uplus}% \DeclareUnicodeCharacter{2291}{\ensuremath\sqsubseteq}% \DeclareUnicodeCharacter{2292}{\ensuremath\sqsupseteq}% \DeclareUnicodeCharacter{2293}{\ensuremath\sqcap}% \DeclareUnicodeCharacter{2294}{\ensuremath\sqcup}% \DeclareUnicodeCharacter{2295}{\ensuremath\oplus}% \DeclareUnicodeCharacter{2296}{\ensuremath\ominus}% \DeclareUnicodeCharacter{2297}{\ensuremath\otimes}% \DeclareUnicodeCharacter{2298}{\ensuremath\oslash}% \DeclareUnicodeCharacter{2299}{\ensuremath\odot}% \DeclareUnicodeCharacter{22A2}{\ensuremath\vdash}% \DeclareUnicodeCharacter{22A3}{\ensuremath\dashv}% \DeclareUnicodeCharacter{22A4}{\ensuremath\ptextop}% \DeclareUnicodeCharacter{22A5}{\ensuremath\bot}% \DeclareUnicodeCharacter{22A8}{\ensuremath\models}% \DeclareUnicodeCharacter{22C0}{\ensuremath\bigwedge}% \DeclareUnicodeCharacter{22C1}{\ensuremath\bigvee}% \DeclareUnicodeCharacter{22C2}{\ensuremath\bigcap}% \DeclareUnicodeCharacter{22C3}{\ensuremath\bigcup}% \DeclareUnicodeCharacter{22C4}{\ensuremath\diamond}% \DeclareUnicodeCharacter{22C5}{\ensuremath\cdot}% \DeclareUnicodeCharacter{22C6}{\ensuremath\star}% \DeclareUnicodeCharacter{22C8}{\ensuremath\bowtie}% \DeclareUnicodeCharacter{2308}{\ensuremath\lceil}% \DeclareUnicodeCharacter{2309}{\ensuremath\rceil}% \DeclareUnicodeCharacter{230A}{\ensuremath\lfloor}% \DeclareUnicodeCharacter{230B}{\ensuremath\rfloor}% \DeclareUnicodeCharacter{2322}{\ensuremath\frown}% \DeclareUnicodeCharacter{2323}{\ensuremath\smile}% % \DeclareUnicodeCharacter{25B3}{\ensuremath\triangle}% \DeclareUnicodeCharacter{25B7}{\ensuremath\triangleright}% \DeclareUnicodeCharacter{25BD}{\ensuremath\bigtriangledown}% \DeclareUnicodeCharacter{25C1}{\ensuremath\triangleleft}% \DeclareUnicodeCharacter{25C7}{\ensuremath\diamond}% \DeclareUnicodeCharacter{2660}{\ensuremath\spadesuit}% \DeclareUnicodeCharacter{2661}{\ensuremath\heartsuit}% \DeclareUnicodeCharacter{2662}{\ensuremath\diamondsuit}% \DeclareUnicodeCharacter{2663}{\ensuremath\clubsuit}% \DeclareUnicodeCharacter{266D}{\ensuremath\flat}% \DeclareUnicodeCharacter{266E}{\ensuremath\natural}% \DeclareUnicodeCharacter{266F}{\ensuremath\sharp}% \DeclareUnicodeCharacter{26AA}{\ensuremath\bigcirc}% \DeclareUnicodeCharacter{27B9}{\ensuremath\rangle}% \DeclareUnicodeCharacter{27C2}{\ensuremath\perp}% \DeclareUnicodeCharacter{27E8}{\ensuremath\langle}% \DeclareUnicodeCharacter{27F5}{\ensuremath\longleftarrow}% \DeclareUnicodeCharacter{27F6}{\ensuremath\longrightarrow}% \DeclareUnicodeCharacter{27F7}{\ensuremath\longleftrightarrow}% \DeclareUnicodeCharacter{27FC}{\ensuremath\longmapsto}% \DeclareUnicodeCharacter{29F5}{\ensuremath\setminus}% \DeclareUnicodeCharacter{2A00}{\ensuremath\bigodot}% \DeclareUnicodeCharacter{2A01}{\ensuremath\bigoplus}% \DeclareUnicodeCharacter{2A02}{\ensuremath\bigotimes}% \DeclareUnicodeCharacter{2A04}{\ensuremath\biguplus}% \DeclareUnicodeCharacter{2A06}{\ensuremath\bigsqcup}% \DeclareUnicodeCharacter{2A3F}{\ensuremath\amalg}% \DeclareUnicodeCharacter{2AAF}{\ensuremath\preceq}% \DeclareUnicodeCharacter{2AB0}{\ensuremath\succeq}% % \global\mathchardef\checkmark="1370% actually the square root sign \DeclareUnicodeCharacter{2713}{\ensuremath\checkmark}% }% end of \unicodechardefs % UTF-8 byte sequence (pdfTeX) definitions (replacing and @U command) % It makes the setting that replace UTF-8 byte sequence. \def\utfeightchardefs{% \let\DeclareUnicodeCharacter\DeclareUnicodeCharacterUTFviii \unicodechardefs } % Whether the active definitions of non-ASCII characters expand to % non-active tokens with the same character code. This is used to % write characters literally, instead of using active definitions for % printing the correct glyphs. \newif\ifpassthroughchars \passthroughcharsfalse % For native Unicode handling (XeTeX and LuaTeX), % provide a definition macro to replace/pass-through a Unicode character % \def\DeclareUnicodeCharacterNative#1#2{% \catcode"#1=\active \def\dodeclareunicodecharacternative##1##2##3{% \begingroup \uccode`\~="##2\relax \uppercase{\gdef~}{% \ifpassthroughchars ##1% \else ##3% \fi } \endgroup } \begingroup \uccode`\.="#1\relax \uppercase{\def\UTFNativeTmp{.}}% \expandafter\dodeclareunicodecharacternative\UTFNativeTmp{#1}{#2}% \endgroup } % Native Unicode handling (XeTeX and LuaTeX) character replacing definition. % It activates the setting that replaces Unicode characters. \def\nativeunicodechardefs{% \let\DeclareUnicodeCharacter\DeclareUnicodeCharacterNative \unicodechardefs } % For native Unicode handling (XeTeX and LuaTeX), % make the character token expand % to the sequences given in \unicodechardefs for printing. \def\DeclareUnicodeCharacterNativeAtU#1#2{% \def\UTFAtUTmp{#2} \expandafter\globallet\csname uni:#1\endcsname \UTFAtUTmp } % @U command definitions for native Unicode handling (XeTeX and LuaTeX). \def\nativeunicodechardefsatu{% \let\DeclareUnicodeCharacter\DeclareUnicodeCharacterNativeAtU \unicodechardefs } % US-ASCII character definitions. \def\asciichardefs{% nothing need be done \relax } % Define all Unicode characters we know about. This makes UTF-8 the default % input encoding and allows @U to work. \iftxinativeunicodecapable \nativeunicodechardefsatu \else \utfeightchardefs \fi \message{formatting,} \newdimen\defaultparindent \defaultparindent = 15pt \chapheadingskip = 15pt plus 4pt minus 2pt \secheadingskip = 12pt plus 3pt minus 2pt \subsecheadingskip = 9pt plus 2pt minus 2pt % Prevent underfull vbox error messages. \vbadness = 10000 % Don't be very finicky about underfull hboxes, either. \hbadness = 6666 % Following George Bush, get rid of widows and orphans. \widowpenalty=10000 \clubpenalty=10000 % Use TeX 3.0's \emergencystretch to help line breaking, but if we're % using an old version of TeX, don't do anything. We want the amount of % stretch added to depend on the line length, hence the dependence on % \hsize. We call this whenever the paper size is set. % \def\setemergencystretch{% \ifx\emergencystretch\thisisundefined % Allow us to assign to \emergencystretch anyway. \def\emergencystretch{\dimen0}% \else \emergencystretch = .15\hsize \fi } % Parameters in order: 1) textheight; 2) textwidth; % 3) voffset; 4) hoffset; 5) binding offset; 6) topskip; % 7) physical page height; 8) physical page width. % % We also call \setleading{\textleading}, so the caller should define % \textleading. The caller should also set \parskip. % \def\internalpagesizes#1#2#3#4#5#6#7#8{% \voffset = #3\relax \topskip = #6\relax \splittopskip = \topskip % \vsize = #1\relax \advance\vsize by \topskip \outervsize = \vsize \advance\outervsize by 2\topandbottommargin \txipageheight = \vsize % \hsize = #2\relax \outerhsize = \hsize \advance\outerhsize by 0.5in \txipagewidth = \hsize % \normaloffset = #4\relax \bindingoffset = #5\relax % \ifpdf \pdfpageheight #7\relax \pdfpagewidth #8\relax % if we don't reset these, they will remain at "1 true in" of % whatever layout pdftex was dumped with. \pdfhorigin = 1 true in \pdfvorigin = 1 true in \else \ifx\XeTeXrevision\thisisundefined \special{papersize=#8,#7}% \else \pdfpageheight #7\relax \pdfpagewidth #8\relax % XeTeX does not have \pdfhorigin and \pdfvorigin. \fi \fi % \setleading{\textleading} % \parindent = \defaultparindent \setemergencystretch } % @letterpaper (the default). \def\letterpaper{{\globaldefs = 1 \parskip = 3pt plus 2pt minus 1pt \textleading = 13.2pt % % If page is nothing but text, make it come out even. \internalpagesizes{607.2pt}{6in}% that's 46 lines {\voffset}{.25in}% {\bindingoffset}{36pt}% {11in}{8.5in}% }} % Use @smallbook to reset parameters for 7x9.25 trim size. \def\smallbook{{\globaldefs = 1 \parskip = 2pt plus 1pt \textleading = 12pt % \internalpagesizes{7.5in}{5in}% {-.2in}{0in}% {\bindingoffset}{16pt}% {9.25in}{7in}% % \lispnarrowing = 0.3in \tolerance = 700 \contentsrightmargin = 0pt \defbodyindent = .5cm }} % Use @smallerbook to reset parameters for 6x9 trim size. % (Just testing, parameters still in flux.) \def\smallerbook{{\globaldefs = 1 \parskip = 1.5pt plus 1pt \textleading = 12pt % \internalpagesizes{7.4in}{4.8in}% {-.2in}{-.4in}% {0pt}{14pt}% {9in}{6in}% % \lispnarrowing = 0.25in \tolerance = 700 \contentsrightmargin = 0pt \defbodyindent = .4cm }} % Use @afourpaper to print on European A4 paper. \def\afourpaper{{\globaldefs = 1 \parskip = 3pt plus 2pt minus 1pt \textleading = 13.2pt % % Double-side printing via postscript on Laserjet 4050 % prints double-sided nicely when \bindingoffset=10mm and \hoffset=-6mm. % To change the settings for a different printer or situation, adjust % \normaloffset until the front-side and back-side texts align. Then % do the same for \bindingoffset. You can set these for testing in % your texinfo source file like this: % @tex % \global\normaloffset = -6mm % \global\bindingoffset = 10mm % @end tex \internalpagesizes{673.2pt}{160mm}% that's 51 lines {\voffset}{\hoffset}% {\bindingoffset}{44pt}% {297mm}{210mm}% % \tolerance = 700 \contentsrightmargin = 0pt \defbodyindent = 5mm }} % Use @afivepaper to print on European A5 paper. % From romildo@urano.iceb.ufop.br, 2 July 2000. % He also recommends making @example and @lisp be small. \def\afivepaper{{\globaldefs = 1 \parskip = 2pt plus 1pt minus 0.1pt \textleading = 12.5pt % \internalpagesizes{160mm}{120mm}% {\voffset}{\hoffset}% {\bindingoffset}{8pt}% {210mm}{148mm}% % \lispnarrowing = 0.2in \tolerance = 800 \contentsrightmargin = 0pt \defbodyindent = 2mm \tableindent = 12mm }} % A specific text layout, 24x15cm overall, intended for A4 paper. \def\afourlatex{{\globaldefs = 1 \afourpaper \internalpagesizes{237mm}{150mm}% {\voffset}{4.6mm}% {\bindingoffset}{7mm}% {297mm}{210mm}% % % Must explicitly reset to 0 because we call \afourpaper. \globaldefs = 0 }} % Use @afourwide to print on A4 paper in landscape format. \def\afourwide{{\globaldefs = 1 \afourpaper \internalpagesizes{241mm}{165mm}% {\voffset}{-2.95mm}% {\bindingoffset}{7mm}% {297mm}{210mm}% \globaldefs = 0 }} % @pagesizes TEXTHEIGHT[,TEXTWIDTH] % Perhaps we should allow setting the margins, \topskip, \parskip, % and/or leading, also. Or perhaps we should compute them somehow. % \parseargdef\pagesizes{\pagesizesyyy #1,,\finish} \def\pagesizesyyy#1,#2,#3\finish{{% \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi \globaldefs = 1 % \parskip = 3pt plus 2pt minus 1pt \setleading{\textleading}% % \dimen0 = #1\relax \advance\dimen0 by \voffset \advance\dimen0 by 1in % reference point for DVI is 1 inch from top of page % \dimen2 = \hsize \advance\dimen2 by \normaloffset \advance\dimen2 by 1in % reference point is 1 inch from left edge of page % \internalpagesizes{#1}{\hsize}% {\voffset}{\normaloffset}% {\bindingoffset}{44pt}% {\dimen0}{\dimen2}% }} % Set default to letter. % \letterpaper % Default value of \hfuzz, for suppressing warnings about overfull hboxes. \hfuzz = 1pt \message{and turning on texinfo input format.} \def^^L{\par} % remove \outer, so ^L can appear in an @comment % DEL is a comment character, in case @c does not suffice. \catcode`\^^? = 14 % Define macros to output various characters with catcode for normal text. \catcode`\"=\other \def\normaldoublequote{"} \catcode`\$=\other \def\normaldollar{$}%$ font-lock fix \catcode`\+=\other \def\normalplus{+} \catcode`\<=\other \def\normalless{<} \catcode`\>=\other \def\normalgreater{>} \catcode`\^=\other \def\normalcaret{^} \catcode`\_=\other \def\normalunderscore{_} \catcode`\|=\other \def\normalverticalbar{|} \catcode`\~=\other \def\normaltilde{~} % This macro is used to make a character print one way in \tt % (where it can probably be output as-is), and another way in other fonts, % where something hairier probably needs to be done. % % #1 is what to print if we are indeed using \tt; #2 is what to print % otherwise. Since all the Computer Modern typewriter fonts have zero % interword stretch (and shrink), and it is reasonable to expect all % typewriter fonts to have this, we can check that font parameter. % \def\ifusingtt#1#2{\ifdim \fontdimen3\font=0pt #1\else #2\fi} % Same as above, but check for italic font. Actually this also catches % non-italic slanted fonts since it is impossible to distinguish them from % italic fonts. But since this is only used by $ and it uses \sl anyway % this is not a problem. \def\ifusingit#1#2{\ifdim \fontdimen1\font>0pt #1\else #2\fi} % Set catcodes for Texinfo file % Active characters for printing the wanted glyph. % Most of these we simply print from the \tt font, but for some, we can % use math or other variants that look better in normal text. % \catcode`\"=\active \def\activedoublequote{{\tt\char34}} \let"=\activedoublequote \catcode`\~=\active \def\activetilde{{\tt\char126}} \let~ = \activetilde \chardef\hatchar=`\^ \catcode`\^=\active \def\activehat{{\tt \hatchar}} \let^ = \activehat \catcode`\_=\active \def_{\ifusingtt\normalunderscore\_} \def\_{\leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em } \let\realunder=_ \catcode`\|=\active \def|{{\tt\char124}} \chardef \less=`\< \catcode`\<=\active \def\activeless{{\tt \less}}\let< = \activeless \chardef \gtr=`\> \catcode`\>=\active \def\activegtr{{\tt \gtr}}\let> = \activegtr \catcode`\+=\active \def+{{\tt \char 43}} \catcode`\$=\active \def${\ifusingit{{\sl\$}}\normaldollar}%$ font-lock fix \catcode`\-=\active \let-=\normaldash % used for headline/footline in the output routine, in case the page % breaks in the middle of an @tex block. \def\texinfochars{% \let< = \activeless \let> = \activegtr \let~ = \activetilde \let^ = \activehat \markupsetuplqdefault \markupsetuprqdefault \let\b = \strong \let\i = \smartitalic % in principle, all other definitions in \tex have to be undone too. } % Used sometimes to turn off (effectively) the active characters even after % parsing them. \def\turnoffactive{% \normalturnoffactive \otherbackslash } \catcode`\@=0 % \backslashcurfont outputs one backslash character in current font, % as in \char`\\. \global\chardef\backslashcurfont=`\\ % \realbackslash is an actual character `\' with catcode other. {\catcode`\\=\other @gdef@realbackslash{\}} % In Texinfo, backslash is an active character; it prints the backslash % in fixed width font. \catcode`\\=\active % @ for escape char from now on. % Print a typewriter backslash. For math mode, we can't simply use % \backslashcurfont: the story here is that in math mode, the \char % of \backslashcurfont ends up printing the roman \ from the math symbol % font (because \char in math mode uses the \mathcode, and plain.tex % sets \mathcode`\\="026E). Hence we use an explicit \mathchar, % which is the decimal equivalent of "715c (class 7, e.g., use \fam; % ignored family value; char position "5C). We can't use " for the % usual hex value because it has already been made active. @def@ttbackslash{{@tt @ifmmode @mathchar29020 @else @backslashcurfont @fi}} @let@backslashchar = @ttbackslash % @backslashchar{} is for user documents. % \otherbackslash defines an active \ to be a literal `\' character with % catcode other. @gdef@otherbackslash{@let\=@realbackslash} % Same as @turnoffactive except outputs \ as {\tt\char`\\} instead of % the literal character `\'. % {@catcode`- = @active @gdef@normalturnoffactive{% @passthroughcharstrue @let-=@normaldash @let"=@normaldoublequote @let$=@normaldollar %$ font-lock fix @let+=@normalplus @let<=@normalless @let>=@normalgreater @let^=@normalcaret @let_=@normalunderscore @let|=@normalverticalbar @let~=@normaltilde @let\=@ttbackslash @markupsetuplqdefault @markupsetuprqdefault @unsepspaces } } % If a .fmt file is being used, characters that might appear in a file % name cannot be active until we have parsed the command line. % So turn them off again, and have @fixbackslash turn them back on. @catcode`+=@other @catcode`@_=@other % \enablebackslashhack - allow file to begin `\input texinfo' % % If a .fmt file is being used, we don't want the `\input texinfo' to show up. % That is what \eatinput is for; after that, the `\' should revert to printing % a backslash. % If the file did not have a `\input texinfo', then it is turned off after % the first line; otherwise the first `\' in the file would cause an error. % This is used on the very last line of this file, texinfo.tex. % We also use @c to call @fixbackslash, in case ends of lines are hidden. { @catcode`@^=7 @catcode`@^^M=13@gdef@enablebackslashhack{% @global@let\ = @eatinput% @catcode`@^^M=13% @def@c{@fixbackslash@c}% % Definition for the newline at the end of this file. @def ^^M{@let^^M@secondlinenl}% % Definition for a newline in the main Texinfo file. @gdef @secondlinenl{@fixbackslash}% % In case the first line has a whole-line command on it @let@originalparsearg@parsearg @def@parsearg{@fixbackslash@originalparsearg} }} {@catcode`@^=7 @catcode`@^^M=13% @gdef@eatinput input texinfo#1^^M{@fixbackslash}} % Emergency active definition of newline, in case an active newline token % appears by mistake. {@catcode`@^=7 @catcode13=13% @gdef@enableemergencynewline{% @gdef^^M{% @par% %@par% }}} @gdef@fixbackslash{% @ifx\@eatinput @let\ = @ttbackslash @fi @catcode13=5 % regular end of line @enableemergencynewline @let@c=@comment @let@parsearg@originalparsearg % Also turn back on active characters that might appear in the input % file name, in case not using a pre-dumped format. @catcode`+=@active @catcode`@_=@active % % If texinfo.cnf is present on the system, read it. % Useful for site-wide @afourpaper, etc. This macro, @fixbackslash, gets % called at the beginning of every Texinfo file. Not opening texinfo.cnf % directly in this file, texinfo.tex, makes it possible to make a format % file for Texinfo. % @openin 1 texinfo.cnf @ifeof 1 @else @input texinfo.cnf @fi @closein 1 } % Say @foo, not \foo, in error messages. @escapechar = `@@ % These (along with & and #) are made active for url-breaking, so need % active definitions as the normal characters. @def@normaldot{.} @def@normalquest{?} @def@normalslash{/} % These look ok in all fonts, so just make them not special. % @hashchar{} gets its own user-level command, because of #line. @catcode`@& = @other @def@normalamp{&} @catcode`@# = @other @def@normalhash{#} @catcode`@% = @other @def@normalpercent{%} @let @hashchar = @normalhash @c Finally, make ` and ' active, so that txicodequoteundirected and @c txicodequotebacktick work right in, e.g., @w{@code{`foo'}}. If we @c don't make ` and ' active, @code will not get them as active chars. @c Do this last of all since we use ` in the previous @catcode assignments. @catcode`@'=@active @catcode`@`=@active @markupsetuplqdefault @markupsetuprqdefault @c Local variables: @c eval: (add-hook 'before-save-hook 'time-stamp) @c page-delimiter: "^\\\\message\\|emacs-page" @c time-stamp-start: "def\\\\texinfoversion{" @c time-stamp-format: "%:y-%02m-%02d.%02H" @c time-stamp-end: "}" @c End: @c vim:sw=2: @enablebackslashhack