[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/latex-table-wizard 7137ca9641 01/70: First commit
|
From: |
ELPA Syncer |
|
Subject: |
[elpa] externals/latex-table-wizard 7137ca9641 01/70: First commit |
|
Date: |
Sat, 13 May 2023 08:59:09 -0400 (EDT) |
branch: externals/latex-table-wizard
commit 7137ca9641955a8526eb497999e893791342f128
Author: Enrico Flor <nericoflor@gmail.com>
Commit: Enrico Flor <nericoflor@gmail.com>
First commit
---
.gitignore | 1 +
COPYING | 674 +++++++++++++++++++++++++++
latex-table-wizard.el | 1242 +++++++++++++++++++++++++++++++++++++++++++++++++
move-01.gif | Bin 0 -> 180937 bytes
readme.org | 245 ++++++++++
select-01.gif | Bin 0 -> 215650 bytes
swap-01.gif | Bin 0 -> 359257 bytes
7 files changed, 2162 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000..c531d9867f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.elc
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000000..f288702d2f
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <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 <https://www.gnu.org/licenses/>.
+
+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:
+
+ <program> Copyright (C) <year> <name of author>
+ 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
+<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
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/latex-table-wizard.el b/latex-table-wizard.el
new file mode 100644
index 0000000000..bbe2553c12
--- /dev/null
+++ b/latex-table-wizard.el
@@ -0,0 +1,1242 @@
+;;; latex-table-wizard.el --- Magic editing of LaTeX tables -*-
lexical-binding: t; -*-
+
+;; Copyright (C) 2022 Enrico Flor
+
+;; Author: Enrico Flor <enrico@eflor.net>
+;; Maintainer: Enrico Flor <enrico@eflor.net>
+;; URL: https://github.com/enricoflor/latex-table-wizard
+;; Version: 0.0.1
+
+;; Package-Requires: ((emacs "27.1") (auctex "12.1") (transient "0.3.7"))
+
+;; SPDX-License-Identifier: GPL-3.0-or-later
+
+;; This file is NOT part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation, either version 3 of the
+;; License, or (at your option) any later version.
+;;
+;; This program is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;; General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see
+;; <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This package provides you with commands to smartly navigate and
+;; edit LaTeX table-like environments with a transient.el-based
+;; interface. Table-like environments are portions of text delimited
+;; by a pair of matching "\begin" and "\end" macros that organize
+;; output text into aligned colums.
+
+;; With this package you can navigate and edit these tables easily
+;; even if the source text is not aligned, because you will have
+;; movement commands that specfically target cells in the four
+;; directions of motion.
+
+;; By default, the syntax this package expects is the one of standards
+;; LaTeX tabular environments, whereby "&" separates columns and "\\"
+;; separates rows. Additional, or different, separators can be added
+;; by appending the correct regexp strings to the variable
+;; latex-table-wizard-column-delimiters and
+;; latex-table-wizard-row-delimiters.
+
+;; This package tries to be smart and not be fooled by the presence of
+;; embedded environments and embedded tables (that is, a table inside
+;; of the cell of a table).
+
+;;; Code:
+
+;;; Dependencies
+
+(require 'cl-lib)
+(require 'rx)
+(require 'regexp-opt)
+(eval-when-compile (require 'subr-x))
+(require 'transient)
+
+;;; Regular expressions
+
+;; (defconst latex-table-wizard--macro-prefix-re
+;; (rx (seq (zero-or-one (or (not 92) (seq 92 92))) "\\"))
+;; "Regexp matching unescaped \"\\\".")
+
+(defconst latex-table-wizard--macro-args-re
+ (rx (seq (zero-or-more ; obligatory arguments
+ (seq "{" (*? anything) "}"))
+ (zero-or-more ; optional arguments
+ (seq "[" (*? anything) "]" ))
+ (zero-or-more ; obligatory arguments
+ (seq "{" (*? anything) "}"))))
+ "Regexp matching argument part of LaTeX macros.")
+
+(defconst latex-table-wizard--macro-re
+ (concat "\\\\"
+ ;; latex-table-wizard--macro-prefix-re
+ (rx (group-n 1 (one-or-more alnum)))
+ latex-table-wizard--macro-args-re)
+ "Regexp matching unescaped LaTeX macros.
+
+Capture group 1 matches the name of the macro.")
+
+(defconst latex-table-wizard-column-delimiters '("[^\\\\]&")
+ "List of regexps matching column delimiters.")
+
+(defconst latex-table-wizard-row-delimiters '("\\\\\\\\")
+ "List of regexps matching row delimiters.")
+
+(defvar latex-table-wizard-hline-macros '("cline"
+ "vline"
+ "midrule"
+ "hline"
+ "toprule"
+ "bottomrule")
+ "Name of macros that draw horizontal lines.
+
+Each member of this list is a string that would be between the
+\"\\\" and the arguments.")
+
+
+;; Every time latex-table-wizard--parse-table is evaluated, the values
+;; of the variables below is set:
+(defvar latex-table-wizard--current-col-delims nil)
+(defvar latex-table-wizard--current-row-delims nil)
+(defvar latex-table-wizard--current-hline-macros nil)
+
+(defvar latex-table-wizard-new-environments-alist nil
+ "Alist mapping environment names to property lists.
+
+The environment name is a string, for example \"foo\" for an
+environment like
+
+ \\begin{foo}
+ ...
+ \\end{foo}
+
+The cdr of each mapping is a property list with three keys:
+
+ :col
+ :row
+ :lines
+
+The values for :col and :row are two lists of regexps.
+
+The value for :lines is a list of strings just like is the case
+for `latex-table-wizard-hline-macros'.")
+
+(defsubst latex-table-wizard--set-current-values ()
+ "Set temporary values that specify the syntax of the environment.
+
+If the current environment is one that is mapped to something in
+`latex-table-wizard-new-environments', set the values accordingly."
+ (if-let ((vals (cdr (assoc (LaTeX-current-environment)
+ latex-table-wizard-new-environments-alist))))
+ (setq latex-table-wizard--current-col-delims
+ (plist-get (cdr (assoc (LaTeX-current-environment)
+ latex-table-wizard-new-environments-alist))
+ :col)
+
+ latex-table-wizard--current-row-delims
+ (plist-get (cdr (assoc (LaTeX-current-environment)
+ latex-table-wizard-new-environments-alist))
+ :row)
+
+ latex-table-wizard--current-hline-macros
+ (plist-get (cdr (assoc (LaTeX-current-environment)
+ latex-table-wizard-new-environments-alist))
+ :lines))
+ (setq latex-table-wizard--current-col-delims
+ latex-table-wizard-column-delimiters
+
+ latex-table-wizard--current-row-delims
+ latex-table-wizard-row-delimiters
+
+ latex-table-wizard--current-hline-macros
+ latex-table-wizard-hline-macros)))
+
+
+
+;;; Parsing
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; The central data structure is the CELL, which is a plist with ;;
+;; four keys: ;;
+;; ;;
+;; + :column (column number, starting from 0 as the leftmost column) ;;
+;; + :row (row number, starting from 0 as the top row) ;;
+;; + :start (marker, beginning of inside of the cell) ;;
+;; + :end (marker, end of inside of the cell) ;;
+;; ;;
+;; A parse of a table is a list of all its cells represented as such ;;
+;; plists. ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defsubst latex-table-wizard--end-of-macro (&optional name)
+ "If looking at unescaped macro named NAME, go to its end.
+
+If NAME is nil, skip any LaTeX macro that point is looking at."
+ (let* ((n (or name (rx (one-or-more alnum))))
+ (macro-re (concat "\\\\" n latex-table-wizard--macro-args-re)))
+ (unless (looking-back "[^\\\\]\\\\")
+ (when (looking-at macro-re)
+ (match-end 0)))))
+
+(defsubst latex-table-wizard--get-cell-boundaries (col-re
+ row-re
+ &optional limit)
+ "Return boundaries of current cell (where point is).
+
+What is returned is a list of the form
+
+ (B E EOR)
+
+where B and E are markers (beginning and end of the cell), and
+EOR is t iff this cell is the rightmost cell in the current row,
+nil otherwise.
+
+COL-RE and ROW-RE are regular expressions matching column and row
+delimiters respectively.
+
+LIMIT is a buffer position at which the parsing stops, and
+defaults to `point-max' if nothing else is passed as the
+argument."
+ (let ((lim (or limit (point-max)))
+ (beg (point-marker))
+ (end)
+ (end-of-row))
+ (while (and (< (point) lim) (not end))
+ (cond ((and (looking-at "%")
+ (not (looking-back "[^\\\\]\\\\")))
+ ;; the first step is important to avoid being fooled by
+ ;; column or row delimiters in comments!
+ (forward-line))
+ ((looking-at "[[:space:]]*\\\\begin{[[:alnum:]]+}")
+ (skip-syntax-forward " ")
+ (forward-char 1)
+ (LaTeX-find-matching-end))
+ ((looking-at (concat "[[:space:]]*" latex-table-wizard--macro-re))
+ (goto-char (match-end 0)))
+ ((looking-at col-re)
+ ;; a column delimiter: bingo
+ (goto-char (match-beginning 0))
+ (setq end (point-marker))
+ (goto-char (match-end 0)))
+ ((looking-at row-re)
+ ;; a row delimiter: bingo
+ (let ((end-of-previous-cell
+ (progn (goto-char (1- (match-beginning 0)))
+ (point-marker))))
+ (goto-char (match-end 0))
+ (setq end end-of-previous-cell
+ end-of-row t)
+ (latex-table-wizard--skip-stuff lim)))
+ (t
+ ;; nothing special, just go one step forward
+ (forward-char 1))))
+ `(,beg ,end ,end-of-row)))
+
+(defsubst latex-table-wizard--get-env-ends (table)
+ "Given TABLE, return beginning and end of the environemnt.
+
+TABLE is a list of cell plists. The return type is a cons
+cell (B . E) with B and E being markers."
+ (cons (apply #'min (mapcar (lambda (x) (plist-get x :start)) table))
+ (apply #'max (mapcar (lambda (x) (plist-get x :end)) table))))
+
+(defvar-local latex-table-wizard--parsed-table-delims nil)
+
+(defsubst latex-table-wizard--disjoin (str-list &optional sep)
+ "Concatenate strings in STR-LIST with separtor SEP.
+
+If SEP is nil, the separator used is \"\\\\|\""
+ (let ((separator (or sep "\\|")))
+ (mapconcat #'identity str-list separator)))
+
+(defsubst latex-table-wizard--skip-stuff (limit)
+ "Skip comments, blank space and hline macros.
+
+Hline macros are LaTeX macros whose name is a string in
+`latex-table-wizard--current-hline-macros'.
+
+Stop the skipping at LIMIT (a buffer position or a marker)."
+ (let ((done)
+ (new-start-of-line))
+ (catch 'stop
+ (while (and (not done) (<= (point) limit))
+ (skip-syntax-forward " ")
+ (let ((temp-pos (point)))
+ (when (looking-at "\n\\|%")
+ (forward-line)
+ (setq new-start-of-line (point))
+ (when (looking-at (concat
+ "[[:space:]]*"
+ (latex-table-wizard--disjoin
+ latex-table-wizard--current-col-delims)))
+ (throw 'stop nil)))
+ (ignore-errors
+ (goto-char (latex-table-wizard--end-of-macro
+ (concat
+ (regexp-opt
+ latex-table-wizard--current-hline-macros)
+ latex-table-wizard--macro-args-re))))
+ (when (looking-at "\n\\|%")
+ (forward-line)
+ (setq new-start-of-line (point)))
+ (when (eq (point) temp-pos)
+ ;; we haven't moved since trying to skip whitespace, we are
+ ;; done here.
+ (setq done t)))))
+ (when new-start-of-line (goto-char new-start-of-line))))
+
+;; (defsubst latex-table-wizard--contentless-string-p (beg end)
+;; (let ((str (buffer-substring-no-properties beg end))
+;; (hline-macros (concat "[^\\\\]\\\\"
+;; (regexp-opt
+;; latex-table-wizard--current-hline-macros)
+;; latex-table-wizard--macro-args-re)))
+;; (thread-last
+;; str
+;; (replace-regexp-in-string "[^\\\\]%.*$" "")
+;; (replace-regexp-in-string hline-macros "")
+;; (string-blank-p))))
+
+(defun latex-table-wizard--parse-table ()
+ "Parse table(-like) environment point is in.
+
+Return a list of plists, each of which is a cells and has the
+form
+
+ (:column C :row R :start S :end E).
+
+Each value is an integer, S and E are markers."
+ (let* ((cells-list '())
+ (col 0)
+ (row 0)
+ (env-beg (save-excursion
+ (LaTeX-find-matching-begin)
+ (goto-char (latex-table-wizard--end-of-macro))
+ (point-marker)))
+ (env-end (save-excursion
+ (LaTeX-find-matching-end)
+ (re-search-backward (concat
+ "\\\\"
+ (rx (one-or-more alnum))
+ latex-table-wizard--macro-args-re)
+ nil t)
+ (forward-char -1)
+ (point-marker)))
+ (done))
+ (latex-table-wizard--set-current-values)
+ (save-excursion
+ (goto-char env-beg)
+ (while (< (point) env-end)
+ ;; (latex-table-wizard--skip-stuff env-end)
+ (when (looking-at-p "[[:space:]]*\\($\\|%\\)")
+ ;; nothing interesting left between point and eol
+ (forward-line))
+ (let* ((col-re
+ (latex-table-wizard--disjoin
+ latex-table-wizard--current-col-delims))
+ (row-re (latex-table-wizard--disjoin
+ latex-table-wizard--current-row-delims))
+ (hline-macro-re (concat
+ (regexp-opt
+ latex-table-wizard--current-hline-macros)
+ latex-table-wizard--macro-args-re))
+ (data (latex-table-wizard--get-cell-boundaries
+ col-re row-re env-end)))
+ (push `( :column ,col
+ :row ,row
+ :start ,(nth 0 data)
+ :end ,(if (nth 1 data) (nth 1 data) env-end))
+ cells-list)
+ (if (nth 2 data) ; this was the last cell in the row
+ (setq row (1+ row)
+ col 0)
+ (setq col (1+ col)))
+ ;; if we just hit the end of a row and the next thing coming
+ ;; is another row delimiter, skip that one (because you are
+ ;; not in a cell)
+ (while (and (nth 2 data)
+ (save-excursion
+ (skip-syntax-forward " ")
+ (looking-at-p row-re)))
+ (re-search-forward row-re nil t)))))
+ (setq latex-table-wizard--parsed-table-delims
+ (latex-table-wizard--get-env-ends cells-list))
+ cells-list))
+
+(defsubst latex-table-wizard--get-cell-pos (column row table)
+ "Return the cell plist from TABLE at (COLUMN, ROW) position."
+ (car (cl-remove-if-not
+ (lambda (x) (and (eq column (plist-get x :column))
+ (eq row (plist-get x :row))))
+ table)))
+
+(defsubst latex-table-wizard--sort (dir x y)
+ "Return t if cell X precedes Y.
+
+Precedence depends on the value of DIR (either 'next',
+'previous', 'forward' or 'backward')."
+ (let ((rows (list (plist-get x :row) (plist-get y :row)))
+ (cols (list (plist-get x :column) (plist-get y :column))))
+ (if (or (eq dir 'next) (eq dir 'previous))
+ (if (apply #'eq cols)
+ (apply #'< rows)
+ (apply #'< cols))
+ (if (apply #'eq rows)
+ (apply #'< cols)
+ (apply #'< rows)))))
+
+(defsubst latex-table-wizard--get-extreme (dir table current-cell)
+ "Return the last cell in a certain row or cell from TABLE.
+
+The goal is to get to the last cell in the same row or same
+column as CURRENT-CELL.
+
+Whether to look for the last column or row depends on the value
+of DIR (either 'next', 'previous', 'forward' or 'backward')."
+ (let* ((prop (if (or (eq dir 'backward) (eq dir 'forward)) :column :row))
+ (curr-prop (if (eq prop :column) :row :column))
+ (curr-value (plist-get current-cell curr-prop))
+ (res (if (or (eq dir 'backward) (eq dir 'previous))
+ 0
+ (thread-last
+ table
+ (cl-remove-if-not
+ (lambda (x) (eq (plist-get x curr-prop) curr-value)))
+ (mapcar (lambda (x) (plist-get x prop)))
+ (apply 'max)))))
+ (thread-last table
+ (cl-remove-if-not
+ (lambda (x) (and (eq (plist-get x curr-prop) curr-value)
+ (eq (plist-get x prop) res))))
+ (car))))
+
+(defsubst latex-table-wizard--point-on-regexp-p (regexp
+ &optional capture-group
+ search-beginning-pos)
+ "Return non-nil if point is on a substring matched by REGEXP.
+
+If CAPTURE-GROUP is non-nil, limit the condition to the substring
+matched by the corresponding capture group. If CAPTURE-GROUP is
+nil, it defaults to 0.
+
+What is returned is a list of the form
+
+ (S B E)
+
+where S is the substring matched, and B and E are the buffer
+position corresponding to the beginning and the end of such
+substring.
+
+Start the search from SEARCH-BEGINNING-POS (a buffer position or
+marker): if this argument is nil, start the search from the
+beginning of the available portion of the buffer."
+ (let ((position (point))
+ (group (or capture-group 0))
+ (search-b (or search-beginning-pos (point-min))))
+ (save-match-data
+ (save-excursion
+ (goto-char search-b)
+ (catch 'found
+ (while (re-search-forward regexp nil t)
+ (when (<= (match-beginning group) position (match-end group))
+ (throw 'found (list (match-string-no-properties group)
+ (match-beginning group)
+ (match-end group))))
+ nil))))))
+
+
+
+;;; Moving around
+
+(defun latex-table-wizard--get-other-cell (dir same-line count table curr)
+ "Return cell plist from TABLE.
+
+The cell that is returned is the one found moving COUNT cells
+from current cell CURR in direction DIR (either 'forward',
+'backward', 'next' or 'previous').
+
+If SAME-LINE is non-nil, loop over the current row (if DIR is
+'forward' or 'backward'), or column (if DIR is 'next' or
+'previous'). Otherwise continue search for cell in a different
+row or column if no cell is left in the current DIR."
+ (let* ((sorted (if same-line
+ (let* ((prop (if (or (eq dir 'forward)
+ (eq dir 'backward))
+ :row
+ :column))
+ (other-prop (if (eq prop :row) :column :row)))
+ (sort (cl-remove-if-not
+ (lambda (x) (eq (plist-get x prop)
+ (plist-get curr prop)))
+ table)
+ (lambda (x y) (< (plist-get x other-prop)
+ (plist-get y other-prop)))))
+ (sort table
+ (lambda (x y)
+ (latex-table-wizard--sort dir x y)))))
+ (cell-num (length sorted))
+ (now (cl-position curr sorted :test 'equal))
+ (new-index (if (or (eq dir 'next)
+ (eq dir 'forward))
+ (+ now count)
+ (- now count))))
+ (cond ((and (>= new-index 0)
+ (> cell-num new-index))
+ ;; we are staying inside of the list
+ (nth new-index sorted))
+ ((<= cell-num new-index)
+ ;; we have to start from the top left corner of the table
+ (nth (1- (- new-index cell-num)) sorted))
+ ((< new-index 0)
+ ;; we have to start from the bottom right corner of the table
+ (nth (- cell-num (abs new-index)) sorted))
+ (t nil))))
+
+
+(defsubst latex-table-wizard--remove-overlays (&optional table beg end)
+ "Remove table internal overlays generated by latex-table-wizard.
+
+These are the overlays that have a non-nil value for the name
+property 'table-inside-ol'.
+
+Optional arguments BEG and END are passed, they are buffer
+positions or markers indicating beginning and end of the table.
+
+Optional arguments TABLE is a list of cell plists: if its not
+given, a value is retrieved with
+`latex-table-wizard--parse-table'."
+ (if beg
+ (remove-overlays beg end 'tabl-inside-ol t)
+ (let* ((tab (or table (latex-table-wizard--parse-table)))
+ (lims (latex-table-wizard--get-env-ends tab)))
+ (remove-overlays (car lims) (cdr lims) 'tabl-inside-ol t))))
+
+(defsubst latex-table-wizard--hl-cells (list-of-cells)
+ "Highlight cells in LIST-OF-CELLS with an overlay.
+
+The overlay has a non-nil value for the name property
+'table-inside-ol'."
+ (let ((ols '()))
+ (dolist (x list-of-cells)
+ (push (make-overlay (plist-get x :start)
+ (plist-get x :end))
+ ols))
+ (dolist (x ols)
+ (overlay-put x 'tabl-inside-ol t)
+ (overlay-put x 'face
+ `((t (:background ,(face-attribute 'region
+ :background))))))))
+
+(defun latex-table-wizard--jump (dir &optional absolute count same-line)
+ "Move point to the beginning of a cell in the table.
+
+DIR is either 'next', 'previous', 'forward' or 'backward' and
+determines the direction of motion. This function assumes being
+evaluated with point inside of a tabular-like environment.
+
+With ABSOLUTE being t, move to the last or first cell in the row
+or column (depending on the value of DIR) point is currently in.
+
+COUNT is a positive integer that determines how many steps in
+direction DIR to take.
+
+If SAME-LINE is non-nil, never leave current column or row."
+ (unless (ignore-errors (save-excursion (LaTeX-find-matching-begin)))
+ (user-error "Not in a LaTeX environment"))
+ (when-let ((macro-at-point
+ (latex-table-wizard--point-on-regexp-p
+ latex-table-wizard--macro-re
+ 0 (line-beginning-position))))
+ (cond ((string-prefix-p "\\begin" (nth 0 macro-at-point))
+ (goto-char (nth 1 macro-at-point)))
+ ((string-prefix-p "\\end" (nth 0 macro-at-point))
+ (goto-char (nth 2 macro-at-point)))))
+ (let* ((cells (latex-table-wizard--parse-table))
+ (curr (latex-table-wizard--get-thing 'cell cells))
+ (target (if absolute
+ (latex-table-wizard--get-extreme dir cells curr)
+ (latex-table-wizard--get-other-cell
+ dir same-line count cells curr))))
+ (latex-table-wizard--remove-overlays cells)
+ (goto-char (plist-get target :start))
+ (latex-table-wizard--hl-cells (list target))
+ (latex-table-wizard--hl-cells latex-table-wizard--selection)))
+
+(defsubst latex-table-wizard--locate-point (pos table)
+ "Return cell from TABLE in which position POS is in.
+
+POS is a buffer position or a marker."
+ (let ((candidate (car (cl-remove-if-not
+ (lambda (x) (<= (plist-get x :start)
+ pos
+ (plist-get x :end)))
+ table)))
+ (ends (latex-table-wizard--get-env-ends table)))
+ (cond (candidate
+ candidate)
+ ((< pos (car ends))
+ (latex-table-wizard--get-cell-pos 0 0 table))
+ ((> pos (cdr ends))
+ (car (cl-remove-if-not
+ (lambda (x) (eq (plist-get x :end) (cdr ends)))
+ table)))
+ (t (goto-char (apply #'max
+ (mapcar (lambda (x) (plist-get x :start))
+ (cl-remove-if-not
+ (lambda (x) (< (plist-get x :end) pos))
+ table))))))))
+
+(defsubst latex-table-wizard--get-thing (thing &optional table)
+ "Return THING point is in.
+
+THING can be either 'cell', 'column' or 'row.
+
+TABLE is a list of cell plists. If it is nil, evaluate
+`latex-table-wizard--parse-table' to get a value.
+
+If THING is 'cell', return one plist, else return a list of
+plists."
+ (let* ((pos (point))
+ (cells-list (or table (latex-table-wizard--parse-table)))
+ (curr (latex-table-wizard--locate-point pos cells-list)))
+ (if (eq thing 'cell)
+ curr
+ (let* ((prop (if (eq thing 'row) :row :column))
+ (other-prop (if (eq thing 'row) :column :row))
+ (curr-value (plist-get curr prop)))
+ (sort (cl-remove-if-not (lambda (x) (eq curr-value (plist-get x prop)))
+ cells-list)
+ (lambda (x y) (> (plist-get x other-prop)
+ (plist-get x other-prop))))))))
+
+
+
+;;; Swapping functions
+
+(defsubst latex-table-wizard--swap-substrings (x y)
+ "Swap two buffer substrings.
+
+X and Y are each a list of the form '(B E)', where B and E are
+markers corresponding to the beginning and the end of the buffer
+substring."
+ (save-excursion
+ (let ((x-string (concat " "
+ (string-trim
+ (apply #'buffer-substring x))
+ " "))
+ (y-string (concat " "
+ (string-trim
+ (apply #'buffer-substring y))
+ " ")))
+ (goto-char (nth 1 x))
+ (apply #'delete-region x)
+ (insert y-string)
+ (just-one-space)
+ (goto-char (nth 1 y))
+ (apply #'delete-region y)
+ (insert x-string)
+ (just-one-space))))
+
+(defsubst latex-table-wizard--swap-cells (x y)
+ "Evaluate `latex-table-wizard--swap-substrings' on cells X and Y."
+ (latex-table-wizard--swap-substrings (list (plist-get x :start)
+ (plist-get x :end))
+ (list (plist-get y :start)
+ (plist-get y :end))))
+
+(defsubst latex-table-wizard--get-this-value-prop (line prop value)
+ "Return the cell from list LINE that has VALUE for property PROP."
+ (car (cl-remove-if-not (lambda (x) (eq value (plist-get x prop))) line)))
+
+(defsubst latex-table-wizard--type-of-selection (sel)
+ "Return type of list of cells SEL.
+
+Non-nil values that are returned are is either 'cell' (if SEL
+only contains one cell), 'column' or 'row'.
+
+If SEL is a list of more than one cell such that not all the
+cells have the same value for either :column or :row, it means
+that this selection is neither a column or a row, and nil is
+returned."
+ (cond ((eq 1 (length sel))
+ 'cell)
+ ((thread-last sel
+ (mapcar (lambda (x) (plist-get x :column)))
+ (delete-dups)
+ (length)
+ (eq 1))
+ 'column)
+ ((thread-last sel
+ (mapcar (lambda (x) (plist-get x :row)))
+ (delete-dups)
+ (length)
+ (eq 1))
+ 'row)
+ (t nil)))
+
+(defun latex-table-wizard--swap-line (type line1 line2)
+ "Swap columns or rows LINE1 and LINE2.
+
+TYPE is either 'column' or 'row'."
+ (save-excursion
+ (let ((prop (if (eq type 'column) :row :column)))
+ (dolist (x line1)
+ (let ((other (latex-table-wizard--get-this-value-prop
+ line2 prop (plist-get x prop))))
+ (latex-table-wizard--swap-cells x other))))))
+
+
+
+;; Swap columns and rows
+
+(defun latex-table-wizard--swap-adjacent-line (dir type)
+ "Swap current thing of type TYPE with the one in direction DIR.
+
+DIR is either 'forward', 'backward', 'next' or 'previous'.
+
+TYPE is either 'cell', 'column' or 'row'."
+ (let* ((table (latex-table-wizard--parse-table))
+ (current-cell (latex-table-wizard--get-thing 'cell table))
+
+ (other-cell (latex-table-wizard--get-other-cell
+ dir t 1 table current-cell)))
+ (if (eq type 'cell)
+ (latex-table-wizard--swap-cells current-cell other-cell)
+ (let* ((current (latex-table-wizard--get-thing type table))
+ (other (cl-loop for x in current
+ collect
+ (latex-table-wizard--get-other-cell
+ dir t 1 table x))))
+ (latex-table-wizard--swap-line type current other)))
+ (let ((new-table (latex-table-wizard--parse-table)))
+ (goto-char (plist-get (latex-table-wizard--get-cell-pos
+ (plist-get other-cell :column)
+ (plist-get other-cell :row)
+ new-table)
+ :start))
+ (latex-table-wizard--remove-overlays new-table)
+ (if (eq type 'cell)
+ (latex-table-wizard--hl-cells
+ (list (latex-table-wizard--get-thing type new-table)))
+ (latex-table-wizard--hl-cells
+ (latex-table-wizard--get-thing type new-table))))))
+
+(defvar latex-table-wizard--selection nil
+ "Current selection, a list of cell objects.")
+
+(defun latex-table-wizard--select-thing (thing)
+ "Add THING point is at to list `latex-table-wizard--selection'.
+
+THING is either 'cell', 'column' or 'row'."
+ (let* ((table (latex-table-wizard--parse-table))
+ (sel (latex-table-wizard--get-thing thing table)))
+ (if (eq thing 'cell)
+ (setq latex-table-wizard--selection
+ (cons sel latex-table-wizard--selection))
+ (setq latex-table-wizard--selection sel))
+ (message (format "%s selected for swapping"
+ (cond ((eq thing 'cell)
+ (format "Cell (%s,%s)"
+ (plist-get sel :column)
+ (plist-get sel :row)))
+ ((eq thing 'row)
+ (format "Row %s"
+ (plist-get (car sel) :row)))
+ (t
+ (format "Column %s"
+ (plist-get (car sel) :column))))))
+ (if (eq thing 'cell)
+ (latex-table-wizard--hl-cells (list sel))
+ (latex-table-wizard--hl-cells sel))))
+
+
+
+;;; Interactive functions
+
+;;;###autoload
+(defun latex-table-wizard-right (&optional n)
+ "Move point N cells to the right.
+
+Leave point at the beginning of the cell.
+
+If N is nil, move one cell to the right.
+
+If there is no cell to the right of where point is, move to the
+leftmost cell of the row below where point is."
+ (interactive "p")
+ (latex-table-wizard--jump 'forward nil n))
+
+;;;###autoload
+(defun latex-table-wizard-left (&optional n)
+ "Move point N cells to the left.
+
+Leave point at the beginning of the cell.
+
+If N is nil, move one cell to the left.
+
+If there is no cell to the left of where point is, move to the
+rightmost cell of the row above where point is."
+ (interactive "p")
+ (latex-table-wizard--jump 'backward nil n))
+
+;;;###autoload
+(defun latex-table-wizard-down (&optional n)
+ "Move point N cells down.
+
+Leave point at the beginning of the cell.
+
+If N is nil, move one row down.
+
+If there is no row below where point is, move to the top cell of
+the column to the right of where point is."
+ (interactive "p")
+ (latex-table-wizard--jump 'next nil n))
+
+;;;###autoload
+(defun latex-table-wizard-up (&optional n)
+ "Move point N cells up.
+
+Leave point at the beginning of the cell.
+
+If N is nil, move one row up.
+
+If there is no row above where point is, move to the bottom cell
+of the column to the left of where point is."
+ (interactive "p")
+ (latex-table-wizard--jump 'previous nil n))
+
+;;;###autoload
+(defun latex-table-wizard-end-of-row ()
+ "Move point to the rightmost cell in current row."
+ (interactive)
+ (latex-table-wizard--jump 'forward t))
+
+;;;###autoload
+(defun latex-table-wizard-beginning-of-row ()
+ "Move point to the leftmost cell in current row."
+ (interactive)
+ (latex-table-wizard--jump 'backward t))
+
+;;;###autoload
+(defun latex-table-wizard-bottom ()
+ "Move point to the bottom cell in current column."
+ (interactive)
+ (latex-table-wizard--jump 'next t))
+
+;;;###autoload
+(defun latex-table-wizard-top ()
+ "Move point to the top cell in current column."
+ (interactive)
+ (latex-table-wizard--jump 'previous t))
+
+;;;###autoload
+(defun latex-table-wizard-end-of-cell ()
+ "Move point to the end of the current cell."
+ (interactive)
+ (let ((cell (latex-table-wizard--get-thing 'cell)))
+ (goto-char (plist-get cell :end))))
+
+;;;###autoload
+(defun latex-table-wizard-beginning-of-cell ()
+ "Move point to the beginning of the current cell."
+ (interactive)
+ (let ((cell (latex-table-wizard--get-thing 'cell)))
+ (goto-char (plist-get cell :start))))
+
+;;;###autoload
+(defun latex-table-wizard-mark-cell ()
+ "Mark current cell.
+
+TABLE is a list of cell plists. If it is nil, evaluate
+`latex-table-wizard--parse-table' to get a value."
+ (interactive)
+ (let* ((cells-list (or table (latex-table-wizard--parse-table)))
+ (cell (latex-table-wizard--get-thing 'cell cells-list)))
+ (push-mark (plist-get cell :start) nil t)
+ (goto-char (plist-get cell :end))))
+
+;;;###autoload
+(defun latex-table-wizard-swap-column-right ()
+ "Swap current column and the one to the right."
+ (interactive)
+ (latex-table-wizard--swap-adjacent-line 'forward 'column))
+
+;;;###autoload
+(defun latex-table-wizard-swap-column-left ()
+ "Swap current column and the one to the left."
+ (interactive)
+ (latex-table-wizard--swap-adjacent-line 'backward 'column))
+
+;;;###autoload
+(defun latex-table-wizard-swap-row-up ()
+ "Swap current row and the one above."
+ (interactive)
+ (latex-table-wizard--swap-adjacent-line 'previous 'row))
+
+;;;###autoload
+(defun latex-table-wizard-swap-row-down ()
+ "Swap current row and the one below."
+ (interactive)
+ (latex-table-wizard--swap-adjacent-line 'next 'row))
+
+;;;###autoload
+(defun latex-table-wizard-swap-cell-right ()
+ "Swap content of current cell and the one to the right."
+ (interactive)
+ (latex-table-wizard--swap-adjacent-line 'forward 'cell))
+
+;;;###autoload
+(defun latex-table-wizard-swap-cell-left ()
+ "Swap content of current cell and the one to the left."
+ (interactive)
+ (latex-table-wizard--swap-adjacent-line 'backward 'cell))
+
+;;;###autoload
+(defun latex-table-wizard-swap-cell-down ()
+ "Swap content of current cell and the one below."
+ (interactive)
+ (latex-table-wizard--swap-adjacent-line 'next 'cell))
+
+;;;###autoload
+(defun latex-table-wizard-swap-cell-up ()
+ "Swap content of current cell and the one above."
+ (interactive)
+ (latex-table-wizard--swap-adjacent-line 'previous 'cell))
+
+;;;###autoload
+(defun latex-table-wizard-insert-column ()
+ "Insert empty column to the right of the one at point."
+ (interactive)
+ (save-excursion
+ (let* ((table (latex-table-wizard--parse-table))
+ (current-cell (latex-table-wizard--get-thing 'cell table))
+ (current-column (latex-table-wizard--get-thing 'column table)))
+ (dolist (x current-column)
+ (goto-char (plist-get x :end))
+ (insert " & ")))))
+
+;;;###autoload
+(defun latex-table-wizard-kill-column ()
+ "Kill content of column at point."
+ (interactive)
+ (save-excursion
+ (let* ((table (latex-table-wizard--parse-table))
+ (current-cell (latex-table-wizard--get-thing 'cell table))
+ (current-column (latex-table-wizard--get-thing 'column table))
+ (kills '()))
+ (dolist (x current-column)
+ (let* ((b (plist-get x :start))
+ (next (latex-table-wizard--get-other-cell
+ 'forward t 1 table x))
+ (e (plist-get next :start)))
+ (push (buffer-substring b e) kills)
+ (delete-region b e)))
+ (kill-region (latex-table-wizard--disjoin
+ (nreverse kills) "\n")))))
+
+;;;###autoload
+(defun latex-table-wizard-insert-row ()
+ "Insert empty row below the one at point."
+ (interactive)
+ (save-excursion
+ (let* ((table (latex-table-wizard--parse-table))
+ (current-cell (latex-table-wizard--get-thing 'cell table))
+ (current-row (latex-table-wizard--get-thing 'row table))
+ (last-in-row (latex-table-wizard--get-extreme 'forward
+ table
+ current-cell)))
+ (goto-char (plist-get last-in-row :end))
+ (if (looking-at (concat "[[:space:]]*\\\\\\\\"))
+ (goto-char (match-end 0))
+ (insert "\\\\"))
+ (insert "\n")
+ (let ((how-many (length current-row)))
+ (dotimes (i (1- how-many))
+ (insert " &"))
+ (insert " \\\\")))))
+
+;;;###autoload
+(defun latex-table-wizard-kill-row ()
+ "Kill row at point."
+ (interactive)
+ (save-excursion
+ (let* ((table (latex-table-wizard--parse-table))
+ (current-cell (latex-table-wizard--get-thing 'cell table))
+ (current-row (latex-table-wizard--get-thing 'row table)))
+ (thread-last (latex-table-wizard--get-env-ends current-row)
+ (apply #'buffer-substring)
+ (kill-region)))))
+
+;;;###autoload
+(defun latex-table-wizard-select-cell ()
+ "Add cell at point to selection for swapping."
+ (interactive)
+ (latex-table-wizard--select-thing 'cell))
+
+;;;###autoload
+(defun latex-table-wizard-select-row ()
+ "Add row at point to selection for swapping."
+ (interactive)
+ (latex-table-wizard--select-thing 'row))
+
+;;;###autoload
+(defun latex-table-wizard-select-column ()
+ "Add column at point to selection for swapping."
+ (interactive)
+ (latex-table-wizard--select-thing 'column))
+
+(defun latex-table-wizard-deselect-cell ()
+ "Remove cell at point from selection for swapping."
+ (interactive)
+ (let* ((table (latex-table-wizard--parse-table))
+ (curr-cell (latex-table-wizard--get-thing 'cell table)))
+ (when (member curr-cell latex-table-wizard--selection))
+ (latex-table-wizard--remove-overlays nil
+ (plist-get curr-cell :start)
+ (plist-get curr-cell :end))
+ (setq latex-table-wizard--selection
+ (remove curr-cell latex-table-wizard--selection))))
+
+(defun latex-table-wizard-deselect-row ()
+ "Remove row at point from selection for swapping."
+ (interactive)
+ (latex-table-wizard--remove-overlays)
+ (setq latex-table-wizard--selection nil))
+
+(defun latex-table-wizard-deselect-column ()
+ "Remove column at point from selection for swapping."
+ (interactive)
+ (latex-table-wizard--remove-overlays)
+ (setq latex-table-wizard--selection nil))
+
+;;;###autoload
+(defun latex-table-wizard-swap ()
+ "Swap selection and thing at point.
+
+Selection is the current value of
+`latex-table-wizard--selection'. Depending on whether it is a
+cell, a column or a row, swap that with the cell, column or row
+at point. If it is none of those object, return nil."
+ (interactive)
+ (unless latex-table-wizard--selection
+ (user-error "Select thing to swap first"))
+ (let* ((table (latex-table-wizard--parse-table))
+ (other latex-table-wizard--selection)
+ (type (latex-table-wizard--type-of-selection other))
+ (current (latex-table-wizard--get-thing type table))
+ (land-coord (cons (plist-get current :column)
+ (plist-get current :row))))
+ (cond ((not type)
+ (latex-table-wizard--cleanup)
+ (setq latex-table-wizard--selection nil))
+ ((eq type 'cell)
+ (latex-table-wizard--swap-cells (car other) current))
+ (t
+ (latex-table-wizard--swap-line type other current)))
+ (latex-table-wizard--remove-overlays table)
+ (latex-table-wizard--hl-cells other)
+ (setq latex-table-wizard--selection nil)))
+
+
+
+;;; Transient
+
+(defconst latex-table-wizard--motion-suffixes
+ '(("f" "move right" latex-table-wizard-right :transient t)
+ ("b" "move left" latex-table-wizard-left :transient t)
+ ("p" "move down" latex-table-wizard-up :transient t)
+ ("n" "move up" latex-table-wizard-down :transient t)
+ ""
+ ("F" "end of row" latex-table-wizard-end-of-row :transient t)
+ ("B" "beginning of row" latex-table-wizard-beginning-of-row :transient t)
+ ("P" "top" latex-table-wizard-top :transient t)
+ ("N" "bottom" latex-table-wizard-bottom :transient t)
+ ""
+ ("a" "beginning of cell" latex-table-wizard-beginning-of-cell :transient t)
+ ("e" "end of cell" latex-table-wizard-end-of-cell :transient t)))
+
+(defconst latex-table-wizard--mark-suffixes
+ '(("x" "exchange point and mark" exchange-point-and-mark :transient t)
+ ("m c" "mark cell" latex-table-wizard-mark-cell :transient t)
+ ("i c" "insert column right" latex-table-wizard-insert-column :transient t)
+ ("i r" "insert row below" latex-table-wizard-insert-row :transient t)
+ ("k c" "kill current column" latex-table-wizard-kill-column :transient t)
+ ("k r" "kill current row" latex-table-wizard-kill-row :transient t)))
+
+(defconst latex-table-wizard--swap-cell-suffixes
+ '(("C-f" "swap cell right" latex-table-wizard-swap-cell-right :transient t)
+ ("C-b" "swap cell left" latex-table-wizard-swap-cell-left :transient t)
+ ("C-p" "swap cell up" latex-table-wizard-swap-cell-up :transient t)
+ ("C-n" "swap cell down" latex-table-wizard-swap-cell-down :transient t)))
+
+(defconst latex-table-wizard--swap-line-suffixes
+ '(("M-f" "swap column right" latex-table-wizard-swap-column-right :transient
t)
+ ("M-b" "swap column left" latex-table-wizard-swap-column-left :transient t)
+ ("M-p" "swap row up" latex-table-wizard-swap-row-up :transient t)
+ ("M-n" "swap row down" latex-table-wizard-swap-row-down :transient t)))
+
+(defconst latex-table-wizard--select-suffixes
+ '(("SPC" "select cell" latex-table-wizard-select-cell :transient t)
+ ("c" "select column" latex-table-wizard-select-column :transient t)
+ ("r" "select row" latex-table-wizard-select-row :transient t)
+ ("d SPC" "deselect cell" latex-table-wizard-deselect-cell :transient t)
+ ("d c" "select column" latex-table-wizard-deselect-column :transient t)
+ ("d r" "select row" latex-table-wizard-deselect-row :transient t)
+ ("s" "swap selection" latex-table-wizard-swap :transient t)))
+
+(defconst latex-table-wizard--other-suffixes
+ '(("/" "undo" undo :transient t)
+ ("u" "universal argument" universal-argument :transient t)
+ ("RET" "done" transient-quit-one)))
+
+;;;###autoload
+(transient-define-prefix latex-table-wizard-do ()
+ [:description " LaTeX table wizard"
+ ["Motion" latex-table-wizard--motion-suffixes]
+ ["Swap"
+ latex-table-wizard--swap-cell-suffixes
+ ""
+ latex-table-wizard--swap-line-suffixes
+ ""
+ "Other"
+ latex-table-wizard--other-suffixes]
+ ["Select and swap"
+ latex-table-wizard--select-suffixes
+ ""
+ "Mark, kill and insert" latex-table-wizard--mark-suffixes]])
+
+(defvar latex-table-wizard-bindings-alist nil
+ "Alist specifying keys in transient prefix `latex-table-wizard-do'.
+
+The members of this alist are cons cells of the form:
+
+ (C . K)
+
+where C is the name of an interactive function and K is a string
+specifying the key (same syntax that `kbd' takes as input).
+
+For example adding this cons cell makes the key 'l' bound to
+`latex-table-wizard-right' in the transient prefix
+`latex-table-wizard-do':
+
+ (latex-table-wizard-right . \"l\")")
+
+(defun latex-table-wizard-modify-bindings (&optional comm-keys-alist)
+ "Modify bindings in transient prefix `latex-table-wizard-do'.
+
+Modify according to the alist COMM-KEYS-ALIST: if nil is passed
+as an argument, use the value of
+`latex-table-wizard-bindings-alist'.
+
+The members of this alist are cons cells of the form:
+
+ (C . K)
+
+where C is the name of an interactive function and K is a string
+specifying the key (same syntax that `kbd' takes as input).
+
+For example adding this cons cell makes the key 'l' bound to
+`latex-table-wizard-right' in the transient prefix
+`latex-table-wizard-do':
+
+ (latex-table-wizard-right . \"l\")"
+ (macroexpand
+ (transient-define-prefix latex-table-wizard-do ()
+ [:description " LaTeX table wizard"
+ ["Motion" latex-table-wizard--motion-suffixes]
+ ["Swap"
+ latex-table-wizard--swap-cell-suffixes
+ ""
+ latex-table-wizard--swap-line-suffixes
+ ""
+ "Other"
+ latex-table-wizard--other-suffixes]
+ ["Select and swap"
+ latex-table-wizard--select-suffixes
+ ""
+ "Mark, kill and insert"
+ latex-table-wizard--mark-suffixes]]))
+ (let* ((new-alist (or comm-keys-alist latex-table-wizard-bindings-alist))
+ (all-commands
+ (list latex-table-wizard--motion-suffixes
+ latex-table-wizard--mark-suffixes
+ latex-table-wizard--swap-cell-suffixes
+ latex-table-wizard--swap-line-suffixes
+ latex-table-wizard--select-suffixes
+ latex-table-wizard--other-suffixes))
+ (def-command-key-alist '()))
+ (dolist (l all-commands)
+ (dolist (x l)
+ (when (listp x)
+ (push (cons (nth 2 x) (nth 0 x)) def-command-key-alist))))
+ (dolist (x new-alist)
+ (let* ((comm (car x))
+ (new-key (cdr x))
+ (def-key (alist-get comm def-command-key-alist)))
+ (transient-suffix-put 'latex-table-wizard-do def-key :key new-key)))))
+
+(advice-add #'latex-table-wizard-do
+ :before #'latex-table-wizard-modify-bindings)
+
+;;; Aesthetics
+
+(defface latex-table-wizard-background-face
+ '((t (:foreground "gray40")))
+ "Face for hiding non-table buffer content.")
+
+(defsubst latex-table-wizard--hide-rest ()
+ "Grey out parts of buffer outside of table at point."
+ (latex-table-wizard--parse-table)
+ (let* ((tab-b (car latex-table-wizard--parsed-table-delims))
+ (tab-e (cdr latex-table-wizard--parsed-table-delims))
+ (ols `(,(make-overlay (point-min) tab-b)
+ ,(make-overlay tab-e (point-max)))))
+ (dolist (x ols)
+ (overlay-put x 'tabl-outside-ol t)
+ (overlay-put x 'face 'latex-table-wizard-background-face))))
+
+(defun latex-table-wizard--cleanup ()
+ "Remove overlays created by 'latex-table-wizard' in the buffer."
+ (setq latex-table-wizard--selection nil)
+ (when-let ((lims latex-table-wizard--parsed-table-delims))
+ (remove-overlays (point-min) (point-max) 'tabl-inside-ol t)
+ (remove-overlays (point-min) (point-max) 'tabl-outside-ol t)))
+
+(defsubst latex-table-wizard--get-out ()
+ "If point is on column or row delimiter, move to its beginning."
+ (latex-table-wizard--set-current-values)
+ (when-let ((macro (latex-table-wizard--point-on-regexp-p
+ (latex-table-wizard--disjoin
+ (list (regexp-opt
+ (cl-union
+ latex-table-wizard--current-row-delims
+ latex-table-wizard--current-col-delims))
+ latex-table-wizard--macro-re))
+ 0 (line-beginning-position))))
+ (thread-last macro
+ (nth 1)
+ (1-)
+ (goto-char))))
+
+(advice-add #'latex-table-wizard-do :after #'latex-table-wizard--get-out)
+(advice-add #'latex-table-wizard-do :after #'latex-table-wizard--hide-rest)
+(advice-add #'transient-quit-one :before #'latex-table-wizard--cleanup)
+(add-hook 'before-save-hook #'latex-table-wizard--cleanup)
+
+(provide 'latex-table-wizard)
+
+;;; _
+;; Local Variables:
+;; indent-tabs-mode: nil
+;; End:
+
+;;; latex-table-wizard.el ends here
diff --git a/move-01.gif b/move-01.gif
new file mode 100644
index 0000000000..467c5bdb25
Binary files /dev/null and b/move-01.gif differ
diff --git a/readme.org b/readme.org
new file mode 100644
index 0000000000..e5a3c93e94
--- /dev/null
+++ b/readme.org
@@ -0,0 +1,245 @@
+#+TITLE: LaTeX table wizard - Magic editing of LaTeX tables
+#+SUBTITLE: for version {{{version}}}
+#+AUTHOR: Enrico Flor
+#+EMAIL: enrico@eflor.net
+
+#+OPTIONS: ':t toc:t author:t email:t
+#+MACRO: version 0.1.0
+#+MACRO: updated last updated 14 November 2022
+
+Copyright (C) 2022 Enrico Flor.
+
+ 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, with the Front-Cover Texts
+ being “A GNU Manual,” and with the Back-Cover Texts as in (a)
+ below. A copy of the license is included in the section entitled
+ “GNU Free Documentation License.”
+
+ (a) The FSF’s Back-Cover Text is: “You have the freedom to copy and
+ modify this GNU manual.”
+* Introduction
+
+One of org-mode's magic features is its table editing capabilities.
+The goal of this package is to replicate that luxury for LaTeX
+table(-like) environments.
+
+LaTeX-table-wizard's UI is based on
[[https://elpa.gnu.org/packages/transient.html][transient]]. While the user can
+change all the bindings, and can use all the commands without the
+transient interface, this readme will refer to the commands through
+the default bindings as they can be seen in the transient below.
+
+[[./default-transient.png][Transient interface]]
+
+The transient interface is invoked through the ~latex-table-wizard-do~
+command, and exited, as usual, with ~C-g~.
+
+An important feature of LaTeX-table-wizard is that it tries to be
+smart: for instance, it should not be fooled if the current table-like
+environments contains embedded tables (that is, other tabular
+environments inside of its cells). The table is parsed so that these
+big cells are treated like any other cell.
+
+[[./move-01.gif]]
+
+[[./select-01.gif]]
+
+[[./swap-01.gif]]
+
+* Available commands
+For now, we will assume a standard LaTeX syntax for tabular
+environments, where ~&~ delimits columns and ~\\~ rows (see
[[#user-defined-envs][below]] for info
+as to how to specify additional syntaxes).
+
+Whenever we say "current" we mean "at point".
+** Relative motion commands
+
+These commands move point N cells to the right, left, down, and up. N
+is passed as a prefix argument, and if it's not passed, it defaults
+to 1.
+
+| Command | Default key |
+|--------------------------+-------------|
+| ~latex-table-wizard-right~ | ~f~ |
+| ~latex-table-wizard-left~ | ~b~ |
+| ~latex-table-wizard-down~ | ~n~ |
+| ~latex-table-wizard-up~ | ~p~ |
+
+With just one of these you can get anywhere you want in the table:
+
+#+begin_src LaTeX
+\begin{tabular}{lll}
+ A0 & B0 & C0 \\\hline
+ A1 & B1 & C1 \\
+ A2 & B2 & C2
+\end{tabular}
+#+end_src
+
+This is because these commands try to Do What You Mean if there is no
+suitable cell to move to:
+
++ Point on ~C0~, ~latex-table-wizard-right~ ⇒ point on ~A1~
++ Point on ~A0~, ~latex-table-wizard-left~ ⇒ point on ~C2~
++ Point on ~C2~, ~latex-table-wizard-down~ ⇒ point on ~A0~
++ Point on ~B0~, ~latex-table-wizard-up~ ⇒ point on ~A2~
+
+and so on.
+** Absolute motion commands
+
+| Command | Default key | Move to...
|
+|--------------------------------------+-------------+-------------------------------|
+| ~latex-table-wizard-beginning-of-cell~ | ~a~ | end of current cell
|
+| ~latex-table-wizard-end-of-cell~ | ~e~ | beginning of
current cell |
+| ~latex-table-wizard-beginning-of-row~ | ~B~ | leftmost cell in
current row |
+| ~latex-table-wizard-end-of-row~ | ~F~ | rightmost cell in
current row |
+| ~latex-table-wizard-bottom~ | ~N~ | bottom cell in
current column |
+| ~latex-table-wizard-top~ | ~P~ | top cell in current
column |
+** Mark, kill and insert commands
+| Command | Default key |
|
+|----------------------------------+-------------+--------------------------------------------|
+| ~latex-table-wizard-mark-cell~ | ~m c~ | mark current cell
|
+| ~latex-table-wizard-insert-column~ | ~i c~ | insert empty column to
the right |
+| ~latex-table-wizard-insert-row~ | ~i r~ | insert row below
|
+| ~latex-table-wizard-kill-column~ | ~k c~ | add content of current
column to kill ring |
+| ~latex-table-wizard-kill-row~ | ~k r~ | add content of current
row to kill ring |
+| ~exchange-point-and-mark~ | ~x~ |
|
+** Swap adjacent fields
+
+| Command | Default key | Swap current...
|
+|--------------------------------------+-------------+----------------------------------|
+| ~latex-table-wizard-swap-cell-right~ | ~C-f~ | cell with the one
to the right |
+| ~latex-table-wizard-swap-cell-left~ | ~C-b~ | cell with the one
to the left |
+| ~latex-table-wizard-swap-cell-down~ | ~C-n~ | cell with the one
below |
+| ~latex-table-wizard-swap-cell-up~ | ~C-p~ | cell with the one
above |
+| ~latex-table-wizard-swap-column-right~ | ~M-f~ | column with the one
to the right |
+| ~latex-table-wizard-swap-column-left~ | ~M-b~ | column with the one
to the left |
+| ~latex-table-wizard-swap-row-down~ | ~M-n~ | row with the one
below |
+| ~latex-table-wizard-swap-row-up~ | ~M-p~ | row with the one
above |
+
+For these commands, think of the cells and columns as circular: if
+there is no item in the direction given, the target is the one on the
+opposite end of the current cell. So for example:
+
+#+begin_src LaTeX
+\begin{tabular}{lll}
+ A0 & B0 & C0 \\\hline
+ A1 & B1 & C1 \\
+ A2 & B2 & C2
+\end{tabular}
+#+end_src
+
+This is because these commands try to Do What You Mean if there is no
+suitable cell to move to:
+
++ Point on ~B0~, ~latex-table-wizard-swap-row-up~
+ ⇒
+#+begin_src LaTeX
+\begin{tabular}{lll}
+ A2 & B2 & C2 \\\hline
+ A1 & B1 & C1 \\
+ A0 & B0 & C0
+\end{tabular}
+#+end_src
++ Point on ~C2~, ~latex-table-wizard-swap-cell-right~
+ ⇒
+#+begin_src latex
+\begin{tabular}{lll}
+ C0 & B0 & A0 \\\hline
+ A1 & B1 & C1 \\
+ A2 & B2 & C2
+\end{tabular}
+#+end_src
+
+** Swap arbitrary fields
+To swap arbitrary fields one must first *select* something and then
+move point somewhere else and perform the swap. Importantly,
+*selecting does not mean marking*: the mark is not even moved when
+selecting.
+
+The simplest case is one in which the current cell, column or row are
+selected:
+
+| Command | Default key | Select current... |
+|----------------------------------+-------------+-------------------|
+| ~latex-table-wizard-select-cell~ | ~SPC~ | cell |
+| ~latex-table-wizard-select-column~ | ~c~ | column |
+| ~latex-table-wizard-select-row~ | ~r~ | row |
+
+Things can be deselected too:
+
+| Command | Default key | Deselect current... |
+|--------------------------------------+-------------+---------------------|
+| ~latex-table-wizard-deselect-cell~ | ~d SPC~ | cell |
+| ~latex-table-wizard-deselect-column~ | ~d c~ | column |
+| ~latex-table-wizard-deselect-row~ | ~d r~ | row |
+
+Once things are selected, you move point somewhere else in the table
+(with the above mentioned motion commands), and then:
+
+| ~latex-table-wizard-swap~ | ~s~ | swap selection and current thing |
+
+What is swapped depends on what is selected: if the selection was only
+a cell, then that cell and the current one are swapped. If it was (a
+potentially discontinuous segment of) a column or a row, then that
+selection is swapped with the current column or row or the
+corresponding portion thereof. If you selected multiple cell that are
+not part of the same column or row, the swap won't happen
+(LaTeX-table-wizard doesn't know what to do).
+
+* Customization
+** Change keybindings
+To change the default keybindings you give an appropriate value to the
+association list ~latex-table-wizard-bindings-alist~ and then call the
+function ~latex-table-wizard-modify-bindings~.
+
+Suppose you want to keep all the defaults except that you want to bind
+~l~ to ~latex-table-wizard-right~ and ~s l~ to
+~latex-table-wizard-swap-cell-right~. To achieve that add this to
+your configuration of ~latex-table-wizard~:
+
+#+begin_src emacs-lisp
+(setq latex-table-wizard-bindings-alist
+ '((latex-table-wizard-right . "l")
+ (latex-table-wizard-swap-cell-right . "s l")))
+#+end_src
+
+The syntax of the values (the keys) is the same accepted by the built
+in ~kbd~ macro.
+** Define rules for new environments
+:PROPERTIES:
+:CUSTOM_ID: user-defined-envs
+:END:
+Remember the default values used for parsing table environments:
+
+#+begin_src emacs-lisp
+(defconst latex-table-wizard-column-delimiters '("[^\\\\]&")
+ "List of regexps matching column delimiters.")
+
+(defconst latex-table-wizard-row-delimiters '("\\\\\\\\")
+ "List of regexps matching row delimiters.")
+
+(defvar latex-table-wizard-hline-macros '("hline"
+ "midrule"
+ "toprule"
+ "bottomrule"))
+#+end_src
+
+ LaTeX-table-wizard will always presume the table you want operate on
+ has a syntax specified like this. But suppose you use different
+ environments with non-standard syntax: suppose you define a
+ table-like environment of your choice, let's call it ~mytable~, that
+ uses ~!ROW~ and ~!COL~ instead of ~&~ and ~\\~ as delimiters, and a macro
+ ~\horizontal~ for horizontal lines. When you are in a ~mytable~
+ environments, you want LaTeX-table-wizard to adapt to this new
+ syntax.
+
+ All you need to do add an appropriate cons cell to the
+ ~latex-table-wizard-new-environments-alist~ association list, mapping
+ the name of the environment, as a string, to a property list
+ specifying the values. For the case of ~mytable~ you would do:
+
+ #+begin_src emacs-lisp
+(add-to-list 'latex-table-wizard-new-environments-alist
+ '("mytable" . (:col ("!COL") :row ("!ROW") :lines
("horizontal"))))
+ #+end_src
diff --git a/select-01.gif b/select-01.gif
new file mode 100644
index 0000000000..8a1e20c8cb
Binary files /dev/null and b/select-01.gif differ
diff --git a/swap-01.gif b/swap-01.gif
new file mode 100644
index 0000000000..2be51568fc
Binary files /dev/null and b/swap-01.gif differ
- [elpa] branch externals/latex-table-wizard created (now b41aac096b), ELPA Syncer, 2023/05/13
- [elpa] externals/latex-table-wizard e738b1c094 02/70: Remove comments, ELPA Syncer, 2023/05/13
- [elpa] externals/latex-table-wizard 7137ca9641 01/70: First commit,
ELPA Syncer <=
- [elpa] externals/latex-table-wizard d71174b247 03/70: Alignment commands added, ELPA Syncer, 2023/05/13
- [elpa] externals/latex-table-wizard a28e0f0ec5 04/70: Some cleanup, ELPA Syncer, 2023/05/13
- [elpa] externals/latex-table-wizard 06d0453f60 10/70: Version bump to 0.0.3, ELPA Syncer, 2023/05/13
- [elpa] externals/latex-table-wizard f9394894f7 05/70: Fix parsing if 0, 0 cell starts in same line as \begin macro, ELPA Syncer, 2023/05/13
- [elpa] externals/latex-table-wizard 29c65891e6 07/70: Fix transient autoload and remove transient change function, ELPA Syncer, 2023/05/13
- [elpa] externals/latex-table-wizard 174739aad8 06/70: Fix byte-compilations warnings about docstrings, ELPA Syncer, 2023/05/13
- [elpa] externals/latex-table-wizard 8402bc0ed9 12/70: Don't call face -face (as per elisp manual ch 40), ELPA Syncer, 2023/05/13
- [elpa] externals/latex-table-wizard 972e8eeba1 16/70: Optimize for compilation and simplify, ELPA Syncer, 2023/05/13
- [elpa] externals/latex-table-wizard c75063522b 17/70: Extend overlay limit to beginning of \end{} macro, ELPA Syncer, 2023/05/13
- [elpa] externals/latex-table-wizard 293bbfc9ed 18/70: Actually remove function definition made useless, ELPA Syncer, 2023/05/13