Showing preview only (527K chars total). Download the full file or copy to clipboard to get everything.
Repository: tbanel/uniline
Branch: main
Commit: cda161166752
Files: 41
Total size: 461.0 KB
Directory structure:
gitextract_1mwm1f40/
├── .gitignore
├── LICENSE
├── README.org
├── tests/
│ ├── bench01.el
│ ├── bench02.el
│ ├── bench03.el
│ ├── bench04.el
│ ├── bench05.el
│ ├── bench06.el
│ ├── bench07.el
│ ├── bench08.el
│ ├── bench09.el
│ ├── bench10.el
│ ├── bench11.el
│ ├── bench12.el
│ ├── bench13.el
│ ├── bench14.el
│ ├── bench15.el
│ ├── bench16.el
│ ├── bench17.el
│ ├── bench18.el
│ ├── bench19.el
│ ├── bench20.el
│ ├── bench21.el
│ ├── bench22.el
│ ├── bench23.el
│ ├── bench24.el
│ ├── bench25.el
│ ├── bench26.el
│ ├── bench27.el
│ ├── bench28.el
│ ├── bench29.el
│ ├── bench30.el
│ ├── bench31.el
│ ├── bench33.el
│ └── uniline-bench.el
├── uniline-core.el
├── uniline-hydra.el
├── uniline-transient.el
├── uniline.el
└── uniline.info
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*~
*.elc
================================================
FILE: LICENSE
================================================
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>.
================================================
FILE: README.org
================================================
# -*- mode: org; coding:utf-8; -*-
#+TITLE: Uniline
#+OPTIONS: ^:{} authors:Thierry Banel, toc:nil
#+LATEX_HEADER: \usepackage{pmboxdraw}
* Getting started in 10 seconds
:PROPERTIES:
:CUSTOM_ID: getting-started-in-10-seconds
:END:
- Type =M-x uniline-mode=
- Move cursor with the arrow-keys on the keyboard =→ ← ↑ ↓=
- Quit =C-c C-c=
[[file:images/first-drawing.png]]
#+begin_example
╷ ╭─────────╮
╰───┤my first ├─╮
│drawing │ ╰───╮
╰─────────╯ │
╭────┬───────╯
╰────╯
#+end_example
* New
:PROPERTIES:
:CUSTOM_ID: new
:END:
Customization & settings now consistently available from the
application menu, from Hydra, and from Transient.
Type =<INS> *=.
* Table of Contents
:PROPERTIES:
:TOC: :include all :depth 3 :force () :ignore (this) :local (nothing)
:CUSTOM_ID: table-of-contents
:END:
:CONTENTS:
- [[#getting-started-in-10-seconds][Getting started in 10 seconds]]
- [[#new][New]]
- [[#gallery-pure-unicode-diagrams-in-emacs][Gallery: pure UNICODE diagrams in Emacs]]
- [[#document-a-command][Document a command]]
- [[#connect-boxes-with-arrows][Connect boxes with arrows]]
- [[#explain-decisions-trees][Explain decisions trees]]
- [[#draw-lines-or-blocks][Draw lines or blocks]]
- [[#outline-the-general-relativity-and-the-schrödingers-equations][Outline the General Relativity and the Schrödinger's equations]]
- [[#explain-the-structure-of-a-sentence-in-a-foreign-language][Explain the structure of a sentence in a foreign language]]
- [[#draw-electronic-diagrams][Draw electronic diagrams]]
- [[#explain-lisp-lists][Explain Lisp lists]]
- [[#draw-sketched-objects][Draw sketched objects]]
- [[#pure-text][Pure text]]
- [[#beware][Beware!]]
- [[#a-minor-mode-for-drawing][A minor mode for drawing]]
- [[#minor-mode][Minor mode]]
- [[#draw-lines-by-moving-the-cursor][Draw lines by moving the cursor]]
- [[#infinite--buffer][Infinite ∞ buffer]]
- [[#brush-style][Brush style]]
- [[#text-direction][Text direction]]
- [[#the-insert-key][The <insert> key]]
- [[#glyphs-------insertion--modification][Glyphs ▷ ▶ → □ ◆ ╮─ insertion & modification]]
- [[#arrows-glyphs------][Arrows glyphs ▷ ▶ → ▹ ▸ ↔]]
- [[#intersection-glyphs---][Intersection glyphs ■ ◆ ●]]
- [[#fine-tweaking-of-lines][Fine tweaking of lines]]
- [[#rectangular-actions][Rectangular actions]]
- [[#drawing-a-rectangle][Drawing a rectangle]]
- [[#filling-a-rectangle][Filling a rectangle]]
- [[#moving-a-rectangular-region][Moving a rectangular region]]
- [[#copying-killing-yanking-a-rectangular-region][Copying, killing, yanking a rectangular region]]
- [[#dashed-lines-and-other-styles][Dashed lines and other styles]]
- [[#ascii-to-unicode][ASCII to UNICODE]]
- [[#long-range-actions-contour-and-flood-fill][Long range actions: contour and flood-fill]]
- [[#tracing-a-contour][Tracing a contour]]
- [[#flood-fill][Flood-fill]]
- [[#macros][Macros]]
- [[#which-fonts][Which fonts?]]
- [[#recommended-fonts][Recommended fonts]]
- [[#use-case-mixing-fonts][Use case: mixing fonts]]
- [[#hydra-or-transient][Hydra or Transient?]]
- [[#selecting-hydra-or-transient][Selecting Hydra or Transient]]
- [[#instantly-selecting-hydra-or-transient][Instantly selecting Hydra or Transient]]
- [[#one-liner-menus][One-liner menus]]
- [[#the-hydra-interface][The Hydra interface]]
- [[#the-transient-interface][The Transient interface]]
- [[#customization][Customization]]
- [[#interface-type][Interface type]]
- [[#insert-key][Insert key]]
- [[#maximum-steps-when-drawing-a-contour][Maximum steps when drawing a contour]]
- [[#cursor-type][Cursor type]]
- [[#hint-style][Hint style]]
- [[#welcome-message-visibility][Welcome message visibility]]
- [[#line-spacing][Line spacing]]
- [[#font][Font]]
- [[#upward-infiniteness-][Upward infiniteness ∞]]
- [[#how-uniline-behaves-with-its-environment][How Uniline behaves with its environment?]]
- [[#language-environment][Language environment]]
- [[#compatibility-with-picture-mode][Compatibility with Picture-mode]]
- [[#compatibility-with-artist-mode][Compatibility with Artist-mode]]
- [[#compatibility-with-whitespace-mode][Compatibility with Whitespace-mode]]
- [[#compatibility-with-org-mode][Compatibility with Org Mode]]
- [[#org-mode-and-latex][Org Mode and LaTex]]
- [[#what-about-t-tabs][What about \t tabs?]]
- [[#what-about-l-page-separation][What about ^L page separation?]]
- [[#emacs-on-the-linux-console][Emacs on the Linux console]]
- [[#emacs-on-a-graphical-terminal-emulator][Emacs on a graphical terminal emulator]]
- [[#emacs-on-windows][Emacs on Windows]]
- [[#compatibility-with-asciiflow][Compatibility with ASCIIFlow]]
- [[#lisp-api][Lisp API]]
- [[#move-the-cursor][Move the cursor]]
- [[#brush][Brush]]
- [[#example-lisp-function-to-draw-a-plus-sign][Example: Lisp function to draw a plus sign]]
- [[#long-range-actions-contour-flood-fill-rectangle][Long range actions (contour, flood-fill, rectangle)]]
- [[#constants][Constants]]
- [[#macro-and-text-direction][Macro and text direction]]
- [[#insert-and-tweak-glyphs][Insert and tweak glyphs]]
- [[#change-to-alternate-styles][Change to alternate styles]]
- [[#mouse-support][Mouse support]]
- [[#installation][Installation]]
- [[#use-package-the-straightforward-way][use-package, the straightforward way]]
- [[#without-use-package][Without use-package]]
- [[#related-packages][Related packages]]
- [[#author-contributors][Author, contributors]]
- [[#license][License]]
:END:
* Gallery: pure UNICODE diagrams in Emacs
:PROPERTIES:
:CUSTOM_ID: gallery-pure-unicode-diagrams-in-emacs
:END:
Draw diagrams like those:
** Document a command
:PROPERTIES:
:CUSTOM_ID: document-a-command
:END:
[[file:images/document-command.png]]
#+begin_example
pdfjam source.pdf 3-5,9
╶─────▲────▲────────▲──▲╴
command╶╯ │ │ │
input file╶──╯ │ │
select pages 3,4,5╶───╯ │
and page 9╶──────────────╯
#+end_example
** Connect boxes with arrows
:PROPERTIES:
:CUSTOM_ID: connect-boxes-with-arrows
:END:
[[file:images/boxes-arrows.png]]
#+begin_example
╭───────────────────────╮
╷123╭────▶┤ hundred and something │
╰───╯ ╰───────────────────────╯
╭────▶──╮A╷
╭───╮ ┏━━━┓ ╔═══╗ │ ╰─╯
0╶─→┤ 1 ┝━━━▶┫ 2 ┣═══▷╣ 3 ╟──●────▶──╮B╷
╰───╯ ┗━┯━┛ ╚═╤═╝ │ ╰─╯
╰────←───╯ ╰────▶──╮C╷
╰─╯
╔══════════╗
║ 1 ║ ▐▀▀▀▀▀▀▀▀▜
║ ╭─────╫───╮ ◁──▷ ▐ 3 ▐
╚════╪═════╝ 2 │ ▐▄▄▄▄▄▄▄▄▟
╰─────────╯
#+end_example
** Explain decisions trees
:PROPERTIES:
:CUSTOM_ID: explain-decisions-trees
:END:
[[file:images/decision-tree.png]]
#+begin_example
┏━━━━━━━━━━━━┓
┃which color?┃
┗━┯━━━━━━━━━━┛
│ ╭──────╮
│ ╭──┤yellow├─▷╮good─choice╭□
▽ │ ╰──────╯ ╰═══════════╯
╰──● ╭───╮ ┏━━━━━┓
├──┤red├───▷┨dark?┠──╮
│ ╰───╯ ┗━━━━━┛ │
│ ╭───◁──────────────╯
│ │ ╭───╮
│ ╰─●─┤yes├▷╮regular─red╭─□
│ │ ╰───╯ ╰═══════════╯
│ │ ╭──╮
│ ╰─┤no├─▷╮pink╭────────□
│ ╰──╯ ╰════╯
│ ╭────╮
├──┤blue├───▷╮next week╭──□
│ ╰────╯ ╰═════════╯
│ ╭─────╮
╰──┤white├──▷╮available╭──□
╰─────╯ ╰═════════╯
#+end_example
** Draw lines or blocks
:PROPERTIES:
:CUSTOM_ID: draw-lines-or-blocks
:END:
[[file:images/lines-blocks.png]]
#+begin_example
╭─╮←─╮
╭╮ │ │ ╰──╴max 235
╭╮││ ╭╯ │
│╰╯│╭─╯ │
╭╮ │ ││ │
╭─╮││╭╮ ╭──╮╭╮ │ ╰╯ ╰╮
╭╯ ╰╯╰╯│ ╭╯ ╰╯╰─╮ │ │ ╭╮
◁─╯ ╰──╯ ╰──╯ ╰─╯╰────▷
◀════════════════════════════════════════▶
╭────────╮
▲ │all time│
┃ ▄ ▗▟█ ←─┤highest │
Qdx █▌ ████ ╰────────╯
┃ ▗▄█▌ █████▙
┃ ▟███████▄█████████▄▄▄ ▗▄
┃▐▄▄████████████████████████████▄▄▖
╺━━━━━━━━━━╸time╺━━━━━━━━━━━━━━━━▶
#+end_example
** Outline the General Relativity and the Schrödinger's equations
:PROPERTIES:
:CUSTOM_ID: outline-the-general-relativity-and-the-schrödingers-equations
:END:
[[file:images/general-relativity-equation.png]]
#+begin_example
╭─────────────────────╴G: Einstein tensor
│ ╭────╴κ: Gravitational coupling constant
╭──▽───╮ ╭───▽──╮
┏━┷━━━━━━┷━━━━━━━━┷━━━━━━┷━━━┓
┃ R - gR/2 + Λg = (8πG/c⁴)×T ┃◁╴General Relativity equation
┗━△━━━△△━━━━━△△━━━━━━△━△━━━△━┛
│ ││ ││ │ │ ╭╯
│ ││ ││ │ │ ╰╴Energy-impulsion tensor
│ ││ ││ │ ╰───╴Speed of light
│ ││ ││ ╰─────╴Gravitational constant
│ ││ ╰┴────────────╴Cosmological constant
│ │╰──────┴────────────╴Scalar curvature
│ ╰───────╰────────────╴Metric tensor
╰────────────────────────╴Ricci tensor
#+end_example
[[file:images/schrodinger-equation.png]]
#+begin_example
╭─────────────────────╴Derivative over time
│ ╭──────────╭────╴State of quantum system at time t
│ │ │ (the square of its absolute value
╭▽─╮ ╭─▽──╮ ╭─▽──╮ is the probability density)
┏━━━━━┷━━┷━┷━━━━┷━━━━━┷━━━━┷━┓
┃ i ħ d/dt |Ψ(t)> = Ĥ |Ψ(t)> ┃◁─╴Schrödinger's equation
┗━△━△━━━━△━━━━△━━━━━△━━━━△━━━┛
│ │ ╰────╰─────┤────╰───╴Time
│ │ ╰────────╴Hamiltonian
│ ╰────────────────────────╴Reduced Plank constant
╰──────────────────────────╴Imaginary number i²=-1
#+end_example
** Explain the structure of a sentence in a foreign language
:PROPERTIES:
:CUSTOM_ID: explain-the-structure-of-a-sentence-in-a-foreign-language
:END:
(which language?)
[[file:images/foreign-language-sentence.png]]
#+begin_example
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ the pretty table is standing ┃
┗┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
│ ╭────┬─────┬─────╴radicals
↕ ╭┴╮ ╭┴─╮ ╭┴─╮
┏┷━━━┿━┿━━┿━━┿━━┿━━┿━━━┓
┃ la bela tablo staras ┃
┗━━━━┿━┿△━┿━━┿△━┿━━┿△━━┛
╰─╯│ ╰──╯│ ╰──╯│ ┏━━━━━suffixes━━━━━┓
│ │ ╰──╂╴as: present tense┃
│ │ ┃ os: future tense ┃
│ │ ┃ is: past tense ┃
│ ╰────────╂╴ o: noun ┃
╰──────────────╂╴ a: adjective ┃
┃ e: adverb ┃
┗━━━━━━━━━━━━━━━━━━┛
#+end_example
** Draw electronic diagrams
:PROPERTIES:
:CUSTOM_ID: draw-electronic-diagrams
:END:
[[file:images/electronic-circuit.png]]
#+begin_example
╭────────╭──────────╮ ┏━━┓
│ ╭┴╮ ╰─┨5V┃
╭┴╮ │░│ ┗━━┛
│░│ │░│1KΩ
│░│10KΩ ╰┬╯
5μF ╰┬╯ ├─────────────● →
╷╷ │ ┠─╯ amplified
→ ●─────┤├───┼──────┨ output
input ╵╵ │ ┠▶╮ 500μF signal
signal ╭┴╮ │ ╷╷
│░│ ├───┤├──╮
│░│1KΩ ╭┴╮ ╵╵ │
╰┬╯ │░│ │ ╭────╮
│ │░│470Ω │ │ ╺━━┷━━╸
│ ╰┬╯ │ │ ╺━━━╸
╰────────╰───────╰────╯ ╺━╸
#+end_example
** Explain Lisp lists
:PROPERTIES:
:CUSTOM_ID: explain-lisp-lists
:END:
[[file:images/lisp-lists.png]]
#+begin_example
'(a b c)
┏━━━┳━━━┓ ┏━━━┳━━━┓ ┏━━━┳━━━┓
●━━━▶┫ ● ┃ ●─╂──▷┨ ● ┃ ●─╂──▷┨ ● ┃nil┃
┗━┿━┻━━━┛ ┗━┿━┻━━━┛ ┗━┿━┻━━━┛
│ ╰──────────╮╰╮
│ ╭─────┬───────────╮ │ │
╰─▷┤"a\0"│properties │ │ │
├─────┼───────────┤ │ │
│"b\0"│properties ├◁╯ │
├─────┼───────────┤ │
│"c\0"│properties ├◁──╯
├─────┼───────────┤
│... │... │
╵ ╵ ╵
#+end_example
** Draw sketched objects
:PROPERTIES:
:CUSTOM_ID: draw-sketched-objects
:END:
[[file:images/sketched-objects.png]]
#+begin_example
◀─(-)────────(+)──▶ ~╭──────╮~
▗──────────────╮ ~~│ ╭~~╮ │~~
▐ ╰╮ ~│ ╵ ╵ │~
╭□▐ 1.5 volts ╭╯□╮ ╰─╖ ╓─╯
│ ▝▀▀▀▀▀▀▀▀▀▀▀▀▀▀▘ │ ╠━━╣
│ ╰──────╯ │
╰─────────────────────────────╯
#+end_example
[[file:images/water-sketch.png]]
#+begin_example
╶╮ ╭╴
┏┳┥▒▒▒▒▒▒▒┝╸
┃┃│▒▒eau▒▒│
┃┃│▒▒▒▒▒▒▒│ ╔═════╗
┃┃╰──╮▒╭──╯ ║ ╶╮ ▽ ╭╴
┃┃ ▒ ║ │ ░ │
┃┃ ▒ ║ │░░░░░░░░░░░░░░│
┃┃ ╚═════╝ │░░░░░░░░░░░░░░╞════▷▒▒
┃┃ │░░░░░akvo░░░░░│ ╶╮ ▒ ╭╴
┃┃ │░░░░░░░░░░░░░░│ │ ▒ │
┃┃ ╰─┲┳━━━━━━━━┳┱─╯ │▒▒▒▒▒▒▒▒▒▒▒│
┃┃ ┃┃ ┃┃ │▒▒▒water▒▒▒│
┃┃ ┃┃ ┃┃ │▒▒▒▒▒▒▒▒▒▒▒│
┃┃ ┃┃ ┃┃ ╰───────────╯
▝▀▀▀▀▀▀▘ ▝▀▘ ▝▀▘ ▀▀▀▀▀▀▀▀▀▀▀▀▀
#+end_example
** Pure text
:PROPERTIES:
:CUSTOM_ID: pure-text
:END:
Those diagrams are pure text. There is nothing graphic. They are
achieved using UNICODE characters. Therefore they can be drawn within
any text formatted document, like Org Mode, Markdown, txt, comments in
any programming language source code (C++, Python, Rust, D,
JavaScript, GnuPlot, LaTex, whatever).
Most often, the text file will be encoded as UTF-8. This is becoming
the de-facto standard for text and source code files.
Creating such diagrams by hand is painfully slow. Use =Uniline= to
draw lines while you move the cursor with keyboard arrows.
** Beware!
:PROPERTIES:
:CUSTOM_ID: beware
:END:
If you see those diagrams miss-aligned, most likely the font used to
display them does not support UNICODE block characters. See bellow the
paragraph [[#which-fonts][Which fonts?]] for details.
If you get misalignment when drawing, this could come from too wide
characters. Emojis are an example. Usual characters may also be
considered twice as wide as normal under some "language
environments". See the paragraph [[#language-environment][Language environment]] for details.
* A minor mode for drawing
:PROPERTIES:
:CUSTOM_ID: a-minor-mode-for-drawing
:END:
** Minor mode
:PROPERTIES:
:CUSTOM_ID: minor-mode
:END:
=Uniline= is a minor mode. Activate it temporarily:
=M-x uniline-mode=
Exit it with:
=C-c C-c=
The current major mode is still active underneath =uniline-mode=.
While in =uniline-mode=, overwriting is active, as well as long lines
truncation. Also, a hollow cursor is provided (customizable). Those
settings are reset to their previous state when exiting =uniline-mode=.
** Draw lines by moving the cursor
:PROPERTIES:
:CUSTOM_ID: draw-lines-by-moving-the-cursor
:END:
Use keyboard arrows to draw lines.
By default, drawing lines only happens over empty space or over other
lines. If there is already text, it will not be erased. However, by
hitting the control-key while moving, lines overwrite whatever there
is.
The usual numeric prefix is available. For instance, to draw a line 12
characters wide downward, type: =M-12 <down>=
** Infinite ∞ buffer
:PROPERTIES:
:CUSTOM_ID: infinite--buffer
:END:
The buffer is infinite ∞ in the south and east directions. Which means
that when the cursor ends up outside the buffer, white space
characters are automatically added.
All algorithms also make use of the infiniteness of the buffer when
needed. Those algorithms are: moving a rectangle, pasting a rectangle,
drawing the external border of a rectangular region, or drawing the
contour of a shape.
The buffer is also infinite ∞ in the upward direction. That is
customizable through the =uniline-infinite-up↑= variable. If its value
is =t=, then the buffer is actually infinite ∞ upward. If it is =nil=,
then the upper border of the buffer is a hard limit. To customize,
type:
=M-x customize-variable uniline-infinite-up↑=
The buffer can be "narrowed", for instance with the =C-x n n= or =M-x
narrow-to-region= command. In this case, the limits are those of the
narrow region. When Uniline needs to bypass the up↑ or down↓ limits,
it adds empty lines. When widening again the buffer, the region which
was narrow will have increased.
** Brush style
:PROPERTIES:
:CUSTOM_ID: brush-style
:END:
Set the current brush with:
- ~-~ single thin line
=╭─┬─╮=
- ~+~ single thick line
=┏━┳━┓=
- ~=~ double line
=╔═╦═╗=
- ~#~ quarter block
=▙▄▟▀=
- =~= toggle dotted lines
=┄┄┄┄=
- ~<delete>~ eraser
- ~<return>~ move without drawing anything
The current brush and the current text direction (see [[#text-direction][Text direction]]) are
reflected in the mode-line (at the bottom of the =Emacs= screen). It
looks like this:
[[file:images/mode-line.png]]
#+begin_example
current text current
direction╶────╮ ╭───╴brush
▼ ▼
══════════════════╧═══════╧══════════════
U:** buff (... →Uniline┼ ...)
═════════════════════════════════════════
#+end_example
The dotted toggle ~~~ is a modifier for the single thin and thick
lines. It circles along 3 styles:
- plain lines,
- 3 dots vertical, 2 dots horizontal,
- 4 dots both vertical & horizontal,
- back to plain line and so on.
[[file:images/dotted-lines.png]]
#+begin_example
║ thin ╷ thick ╷
║ │ │
══════════╬═════════╪═════════╡
║ ╭╌╌╌╮ │ ┏╍╍╍┓ │
3,2 dots ║ ┆ ┆ │ ┇ ┇ │
║ ╰╌╌╌╯ │ ┗╍╍╍┛ │
──────────╫─────────┼─────────┤
║ ╭┈┈┈╮ │ ┏┉┉┉┓ │
4,4 dots ║ ┊ ┊ │ ┋ ┋ │
║ ╰┈┈┈╯ │ ┗┉┉┉┛ │
──────────╨─────────┴─────────╯
#+end_example
Note that the UNICODE standard offers very limited support for dotted
lines. Only vertical and horizontal lines are available. So, no
crossing of line is possible. In case a line crosses a dotted line,
Uniline falls back to a plain line crossing character (but still
preserving thickness). There is no dotted versions of double lines
either.
** Text direction
:PROPERTIES:
:CUSTOM_ID: text-direction
:END:
Usually, inserting text in a buffer moves the cursor to the right. (And
sometimes to the left for some locales). Any of the 4 directions can be
selected under =Uniline=. Just type any of:
- =<insert> C-<up>=
- =<insert> C-<right>=
- =<insert> C-<down>=
- =<insert> C-<left>=
The current direction is reflected in the mode-line, just before the
word ="uniline"=.
* The =<insert>= key
:PROPERTIES:
:CUSTOM_ID: the-insert-key
:END:
The =<insert>= key is a prefix for other keys:
- for drawing arrows, squares, crosses, o-shapes glyphs,
- for handling rectangles,
- for inserting =# = - += which otherwise change the brush style,
- for trying a choice of mono-spaced fonts.
Why =<insert>=? Because:
- =Uniline= tries to leave their original meaning to as many keys as
possible,
- the standard meaning of =<insert>= is to toggle the =overwrite-mode=;
but =Uniline= is already in =overwrite-mode=, and de-activating
overwrite would break =Uniline=.
So preempting =<insert>= does not sacrifice anything.
*Customization*
Another key may be defined instead of =<insert>=. Type:
#+begin_example
M-x customize-variable uniline-key-insert
#+end_example
* Glyphs =▷ ▶ → □ ◆ ╮─= insertion & modification
:PROPERTIES:
:CUSTOM_ID: glyphs-------insertion--modification
:END:
Individual character glyphs may be inserted and changed.
- Put the cursor where a glyphs should be edited or inserted.
- Then press =<insert>= (this key may be customized, see [[#insert-key][Insert key]]).
Arrows, squares, circles, crosses may be handled. Also lines may be
fine tweaked a single character at a time.
** Arrows glyphs =▷ ▶ → ▹ ▸ ↔=
:PROPERTIES:
:CUSTOM_ID: arrows-glyphs------
:END:
When inserting an arrow, it points in the direction that the line
drawing follows.
=Uniline= supports 6 arrows types: =▷ ▶ → ▹ ▸ ↔=
[[file:images/arrow-styles.png]]
#+begin_example
□
╰─◁──▷─╮ □─╮ ╭─╮ ╭─╮ ╭─□
╭─◀──▶─╯ △ ▲ ↑ ▵ ▴ ↕
╰─←──→─╮ │ │ │ │ │ │
╭─◃──▹─╯ ▽ ▼ ↓ ▿ ▾ ↕
╰─◂──▸─╮ ╰─╯ ╰─╯ ╰─╯
╭─↔──↔─╯
□
#+end_example
Actually, there are tons of arrows of all styles in the UNICODE
standard. Unfortunately, support by fonts is weak. So =Uniline=
restrains itself to those six safe arrows.
To insert an arrow, type: =<insert> a= or =<insert> a a= or =<insert> a a a=. (=a=
cycles through the 6 styles, =A= cycles backward).
=<insert> 4 a= is equivalent to =<insert> a a a a=, which is also equivalent to
=<insert> A A A=. Those 3 shortcuts insert an arrow of this style: =▵▹▿◃=. The
actual direction where the arrow points follows the last movement of
the cursor.
To change the direction of the arrow, use shift-arrow, for example:
=S-<up>= will change from =→= to =↑=.
** Intersection glyphs =■ ◆ ●=
:PROPERTIES:
:CUSTOM_ID: intersection-glyphs---
:END:
There are a few UNICODE characters which are mono-space and symmetric
in the 4 directions. They are great at line intersections:
To insert a square =□ ■ ▫ ▪ ◆ ◊= type:
=<insert> s s s…= (=s= cycles, =S= cycles backward).
To insert a circular shape =· ∙ • ● ◦ Ø ø= type:
=<insert> o o o…= (=o= cycles, =O= cycles backward).
To insert a cross shape =╳ ╱ ╲ ÷ × ± ¤= type:
=<insert> x x x…= (=x= cycles, =X= cycles backward).
To insert a grey character =░▒▓█= from pure white to pure black type:
=<insert> SPC SPC SPC…= or =<insert> DEL DEL DEL…= (space key goes from
white to black, back-space key goes from black to white)
To insert a usual ASCII letter or symbol, just type it.
As the keys =- + = # ~= are preempted by =uniline-mode=, to type them,
prefix them with =<insert>=. Example: =<insert> -= inserts a =-= and
=<insert> += inserts a =+=.
[[file:images/insert-glyphs.png]]
#+begin_example
<insert>
│
├────────────────────────────╮
▼ ╭─arrows──────╮ ▼ ╭───╮
╰──▶─(a)─┤ ▷ ▶ → ▹ ▸ ↔ │ ╰──▶─(+)─┤ + │
│ ╰─────────────╯ │ ╰───╯
│ ╭─squares─────╮ │ ╭───╮
╰──▶─(s)─┤ □ ■ ▫ ▪ ◆ ◊ │ ╰──▶─(-)─┤ - │
│ ╰─────────────╯ │ ╰───╯
│ ╭─circles───────╮ │ ╭───╮
╰──▶─(o)─┤ · ∙ • ● ◦ Ø ø │ ╰──▶─(=)─┤ = │
│ ╰───────────────╯ │ ╰───╯
│ ╭─crosses───────╮ │ ╭───╮
╰──▷─(x)─┤ ╳ ╱ ╲ ÷ × ± ¤ │ ╰──▶─(#)─┤ # │
│ ╰───────────────╯ │ ╰───╯
│ ╭───────╮ │ ╭───╮
╰──▶─(SPC DEL)─┤ ░▒▓█ │ ╰──▶─(~)─┤ ~ │
╰───────╯ ╰───╯
#+end_example
** Fine tweaking of lines
:PROPERTIES:
:CUSTOM_ID: fine-tweaking-of-lines
:END:
[[file:images/fine-tweaking.png]]
#+begin_example
convert this ═══▶ into that
╭───────────╮ ╭───────────╮
│╶───┬────▷ │ │╶───╮────▷ │
│ │ │ │ │ │
│ │ │ │
│ ▀▀▀ │ │ ▀▟▀ │
╰───────────╯ ╰───────────╯
#+end_example
At the crossing of lines, it may be appealing to do small
adjustments. In the above example, we removed a segment of line which
occupies 1/4 of a character. This cannot be achieve with line tracing
alone. We also modified a quarter-block line in a non-obvious way.
- Put the point (the cursor) on the character where lines cross each other.
- type =INS S-<right> S-<right>=
=<right>= here refers to the right part of the character under the
point. The 1/4 line segment will cycle through all displayable
forms. On the second stroke, no segment will be displayed, which is
what we want.
Caveat! The UNICODE standard does not define all possible combinations
including double line segments. (It does for all combinations of thin
and tick lines). So sometimes, when working with double lines, the
process may be frustrating.
This works also for lines made of quarter-blocks. There are 4
quarter-blocks in a character, either on or off. Each of the 4 shifted
keyboard arrows flips a quarter-block on-and-off.
In the above example, the effect was achieved with:
=INS S-<up> S-<down> S-<left>=
* Rectangular actions
:PROPERTIES:
:CUSTOM_ID: rectangular-actions
:END:
- Drawing,
- filling,
- moving,
- copying & yanking,
- change line & glyph styles,
those actions may be performed on a rectangular selection.
Select a rectangular region with =C-SPC= or =C-x SPC= and move the cursor.
You may also use =S-<arrow>= (=<arrow>= being any of the 4
directions) to extend the selection. The buffer grows as needed with
white spaces to accommodate the selection. Selection extension mode is
active when =shift-select-mode= is non-nil.
Or you may use the mouse to highlight the desired region.
All those region-highlighting are standard in =Emacs=, and unrelated to
=Uniline=.
Once you have a region highlighted, press =<insert>= (this key can be
customized, see [[#insert-key][Insert key]]). The selection becomes rectangular if it
was not. You are offered a menu of possible actions.
** Drawing a rectangle
:PROPERTIES:
:CUSTOM_ID: drawing-a-rectangle
:END:
To draw a rectangle in one shot, select a region, press =<insert>=, then
hit:
- =r= to draw a rectangle inside the selection
- =S-R= to draw a rectangle outside the selection
- =C-r= to overwrite a rectangle inside the selection
- =C-S-R= to overwrite a rectangle outside the selection
If needed, change the brush with any of =- + = # <delete>=
[[file:images/draw-rectangle.png]]
#+begin_example
╭───────╮ r: inside╮╭───────╮
│ one │ ▗▄▄▄▄▄▄▖╭┤│▛▀▀▀▀▀▜│
│ ┏━━━━┿━━━━━━┓ ▐╭────╮▌│╰┼▌ ▐│
╰──╂────╯ two ┃ ▐│ │▌│ │▙▄▄▄▄▄▟│
┃ ╔═══════╋═╗ ▐│ ├▌╯ ╰─────┬─╯
┗━━━╋━━━━━━━┛ ║ ▐╰────╯▌────────┴───╮
║ three ║ ▝▀▀▀▀▀▀▘ R: outside╯
╚═════════╝
╭─────────╮
my text I │my text I│
want to ╶─<insert>R─▷ │want to │
box │box │
╰─────────╯
#+end_example
The usual =C-_= or =C-/= keys may be hit to undo, even with the region
still active visually.
** Filling a rectangle
:PROPERTIES:
:CUSTOM_ID: filling-a-rectangle
:END:
While the rectangular mode is active, press =i= to fill the
rectangle. You will be asked to choose a character. You have those
options:
- for a regular character like =t=, just type it.
- =SPC= or =DEL= for a shade of grey =" ░▒▓█"= among the 5 available in
UNICODE. =SPC= to make it darker and darker. =DEL= to make the rectangle
lighter and lighter.
- =C-y= to chose the first character in the top of the kill ring.
The above selection is the same as for the flood-fill action (see
[[#flood-fill][Flood-fill]]).
** Moving a rectangular region
:PROPERTIES:
:CUSTOM_ID: moving-a-rectangular-region
:END:
Select a region, then press =<insert>=.
Use arrow keys to move the rectangle around. A numeric prefix may be
used to move the rectangle that many characters.
- Under =Hydra=, be sure to specify the numeric prefix with just digits,
without the =Alt= key. Typing =15 <left>= moves the rectangle 15
characters to the left. =M-15 <left>= does not work.
- Under =Transient=, use the =Alt= key, like anywhere else in =Emacs=. Type
=M-15 <left>= to move the selected rectangle 15 characters to the left.
Press =q=, =<return>=, or =C-g= to stop moving the rectangle.
The =C-_= key may also be used to undo the previous movements, even
though the selection is still active.
[[file:images/move-rectangle.png]]
#+begin_example
▲
│
<up>
╭─────┴──────╮
│this is │
│my rectangle│
◀─<left>──┤I want to ├─<right>─▶
│move │
╰─────┬──────╯
<down>
│
▼
#+end_example
What is leakage? When moving a rectangular region, the rectangle
leaves behind lines oriented in the movement direction. This is not a
bug, but a feature. Leakage allows growing a drawing without breaking
it in two parts.
[[file:images/rect-region-leak.png]]
#+begin_example
┏┯━━━┓ ┏┯━━━┓
┏━┛│ ┗━╦━━┓ with ┏━┛│ ┗━╦━━┓
┃ │ ║ ┃╶──╮leak ┃ │ ║ ┃ leaked
┃ │ ║ ┃ ╰────▶ ┃ │ ║ ┃◀──────╴
┗━━┷━━━━━╩━━┛ ┃ │ ║ ┃ lines
│ ┗━━┷━━━━━╩━━┛
│without
│leak
╰──────╮
▼
┏┯━━━┓
┏━┛│ ┗━╦━━┓
┃ │ ║ ┃ broken
◀───────╴
┃ │ ║ ┃ drawing
┗━━┷━━━━━╩━━┛
#+end_example
** Copying, killing, yanking a rectangular region
:PROPERTIES:
:CUSTOM_ID: copying-killing-yanking-a-rectangular-region
:END:
A rectangle can be copied or killed, then yanked somewhere else.
Select a region, press =<insert>=, then:
- =c= to copy
- =k= to kill
- =y= to yank (aka paste)
This is similar to the =Emacs= standard rectangle handling:
- =C-x r r= copy rectangle to register
- =C-x r k= kill rectangle
- =C-x r y= yank killed rectangle
The first difference is that =Uniline= rectangles, when killed and
yanked, do not move surrounding characters.
The second difference is that the white characters of the yanked
rectangle are considered transparent. As a result, only non-blank
parts of the yanked rectangle are over-printed.
=Uniline= and =Emacs= standard rectangle share the same storage for copied
and killed rectangles, namely the =killed-rectangle= Lisp variable. So,
a rectangle can be killed one way, and yanked another way.
** Dashed lines and other styles
:PROPERTIES:
:CUSTOM_ID: dashed-lines-and-other-styles
:END:
[[file:images/four-styles.png]]
#+begin_example
╭────▷───╮ ┏━━━━▶━━━┓ ╔════▶═══╗
│ ╭─□──╮ │ ┃ ┏━■━━┓ ┃ ║ ╔═■══╗ ║
△ │ │ ▽ ▲ ┃ ┃ ▼ ▲ ║ ║ ▼
│ ╰───◦╯ │ ┃ ┗━━━•┛ ┃ ║ ╚═══•╝ ║
╰───◁────╯ ┗━━━◀━━━━┛ ╚═══◀════╝
╭╌╌╌╌▷╌╌╌╮ ┏╍╍╍╍▶╍╍╍┓
┆ ╭╌□╌╌╮ ┆ ┇ ┏╍■╍╍┓ ┇
△ ┆ ┆ ▽ ▲ ┇ ┇ ▼
┆ ╰╌╌╌◦╯ ┆ ┇ ┗╍╍╍•┛ ┇
╰╌╌╌◁╌╌╌╌╯ ┗╍╍╍◀╍╍╍╍┛
╭┈┈┈┈▷┈┈┈╮ ┏┉┉┉┉▶┉┉┉┓
┊ ╭┈□┈┈╮ ┊ ┋ ┏┉■┉┉┓ ┋
△ ┊ ┊ ▽ ▲ ┋ ┋ ▼
┊ ╰┈┈┈◦╯ ┊ ┋ ┗┉┉┉•┛ ┋
╰┈┈┈◁┈┈┈┈╯ ┗┉┉┉◀┉┉┉┉┛
#+end_example
A base drawing can be converted to dashed lines. Moreover, lines can
be made either thin or thick.
- Select the rectangular area you want to operate on (with mouse drag
or =S-<left>=, =S-<down>= and so on as described earlier).
- Type =INS=, then =s= (as "style").
You will be offered a choice of styles:
- =3=: vertical lines will become 3 dashes per character, while
horizontal ones will get 2 dashes per character.
- =4=: vertical and horizontal lines will get 4 dashes per character.
- =h=: thin lines corners, which are usually rounded, become hard angles.
- =+=: thin lines and intersections become thick, empty glyphs get
filled.
- =-=: thick lines and intersections become thin, filled glyphs are
emptied.
- ~=~: thick and thin lines become double lines.
- =0=: come back to standard base-line =Uniline= style: plain, not-dashed
lines, thin corner rounded, ASCII art is converted to UNICODE.
- =a=: apply the =aa2u-rectangle= function from the unrelated
=ascii-art-to-unicode= package, to convert ASCII art to UNICODE (this
only works if =ascii-art-to-unicode= is already installed).
Converting parts of a drawing from one style to another can produce
nice looking sketches.
[[file:images/same-sketch-several-styles.png]]
#+begin_example
╭───╮ ╭───╮ ╭───╮
│░░░│ │░░░│ │░░░┝━▶┓ ╭╌╌╌╌╌╮
│░░░╰───╯░░░╰───╯░░░│ ┃ ┆░░░░░╰╌╌╌╌╌╮
□░░░░░░░░░░░░░░░░░░░│ ┗━┥░░░░░░░░░░░┆
│░░░╭───╮░░░╭───╮░░░│ ┆░░░░░╭╌╌╌╌╌╯
╰───╯ ╰─┰─╯ ╰─┰─╯ ╰╌╌┰╌╌╯
▲ ┃ ▼
┗━━━━━━━┻━━━━━━━━━┛
┏━━━┓ ┏━━━┓ ┏━━━┓
┃░░░┃ ┃░░░┃ ┃░░░┠─▷╮ ┏╍╍╍╍╍┓
┃░░░┗━━━┛░░░┗━━━┛░░░┃ │ ┇░░░░░┗╍╍╍╍╍┓
■░░░░░░░░░░░░░░░░░░░┃ ╰─┨░░░░░░░░░░░┇
┃░░░┏━━━┓░░░┏━━━┓░░░┃ ┇░░░░░┏╍╍╍╍╍┛
┗━━━┛ ┗━┯━┛ ┗━┯━┛ ┗╍╍┯╍╍┛
△ │ ▽
╰───────┴─────────╯
#+end_example
** ASCII to UNICODE
:PROPERTIES:
:CUSTOM_ID: ascii-to-unicode
:END:
The standard base-line =Uniline= (=INS s 0=) or =aa2u-rectangle= (=INS s a=)
conversions may be used to convert ASCII art to UNICODE. The original
ASCII art may be drawn for instance by the =artist-mode= or the
=picture-mode= packages.
To use =aa2u-rectangle=, install the =ascii-art-to-unicode= package by
Thien-Thi Nguyen (RIP), available on ELPA. =Uniline= does not requires a
dependency on this package, by lazy evaluating any call to
=aa2u-rectangle=.
See https://elpa.gnu.org/packages/ascii-art-to-unicode.html
[[file:images/ascii-2-unicode.png]]
#+begin_example
+-------------+ +--+
| +-->-| +-----+ ASCII art
| 1 +--------+--+ | 3 | made by
+----+--------+ | +----+---+ Artist-mode
| 2 +-<----+
+-----------+
╭─────────────╮ ╭──╮
│ ├──▷─│ ╰─────╮ Converted to
│ 1 ╭────────┼──╮ │ 3 │ Uniline base style
╰────┼────────╯ │ ╰────┬───╯ INS s 0
│ 2 ├─◁────╯
╰───────────╯
┌─────────────┐ ┌──┐
│ ├──>─│ └─────┐ Converted by
│ 1 ┌────────┼──┐ │ 3 │ aa2u-rectangle
└────┼────────┘ │ └────┬───┘ INS s a
│ 2 ├─<────┘
└───────────┘
#+end_example
=INS s 0= with selection active calls the =uniline-change-style-standard=
function. It converts what looks ASCII-art to UNICODE-art. Of course,
there are ambiguities regarding whether a character is part of a
sketch or not.
The heuristic is to consider that a character is part of a sketch if
it is surrounded by at least one other character which is part of a
sketch. So, an isolated =-= minus character will be left alone, while
two such characters =--= will be converted to UNICODE. Conversion will
happens also for =<-= for instance.
Here is a fairly convoluted ASCII-art example, along with its
conversion by =INS s 0=:
[[file:images/ascii-2-unicode-b.png]]
#+begin_example
╭─↔--<-◁-◀--━+ +--->------==+
/----/ Rectangle1 |-----+-----+ Rectangle2 v v
| | <uni^code> ^ " | "quote" +-\ ▼
^^ \------------/ /-+-\ +------------+ " v
| \--+------+--/ | | +----\----/--+ " >▷▶>
\>--\ | | \---/ | | "
v \==<===/ a=b 1=2 a-to-b +----+ ◁==/ >->
╭─↔──◁─◁─◀──━┑ ╭───▷──────══╕
╭────┤ Rectangle1 │─────╥─────┤ Rectangle2 ▽ ▽
│ │ <uni^code> △ ║ │ "quote" ├─╖ ▼
△^ ├────────────┤ ╭─╨─╮ ├────────────┤ ║ ▽
│ ╰──┬──────┬──╯ │ │ ╰────┬────┬──╯ ║ ▷▷▶▷
╰▷──╮ │ │ ╰───╯ │ │ ║
▽ ╘══◁═══╛ a=b 1=2 a-to-b ╰────╯ ◁══╝ ▷─▷
#+end_example
* Long range actions: contour and flood-fill
:PROPERTIES:
:CUSTOM_ID: long-range-actions-contour-and-flood-fill
:END:
** Tracing a contour
:PROPERTIES:
:CUSTOM_ID: tracing-a-contour
:END:
[[file:images/contour-tracing.png]]
#+begin_example
╭──────────────╮
╭─╯A.written.text╰────────╮
│outlined by the.`contour'│
╰─╮function.gets╶┬────────╯
╰╮a.surrounding╰───────╮
╰─╮line.in.the.current│
╰─╮brush.style╭─────╯
╰───────────╯
#+end_example
Choose or change the brush style with any of =-,+,=_,#,<delete>=. Put
the cursor anywhere on the shape or outside but touching it. Then
type:
=<insert> c=
A contour line is traced (or erased if brush style is =<delete>=)
around the contiguous shape close to the cursor.
When hitting capital letter: =<insert> S-C= the contour is
overwritten. This means that if there was already a different style of
line on the contour path, it is overwritten.
The shape is distinguished because it floats in a blank characters
ocean. For the shake of the contour function, blank characters are
those containing lines as drawn by =Uniline= (including true blank
characters). Locations outside the buffer are also considered blank.
The algorithm has an upper limit of =10000= steps. This avoids an
infinite loop in which the algorithm may end up in some rare
cases. One of those cases is when the contour crosses a new-page
character, displayed by =Emacs= as =^L=. =10000= steps require a fraction of
a second to run. For shapes really huge, you may launch the contour
command once again, at the point where the previous run ended.
This =10000= steps limit is customizable. Type:
#+begin_example
M-x customize-variable uniline-contour-max-steps
#+end_example
** Flood-fill
:PROPERTIES:
:CUSTOM_ID: flood-fill
:END:
[[file:images/flood-fill.png]]
#+begin_example
this.text.surrounds this.text.surrounds
. / .▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒/
. //╶───▷╴.▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒//
... //// ...▒▒▒▒▒▒▒▒▒▒▒▒////
...a.hole///// ...a.hole/////
#+end_example
A hollow shape is a contiguous region of identical characters (not
necessarily blank), surrounded by a boundary of different
characters. The end of the buffer in any direction is also considered
a boundary.
Put the cursor anywhere in the hole. Then type:
=<insert> i=
Answer by giving a character to fill the hole.
If instead of a character, =SPC= or =DEL= is typed, then a shade of grey
character is picked. =SPC= selects a darker grey than the one the point
is on, while =DEL= selects a lighter. There are 5 shades of grey in the
UNICODE standard: =" ░▒▓█"=. Those grey characters are well supported
by the suggested fonts.
=C-y= is also an option. The first character in the top of the kill
ring will be chosen as the filling character. (The kill ring is filled
by functions like =C-k= or =M-w=, unrelated to =Uniline=).
Typing =<return>= or =C-g= aborts the filling operation.
A rectangular shape may also be filled.
- Mark a region
- =<insert> i=
- answer which character should be used to fill.
There is no limit on the area to fill. Therefore, the filling
operation may flood the entire buffer (but no more).
* Macros
:PROPERTIES:
:CUSTOM_ID: macros
:END:
=Uniline= adds directional macros to the =Emacs= standard macros.
Record a macro as usual with =C-x (= … =C-x )=.
Then call it with the usual =C-x e=. But then, instead of executing
the macro, a menu is offered to execute it in any of the 4 directions.
When a macro is executed in a direction other than the one it was
recorded, it is twisted in that direction. This means that recorded
hits on the 4 keyboard arrows are rotated. It happens also for shift
and control variations of those keys. Direction of text insertion is
also rotated.
There is still the classical =e= option to call the last recorded
macro. So instead of the usual =C-x e=, type =C-x e e=. And of course,
the usual repetition typing repeatedly =e= is available.
Why are directional macros useful? To create fancy lines. For
instance, if we want a doted-line instead of the continuous one, we
record a macro for one step:
#+begin_example
C-x ( ;; begin recording
INS o ;; insert a small dot
<right> <right> ;; draw a line over 2 characters
C-x ) ;; stop recording
#+end_example
Then we call this macro repeatedly in any of the 4 directions:
[[file:images/macro-doted-line.png]]
#+begin_example
·─·─·─·─· ╷ ·──·
│ │ │ │
· · · ·
│ │ │ │
· ·─·─·─· ·
│ │
·─·─·─·─·─·─·
#+end_example
We can draw complex shapes by just drawing one step. Hereafter, we
call a macro in 4 directions, closing a square:
[[file:images/macro-fancy-squares.png]]
#+begin_example
╭╮╭╮╭╮╭╮╭╮╭╮ △ △ △ △ △ △ ╭─╮ ╭─╮ ╭─╮ ╭─╮ ╭─╮ ╭─╮ ╭─╮ ╭─╮
╭─╯╰╯╰╯╰╯╰╯╰╯│ ╶╯╶╯╶╯╶╯╶╯╶╯╷ ╭──╯∙╰─╯∙╰─╯∙╰─╯∙│ ▷┤□├▷┤□├▷┤□├▷┤□├▽
╰╮ ╰╮ ◁╮ ╰▷ │∙ │ ╭┴┼─╯ ╰─╯ ╰─╯ ╰─┼┴╮
╭╯ ╭╯ ╵ ╷ ╰╮ ╰╮ │□│ │□│
╰╮ ╰╮ ◁╮ ╰▷ │ ∙│ ╰┬╯ ╰┬╯
╭╯ ╭╯ ╵ ╷ ╭╯ ╭╯ △ ▽
╰╮ ╰╮ ◁╮ ╰▷ │∙ │ ╭┴╮ ╭┴╮
╭╯ ╭╯ ╵ ╷ ╰╮ ╰╮ │□│ │□│
╰╮ ╰╮ ◁╮ ╰▷ │ ∙│ ╰┬┼─╮ ╭─╮ ╭─╮ ╭─┼┬╯
│╭╮╭╮╭╮╭╮╭╮╭─╯ ╵╭╴╭╴╭╴╭╴╭╴╭╴ │∙╭─╮∙╭─╮∙╭─╮∙╭──╯ △┤□├◁┤□├◁┤□├◁┤□├◁
╰╯╰╯╰╯╰╯╰╯╰╯ ▽ ▽ ▽ ▽ ▽ ▽ ╰─╯ ╰─╯ ╰─╯ ╰─╯ ╰─╯ ╰─╯ ╰─╯ ╰─╯
#+end_example
* Which fonts?
:PROPERTIES:
:CUSTOM_ID: which-fonts
:END:
A mono-space character font must be used. It must also support UNICODE.
** Recommended fonts
:PROPERTIES:
:CUSTOM_ID: recommended-fonts
:END:
Not all fonts are born equal.
- =(set-frame-font "DejaVu Sans Mono" )=
- =(set-frame-font "Unifont" )=
- =(set-frame-font "Hack" )=
- =(set-frame-font "JetBrains Mono" )=
- =(set-frame-font "Cascadia Mono" )=
- =(set-frame-font "Agave" )=
- =(set-frame-font "JuliaMono" )=
- =(set-frame-font "FreeMono" )=
- =(set-frame-font "Iosevka Comfy Fixed" )=
- =(set-frame-font "Iosevka Comfy Wide Fixed")=
- =(set-frame-font "Aporetic Sans Mono" )=
- =(set-frame-font "Aporetic Serif Mono" )=
- =(set-frame-font "Source Code Pro" )=
Those fonts are known to support the required UNICODE characters, AND
display them as mono-space. There are fonts advertised as mono-space
which give arbitrary widths to non-ASCII characters. That is bad for
the kind of drawings done by =Uniline=.
You may want to try any of the suggested fonts. Just hit the
corresponding entry in the =Uniline= menu, or type =<insert> f=. You may
also execute the above Lisp commands like that:
=M-: (set-frame-font "DejaVu Sans Mono")=
This setting is for the current session only. If you want to make it
permanent, you may use the =Emacs= customization:
=<insert> f *=
or
=M-x customize-face default=
Beware that =Emacs= tries to compensate for missing UNICODE support by
the current font. =Emacs= substitutes one font for another, character
per character. The user may not notice until the drawings done under
=Emacs= are displayed on another text editor or on the Web. Of course,
using the suggested fonts and the UNICODEs drawn by =Uniline= keeps you
away from those glitches.
To know which font =Emacs= has chosen for a given character, type:
=C-u C-x ==
Note that none of those commands downloads a font from the Web.
The font should already be available.
** Use case: mixing fonts
:PROPERTIES:
:CUSTOM_ID: use-case-mixing-fonts
:END:
A user on GitHub, dmullis, exposed his use-case. A source-code base is
usually edited with a font not in the Uniline list of recommended
fonts. However, it is desirable to document the source code with
Uniline, either directly along the source or in separate files. How to
achieve that without messing with the fonts in several Emacs buffers?
Several solutions have emerged from the discussion.
- =face-remap-add-relative=
A line like this at the top of the files reserved for Uniline drawings:
#+begin_example
-*- eval: (face-remap-add-relative 'default :family "DejaVu Sans Mono"); -*-
#+end_example
This confines its effect to just the one single buffer.
- =uniline-mode-hook=
Add a hook (a function called when entering =uniline-mode=):
#+begin_example
(add-hook
'uniline-mode-hook
(lambda () (face-remap-add-relative 'default :family "DejaVu Sans Mono")))
#+end_example
There are also =uniline-mode-on-hook= & =uniline-mode-off-hook= which can be handy.
- =font-lock-comment-face=
An alternative mean of limiting the scope of the font change is the
Emacs standard font-lock mechanism.
#+begin_example
(customize-face '(font-lock-comment-face))
#+end_example
Then check =Font Family=, type in value ="DejaVu Sans Mono",= and =C-x C-s=.
Now any major mode that understands "comments" as distinct from other
text can safely nest a Uniline drawing within its boundaries, all text
outside the "comment" unaffected (except perhaps by spacing).
Look also at the =font-lock-constant-face= face.
- Org Mode
In Org Mode, the usable faces could be =org-block=, =org-quote=,
=org-verse=. But first the =org-fontify-quote-and-verse-blocks= variable
must be set to =t=.
- Markdown
In Markdown mode, customize the =markdown-pre-face= or
=markdown-code-face= faces.
* Hydra or Transient?
:PROPERTIES:
:CUSTOM_ID: hydra-or-transient
:END:
The basic usage of =Uniline= should be easy: just move the point, and lines
are traced. Change brush to draw thicker lines.
More complex actions are summoned by the =<insert>= key, with or without
selection. This is a single key to remember. Then a textual menu is
displayed, giving the possible keys continuations and their
meaning. All that is achieved by the =Hydra= or =Transient= libraries,
which are now part of =Emacs= (thanks!).
The =Hydra= and =Transient= libraries offer similar features. Some users
may prefer one or the other.
=Uniline= was developed from day one with =Hydra=. =Transient= is a late
addition.
** Selecting Hydra or Transient
:PROPERTIES:
:CUSTOM_ID: selecting-hydra-or-transient
:END:
Two files are compiled when installing =Uniline=
- =uniline-hydra.el=
- =uniline-transient.el=
One of them should be loaded (but not both). There are several
ways. The cleanest is =use-package=. Add those lines to your =~/.emacs=
file:
#+begin_src elisp
(use-package uniline-hydra
:bind ("C-<insert>" . uniline-mode))
#+end_src
or:
#+begin_src elisp
(use-package uniline-transient
:bind ("C-<insert>" . uniline-mode))
#+end_src
The following key sequences can assist in modifying the =.emacs= file:
- =<INS> * H=
- =<INS> * T=
Note: there used to be a customizable setting to switch between the
two interfaces. This had many issues. One of them is that the
native-compiler is blind to all user-customized settings.
There is a third file, =uniline-code.elc=. Loading =uniline-hydra.elc= or
=uniline-transient.elc= automatically loads =uniline-core.elc=.
** Instantly selecting Hydra or Transient
:PROPERTIES:
:CUSTOM_ID: instantly-selecting-hydra-or-transient
:END:
It is now possible to switch user interfaces on the fly.
To do so, look at the "Customize" entry in the Uniline menu. This
menu is available:
- from the menu-bar at the top of the Emacs screen (if not made
invisible),
- by left-clicking on ="Uniline"= in the mode-line, at the bottom of the
Emacs screen.
Note that the changes are for the current session only. To permanently
choose Hydra or Transient, change your =~/.emacs=initialization file
as describe in [[#selecting-hydra-or-transient][Selecting Hydra or Transient]].
The actions performed by the menu are:
- =(load-library "uniline-hydra")=
- =(load-library "uniline-transient")=
You can execute them directly or by other means.
** One-liner menus
:PROPERTIES:
:CUSTOM_ID: one-liner-menus
:END:
The multi-lines menus in Hydra and Transient are quite useful for
casual users. For seasoned users, those huge textual menus may
distract them from their workflow.
It is now possible to switch to less distracting textual menus. They
are displayed in the echo-area on a single line.
To do so, type:
- =C-t= within a sub-mode (glyph insertion mode, rectangle handling,
etc.)
- =C-h TAB= at the top-level.
This will flip between the two sizes of textual menus. It also affects
the welcome message, the one displayed when entering the =Uniline= minor
mode.
The current size is controlled by the =uniline-hint-style= variable:
- =t= for full fledged messages over several lines
- =1= for one-liner messages
- =0= for no message at all
The variable is "buffer-local", which means that it can take distinct
values on distinct buffers.
Its default value can be customized and saved for future sessions:
=M-x customize-variable uniline-hint-style=
After customization it can be changed later, on a buffer per buffer
basis, with the =C-t= or =C-h TAB= keys.
Transient natively offers a similar setting:
=transient-show-popup=. (There is no such variable in Hydra). It can be
customized with =t=, =nil=, =0= (zero), or a number. This is similar but not
exactly the same as the Hydra behavior and the =uniline-hint-style=.
the Transient setting stays in effect until the =C-t= or =C-h TAB= keys
are not used, . As soon as one of those keys is invoked,
=transient-show-popup= is toggled (which does not happens in Transient
alone). The change is kept in effect throughout the =Uniline= session,
but no longer.
** The Hydra interface
:PROPERTIES:
:CUSTOM_ID: the-hydra-interface
:END:
Put that in your =~/.emacs= file:
#+begin_src elisp
(use-package uniline-hydra
:bind ("C-<insert>" . uniline-mode))
#+end_src
It has been asked by =Transient=-only users to avoid installing the
=Hydra= package. Currently, it is not possible to make dependencies
conditional in =Melpa=. And removing the =Hydra= dependency would hurt
=Hydra= users. Therefore, for the time being, the =Hydra= package is still
installed when installing =Uniline= through =Melpa=.
** The Transient interface
:PROPERTIES:
:CUSTOM_ID: the-transient-interface
:END:
Put that in your =~/.emacs= file:
#+begin_src elisp
(use-package uniline-transient
:bind ("C-<insert>" . uniline-mode))
#+end_src
=Transient= interface was added recently to =Uniline=. This leaded to the
splitting of the single =uniline.el= file into 4 source
files. Hopefully, the added complexity remains hidden by the =Elpa= -
=Melpa= packaging system.
* Customization
:PROPERTIES:
:CUSTOM_ID: customization
:END:
Type: =M-x customize-group uniline=.
Or =Menu bar ⟶ Options ⟶ Customize Emacs ⟶ Specific Group… ⟶ "uniline"=.
This invokes the standard =Emacs= customization system. Your settings
will be saved in the file pointed to by the =custom-file= variable if
set, or your =~/.emacs= file. (Along with all your other settings
unrelated to =Uniline=).
Two settings are special: interface type (obsolete) & the insert
key. The other settings are self-explanatory
** Interface type
:PROPERTIES:
:CUSTOM_ID: interface-type
:END:
The =uniline-interface= variable is *obsolete*. Choosing between =Hydra= or
=Transient= interface is done by loading one or the other
sub-package. This is best done in the =.emacs= initialization file. See
[[#installation][Installation]] for details.
Typing either of the following key sequences can assist in modifying the =.emacs= file:
- =<INS> * H=
- =<INS> * T=
** Insert key
:PROPERTIES:
:CUSTOM_ID: insert-key
:END:
By default, the =<insert>= or =INS= key is the prefix for most of the
=Uniline= actions. Some computers do not have an =INS= key, or it is bound
to some other command (Apple?).
This can be changed temporarily or permanently. The customization
allows to set several keys at the same time.
Depending on whether =Emacs= is run in a graphical environment or a
text-only terminal, either the =<insert>= or the =<insertchar>= events are
generated by the =INS= key. Therefore, by default =Uniline= defines both
events as the =INS= key.
Variable =uniline-key-insert=.
** Maximum steps when drawing a contour
:PROPERTIES:
:CUSTOM_ID: maximum-steps-when-drawing-a-contour
:END:
Defaults to =10000=.
To avoid an infinite loop in some rare cases.
Variable =uniline-contour-max-steps=.
** Cursor type
:PROPERTIES:
:CUSTOM_ID: cursor-type
:END:
Hollow by default, so that what is under the cursor remains visible.
There is the option to leave the cursor as it is.
Variable =uniline-cursor-type.=
** Hint style
:PROPERTIES:
:CUSTOM_ID: hint-style
:END:
Currently only applicable to the =Hydra=.
It defaults to "full fledged menus".
Variable =uniline-hint-style=.
=Transient= offers a similar setting: =transient-show-popup=.
** Welcome message visibility
:PROPERTIES:
:CUSTOM_ID: welcome-message-visibility
:END:
Default is "on". Turn it "off" for less distraction.
Even when turned of, the welcome message can still be displayed by
pressing =C-h TAB=.
Variable =uniline-show-welcome-message=.
** Line spacing
:PROPERTIES:
:CUSTOM_ID: line-spacing
:END:
The =line-spacing= setting in =Emacs= can change the display of a
sketch. (This setting is unrelated to =Uniline=).
The best looking effect is given by:
: (setq line-spacing nil)
You may want to change your current setting. =Uniline= may handle this
variable some day. Right now, =line-spacing= is left as a matter of
choice for everyone.
[[file:images/line-spacing.png]]
#+begin_example
╭────┬────────┬────╮ ╺┯━━━━┯┯━━┯┯━┯┯━━━━━━━━┯┯━━━━━━━┯┯━━━━━━┯╸
│▒▒▒▒╰────────╯▒▒▒▒│ │ │╰is╯╰a╯│ ││ │╰around╯
│▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ ╰this╯ ╰sentence╯╰hanging╯
│▒▒▒╭─╮▒▒▒▒▒▒╭─╮▒▒▒│ △
│▒▒▒╰─╯▒▒▒▒▒▒╰─╯▒▒▒│ │ △
│▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ ╰─────────┬────────╯
╰──────────────────╯ verbs
(setq line-spacing nil)
#+end_example
** Font
:PROPERTIES:
:CUSTOM_ID: font
:END:
Face customization is unrelated to =Uniline=. However, =Uniline= can
assist in choosing a good font and customizing the =default= face. See
[[#which-fonts][Which fonts?]].
Type =<insert> f= to select a font just for the current =Uniline=
session. Type =*= to enter the =Emacs= customization of the =default= face
and retain your choice for future sessions.
** Upward infiniteness ∞
:PROPERTIES:
:CUSTOM_ID: upward-infiniteness-
:END:
If the variable =uniline-infinite-up↑= is:
- =t=, then the buffer grows at the top of the buffer (or at the top of
the narrowed region), by adding empty lines as needed.
- =nil=, then the top of the buffer (or the top of the narrowed region)
is a non-trespassable limit. This is the default, and the behaviour
of previous versions of Uniline.
* How Uniline behaves with its environment?
:PROPERTIES:
:CUSTOM_ID: how-uniline-behaves-with-its-environment
:END:
** Language environment
:PROPERTIES:
:CUSTOM_ID: language-environment
:END:
The so called "language environment" in Emacs can cause unwanted line
breaks, like in this drawing:
[[file:images/broken-lines-lang-env.png]]
#+begin_example
╶───────────┬─────╮
│ │
│ │
│ │
╰───────────╯
unexpected broken lines
╶─────┬───────────╮
│ │
│ │
│ │
╰───────────╯
expected continuous lines
#+end_example
The above example was drawn first with the =Chinese-BIG5= language
environment, then with the =English= environment.
There is nothing specific about =Chinese-BIG5=. It is just an instance
picked out from more than 100 language environments.
#+begin_example
C-x RET l Chinese-BIG5
C-x RET l English
#+end_example
In =Chinese-BIG5=, some characters are considered twice as wide as
standard characters. Whereas in =English=, all characters needed by
Uniline are 1 unit wide.
Thanks to *rumengling* (GitHub) for discovering and diagnosing the
issue!
To workaround the issue, when entering =uniline-mode=, the width of all
characters Uniline uses is checked. If some of them are more than 1,
the =char-width-table= variable is patched.
What are the consequences of this patch? The =char-width-table= variable
is an Emacs global. Therefore the patch by Uniline will affect all
buffers. As the characters touched by the patch are graphic, and have
nothing to do with Chinese, it should not have any significance on
text written in Chinese.
It was pondered whether Uniline should put back =char-width-table= at
its original value upon exiting =uniline-mode=, or leaving the
patch. For now, it has been decided to leave it. Because anyway,
intertwining several =uniline-mode= and changes to the language
environment is intractable.
In case of something, re-setting the language environment to its same
value cancels the patch to =char-width-table= by Uniline.
** Compatibility with Picture-mode
:PROPERTIES:
:CUSTOM_ID: compatibility-with-picture-mode
:END:
=Picture-mode= and =uniline-mode= are compatible. Their features overlap
somehow:
- Both implement an unlimited buffer in east and south directions.
- Both visually truncate long lines (actual text is not truncated).
- Both set the overwrite mode (=uniline-mode= activates
=overwrite-mode=, while =picture-mode= re-implements it)
- Both are able to draw rectangles (=uniline-mode= in UNICODE,
=picture-mode= in ASCII), copy and yank them.
They also have features unique to each:
- =Picture-mode= writes in 8 possible directions
- =Picture-mode= handles TAB stops
- =Uniline-mode= draws lines and arrows
** Compatibility with Artist-mode
:PROPERTIES:
:CUSTOM_ID: compatibility-with-artist-mode
:END:
=Artist-mode= and =uniline-mode= are mostly incompatible. This is because
=artist-mode= preempts the arrow keys, which give access to a large part
of =uniline-mode= features.
However, it is possible to use both one after the other.
** Compatibility with Whitespace-mode
:PROPERTIES:
:CUSTOM_ID: compatibility-with-whitespace-mode
:END:
=Whitespace-mode= and =uniline-mode= are mostly compatible.
Why activate =whitespace-mode= while in =uniline-mode=? Because
=Uniline= creates a lot of white-spaces to implement an infinite
buffer. And it is funny to look at this activity.
To make =uniline-mode= and =whitespace-mode= fully compatible, disable
the newline visualization:
- =M-x customize-variable whitespace-style=
- uncheck =(Mark) NEWLINEs=
This is due to a glitch in =move-to-column= when a visual property is
attached to newlines. And =uniline-mode= makes heavy use of =move-to-column=.
** Compatibility with Org Mode
:PROPERTIES:
:CUSTOM_ID: compatibility-with-org-mode
:END:
You may want to customize the shift extension mode in =Org Mode=. This
is because =Org Mode= preempts =shift-select-mode= for other useful
purposes. Just type:
#+begin_example
M-x customize-variable org-support-shift-select
#+end_example
and choose "when outside special context", which sets it to =t=.
You then get the shift-selection from =Org Mode=, not from =Uniline=. The
difference is that the =Uniline='s one handles the infinite-ness of the
buffer.
Other than that, =Uniline= is compatible with =Org Mode=
Thanks to jdtsmith (GitHub) for sharing a funny fact he discovered. If
a source block is created with the =Uniline= language (=Uniline= is
*not* a language like =C++,= =Python=, or =Bash=), then it can be
edited (=M-x org-edit-special=) with =uniline-mode= automatically
activated.
[[file:images/org-src-block.png]]
#+begin_example
#+begin_src uniline
╭───╮ ╭───╮
│ ╷ ╰───╯ ╷ │
│ ╰─ ╶─╯ │
╰╮ ● ● ╭╯
│ ╷ │
╰╮ ────╯ ╭╯
╰───────╯
#+end_src
#+end_example
** Org Mode and LaTex
:PROPERTIES:
:CUSTOM_ID: org-mode-and-latex
:END:
Use the =pmboxdraw= LaTex module. This gives limited support for "box
drawing" characters in LaTex documents.
Example:
[[file:images/latex-block.png]]
#+begin_example
#+LATEX_HEADER: \usepackage{pmboxdraw}
#+begin_src text
this works:
┌─────┐ ┌────────────┐
│ ├───────┤ │
└─────┘ │ │
┌─────┐ ┌────┤ │
│ ├──┘ │ │
└─────┘ ┌────┤ │
┌─────┐ │ │ │
│ ├──┘ └────────────┘
└─────┘
this does not quite work:
┏━━━┓ ┏━━┓ ┏━━━━━┓
┃ ┃ ┃ ┣━━━━━┫ ┃
┃ ┗━━┛ ┃ ┏┛ ┃
┗━━━━━━━━━┛ ┗━━━━━━┛
but that is OK:
┏━━━┓
┃ ┃
┗━━━┛
that is OK too:
╺════╦══╗ ╔════╗
║ A║ ║ B ╚══╗
╚══╝ ╚═══════╝
this works:
├── dev
└┬┬ release
│├── new
│└── old
├── graph
└── non-graph
#+end_src
#+end_example
Note that corners of thin lines should be sharp. There is no support
for rounded corners.
To export this Org Mode example to PDF through LaTex, type:
=C-c C-E l o=
** What about =\t= tabs?
:PROPERTIES:
:CUSTOM_ID: what-about-t-tabs
:END:
Some files may contain tabs (the character =\t=). Those include
programming code (Python, Perl, C++, D, Rust, JavaScript and so on).
When =Uniline= draws something in the middle of a TAB, or right onto a
TAB, it first converts it to spaces, then proceeds as usual. This
process is invisible. So be cautious if TABs have a special meaning in
the file.
Also, rectangles are first untabified (if there are TABs) before
moving them. This avoids some rare instances of misalignment.
One way to see what is going on, is to activate the =whitespace-mode=.
** What about =^L= page separation?
:PROPERTIES:
:CUSTOM_ID: what-about-l-page-separation
:END:
=Uniline= does not work well with =^L= (page separation)
character. Nor with similar characters, like =^T=. When trying to
draw a line over such a character, the cursor may get stuck. This is
because those characters occupy twice the width of a normal character.
Just try to get away from =^L=, =^T= and such when drawing with
=Uniline=.
** Emacs on the Linux console
:PROPERTIES:
:CUSTOM_ID: emacs-on-the-linux-console
:END:
Linux consoles are the 7 non-graphic screens which can be accessed
usually typing =C-M-F1=, =C-M-F2=, and so on. Such a screen is also
presented when connecting through =ssh= or =tls= into a non-graphical server.
By default they use a font named "Fixed" with poor support for
Unicode. However, it supports lines of the 3 types, mixing all of them
in thin lines though.
Another problem is that by default =S-<left>= and =C-<left>= are
indistinguishable from =<left>=. Same problem with =<right>=, =<up>=, =<down>=
and =<insert>=. This has nothing to do with =Emacs=. A solution can be
found here: https://www.emacswiki.org/emacs/MissingKeys
** Emacs on a graphical terminal emulator
:PROPERTIES:
:CUSTOM_ID: emacs-on-a-graphical-terminal-emulator
:END:
This is the =Emacs= launched from a terminal typing =emacs -nw=. In this
environment, =<insert>= does not exist. It is replaced by
=<insertchar>=. This has already been taken into account by =Uniline=
by duplicating the key-bindings for the two flavors of this key.
If you decide to bind globally =C-<insert>= to the toggling of
=Uniline= minor mode as suggested, then you will have to do the same
for =C-<insertchar>=, for example with =use-package= in your
=~/.emacs= file:
#+begin_src elisp
(use-package uniline
:defer t
:bind ("C-<insert>" . uniline-mode)
:bind ("C-<insertchar>" . uniline-mode))
#+end_src
** Emacs on Windows
:PROPERTIES:
:CUSTOM_ID: emacs-on-windows
:END:
On Windows the only native mono-spaced fonts are =Lucida Console= and
=Courier New=. They are not mono-spaced for the Unicodes used by
=Uniline=.
Often, the =Consolas= font is present on Windows. It supports quite well
the required Unicodes to draw lines. A few glyphs produce unaligned
result though. They should be avoided under =Consolas=: =△▶▹◆=
Of course, other fonts may be installed. It is quite easy.
** Compatibility with ASCIIFlow
:PROPERTIES:
:CUSTOM_ID: compatibility-with-asciiflow
:END:
ASCIIFlow is a ASCII-UNICODE diagram drawing tool (as Uniline). It
works on a web browser. Just open https://asciiflow.com and start
drawing. There is no server, ASCIIFlow operates locally on your
PC. Your diagrams survive web browser sessions, as they are saved
locally behind the scene.
When your drawing is complete, you can export it to Emacs-Uniline:
- Click on the download button
- Select ="ASCII Extended"=
- Paste your diagram in Emacs with =C-y=
- Modify it with Uniline
For the other way around, a Uniline drawing can be exported to
ASCIIFlow:
- Copy it from Emacs (with =M-w= for instance).
- In ASCIIFlow, choose ="Select & Move"=
- Type =C-v=
- Edit with ASCIIFlow
* Lisp API
:PROPERTIES:
:CUSTOM_ID: lisp-api
:END:
Could =Uniline= be programmed (versus used interactively)?
Yes!
The API is usable programmatically:
** Move the cursor
:PROPERTIES:
:CUSTOM_ID: move-the-cursor
:END:
Move cursor while drawing lines by calling any of the 4 directions
functions:
- =uniline-write-up↑=
- =uniline-write-ri→=
- =uniline-write-dw↓=
- =uniline-write-lf←=
They expect a repeat =count= (usually 1) and optionally =force=t= to
overwrite the buffer
** Brush
:PROPERTIES:
:CUSTOM_ID: brush
:END:
Set the current brush by calling any of the following:
- =uniline--set-brush-nil= ;; write nothing
- =uniline--set-brush-0= ;; eraser
- =uniline--set-brush-1= ;; single thin line╶─╴
- =uniline--set-brush-2= ;; single thick line╺━╸
- =uniline--set-brush-3= ;; double line╺═╸
- =uniline--set-brush-block= ;; blocks ▙▄▟▀
Those functions are equivalent to:
- =(setq uniline--brush nil)=
- =(setq uniline--brush 0)=
- =(setq uniline--brush 1)=
- =(setq uniline--brush 2)=
- =(setq uniline--brush 3)=
- =(setq uniline--brush :block)=
except the functions also update the mode-line.
** Example: Lisp function to draw a plus sign
:PROPERTIES:
:CUSTOM_ID: example-lisp-function-to-draw-a-plus-sign
:END:
For instance, if we want to create a function to draw a "plus" sign,
we can code it as follows:
#+begin_src elisp
(defun uniline-draw-plus ()
(interactive)
(uniline-write-ri→ 1)
(uniline-write-dw↓ 1)
(uniline-write-ri→ 1)
(uniline-write-dw↓ 1)
(uniline-write-lf← 1)
(uniline-write-dw↓ 1)
(uniline-write-lf← 1)
(uniline-write-up↑ 1)
(uniline-write-lf← 1)
(uniline-write-up↑ 1)
(uniline-write-ri→ 1)
(uniline-write-up↑ 1))
#+end_src
Calling =M-x uniline-draw-plus= will result in this nice little
plus-shape:
[[file:images/plus-shape.png]]
#+begin_example
╭╮
╭╯╰╮
╰╮╭╯
╰╯
generated by
M-x uniline-draw-plus
#+end_example
We may modify the function to accept the size of the shape as a
parameter:
#+begin_src elisp
(defun uniline-draw-plus (size)
(interactive "Nsize? ")
(uniline-write-ri→ size)
(uniline-write-dw↓ size)
(uniline-write-ri→ size)
(uniline-write-dw↓ size)
(uniline-write-lf← size)
(uniline-write-dw↓ size)
(uniline-write-lf← size)
(uniline-write-up↑ size)
(uniline-write-lf← size)
(uniline-write-up↑ size)
(uniline-write-ri→ size)
(uniline-write-up↑ size))
#+end_src
The =(interactive "Nsize? ")= form prompts user for the size of the
shape if not given as a parameter.
This API works in any mode, not only in =Uniline= minor mode. It takes
care of the infiniteness of the buffer in the right and down
directions.
** Long range actions (contour, flood-fill, rectangle)
:PROPERTIES:
:CUSTOM_ID: long-range-actions-contour-flood-fill-rectangle
:END:
There are other useful functions operating on many characters at
once. Contour tracing and flood-filling are among them:
- =uniline-contour=
- =uniline-fill=
The following functions operate on a rectangular region, which must be
active prior to calling them:
- =uniline-draw-inner-rectangle=
- =uniline-draw-outer-rectangle=
- =uniline-copy-rectangle=
- =uniline-kill-rectangle=
- =uniline-yank-rectangle=
- =uniline-fill-rectangle=
- =uniline-move-rect-up↑=
- =uniline-move-rect-ri→=
- =uniline-move-rect-dw↓=
- =uniline-move-rect-lf←=
** Constants
:PROPERTIES:
:CUSTOM_ID: constants
:END:
Constants for the 4 directions:
- =uniline-direction-up↑= ;; constant 0
- =uniline-direction-ri→= ;; constant 1
- =uniline-direction-dw↓= ;; constant 2
- =uniline-direction-lf←= ;; constant 3
** Macro and text direction
:PROPERTIES:
:CUSTOM_ID: macro-and-text-direction
:END:
Changing text direction:
- =uniline-text-direction-up↑=
- =uniline-text-direction-ri→=
- =uniline-text-direction-dw↓=
- =uniline-text-direction-lf←=
or (in this case the mode-line is not updated):
- =(setq uniline-text-direction uniline-direction-up↑)=
- =(setq uniline-text-direction uniline-direction-ri→)=
- =(setq uniline-text-direction uniline-direction-dw↓)=
- =(setq uniline-text-direction uniline-direction-lf←)=
Call macro in any direction:
- =uniline-call-macro-in-direction-up↑=
- =uniline-call-macro-in-direction-ri→=
- =uniline-call-macro-in-direction-dw↓=
- =uniline-call-macro-in-direction-lf←=
** Insert and tweak glyphs
:PROPERTIES:
:CUSTOM_ID: insert-and-tweak-glyphs
:END:
Insert and cycle intersection glyphs:
- =uniline-insert-fw-arrow=
- =uniline-insert-fw-square=
- =uniline-insert-fw-oshape=
- =uniline-insert-fw-cross=
- =uniline-insert-fw-grey=
- =uniline-insert-bw-arrow=
- =uniline-insert-bw-square=
- =uniline-insert-bw-oshape=
- =uniline-insert-bw-cross=
- =uniline-insert-bw-grey=
Rotate arrow or tweak 4-half-lines or 4-block characters:
- =uniline-rotate-up↑=
- =uniline-rotate-ri→=
- =uniline-rotate-dw↓=
- =uniline-rotate-lf←=
Here are the lowest level functions. Move point, possibly extending
the buffer in right and bottom directions:
- =uniline-move-to-column=
- =uniline-move-to-line=
- =uniline-move-to-lin-col=
- =uniline-move-to-delta-column=
- =uniline-move-to-delta-line=
** Change to alternate styles
:PROPERTIES:
:CUSTOM_ID: change-to-alternate-styles
:END:
A drawing in a rectangular selection may have its style changed:
- =uniline-change-style-dot-3-2= ;; 3 dashes vert. ┆, 2 horiz. ╌
- =uniline-change-style-dot-4-4= ;; 4 dashes vert. ┊ & horiz. ┈
- =uniline-change-style-standard= ;; back to Uniline base style
- =uniline-change-style-hard-corners= ;; rounded corners╭╴become hard┌
- =uniline-change-style-thin= ;; convert to ╭╴ thin lines
- =uniline-change-style-thick= ;; convert to ┏╸ thick lines
- =uniline-change-style-double= ;; convert to ╔═ thick lines
- =uniline-aa2u-rectangle= ;; call aa2u to convert ASCII to Unicode
The above functions require a region to be marked.
* Mouse support
:PROPERTIES:
:CUSTOM_ID: mouse-support
:END:
The out-of-the-box mouse support of =Emacs= works perfectly. Except when
the mouse clicks on a position outside the buffer. This happens when
clicking past the end of a too short line, or past the end of the buffer.
To handle those cases, a few standard =Emacs= functions have been
extended to add blank characters or blank lines. Doing so, the
mouse-click now falls on a valid part of the buffer. Of course, those
extensions are only active on =uniline-mode= activated buffers.
Beware that when the window is at the same time zoomed with =C-x C-+
C--= AND horizontally scrolled with =C-x <=, the cursor positioning is
not accurate. This is due to =Emacs= limitations and bugs. Just click
twice to fix the inaccuracy.
* Installation
:PROPERTIES:
:CUSTOM_ID: installation
:END:
** use-package, the straightforward way
:PROPERTIES:
:CUSTOM_ID: use-package-the-straightforward-way
:END:
The =use-package= library became the de-facto standard to manage
packages in your =.emacs= initialization file. The =use-package= library
comes along with Emacs. It can (among other services) delay loading
external packages until they are used, and bind keyboard shortcuts to
the package's entry points.
Add the following lines to your =.emacs= file, and reload it, if not
already done. This says that the popular Melpa repository is one of
the central store of third parties packages. To day, it provides almost
7000 packages to choose from.
#+begin_src elisp
(add-to-list 'package-archives
'("melpa" . "http://melpa.org/packages/")
t)
(package-initialize)
#+end_src
Alternately you may customize this variable:
#+begin_example
M-x customize-variable package-archives
#+end_example
Then add those lines in your Emacs initialization file (usually =~/.emacs=):
#+begin_src elisp
(use-package uniline-hydra
:bind ("C-<insert>" . uniline-mode))
#+end_src
or:
#+begin_src elisp
(use-package uniline-transient
:bind ("C-<insert>" . uniline-mode))
#+end_src
This tell Emacs:
- Be prepared to load =uniline-mode= when the user request it, but do
not load it now.
- Bind the =C-<insert>= keys to the function =uniline-mode=. This shortens
the longer =M-x uniline-mode= command. Any other key combinations can
be bound, as you prefer. =<insert>= happens to also be the key used
inside =Uniline= (customizable).
- Load either the =uniline-hydra= or the =uniline-transient= file, as you
prefer. This gives Uniline one or the other flavour of
user-interface.
There is an alias to =uniline-hydra=:
#+begin_src elisp
(use-package uniline
:bind ("C-<insert>" . uniline-mode))
#+end_src
If you are using [[https://github.com/radian-software/straight.el][straight.el]] with =use-package=, and have
=(setq straight-use-package-by-default t)=, you have the following options:
#+begin_src elisp
;; uniline-hydra using the alias
(use-package uniline)
;;; uniline-hydra explicitly requested
(use-package uniline-hydra
:straight uniline)
;;; install and load uniline-transient
(use-package uniline-transient
:straight uniline)
#+end_src
** Without use-package
:PROPERTIES:
:CUSTOM_ID: without-use-package
:END:
Download the package from Melpa:
#+begin_src elisp
(package-install "uniline")
#+end_src
Alternately, you can download the Lisp files, and load them manually:
#+begin_src elisp
(load-file "uniline-hydra.el") ;; interpreted form
(load-file "uniline-hydra.elc") ;; byte-compiled form
(load-file "uniline-hydra.eln") ;; native-compiled form
;; this automatically
;; loads "uniline-core.el"
;; or "uniline-core.elc"
;; or "uniline-core.eln"
#+end_src
or if you prefer the Transient interface over the Hydra one:
#+begin_src elisp
(load-file "uniline-transient.el") ;; interpreted form
(load-file "uniline-transient.elc") ;; byte-compiled form
(load-file "uniline-transient.eln") ;; native-compiled form
;; this automatically
;; loads "uniline-core.el"
;; or "uniline-core.elc"
;; or "uniline-core.eln"
#+end_src
You should prefer the byte-compiled or native-compiled forms over the
interpreted forms, because there are a lot of optimizations performed
at compile time.
You may want to give =uniline-mode= a key-binding. A way to do that
without =use-package= is to add those lines to your initialization file
(usually =~/.emacs=):
#+begin_src elisp
(require 'uniline-hydra)
(bind-keys :package uniline-hydra ("C-<insert>" . uniline-mode))
#+end_src
The downside is that =Uniline= will be loaded as soon as =Emacs= is
launched, rather than deferred until invoked.
* Related packages
:PROPERTIES:
:CUSTOM_ID: related-packages
:END:
- =artist-mode=: the ASCII art mode built into =Emacs=.
- =ascii-art-to-unicode=: as the name suggest, converts ASCII drawings
to UNICODE, giving results similar to those of =Uniline=.
- =picture-mode=: as in =Uniline=, the buffer is infinite in east & south
directions.
- =ascii-art-to-unicode= ASCII art to UNICODE in =Emacs=. This is a
standard ELPA package by Thien-Thi Nguyen (rest in peace). =Uniline=
may call it to convert ASCII art drawings to equivalent
UNICODE. =Uniline= arranges to not require a dependency on
=ascii-art-to-unicode= by lazy evaluating a call to =aa2u=.
- =org-pretty-table=: Org Mode tables /appear/ to be drawn in UNICODE
characters (actually they are still in ASCII).
- =boxes=: draws artistic boxes around text, with nice looking unicorns,
flowers, parchments, all in ASCII art.
- =org-drawio=: a bridge between the Draw.Io editor and =Emacs=, producing
drawing similar to those of =Uniline=, but in =.svg=.
- =syntree=: draws ASCII trees on-the-fly from description.
- =unicode-enbox=: create a UNICODE box around a text; input and output
are strings.
- =unicode-fonts=: in =Emacs=, helps alleviate the lack of full UNICODE
coverage of most fonts.
- =org-superstar=: prettify headings and plain lists in Org Mode, using
UNICODE glyphs.
- =charmap=: UNICODE table viewer for =Emacs=.
- =insert-char-preview=: insert UNICODEs with character preview in
completion prompt.
- =list-unicode-display=: list all UNICODE characters, or a selection of
them.
- =show-font=: show font features in a buffer.
- =ob-svgbob=: convert your ascii diagram scribbles into happy little
SVG
- =el-easydraw=: a full featured SVG editor right inside your =Emacs=
- =asciiflow=: (not =Emacs=) draw on the web, then copy-paste your UNICODE text
- =ascii-draw=: like =asciiflow= with Unicodes.
- =dot-to-ascii.ggerganov.com:= (not =Emacs=) describe your schema in the
Graphviz language, and copy-past your UNICODE text.
- =monosketch=: (not =Emacs=) draw on the web, then copy-paste your UNICODE text.
- =ibm-box-drawing-hydra.el=: keyboard interface to insert UNICODE
box-drawing characters one at a time.
- =excalidraw.com=: inline drawing, but not in Unicode or Ascii.
- =org-excalidraw=: integrate SVG images generated by excalidraw into
Org Mode.
- =rcd-box=: create tables surrounded by box-drawing characters from
Lisp descriptions.
- =ob-diagram=: generate various diagrams using diagrams backend.
- =ob-mermaid=: generate Mermaid diagrams within org-mode babel.
- =quail-boxdrawing.el=: input method for box drawing characters.
- =make-box.el=: box around part of a buffer.
- =vim drawit ascii diagrams=: in Vim, in ASCII.
- =MarkDeep=: (Casual Effects): write in Markdown, render on the Web on
the fly. Uniline may be used to author part of the Markdown source.
- =org-utf-to-xetex=: export Org-Mode utf-8 documents to various formats
preserving smileys and other Unicode characters.
- =image-to-ascii=: turns a photo into ASCII.
- =ascii-maze-generator=: web-inline generator of mazes, with the same
lines drawn by Uniline.
- =diagon.arthursonzogni.com=: web-inline drawing of diagrams similar to
those that Unline enables.
- =cascii.html=: a single Html-JavaScript file to draw Unicode diagrams.
- =elm-svgbob=: think of Ditaa for converting Ascii to SVG.
- =rasciigraph=: script to plot time-series in Unicode.
- =asciichart-sharp=: another script to plot time-series in Unicode.
- =d2=, =ob-d2=: think of Mermaid to convert diagram textual descriptions
to SVG.
- =dag-draw.el=: in Emacs program diagrams using Lisp, the output is
Unicode.
- =https://mbork.pl/2025-11-10_ASCII_art_timeline_diagrams=: not a
package, just an example of how easy is to draw time-series in Ascii
with Emacs.
- =pikchr=: describe objects and their relationship in Markdown, render
in Unicode.
- =ob-pikchr.el=: integration of =pikchr= in Emacs Org-Mode.
- =GoAT=: Go-based Text-Art to SVG refinement.
- =SvgBob=: another script to convert Ascii to SVG (like Ditaa). Written
in Rust.
- =Durdraw=: an Ascii, Unicode and Ansi art editor for Unix-like
systems. With colors.
- =figlet=: makes large letters out of ordinary text, in text. There is
an Emacs integration by J. Kotta.
* Author, contributors
:PROPERTIES:
:CUSTOM_ID: author-contributors
:END:
- Thierry Banel, author
Feedback:
- Chris Rayner (@riscy), gave recommendations prior to insertion in
MELPA
- Adam Porter (@alphapapa), suggested submitting =Uniline= to =ELPA=;
should I?
- Joost Kremers https://github.com/joostkremers found a bug in the
minor-mode key-binding definitions, and incompatibility with
- DogLooksGood https://github.com/DogLooksGood gave feedback on
inserting usual characters not moving the cursor
- LuciusChen & lhindir on GitHub, arthurno1 & karthink on Reddit,
pushed toward =Transient= as the default interface instead of =Hydra=
- karthink noted that =Transient= was now built into =Emacs=, loosening
the dependencies conundrum, arthurno1 participated in the =Hydra= -
=Transient= discussion
- karthink pointed to the new =Aporetic= font family, which was then
added to the =Uniline= supported fonts
- rumengling on GitHub found and diagnosed the misaligned lines issue
produced by some "language environments" (see [[#language-environment][Language environment]]).
- tpapp documented the installation using Straight, and fixed some
typos.
- tskinner-oppfi (GitHub) repoted Elpaca packaging break due to a bad
version number.
Contributors:
- JD Smith (jdtsmith on GitHub) rewrote the =:lighter= for added
flexibility (the information in the mode-line about the state of
=Uniline=)
- JD Smith also pointed to =#+begin_src uniline= Org Mode block
suprising behavior (editing its content automatically switches to
=uniline-mode=)
Utilities:
- Oleh Krehel alias abo-abo for his package =Hydra=
- The =Magit= team for the =Transient= library
- Thien-Thi Nguyen (RIP) for his package =ascii-art-to-unicode=
* License
:PROPERTIES:
:CUSTOM_ID: license
:END:
Copyright (C) 2024-2026 Thierry Banel
Uniline 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.
Uniline 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 <http://www.gnu.org/licenses/>.
================================================
FILE: tests/bench01.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
(let ((uniline-infinite-up↑ nil))
(uniline-bench
""
"b RET c
- 3*<right> <down> 5*<left> <up>
C-SPC <down> 6*<right> <insert> <down> 11*<right> 2*RET
<down> 2*<right>
+ <down> 4*<left>
<insert> 2*a
3*<left>
<insert> a S-<up>
8*<left>
RET 2*<up> 14*<right>
- 3*<up>"
"\
│
bc╶┴─╮
╰─┰──╯
━━━━━━△━━◀━━━┛
"))
================================================
FILE: tests/bench02.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
(let ((uniline-infinite-up↑ nil))
(uniline-bench
"\
initial text
good
"
"C-SPC <down> 12*<right> <insert>
2*<down> 3*<right> R
<end> <return>
<down>
<kp-subtract> <right> q
<return> <left>
<kp-subtract> <down> 2*<left>
<return> 2*<up> 2*<right> <down>
<kp-subtract> <up> 3*<right> 3*<up>
<return> 3*<down> 2*<up> <right> 3*<down>
C-SPC 19*<left> 3*<up> <insert>
<up> 2*<down> c
C-a 22*<right> C-SPC <insert> y
2*<return> 3*<down> 4*<right>
= <down> 6*<left>
<insert> s s s
5*<left>
<insert> o
6*<left> <up>"
"\
╷
│
╭────────────╮ ╭──╯ ╭────────────╮ ╭──╯
│initial text│ q │initial text│ q
│ good ├─╯ │ good ├─╯
╰──────╥─────╯ ╰───╥────────╯
╚═════·════▫═════╝
"))
================================================
FILE: tests/bench03.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
(uniline-bench
""
"2*<return>
2*<down> 3*<right>
# 6*<right> 3*<down> 6*<right> 3*<up> 6*<right> 3*<down> 6*<right> 4*<up> 3*<right> 6*<down> 12*<left>
<return> <up> 15*<right> <up>
C-SPC 4*<down> 12*<right> <insert>
r # R
<return> <return> <down> <right>
<kp-subtract> 4*<down> 11*<right> 4*<up> 11*<left>
<return> 23*<left> 2*<up>
C-SPC 4*<down> 16*<right> <insert>
2*<down> 3*<right>
<return> C-c C-c"
"\
▗▄▄▄▄▄▄▄▄▄▄▄▄▖
▐╭──────────╮▌
▄▄ ▐│ │▌
▝▀▀▌ ▛▀▀▌ ▌▐ ▐│ │▌
▙▄▄▌ ▙▄▄▌▐ ▐│ │▌
▗▄▄▄▄▄▟ ▐╰──────────╯▌
▝▀▀▀▀▀▀▀▀▀▀▀▀▘
")
================================================
FILE: tests/bench04.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
(uniline-bench
""
"<return> <down> 4*<right> 2*<down> 4*<right> <down> <left>
M
<left>
= 3*<up> M-6 <right> 3*<down> 2*<left>
<insert> a
M-7 <left>
<insert> 3*a
<left> 3*<down> M-4 <right> 3*<up>
<return> 2*<up> 2*<right>
<insert> s
<down> 2*<right>
<insert> 2*s
3*<down> M-6 <left>
<insert> x
<up>
<insert> 5*o
4*<up> M-9 <right>
C-SPC M-6 <down> M-12 <left>
<insert> c
<right> <return> M-13 <right> M-6 <up>
C-SPC <insert> y <return>"
"\
╔═════╗ ╔═════╗
║ □ ║ ║ □ ║
║ ■ ║ ║ ■ ║
╔←══M═══◁═╝ ╔←══M═══◁═╝
║ ◦ ║ ║ ◦ ║
║ ╳ ║ ║ ╳ ║
╚═══╝ ╚═══╝
")
================================================
FILE: tests/bench05.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
(uniline-bench
"\
aaaa aaaa aaa aaaaaaaa"
"<return> M-> C-SPC M-26 <left> <insert> c
<down> <right> <right> C-SPC <insert> y c
<down> <right> <right> C-SPC <insert> y
<down> <up> c <down> <right> <right> C-SPC <insert> y c
<return> <up> <up> <up> <right>
C-SPC <down> <down> <down> M-9 <right> <kp-subtract> <insert>
C-r <return> M-7 <right> <down>
C-SPC <down> <down> <down> M-6 <right> <kp-add>
<insert> C-r = R RET"
"\
aaaa aa╭─────╦╤╤══aaaaaaaa
aaaa │aaa ║┏┿━━━┓aaaaaaaa
aaa│ aaaa║┃│aaa┃║ aaaaaaaa
a╰─────╫╂╯ a┃a aaaaaaaa
║┗━━━━┛║
╚══════╝
")
================================================
FILE: tests/bench06.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
(uniline-bench
"\
mùq
v
2iii hu
1 u
é u
au uuuu
uuuu
"
"<return> 2*<down> 2*<right>
C-SPC 6*<down> 9*<right>
<insert> c <return>
M-3 <right> M-5 <up>
C-SPC <insert> y <return>
6*<left>
<insert> <return>
<kp-subtract>
<insert> c <return>
7*<right>
j <down> <left>
j 2*<down>
<insert> i SPC"
"\
╭───╮
│mùq╰╮
╭──┴─┬╮v╰─╮ jmùq
│2iii│╰╮hu│ j░░░v
│1╶┬─╯ ╰╮u│ 2iii░░░hu
╰╮é╰╮╭──╯u│ 1░░░░░░░u
│au╰╯uuuu│ é░░░░░░u
╰╮uuuu╭──╯ au░░uuuu
╰────╯ uuuu
")
================================================
FILE: tests/bench07.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
(let ((uniline-infinite-up↑ nil))
(uniline-bench
"\
aaaaa aaaaa
a a a
a a a
a aaaaaa
a
aaaa
a
a
aaaa
a"
" <left> <kp-subtract> <insert> c"
"\
aaaaa╷ ╷aaaaa╷
a╭─╮a│ │a╭───╯
a│ │a╰──╯a│
a│ │aaaaaa│
a╰─┴┬─────╯
aaaa│
╶─╮a│
╶─╯a│
aaaa│
a╭──╯
╶╯
"))
================================================
FILE: tests/bench08.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
(let ((uniline-infinite-up↑ nil))
(uniline-bench
"\
aaaa hhhh hhhh
a hh hh
a hhhhhhhhhh
gggg oooooooo
g o o
g o o
g o o
g o o
gggg o o
gggg o o
g o o
g o o
g ooooo
gggg
"
"<kp-add> <insert> c
<return> 8*<right>
+ <insert> c
<return>
M-< 7*<down> 2*<right>
<kp-subtract> <insert> c
<return> 8*<right> <down>
<kp-subtract> <insert> c
<return> <down> <right> <insert> i *
<return> 2*<up> <kp-subtract> <insert> c"
"\
aaaa╻ ╻hhhh╻ ╻hhhh╻
a┏━━┛ ┗━┓hh┗━━━━┛hh┏━┛
a┃ ┃hhhhhhhhhh┃
╺┛ ┗━━━━━━━━━━┛
╶───╮ ╭────────╮
gggg│ ╭╯oooooooo╰╮
╶─╮g│ │o╭──────╮o│
│g│ │o│******│o│
│g│ │o│******│o│
╶─╯g│ │o│******│o│
gggg│ │o╰╮*****│o│
gggg│ ╰╮o╰╮****│o│
╶─╮g│ ╰╮o╰╮***│o│
│g│ ╰╮o╰───╯o│
╶─╯g│ ╰╮ooooo╭╯
gggg│ ╰─────╯
╶───╯
"))
================================================
FILE: tests/bench09.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
(let ((uniline-infinite-up↑ nil))
(uniline-bench
"\
ggggggggggg gg ggggg
ggggggg g g
g gggggggggg
g
ggggggg pppppp
g pppppp
ggggg
gg
g
gggg
g
"
"<return> 5*<down> <kp-subtract> <insert> c"
"\
ggggggggggg╷ gg ╷ggggg╷
ggggggg╭─╮g╰──────╯g╭───╯
╶────╮g│ │gggggggggg│
╶────╯g│ ╰──────────╯
ggggggg│pppppp
g╶───┬─╯pppppp
ggggg│
╶─╮gg│
╶─╯g╭╯
gggg│
g╭──╯
╶╯
"))
================================================
FILE: tests/bench10.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
;; a macro cannot be defined inside another macro,
;; so this macro is defined outside
(setq last-kbd-macro (kbd "<kp-subtract> <right> C-<up> C-<right> <down>"))
(uniline-bench
"\
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
"
"<return> <right> <down>
C-x 4*e 3*<right> 3*<down> 5*<left> 3*<up>
<return>"
"\
╭╮╭╮╭╮╭╮╭╮╭╮
╶┼┴╯╰╯╰╯╰╯╰╯│
╰╴aaaaaaaaa╶╮aaaaaaa
╭aaaaaaaaaaa╯aaaaaaa
╰╴aaaaaaaaa╶╮aaaaaaa
╭╯ ╭╯
╰╮ ╰╮
│╭╮╭╮╭╮╭╮╭─╯
╰╯╰╯╰╯╰╯╰╯
")
================================================
FILE: tests/bench11.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
;; Check if TABs are converted to SPC when drawing on them
(uniline-bench
"\
tabs ?
2 tabs "
"<return> M-> <left> <right> <down> M-<
<return> 2*<down> <home>
<kp-subtract> 2*<right> 2*<up> 10*<right> 2*<down>
<insert> s <return>"
"\
╭─────tabs╮?
│ │ 2 tabs
╶─╯ □
")
================================================
FILE: tests/bench12.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
;; Test rectangle fill
(uniline-bench
""
"<return> 3*<down> 5*<right>
6*S-<down> 11*S-<right> <insert> i SPC
<return> 3*<down> 7*<right> <down> 6*S-<down> 8*S-<right> <insert> i y
<return> 2*<up> 5*S-<left> S-<up> <insert> i SPC
<return>"
"\
░░░░░░░░░░░
░░▒▒▒▒▒░░░░
░░▒▒▒▒▒░░░░
░░░░░░░░░░░
░░░░░░░yyyyyyyy
░░░░░░░yyyyyyyy
░░░░░░░yyyyyyyy
yyyyyyyy
yyyyyyyy
yyyyyyyy
yyyyyyyy
")
================================================
FILE: tests/bench13.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
;; Test glyph insertion
(uniline-bench
""
"<kp-subtract> <return>
2*<down> 4*<right>
<kp-subtract> 4*<right> 2*<down>
<insert> a
2*<up> 3*<right> <up>
<insert> s
<down> 3*<right> 2*<down> 3*<right> 3*<left> 2*<down>
<insert> 2*o
2*<up> <return> 2*<left> <up> 4*<left> <up>
<insert> 2*S-<right> 3*<right>
<insert> 2*S-<right> 3*<right> 2*<down>
<insert> 2*S-<down> 3*<right>
<insert> 2*S-<right>
<return>
13*<left> 2*<up> <insert> 2*S-<left>"
"\
□
╾───╮──╯──╮
│ │
▽ ╰──╼
│
∙
")
================================================
FILE: tests/bench14.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
(uniline-bench
""
"<return> <return>
2*<down> 3*<right> 12*S-<right> 6*S-<down> # <insert> r
<return> <insert> S-<down>
<return> 4*<right>
<return> 4*<right> <insert> S-<left> 5*<right>
<insert> S-<up> S-<right> S-<left> S-<up> 3*<down>
<insert> S-<left> 3*<down>
<insert> 2*S-<right> S-<down> S-<up> 5*<left>
<insert> S-<up> S-<right> 6*<left>
<insert> S-<up> S-<right> S-<left>
<return> 3*<up>
<kp-subtract> 4*<right>
<insert> a
S-<up> 2*<right>
<insert> a
S-<down> 2*<right>
<insert> a
S-<left> 2*<right>
<insert>
2*S-<left>
<return> 9*<left>
<return> <insert> 2*S-<right> 2*S-<left> S-<up> S-<right> 2*<right>
<insert> 2*S-<right> S-<up> S-<down>
<return>"
"\
█▀▀▀▀▀▛▀▀▀▀▙
▌ ▐
▌ ▐
▌╰─┤△─▽─◁─╴▟
▌ ▐
▌ ▐
▐▄▄▄▄▄█▄▄▄▄▛
")
================================================
FILE: tests/bench15.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
(uniline-bench
""
"<return> 3*<down> 10*<right>
<kp-subtract>
2*<up> 3*<right> <insert> a
<right> <insert> 2*a
<right> <insert> 3*a
<right> <insert> 4*a
<right> <insert> 5*a
<right> <insert> 6*a
3*<right> <down> <insert> A
<down> <insert> 2*A
<down> <insert> a
<down> <insert> 4*A
<down> <insert> a S-<right>
<down> 3*<left> <insert> A
<left> <home>
<return> 18*<right> <left> <insert> A S-<up>
<left> <insert> a
2*<left>
<kp-subtract> 2*<left> <insert> 4*A
2*<left> <up> <insert> 3*a S-<left>
<up> <insert> a S-<right>
<up> <insert> 3*a
<up> <insert> 5*a
<up> <insert> 6*a S-<right>
3*<right>
<return>
<insert> a
3*<right> <up> <insert> S-<down>
3*<down> 5*<right> <insert> S-<right>
<return>"
"\
╭──▷▶→▿▸↔──╮
↔──▷ ↕
▴ ▾
↑ ▷
▷ ↓
← ▷
╰─←─╴ ◁↕↔──╯
")
================================================
FILE: tests/bench16.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
(uniline-bench
""
"<return>
<down> 5*<right> <insert> a
2*<right> <insert> A
2*<right> <insert> a S-<up>
2*<right> <insert> A S-<up>
<down> C-a <up> C-k C-y
<down> <home> C-y
<left> <insert> a
2*<left> <insert> a
2*<left> <insert> a
2*<left> <insert> a
C-a C-k C-y
<right> <down> C-a C-y
C-a 5*<right> <insert> 2*A
2*<right> <insert> A
2*<right> <insert> 2*A
2*<right> <insert> A"
"\
▷ ↔ △ ↕
▶ ◁ ▲ ◁
↔ ↔ ↕ ↔ ")
================================================
FILE: tests/bench17.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
(uniline-bench
""
"3*<right> a b c
C-SPC 3*C-b <insert>
2*<left> <return>
<left> 2*<down> d e f
C-SPC C-a <insert>
<up> <right> <return>
C-SPC 3*S-<right> S-<up> <insert> 2*<down> <left> <up> <return>"
"\
╭
abc
def
")
================================================
FILE: tests/bench18.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
;; Check # blocks contour
(uniline-bench
""
"<return>
<down> 3*<right> 6*t
<down> 3*<left> 2*y
<down> 5*<left> 4*r
<down>
<insert> C-<left> 4*o
<right> <down> 5*o
<insert> C-<right> 2*<up> 5*<right>
# <insert> c"
"\
▗▄▄▄▄▄▄▖
▐tttttt▌
▐███yy▛▘
▐rrrr█▌
▗▄▟█oooo▌
ooooo▛▀▀▘
▀▀▀▀▀▘
")
================================================
FILE: tests/bench19.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
(let ((uniline-infinite-up↑ nil))
(uniline-bench
""
"<return> 3*<right>
<insert> C-<down> tttt
<insert> C-<right> yyyyyy
<insert> C-<up> hhhhh
<insert> C-<right> gggg
<insert> C-<down> aaaaaaaa
<insert> C-<left> bbbbbbbbbbbbbb
<insert> C-<up> ff
<insert> C-<right> ffff
<home> 5*<down> dddddd
<home> <down> ggggg
<down> <home> 4*SPC hhh
<down> <home> 5*SPC iiii
<down> <home> hh
<down> <left> hhhhhh
<up> <left> h
<home>
M-< 19*<right> ii
<down> <left> ii
<down> <left> ii
<down> <left> iiii
<up> <left> ii
<up> <left> ii
<up> <left> ii
# <insert> c
M-< <return> 3*<right> # <insert> c
<return> <home> 12*<down> <end> C-a M-f <insert> c
# <insert> c"
"\
▐t▖ ▐gggga▖ ▐ii▄▖ ▗▟ii▖
▐t▌ ▐h▛▀▜a▌ ▝▜ii▙▖▗▟ii▛▘
▐t▌ ▐h▌ ▐a▌ ▝▜ii▙▟ii▛▘
▐t▙▄▄▄▟h▌ ▐a▌ ▝▜iiii▛▘
▐yyyyyyh▌ ▐a▌ ▝▀▀▀▀▘
▗▄▟█▛▀▀▀▀▀▘ ▐a▌
ffff▌ ▐a▌
f███▙▄▄▄▄▄▄▄▟a▌
fbbbbbbbbbbbbb▌
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▘
▗▄▄▄▄▄▖
dddddd▌
ggggg█▙▖
▀▀▀▜hhh▙▄▖
▗▄▖▝▜iiii▌
hh▙▄▟█h▛▀▘
▜hhhhhh▌
▝▀▀▀▀▀▀▘
"))
================================================
FILE: tests/bench20.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
;; Test fine tweaking
(uniline-bench
""
"<return>
2*<down> 2*<right>
<kp-subtract> 4*<right> <down> 3*<right>
<kp-add> 2*<up> 3*<left>
= 4*<left> 4*<down> <right>
<kp-add> 5*<right> 2*<up>
<insert> 2*S-<left>
<return>
<up> <return> <left> <up> 2*<left>
<insert> S-<up> S-<left>
<end> <return> 2*<right> <down>
# 8*<right> 3*<left>
<insert> S-<up>
<down> <left>"
"\
╔══╘═━━┓
╟───╮ ╻┃ ▝▀▟▀▘
║ ╰─┠┚
║ ┃
╚━━━━━┛
")
================================================
FILE: tests/bench21.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
;; Test rectangle tracing & moving
(uniline-bench
""
"<return>
5*<down> 11*<right>
15*S-<down> 31*S-<right> 2*S-<left>
# <insert> R
2*<return>
<down> <right>
<down> 3*<right> <left>
13*S-<down> 25*S-<right> S-<left> S-<right>
<insert> # r
2*<return>
2*<down> 3*<right>
9*S-<down> 18*S-<right>
<insert> <kp-subtract> r
2*<return>
2*<down> 3*<right>
5*S-<down> 11*S-<right>
<insert> S-<right> = r
2*<return>
5*<up> 2*<down> 3*<right> 4*<up>
19*S-<down> 16*S-<left>
<insert> 2*<left>
2*<return>
9*S-<down> 27*S-<right> 12*S-<right>
<insert> 2*<up>
2*<return>
15*<down> 2*<up>
27*S-<right> 12*S-<right> 9*S-<down>
<insert> 2*<down>
2*<return>
19*<right> 9*<down>
24*S-<up> 21*S-<right>
<insert> 2*<right>
<return>"
"\
▗▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▖
▐ ▌
▐ ▛▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▜ ▌
▐ ▌ ▐ ▌
▐ ▌ ╭────────────────────╮ ▐ ▌
▐ ▌ │ │ ▐ ▌
▐ ▌ │ ╔═════════════╗ │ ▐ ▌
▐ ▌ │ ║ ║ │ ▐ ▌
▐ ▌ │ ║ ║ │ ▐ ▌
▐ ▌ │ ║ ║ │ ▐ ▌
▐ ▌ │ ║ ║ │ ▐ ▌
▐ ▌ │ ║ ║ │ ▐ ▌
▐ ▌ │ ║ ║ │ ▐ ▌
▐ ▌ │ ║ ║ │ ▐ ▌
▐ ▌ │ ║ ║ │ ▐ ▌
▐ ▌ │ ╚═════════════╝ │ ▐ ▌
▐ ▌ │ │ ▐ ▌
▐ ▌ ╰────────────────────╯ ▐ ▌
▐ ▌ ▐ ▌
▐ ▙▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▟ ▌
▐ ▌
▝▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▘
")
================================================
FILE: tests/bench22.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
(uniline-bench
""
"<return> 2*<down> M-3 <right>
<kp-subtract> M-9 <right> M-3 <down> M-5 <left> M-4 <up> M-11 <right> 2*<down>
<insert> a
<down> 3*<left> <down>
<insert> 2*a
<down> 3*<left>
<insert> 3*a
3*<left>
<insert> 4*a
4*<left> <up>
<insert> 5*a
2*<up> 2*<left> <up>
<return> <up>
C-x SPC M-5 <down> M-16 <right>
<insert> c <return>
M-5 <up> M-4 <right>
C-SPC <insert> y s h <return>
3*A M-3 <left>
<return> M-20 <right>
C-SPC <insert> y s 4 <return>
3*B M-3 <left>
C-SPC <insert> y s + 4 <return>
3*C M-3 <left>
M-15 <down> M-40 <left>
C-x SPC <insert> y s + <return>
3*D M-3 <left>
M-0 <right> M-8 <up>
C-x SPC y <backspace>
C-x SPC <insert> y s 3 <return>
3*E M-3 <left>
M-20 <right>
C-x SPC <insert> y s 4 <return>
3*F M-3 <left>
M-19 <right>
C-SPC <insert> y s + <return>
3*G M-3 <left>
M-3 <down> <return> M-5 <down> <left>
<return>
M-18 <left>
C-x SPC <insert> y s + 3 <return>
3*H M-3 <left>
"
"\
╭──────────╮ AAA ┌──────────┐ CCC ┏┉┉┉┉┉┉┉┉┉┉┓
╭───┼────╮ │ ┌───┼────┐ │ ┏┉┉┉╋┉┉┉┉┓ ┋
╰─╮ │ │ ▽ └─┐ │ │ ▽ ┗┉┓ ┋ ┋ ▼
│ │ │ ╭──╯ │ │ │ ┌──┘ ┋ ┋ ┋ ┏┉┉┛
▴ ╰────╯ ▼ ▴ └────┘ ▼ ▴ ┗┉┉┉┉┛ ▼
╰───◃──←──╯ └───◃──←──┘ ┗┉┉┉◂┉┉←┉┉┛
EEE ╭╌╌╌╌╌╌╌╌╌╌╮ FFF ╭┈┈┈┈┈┈┈┈┈┈╮ GGG ┏━━━━━━━━━━┓
╭╌╌╌┼╌╌╌╌╮ ┆ ╭┈┈┈┼┈┈┈┈╮ ┊ ┏━━━╋━━━━┓ ┃
╰╌╮ ┆ ┆ ▽ ╰┈╮ ┊ ┊ ▽ ┗━┓ ┃ ┃ ▼
┆ ┆ ┆ ╭╌╌╯ ┊ ┊ ┊ ╭┈┈╯ ┃ ┃ ┃ ┏━━┛
▴ ╰╌╌╌╌╯ ▼ ▴ ╰┈┈┈┈╯ ▼ ▴ ┗━━━━┛ ▼
╰╌╌╌◃╌╌←╌╌╯ ╰┈┈┈◃┈┈←┈┈╯ ┗━━━◂━━←━━┛
DDD ┏━━━━━━━━━━┓ HHH ┏╍╍╍╍╍╍╍╍╍╍┓
┏━━━╋━━━━┓ ┃ ┏╍╍╍╋╍╍╍╍┓ ┇
┗━┓ ┃ ┃ ▼ ┗╍┓ ┇ ┇ ▼
┃ ┃ ┃ ┏━━┛ ┇ ┇ ┇ ┏╍╍┛
▴ ┗━━━━┛ ▼ ▴ ┗╍╍╍╍┛ ▼
┗━━━◂━━←━━┛ ┗╍╍╍◂╍╍←╍╍┛
")
================================================
FILE: tests/bench23.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
(uniline-bench
""
"<return>
2*<down> 5*<right>
M-x p i c t u r e - m o d e <return>
5*S-<down> 19*S-<right> 2*S-<left> C-c C-r
3*<up> 10*<left>
5*S-<down> 17*S-<right> C-c C-r
6*<up>
M-x M-p <end>
( <backspace>
-
r <backspace>
e x i t <return>
<up> <home> 2*<right> 5*<down> 10*<right>
<insert> <kp-add>
5*<up> 10*<left> <up>
9*S-<down> 27*S-<right> 2*S-<right>
<insert> s <kp-0>
<return>
<down> 6*<right> <up>
M-x 2*M-p <return>
5*S-<down> 9*S-<right> C-c C-r
M-x 2*M-p <return>
6*<left>
<insert> <kp-add>
5*<up> 10*<left> 5*<right>
7*S-<down> 7*S-<right>
<insert> s <kp-0> <kp-add>
<return>
4*<down> 8*<right>
5*S-<down> 10*S-<right>
<insert> s = <return>"
"\
┏━━━━━---+
╭──━┃━━━━━───|───╮
│ ┃ | │
│ ┃ ┏━━───|──────────╮
│ ┃ ┃ | ║ │
│ ┗━━╋━━---+ ║ │
╰──━━━━╋━━─══════╝ │
┃ │
╰───══════════───╯
")
================================================
FILE: tests/bench24.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
;; Test undo
(uniline-bench
"\
uuuuuu
uuuuuu"
"M-> #
2*<down> 2*<left> <down> 7*<left> <up> 3*<left>
6*C-_
2*<down>
<return>
4*<up>
<kp-subtract>
3*<left> M-4 <down> M-6 <right> 4*<up> 4*<left>
<return> 3*<left>
4*S-<down> M-8 S-<right>
<insert>
2*<right>
s <kp-add>
<kp-3>
5*C-_
2*<return>"
"\
╭─────╮
uuuuuu │
│uuuuuu▖
│ ▗▄▄▛▘
╰──▐──╯
")
================================================
FILE: tests/bench25.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
(uniline-bench
""
"<return>
<down> 2*<right> 5*a
<down> <left> 5*a
<down> M-7 <left> 5*t
2*<up> M-5 <right>
2*S-<down> M-7 S-<right>
<insert> <kp-subtract> r
2*<return>
<down> 4*<right> <insert> 3*o
<up> M-4 <left>
C-SPC 2*<down> M-7 <right>
<insert> c
<return>
M-5 <left> <up>
C-x SPC <insert> y
2*<return>
M-12 <left> <up> <left> <up>
C-SPC <insert> y
<return>
3*<down> 3*<right> <up>
C-SPC <insert> y
<return>"
"\
╭─────╮
a│aaa• │ ╭─────╮
╰──╭─────╮ │ ╭─────╮
tt│tt • │ ╰─│───• │
╰─────╯ ╰─────╯
")
================================================
FILE: tests/bench26.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
;; Test ASCII → Uniline base-line
(uniline-bench
"\
+-------------+ +--------------+
/-----/ Rectangle 1 |-------------+ Rectangle 2 |
| | <sin^gle> | | \"quote\" +-
| \\-------------/ /-----\\ +--------------+
| \\--+-------+--/ | vav | +----\\-----/---+
\\>--\\ | | \\-----/ | |
v \\-->----/ +--<--+
"
"<return> M-< <down> C-SPC M-> <up> <home> 54*<right> <insert> s <kp-0> <return>"
"\
╭─────────────╮ ╭──────────────╮
╭─────┤ Rectangle 1 │─────────────┤ Rectangle 2 │
│ │ <sin^gle> │ │ \"quote\" ├─
│ ├─────────────┤ ╭─────╮ ├──────────────┤
│ ╰──┬───────┬──╯ │ vav │ ╰────┬─────┬───╯
╰▷──╮ │ │ ╰─────╯ │ │
▽ ╰──▷────╯ ╰──◁──╯
")
================================================
FILE: tests/bench27.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
;; Test drawing right onto a TAB and also on the trail of a TAB
;; (drawing should occur)
;; Test writing on those 2 characters ╜┡ which are in 2-entries buckets
;; in the uniline--char-to-4halfs hash-table
(uniline-bench
""
"<escape> 3 0 <right>
<escape> 2 0 <down>
<return> 16*<up> 8*<left>
= 2*<down> <left> <right>
<kp-add> <down> <right> <left> <down> <return> <up>
<insert> 2*S-<down>
<up>
<insert> S-<down> S-<left>
C-x h M-x t a b i f y <return>
<kp-subtract> 8*<right> 10*<down> 14*<right> <return> 2*<up> 2*<right>
C-SPC 4*C-p 3*C-b
<insert> c
<return>
4*<right>
C-SPC <insert> y
<return>
<right> <up> <kp-subtract> 7*<down> 4*<left>"
"\
╶───────┬─────────────────────╮
│ │
│ │
│ ╷ │
│ ╻ │ │
│ ║ ║ │
│ ╺╜ ╺┤ │
│ ┡╸ ┝╸ │
│ ╹ │ │
│ │ │
╰─────────────────╯ │
│
│
│
│
│
│
│
│
│
╵
")
================================================
FILE: tests/bench28.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
(let ((uniline-infinite-up↑ nil))
(uniline-bench
"aa\t\ta\t\ta\t\t\t\t \t\t a
"
"2*C-k 29*C-y
M-< 5*<right> <down> 3*<right> <down> 2*<right>
7*<right> 2*<down> 9*<left> <up> 16*<right> 2*<up> 12*<right> 3*<down> 10*<left>
5*C-n 14*r <return>
12*<left> <down> 16*r
14*<left> <down> 12*r
2*C-b <kp-subtract> <insert> c
<return> 12*<right> 3*<down> 11*r
<down> 9*<left> 12*r
<down> 8*<left> 8*r
<down> 2*<left> 2*r
<down> <left> 3*r
C-SPC 3*<left> <up> 2*<left> 3*<up> 11*<left> <insert>
2*<right> c
<return> 4*<right> 6*<down> 10*<left>
C-SPC <insert> y
<down> <right> <return>"
"\
aa───╮ a ╷a╷ a
aa ╰──╮ a ╭──────┼a┼──╮ a
aa ╰───────a╮ │ │a│ │ a
aa ╭───────a┼──────╯ │a│ │ a
aa ╰───────a╯ ╶────┼a┼──╯ a
aa a │a│ a
aa a │a│ a
aa a │a│ a
aa a ╭───────╯a╰────╮ a
aa a │rrrrrrrrrrrrrr╰───╮ a
aa a ╰─╮rrrrrrrrrrrrrrrr│ a
aa a ╰─╮rrrrrrrrrrrr╭─╯ a
aa a ╰───╮a╭──────╯ a
aa a │a│ a
aa a │a│ rrrrrrrrrrr a
aa a │a│ rrrrrrrrrrrr a
aa a │a│ rrrrrrrr a
aa a │a│ rr a
aa a │a│ rrr a
aa a │a│ a
aa a │a│ a
aa a │a│ rrrrrrrrrrr a
aa a │a│ rrrrrrrrrrrr a
aa a │a│ rrrrrrrr a
aa a │a│ rr a
aa a │a│ rrr a
aa a │a│ a
aa a │a│ a
aa a │a│ a
╰─╯
"))
================================================
FILE: tests/bench29.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
(uniline-bench
"\
aaaaaa
aaaaaaaa
aaaa aaaa
aaa
aa"
"<return> 2*<down> 3*<right>
- 2*<right>
~ 3*<right> 2*<up> 2*<right> 3*<down> 5*<left> <down> 2*<right> <up> 2*<right> <down> 3*<right>
~ 2*<right> 2*<up> 2*<right> <down> 4*<left> 3*<down>
<kp-add> 5*<right> 2*<up> 5*<right>
~ 3*<right> 2*<down>
~ 5*<left> 2*<down> 4*<right> <up> <left> 2*<up> 8*<right>
<return> 3*<right> 3*<up>
C-SPC 2*<down> 6*<right> <insert> r
<kp-subtract> r <return>
<return> <end> <left> <kp-add>
<insert> c"
"\
╭┄╮
┆ ┆ ┏┅┅┅┅┅┅┓
╶─┄┄┄╯ ┆ ╭┈╮ ╭┄┄┄┄╮ ┇aaaaaa┗┅┅┓
╭┄┬┄┬╯ ╭┈┼┈╯ ┆ ┆ ┗┓aaaaaaaa┗┅┅┓
╰┄╯ ╰┄┄┼┈╯ ┏┉┉┉┉━━━┓ ╰┄┄┄┄╯ ┗┅┓aaaa╻aaaa┇
┊ ┋ ┏┅╋┅┅┅┅┅╸ ┗┅┅┅┅┻┓aaa┇
┕┉┉┉┉┛ ┏┅┅╋┅┛ ┇aa┏┛
┇ ┗┓ ┗┅┅┛
┗┅┅┅┛
")
================================================
FILE: tests/bench30.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
;; Test leakage when moving a rectangle down, from all styles of lines:
;; plain, 3-dotted, 4-dotted, thin and thick, double, block
(uniline-bench
""
"<return> 2*<down> 5*<right>
<insert> s <return>
<kp-subtract> 6*<right> 5*<down>
<kp-add> 5*<right> 5*<up>
= 5*<right> 5*<down>
# 11*<right> 9*<up> <return>
<right> C-SPC 5*<down> 23*<left> <insert> c
5*<up> 24*<right> C-SPC <insert> y s <kp-3> <return>
24*<right> C-SPC <insert> y s <kp-4> <return>
5*<down> 25*<right> <down> C-SPC 4*<up> 74*<left> <down> <insert>
<down>
~ <down>
<return>"
"\
□─────╮ ╔════╗ ▗ □╌╌╌╌╌╮ ╔╍╍╍╍╗ ▗ □┈┈┈┈┈╮ ╔┉┉┉┉╗ ▗
│ ┃ ║ ▐ ┆ ┇ ┇ ▐ ┊ ┋ ┋ ▐
│ ┃ ║ ▐ ┆ ┇ ┇ ▐ ┊ ┋ ┋ ▐
│ ┃ ║ ▐ ┆ ┇ ┇ ▐ ┊ ┋ ┋ ▐
│ ┃ ║ ▐ ┆ ┇ ┇ ▐ ┊ ┋ ┋ ▐
│ ┃ ║ ▐ ┆ ┇ ┇ ▐ ┊ ┋ ┋ ▐
│ ┃ ║ ▐ ┆ ┇ ┇ ▐ ┊ ┋ ┋ ▐
┕━━━━┛ ╹▀▀▀▀▀ ┕╍╍╍╍┛ ╹▀▀▀▀▀ ┕┉┉┉┉┛ ╹▀▀▀▀▀
")
================================================
FILE: tests/bench31.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
;; Test leakage when moving a rectangle right, and another left.
;; Test for all styles of lines:
;; plain, 3-dotted, 4-dotted, thin and thick, double, block
(uniline-bench
""
"<return> 2*<down> 3*<right> <insert> 4*o <return>
<kp-subtract> 6*<right> <down>
<kp-add> 6*<left> <down>
= 6*<right> <down>
# 12*<left> 2*<down>
<return> <down> <left>
C-SPC 5*<up> 9*<right> <insert> c
8*<left> 5*<down> <left> C-SPC <insert> y s <kp-3> <return>
5*<down> C-SPC <insert> y s <kp-4> <return>
5*<down> 9*<right> C-SPC 16*<up> 4*<left> <insert>
<right> ~ <right> <return>
7*<left> <right> C-SPC 16*<down> 4*<right> <left> <insert>
<left> ~ <left> ~ <left> <return>"
"\
●──────────╮
┏━━━━━━━━━━┙
╚══════════╗
▛▀▀▀▀▀▀▀▀▀▀╹
▘
●╌╌╌╌╌╌╌╌╌╌╮
┏╍╍╍╍╍╍╍╍╍╍┙
╚╍╍╍╍╍╍╍╍╍╍╗
▛▀▀▀▀▀▀▀▀▀▀╹
▘
●┈┈┈┈┈┈┈┈┈┈╮
┏┉┉┉┉┉┉┉┉┉┉┙
╚┉┉┉┉┉┉┉┉┉┉╗
▛▀▀▀▀▀▀▀▀▀▀╹
▘
")
================================================
FILE: tests/bench33.el
================================================
;;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
;; Test leakage when moving a rectangle down, from all styles of lines:
;; plain, 3-dotted, 4-dotted, thin and thick, double, block
(let ((uniline-infinite-up↑ t))
(uniline-bench
""
"<insert> s <right>
<kp-add> 2*<right> <down> 3*<right> 3*<up> 3*<right> 2*<down>
# 6*<right> 2*<up> 3*<right> 4*<up> 5*<right> <down> 4*<right> 4*<down> 2*<right>
<return> 4*<right> 3*a <up> 2*<left> 6*a <up> 4*<left> 6*a 4*SPC 4*a <down> 3*<left> 2*a <down> 3*<left> <right>
<insert> C-<left> 9*a <right>
<kp-subtract> <insert> c
<return> 8*<right> 2*<up> 4*r <down> 5*<right> <left> 4*r
C-SPC 5*<right> <up>
<insert> 3*<right> 2*<up> 3*<right>
<insert> R
<kp-subtract> r
<return> 3*<down> 12*<right>
<return> <right>
<insert> C-<up> 5*y
<insert> C-<right> 3*y"
"\
yyy
y
╭─────╮ y
│ rrrr│ y
╭──────╮ ╭────╮ │ rrrr│ y
▐▀▀▙▄▖ ╭─╯aaaaaa│ │aaaa│ ╰─────┴─────╴y
┏━━┓ ▐ ▌ ╭╯aaaaaa╶─┴──┴╴aa╭╯
┃ ┃ ▛▀ ▙▖ │aaa╭─╮aaaaaaaaa╭╯
□╼━┓ ┃ ╹▀▀▘ ╰───╯ ╰─────────╯
┗━━┛
"))
================================================
FILE: tests/uniline-bench.el
================================================
;;; uniline-bench.el --- Regression tests for Uniline -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; Package-Requires: ((emacs "29.1") (hydra "0.15.0"))
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Running regression tests
;; Just load this file:
;; (load "uniline-bench.el")
;; or
;; (eval-buffer)
;;
;; If OK, a message is displayed
;; If ERROR, two windows are displayed, the actual and the expected sketchs
;; with points on the first difference.
;; Creating a new bench
;; - Eval (uniline-bench-create)
;; - Draw a sketch
;; - When done, type $
;; A Lisp buffer implementing the new test is displayed
;; - Save it permanently in a file ending in *.el along with this one
;;; Code:
(defvar uniline-bench-result
nil
"Boolean where a bench result is stored.
t if the bench ran as expected.
nil if there was an error.")
(defun uniline-bench (initial commands result)
"Run a bench.
COMMANDS is a string describing a sequence of keyboard strokes,
supposed to draw a sketch using uniline minor-mode.
Its format is the one used to store keyboard macros.
RESULT is a string representing the expected result.
INITIAL is the initial content of the buffer"
(ignore-errors (kill-buffer "*uniline-interactive*"))
(switch-to-buffer "*uniline-interactive*")
(insert initial)
(goto-char (point-min))
;; (set-default 'uniline-hint-style 1)
(let ((uniline-show-welcome-message nil))
(uniline-mode 1))
(if (fboundp 'hydra-keyboard-quit)
(hydra-keyboard-quit)) ;; clear any left-over from previous bench
(if (fboundp 'transient-quit-all)
(transient-quit-all)) ;; clear any left-over from previous bench
(setq uniline--which-quadrant (uniline--char-to-4quadb ?▘))
(uniline-set-brush-1)
(uniline-set-brush-0dots)
(execute-kbd-macro (kbd commands))
(when nil
;; ignore trailing spaces in resulting drawing
(goto-char (point-min))
(replace-regexp (rx (+ space) eol) "")
;; ignore trailing spaces in expected result
(setq result (replace-regexp-in-string (rx (+ space) eol) "" result)))
(setq uniline-bench-result
(string-equal
(buffer-substring (point-min) (point-max))
result))
(unless uniline-bench-result
(delete-other-windows)
(switch-to-buffer "*uniline-interactive*")
(goto-char (point-min))
(ignore-errors (kill-buffer "*uniline-expected*"))
(switch-to-buffer-other-window "*uniline-expected*")
(insert result)
(goto-char (point-min))
(compare-windows nil))
uniline-bench-result)
(defun uniline-bench-create ()
"Interactively create a bench.
An empty buffer is made available, with uniline mode active.
Draw a sketch.
When done, type $.
A Lisp buffer able to automatically re-run the drawing is presented.
Save it in a *.el file along with other benches."
(interactive)
(ignore-errors (kill-buffer "*uniline-interactive*"))
(switch-to-buffer "*uniline-interactive*")
(local-set-key "$" (lambda () (interactive) (throw 'exit nil)))
(message "draw the initial state of buffer, prior to invoking Uniline, type $ when done")
(recursive-edit)
(setq uniline-initial-text
(buffer-substring-no-properties (point-min) (point-max)))
(goto-char (point-min))
(uniline-mode)
(local-set-key "$" 'uniline-bench-collect)
(message "draw a sketch, type $ whend done")
(kmacro-start-macro nil))
(defun uniline-bench-collect ()
"Called when typing $ to close the interactive drawing.
Do not call it directly."
(interactive)
(kmacro-end-macro 1)
(ignore-errors (kill-buffer "b.el"))
(switch-to-buffer "b.el")
(insert "(uniline-bench\n\"\\\n" uniline-initial-text "\"\n\n\"")
(insert (key-description (kmacro--keys (kmacro last-kbd-macro))))
(insert "\"\n\n\"\\\n")
(insert-buffer-substring "*uniline-interactive*")
(insert "\")\n")
(lisp-mode))
(defmacro uniline-bench-numcompact-n (n)
"If N is 3, replace in the buffer <left> <left> <left> to 3*<left>
It is a macro, because N is a dynamic variable which must be
inserted into a regexp."
`(save-excursion
(replace-regexp
(rx " "
(group (+ (any "a-zA-Z<>-")))
(= ,(1- n)
(+ blank)
(backref 1)))
,(format " %d*\\1" n))))
(defun uniline-bench-numcompact ()
"Change <left> <left> <left> to 3*<left>.
The `kbd' function understand this notation.
And it is easier to read.
Put the cursor inside the string to modify."
(interactive)
(search-backward "\"")
(narrow-to-region (point) (progn (forward-sexp) (point)))
(goto-char (point-min))
(uniline-bench-numcompact-n 27)
(uniline-bench-numcompact-n 26)
(uniline-bench-numcompact-n 25)
(uniline-bench-numcompact-n 24)
(uniline-bench-numcompact-n 23)
(uniline-bench-numcompact-n 22)
(uniline-bench-numcompact-n 21)
(uniline-bench-numcompact-n 20)
(uniline-bench-numcompact-n 19)
(uniline-bench-numcompact-n 18)
(uniline-bench-numcompact-n 17)
(uniline-bench-numcompact-n 16)
(uniline-bench-numcompact-n 15)
(uniline-bench-numcompact-n 14)
(uniline-bench-numcompact-n 13)
(uniline-bench-numcompact-n 12)
(uniline-bench-numcompact-n 11)
(uniline-bench-numcompact-n 10)
(uniline-bench-numcompact-n 9)
(uniline-bench-numcompact-n 8)
(uniline-bench-numcompact-n 7)
(uniline-bench-numcompact-n 6)
(uniline-bench-numcompact-n 5)
(uniline-bench-numcompact-n 4)
(uniline-bench-numcompact-n 3)
(uniline-bench-numcompact-n 2)
(widen))
(defun uniline-bench-run (&rest files)
"Run all benches, or a specified list.
When FILES is nil, the benches are all files with *.el suffix.
Stops on the first error, presenting two buffers,
- one with the actual drawing,
- the other with the expected drawing,
with points on the first difference.
If there are no errors, a summary is presented."
(interactive)
(unless files
(setq files
(directory-files "." nil "^bench.*\\.el$")))
(let* ((buf (current-buffer))
(nbpassed 0)
(nbfailed 0)
(failed
(cl-loop
for file in files
do
(load (format "%s%s" default-directory file) nil nil t)
(if uniline-bench-result
(cl-incf nbpassed)
(cl-incf nbfailed)
(message "%s FAILED" file))
unless uniline-bench-result
collect file)))
(switch-to-buffer buf)
(message "%s PASSED / %s FAILED %s" nbpassed nbfailed failed)))
(pcase 0
(0
(uniline-bench-run))
(1
(garbage-collect)
(profiler-start 'cpu+mem)
(uniline-bench-run)
(profiler-stop)
(profiler-report))
(2
(garbage-collect)
(elp-instrument-package "uniline")
(uniline-bench-run)
(elp-results)
(elp-restore-all)))
(if nil
(uniline-bench-run "bench26.el" "bench27.el"))
(provide 'uniline-bench)
;;; uniline-bench.el ends here
================================================
FILE: uniline-core.el
================================================
;;; uniline-core.el --- Add▶ ■─UNICODE based diagrams─■ to▶ ■─text files─■ -*- coding:utf-8; lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Thierry Banel
;; Author: Thierry Banel tbanelwebmin at free dot fr
;; Version: 1.0
;; Package-Requires: ((emacs "29.1"))
;; Keywords: convenience, text
;; URL: https://github.com/tbanel/uniline
;; Uniline 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.
;; Uniline 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 <http://www.gnu.org/licenses/>.
;;; Commentary:
;; ┏━━━━━━━┓
;; ╭──────╮ ┃ thick ┣═◁═╗
;; │ thin ┝◀━━━┫ box ┃ ║
;; │ box │ ┗━━━━━━━┛ ║
;; ╰───┬──╯ ╔══════╩═╗
;; ↓ ║ double ║
;; ╰────────────╢ box ║
;; ╚════╤═══╝
;; ▛▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▜ │
;; ▌quadrant-blocks▐─◁─╯
;; ▙▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▟
;;
;;╭─Pure text────────────────□
;;│ UNICODE characters are available to draw nice boxes and lines.
;;│ They come in 4 flavours: thin, thick, double, and quadrant-blocks.
;;│ Uniline makes it easy to draw and combine all 4 flavours.
;;│ Use the arrows on the keyboard to move around leaving a line behind.
;;╰──────────────────────────╮
;;╭─Minor mode───────────────╯
;;│ Uniline is a minor mode. Enter it with:
;;│ M-x uniline-mode
;;│ Leave it with:
;;│ C-c C-c
;;╰──────────────────────────╮
;;╭─Fonts────────────────────╯
;;│ A font able to displays the needed UNICODE characters have to
;;│ be used. It works well with the following families:
;;│ - DejaVu Sans Mono
;;│ - Unifont
;;│ - Hack
;;│ - JetBrains Mono
;;│ - Cascadia Mono
;;│ - Agave
;;│ - JuliaMono
;;│ - FreeMono
;;│ - Iosevka Comfy Fixed, Iosevka Comfy Wide Fixed
;;│ - Aporetic Sans Mono, Aporetic Serif Mono
;;│ - Source Code Pro
;;╰──────────────────────────╮
;;╭─UTF-8────────────────────╯
;;│ Also, the encoding of the file must support UNICODE.
;;│ One way to do that, is to add a line like this one
;;│ at the top of your file:
;;│ -*- coding:utf-8; -*-
;;╰──────────────────────────╮
;;╭─Hydra or Transient───────╯
;;│ Uniline comes with two flavours of user interfaces:
;;│ Hydra and Transient.
;;│ Both versions are compiled when installing the package.
;;│
;;│ Then one or the other packages must be loaded (not both)
;;│ for example with:
;;│ (require 'uniline-hydra)
;;│ or
;;│ (use-package uniline-hydra
;;│ :bind ("C-<insert>" . uniline-mode))
;;│
;;│ This file, uniline-core.el, is the largest one, the one
;;│ implementing all the core functions independent from
;;│ Hydra or Transient
;;╰──────────────────────────□
;;; Requires:
(require 'cl-lib)
(require 'rect)
(cl-proclaim '(optimize (speed 3) (safety 1)))
;;; Code:
;;;╭────────────────╮
;;;│Coding decisions│
;;;╰────────────────╯
;; Why so much use of (defmacro)?
;; Because we handle 4 directions: north, east, south, west.
;; We end up writing slightly the same algorithms 4 times.
;; It is easier to centralise the algorithms in a set
;; of (defmacro) to write them just once.
;; Isn't (defun) enough and more readable than (defmacro)?
;; Yes, absolutely!
;; We can centralize algorithms in a set of (defun).
;; However a (defun) generically handling all four directions
;; misses nice code-folding.
;; For instance somewhere we have:
;; (ash 3 (* 2 dir))
;; If the compiler sees `dir' as `uniline-direction-dw↓',
;; for example, then it can fold this expression to just 48,
;; which is nice©
;; We call four times a (defmacro) with hard-coded directions.
;; The hard-coded parameter must go all the way down
;; to the last instructions needing that direction.
;; So we have (defmacro) calling other (defmacro).
;; What is the purpose `eval-when-compile'?
;; It folds down a numerical expression to just one number
;; for instance
;; (eval-when-compile (ash 3 (* 2 uniline-direction-lf←)))
;; is converted to just 192
;; This happens both in interpreted and byte-compiled code
;; Otherwise the operations ash, multiplication,
;; and retrieval from `uniline-direction-lf←'
;; would be computed over and over at runtime,
;; with always the same 192 result.
;; We could put directly 192 in the source code,
;; but this would defeat maintenance and readability.
;; What is the purpose `eval-and-compile'?
;; The byte-compiler expands all defmacro' called in defun's.
;; In turn, those defmacro's need to access some defconst's,
;; notably `uniline-direction-up↑' and sisters.
;; So those defconst's are made available to the byte-compiler
;; as well as to the runtime, by embedding them in a
;; `eval-and-compile' declaration.
;; When a decision makes the byte-compiled code better
;; (faster to load and run), at the expense of a slower
;; interpreted counterpart, then go ahead, bias toward
;; byte-compiled code.
;;;╭────────────────────────────────────────────────────────╮
;;;│4 directions, infinite buffer in right & down directions│
;;;╰────────────────────────────────────────────────────────╯
(eval-and-compile
(defconst uniline-direction-up↑ 0) (defmacro uniline-direction-up↑ () 0)
(defconst uniline-direction-ri→ 1) (defmacro uniline-direction-ri→ () 1)
(defconst uniline-direction-dw↓ 2) (defmacro uniline-direction-dw↓ () 2)
(defconst uniline-direction-lf← 3) (defmacro uniline-direction-lf← () 3))
;; △ △ △
;; usual constant╶──╯ ╰──────────────────────╮ │
;; when we insist in having a numeric byte-code: │ │
;; constant 2╶───────────────────────────────────╯ │
;; varref uniline-direction-dw↓╶─────────────────────╯
(eval-when-compile ; not needed at runtime
(defmacro uniline--switch-with-cond (dir &rest body)
"Macro to simplify `cond' applied to possible values of DIR.
BODY is ((CASE1 EXPR1...) (CASE2 EXPR2...) ...)
DIR is an expression which is compared to each of the CASE1, CASE2, ...
until it matches one. Then the corresponding EXPRN is executed.
It expands to a (cond) form.
It is somehow similar to `cl-case', except that the cases are evaluated
while `cl-case' quotes them."
(declare (indent 1))
`(cond
,@(cl-loop
for c in body
collect
`((eq ,dir (eval-when-compile ,(car c)))
,@(cdr c)))
(t (error "direction not known"))))
(defmacro uniline--switch-with-table (dir &rest body)
"Macro to return a value by looking in a table at the DIR entry.
BODY is ((CASE1 EXPR1) (CASE2 EXPR2) ...)
It is similar to `uniline--switch-with-cond', but instead of executing
an EXPRN, it just returns it as is.
It is expanded into a lookup into a vector or a plist, depending on the CASES.
It it faster than an equivalent (cond) form."
(declare (indent 1))
(let* ((lambda
(if (eq (caar body) 'lambda)
(pop body)))
(max
(cl-loop
for c in body
for e = (eval (car c))
always (and (fixnump e) (<= 0 e 30))
maximize e)))
(if max
;; create a vector for fast lookup
(let ((vec (make-vector (1+ max) nil)))
(cl-loop
for c in body
do (aset
vec
(eval (car c))
(if lambda
(funcall lambda (eval (car c)))
(eval (cadr c)))))
(if (fixnump dir)
(aref vec dir)
`(aref ,vec ,dir)))
;; create a plist for versatile lookup
(let ((plist
(cl-loop
for c in body
collect (eval (car c))
collect (if lambda
(funcall lambda (eval (car c)))
(eval (cadr c))))))
(if (fixnump dir)
(plist-get plist dir)
`(plist-get ',plist ,dir))))))
)
(eval-when-compile ; not needed at runtime
(defmacro uniline--reverse-direction (dir)
"Reverse DIR.
DIR is any of the 4 `uniline-direction-*'.
Exchange left with right, up with down."
`(% (+ 2 ,dir) 4))
(defmacro uniline--turn-right (dir)
"Return DIR turned 90° clockwise.
DIR & returned values are in [0,1,2,3]."
`(% (1+ ,dir) 4))
(defmacro uniline--turn-left (dir)
"Return DIR turned 90° anti-clockwise.
DIR & returned values are in [0,1,2,3]."
`(% (+ 3 ,dir) 4))
)
(defmacro uniline-move-to-column (x)
"Move to column X staying on the same line.
Add blanks if line is too short.
Move to 0 if X negative."
;; x is never numeric in the Uniline calls
;; so it is pointless to optimize this case
`(move-to-column (max ,x 0) t))
(defmacro uniline-move-to-delta-column (x)
"Move X characters, staying on the same line.
Add blanks if line is too short.
X may be negative to move backward.
Move to 0 if target is beyond the left border of buffer."
(cond
((eq x 0) ()) ;; this case never happens in Uniline calls
((eq x 1) `(move-to-column (1+ (current-column) ) t))
((eq x -1) `(move-to-column (max (1- (current-column) ) 0) t))
((and
(numberp x)
(>= x 0))
(progn `(move-to-column (+ (current-column) ,x) t)))
(t `(move-to-column (max (+ (current-column) ,x) 0) t))))
(defcustom uniline-infinite-up↑ nil
"Is the buffer infinitely extensible in the upper direction?
If not, the begining of buffer is a hard limit, as usual in most
Emacs modes.
Note that if the buffer is narrowed to a region, for example through
the use of C-x n n, then the buffer the narrowed region may grow
in both the upper and lower direction by automatic insertion of
blank lines."
:type 'boolean
:group 'uniline)
(defun uniline--forward-line-force (y p c)
"Helper function to move cursor Y lines.
Create lines at the end of the buffer if there
are not enough lines to honor Y.
Y may be negative.
Does not preserve current column.
P may be (point) for a relative Y move,
or (point-min) for an absolute Y move.
C is the expected column number."
;; here we fix a bug in the return of (forward-line):
;; when (forward-line) cannot move enough lines
;; because it is stuck at the end of buffer,
;; it erroneously returns one less missing forwards
;; but the bug does not appears if the end-of-buffer
;; is at the beginning-of-line
;; so here we get out of the corner-case of the
;; (forward-line) bug, by ensuring that there is an empty
;; line at the end of buffer
(goto-char (point-max))
(or (bolp) (insert ?\n))
(goto-char p)
(let ((n (forward-line y)))
(if (>= n 0)
(insert-byte ?\n n)
(when uniline-infinite-up↑
(insert-byte ?\n (- n))
(forward-line n))))
(move-to-column c t))
(defmacro uniline-move-to-line (y)
"Move to line Y, while staying on the same column.
Create blank lines at the end of the buffer if needed,
or blank characters at the end of target line.
Y=0 means first line in buffer."
`(uniline--forward-line-force ,y (point-min) (current-column)))
(defmacro uniline-move-to-delta-line (y)
"Move Y lines while staying on the same column.
Create blank lines at the end of the buffer if needed,
or blank characters at the end of target line.
Y may be negative to move backward."
`(uniline--forward-line-force ,y (point) (current-column)))
(defmacro uniline-move-to-lin-col (y x)
"Move to line Y and column X.
Create blank lines at the end of the buffer if needed,
or blank characters at the end of target line if needed.
Y=0 means first line of buffer.
X=0 means first column of buffer."
`(uniline--forward-line-force ,y (point-min) ,x))
(eval-when-compile ; not needed at runtime
(defmacro uniline--move-in-direction (dir &optional nb)
"Move NB char in direction DIR.
NB defaults to 1.
This is a macro, therefore it is as if writing
directly (uniline-move-to-delta-line -1) and such,
with no overhead."
(declare (debug (form)))
(unless nb (setq nb 1))
(let ((mnb (if (fixnump nb) (- nb) `(- ,nb))))
(uniline--switch-with-cond dir
(uniline-direction-up↑ `(uniline-move-to-delta-line ,mnb))
(uniline-direction-ri→ `(uniline-move-to-delta-column , nb))
(uniline-direction-dw↓ `(uniline-move-to-delta-line , nb))
(uniline-direction-lf← `(uniline-move-to-delta-column ,mnb))))))
(eval-when-compile ; not needed at runtime
(defmacro uniline--at-border-p (dir)
"Test if at a non-trespass-able border of buffer.
This happens at the first line or at the first column,
when trying to go further when DIR is up or left:
`uniline-direction-up↑' or `uniline-direction-lf←'.
In the bottom & right directions the buffer is infinite."
(declare (debug (form)))
(setq dir (eval dir))
(uniline--switch-with-table dir
(uniline-direction-up↑ '(eq (pos-bol) 1))
(uniline-direction-ri→ nil)
(uniline-direction-dw↓ nil)
(uniline-direction-lf← '(bolp)))))
(defun uniline--char-after (&optional point)
"Same as `char-after', except for right and bottom edges of buffer.
Outside the buffer, return a blank character.
POINT default to `(point)', as for `char-after'"
(let ((c (char-after point)))
(if (or (not c) (eq c ?\n))
? ;; eol & eob are considered blank
c)))
;;;╭───────────────────╮
;;;│Perfect hash tables│
;;;╰───────────────────╯
;; When an hash-table is constant, with entries known at compile-time,
;; then a perfect hash-table can be built. Perfect means that there
;; are no collisions: each bucket contains one entry, or none, never
;; two entries. This makes the hash-table very efficient.
;; The usual multiple entries scanning can be entirely bypassed.
;;
;; Uniline has 13 constant hash-tables. We want to make them
;; perfect, collision-less. 3 Options:
;;
;; 1- Define a custom hash-function like
;; (% key 191)
;; by looking for the right constant like 191, we can get a
;; collision-less hash-table
;; as shown by (internal--hash-table-histogram)
;; - Unfortunately, this makes the resulting hash-table roughly
;; 3 times slower than a regular (make-hash-table), even one
;; with collisions.
;;
;; 2- Use a custom vector instead of a hash-table, along with a
;; hashing function like
;; (% key (length custom-vector))
;; This amounts to re-creating hash-tables with vectors.
;; - The result is slightly slower than (make-hash-table), even
;; with collisions, about 30% slower.
;;
;; 3- Use a standard hash-table and tweak its size
;; (make-hash-table :size 114)
;; - Unfortunately, the specified size is not retained in the
;; compiled *.elc file
;; Beside, the specified size gets rounded to the next power of 2
;;
;; 4- Create the constant hash-table with
;; (make-hash-table :size 114)
;; when loading the compiled *.elc file
;; Store in the *.elc a list of pairs (key . value)
;; - This bypasses the #s(hash-table …) form that was handy in the
;; *.elc, but at no cost in the *.elc size and almost nothing
;; for the hash-table creation process at load-time
;;
;; This 4th solution is implemented hereafter through a macro
;; which bundles:
;; - (defconst …)
;; - (make-hash-table :size my-preferred-size)
;; - populating the hash-table from a list of pairs loaded from *.elc
(eval-when-compile ;; not needed at runtime
(defmacro uniline--defconst-hash-table (name pairs size test doc)
"Bundles construction of a constant hash-table in one place.
NAME is the name of the hash-table to be passed to `defconst'
PAIRS is a list of (key . value) pairs to populate the table
SIZE is the desired number of buckets
TEST is the comparison function between 2 keys, like `eq' or `equal'"
(declare (indent 1) (doc-string 5))
`(defconst ,name
(let ((table (make-hash-table :size ,size :test ,test)))
(dolist (pair ,pairs)
(puthash (car pair) (cdr pair) table))
table)
,doc)))
;; Display useful metrics to tune constant hash-tables
;; those created by `uniline--defconst-hash-table'
;; Useful only in development cycle.
(if nil
(defun uniline--hash-tables-metrics ()
(interactive)
(switch-to-buffer "*hash-tables metrics*")
(erase-buffer)
(local-set-key "g" 'uniline--hash-tables-metrics)
(insert "\
try to make a single element list in the last column (co
gitextract_1mwm1f40/ ├── .gitignore ├── LICENSE ├── README.org ├── tests/ │ ├── bench01.el │ ├── bench02.el │ ├── bench03.el │ ├── bench04.el │ ├── bench05.el │ ├── bench06.el │ ├── bench07.el │ ├── bench08.el │ ├── bench09.el │ ├── bench10.el │ ├── bench11.el │ ├── bench12.el │ ├── bench13.el │ ├── bench14.el │ ├── bench15.el │ ├── bench16.el │ ├── bench17.el │ ├── bench18.el │ ├── bench19.el │ ├── bench20.el │ ├── bench21.el │ ├── bench22.el │ ├── bench23.el │ ├── bench24.el │ ├── bench25.el │ ├── bench26.el │ ├── bench27.el │ ├── bench28.el │ ├── bench29.el │ ├── bench30.el │ ├── bench31.el │ ├── bench33.el │ └── uniline-bench.el ├── uniline-core.el ├── uniline-hydra.el ├── uniline-transient.el ├── uniline.el └── uniline.info
Condensed preview — 41 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (540K chars).
[
{
"path": ".gitignore",
"chars": 9,
"preview": "*~\n*.elc\n"
},
{
"path": "LICENSE",
"chars": 35149,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
},
{
"path": "README.org",
"chars": 80732,
"preview": "# -*- mode: org; coding:utf-8; -*-\n#+TITLE: Uniline\n#+OPTIONS: ^:{} authors:Thierry Banel, toc:nil\n#+LATEX_HEADER: \\usep"
},
{
"path": "tests/bench01.el",
"chars": 1262,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench02.el",
"chars": 1683,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench03.el",
"chars": 1634,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench04.el",
"chars": 1583,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench05.el",
"chars": 1549,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench06.el",
"chars": 1470,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench07.el",
"chars": 1182,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench08.el",
"chars": 1927,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench09.el",
"chars": 1396,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench10.el",
"chars": 1404,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench11.el",
"chars": 1212,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench12.el",
"chars": 1367,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench13.el",
"chars": 1459,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench14.el",
"chars": 1703,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench15.el",
"chars": 1791,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench16.el",
"chars": 1391,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench17.el",
"chars": 1135,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench18.el",
"chars": 1197,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench19.el",
"chars": 1955,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench20.el",
"chars": 1344,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench21.el",
"chars": 2850,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench22.el",
"chars": 2933,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench23.el",
"chars": 1876,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench24.el",
"chars": 1303,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench25.el",
"chars": 1463,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench26.el",
"chars": 1811,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench27.el",
"chars": 1939,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench28.el",
"chars": 3257,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench29.el",
"chars": 2085,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench30.el",
"chars": 2161,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench31.el",
"chars": 1816,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/bench33.el",
"chars": 2196,
"preview": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyrigh"
},
{
"path": "tests/uniline-bench.el",
"chars": 7571,
"preview": ";;; uniline-bench.el --- Regression tests for Uniline -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-"
},
{
"path": "uniline-core.el",
"chars": 162657,
"preview": ";;; uniline-core.el --- Add▶ ■─UNICODE based diagrams─■ to▶ ■─text files─■ -*- coding:utf-8; lexical-binding: t; -*-\n\n;;"
},
{
"path": "uniline-hydra.el",
"chars": 16968,
"preview": ";;; uniline-hydra.el --- Add▶ ■─UNICODE based diagrams─■ to▶ ■─text files─■ -*- coding:utf-8; lexical-binding: t; -*-\n\n;"
},
{
"path": "uniline-transient.el",
"chars": 16566,
"preview": ";;; uniline-transient.el --- Add▶ ■─UNICODE based diagrams─■ to▶ ■─text files─■ -*- coding:utf-8; lexical-binding: t; -*"
},
{
"path": "uniline.el",
"chars": 3192,
"preview": ";;; uniline.el --- Add▶ ■─UNICODE based diagrams─■ to▶ ■─text files─■ -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copy"
},
{
"path": "uniline.info",
"chars": 93863,
"preview": "This is uniline.info, produced by makeinfo version 6.8 from\nuniline.texi.\n\n\nINFO-DIR-SECTION Emacs\nSTART-INFO-DIR-ENTRY\n"
}
]
About this extraction
This page contains the full source code of the tbanel/uniline GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 41 files (461.0 KB), approximately 145.4k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.