[
  {
    "path": ".gitignore",
    "content": "*~\n*.elc\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "README.org",
    "content": "# -*- mode: org; coding:utf-8; -*-\n#+TITLE: Uniline\n#+OPTIONS: ^:{} authors:Thierry Banel, toc:nil\n#+LATEX_HEADER: \\usepackage{pmboxdraw}\n\n* Getting started in 10 seconds\n:PROPERTIES:\n:CUSTOM_ID: getting-started-in-10-seconds\n:END:\n\n- Type =M-x uniline-mode=\n- Move cursor with the arrow-keys on the keyboard =→ ← ↑ ↓=\n- Quit =C-c C-c=\n\n[[file:images/first-drawing.png]]\n\n#+begin_example\n ╷   ╭─────────╮\n ╰───┤my first ├─╮\n     │drawing  │ ╰───╮\n     ╰─────────╯     │\n        ╭────┬───────╯\n        ╰────╯\n#+end_example\n\n* New\n:PROPERTIES:\n:CUSTOM_ID: new\n:END:\n\nCustomization & settings now consistently available from the\napplication menu, from Hydra, and from Transient.\n\nType =<INS> *=.\n\n* Table of Contents\n:PROPERTIES:\n:TOC:      :include all :depth 3 :force () :ignore (this) :local (nothing)\n:CUSTOM_ID: table-of-contents\n:END:\n\n:CONTENTS:\n- [[#getting-started-in-10-seconds][Getting started in 10 seconds]]\n- [[#new][New]]\n- [[#gallery-pure-unicode-diagrams-in-emacs][Gallery: pure UNICODE diagrams in Emacs]]\n  - [[#document-a-command][Document a command]]\n  - [[#connect-boxes-with-arrows][Connect boxes with arrows]]\n  - [[#explain-decisions-trees][Explain decisions trees]]\n  - [[#draw-lines-or-blocks][Draw lines or blocks]]\n  - [[#outline-the-general-relativity-and-the-schrödingers-equations][Outline the General Relativity and the Schrödinger's equations]]\n  - [[#explain-the-structure-of-a-sentence-in-a-foreign-language][Explain the structure of a sentence in a foreign language]]\n  - [[#draw-electronic-diagrams][Draw electronic diagrams]]\n  - [[#explain-lisp-lists][Explain Lisp lists]]\n  - [[#draw-sketched-objects][Draw sketched objects]]\n  - [[#pure-text][Pure text]]\n  - [[#beware][Beware!]]\n- [[#a-minor-mode-for-drawing][A minor mode for drawing]]\n  - [[#minor-mode][Minor mode]]\n  - [[#draw-lines-by-moving-the-cursor][Draw lines by moving the cursor]]\n  - [[#infinite--buffer][Infinite ∞ buffer]]\n  - [[#brush-style][Brush style]]\n  - [[#text-direction][Text direction]]\n- [[#the-insert-key][The <insert> key]]\n- [[#glyphs-------insertion--modification][Glyphs ▷ ▶ → □ ◆ ╮─ insertion & modification]]\n  - [[#arrows-glyphs------][Arrows glyphs ▷ ▶ → ▹ ▸ ↔]]\n  - [[#intersection-glyphs---][Intersection glyphs ■ ◆ ●]]\n  - [[#fine-tweaking-of-lines][Fine tweaking of lines]]\n- [[#rectangular-actions][Rectangular actions]]\n  - [[#drawing-a-rectangle][Drawing a rectangle]]\n  - [[#filling-a-rectangle][Filling a rectangle]]\n  - [[#moving-a-rectangular-region][Moving a rectangular region]]\n  - [[#copying-killing-yanking-a-rectangular-region][Copying, killing, yanking a rectangular region]]\n  - [[#dashed-lines-and-other-styles][Dashed lines and other styles]]\n  - [[#ascii-to-unicode][ASCII to UNICODE]]\n- [[#long-range-actions-contour-and-flood-fill][Long range actions: contour and flood-fill]]\n  - [[#tracing-a-contour][Tracing a contour]]\n  - [[#flood-fill][Flood-fill]]\n- [[#macros][Macros]]\n- [[#which-fonts][Which fonts?]]\n  - [[#recommended-fonts][Recommended fonts]]\n  - [[#use-case-mixing-fonts][Use case: mixing fonts]]\n- [[#hydra-or-transient][Hydra or Transient?]]\n  - [[#selecting-hydra-or-transient][Selecting Hydra or Transient]]\n  - [[#instantly-selecting-hydra-or-transient][Instantly selecting Hydra or Transient]]\n  - [[#one-liner-menus][One-liner menus]]\n  - [[#the-hydra-interface][The Hydra interface]]\n  - [[#the-transient-interface][The Transient interface]]\n- [[#customization][Customization]]\n  - [[#interface-type][Interface type]]\n  - [[#insert-key][Insert key]]\n  - [[#maximum-steps-when-drawing-a-contour][Maximum steps when drawing a contour]]\n  - [[#cursor-type][Cursor type]]\n  - [[#hint-style][Hint style]]\n  - [[#welcome-message-visibility][Welcome message visibility]]\n  - [[#line-spacing][Line spacing]]\n  - [[#font][Font]]\n  - [[#upward-infiniteness-][Upward infiniteness ∞]]\n- [[#how-uniline-behaves-with-its-environment][How Uniline behaves with its environment?]]\n  - [[#language-environment][Language environment]]\n  - [[#compatibility-with-picture-mode][Compatibility with Picture-mode]]\n  - [[#compatibility-with-artist-mode][Compatibility with Artist-mode]]\n  - [[#compatibility-with-whitespace-mode][Compatibility with Whitespace-mode]]\n  - [[#compatibility-with-org-mode][Compatibility with Org Mode]]\n  - [[#org-mode-and-latex][Org Mode and LaTex]]\n  - [[#what-about-t-tabs][What about \\t tabs?]]\n  - [[#what-about-l-page-separation][What about ^L page separation?]]\n  - [[#emacs-on-the-linux-console][Emacs on the Linux console]]\n  - [[#emacs-on-a-graphical-terminal-emulator][Emacs on a graphical terminal emulator]]\n  - [[#emacs-on-windows][Emacs on Windows]]\n  - [[#compatibility-with-asciiflow][Compatibility with ASCIIFlow]]\n- [[#lisp-api][Lisp API]]\n  - [[#move-the-cursor][Move the cursor]]\n  - [[#brush][Brush]]\n  - [[#example-lisp-function-to-draw-a-plus-sign][Example: Lisp function to draw a plus sign]]\n  - [[#long-range-actions-contour-flood-fill-rectangle][Long range actions (contour, flood-fill, rectangle)]]\n  - [[#constants][Constants]]\n  - [[#macro-and-text-direction][Macro and text direction]]\n  - [[#insert-and-tweak-glyphs][Insert and tweak glyphs]]\n  - [[#change-to-alternate-styles][Change to alternate styles]]\n- [[#mouse-support][Mouse support]]\n- [[#installation][Installation]]\n  - [[#use-package-the-straightforward-way][use-package, the straightforward way]]\n  - [[#without-use-package][Without use-package]]\n- [[#related-packages][Related packages]]\n- [[#author-contributors][Author, contributors]]\n- [[#license][License]]\n:END:\n\n* Gallery: pure UNICODE diagrams in Emacs\n:PROPERTIES:\n:CUSTOM_ID: gallery-pure-unicode-diagrams-in-emacs\n:END:\nDraw diagrams like those:\n\n** Document a command\n:PROPERTIES:\n:CUSTOM_ID: document-a-command\n:END:\n\n[[file:images/document-command.png]]\n\n#+begin_example\n   pdfjam source.pdf 3-5,9\n  ╶─────▲────▲────────▲──▲╴\ncommand╶╯    │        │  │\ninput file╶──╯        │  │\nselect pages 3,4,5╶───╯  │\nand page 9╶──────────────╯\n#+end_example\n\n** Connect boxes with arrows\n:PROPERTIES:\n:CUSTOM_ID: connect-boxes-with-arrows\n:END:\n\n[[file:images/boxes-arrows.png]]\n\n#+begin_example\n            ╭───────────────────────╮\n  ╷123╭────▶┤ hundred and something │\n  ╰───╯     ╰───────────────────────╯\n                             ╭────▶──╮A╷\n    ╭───╮    ┏━━━┓    ╔═══╗  │       ╰─╯\n0╶─→┤ 1 ┝━━━▶┫ 2 ┣═══▷╣ 3 ╟──●────▶──╮B╷\n    ╰───╯    ┗━┯━┛    ╚═╤═╝  │       ╰─╯\n               ╰────←───╯    ╰────▶──╮C╷\n                                     ╰─╯\n   ╔══════════╗\n   ║ 1        ║          ▐▀▀▀▀▀▀▀▀▜\n   ║    ╭─────╫───╮ ◁──▷ ▐ 3      ▐\n   ╚════╪═════╝ 2 │      ▐▄▄▄▄▄▄▄▄▟\n        ╰─────────╯\n#+end_example\n\n** Explain decisions trees\n:PROPERTIES:\n:CUSTOM_ID: explain-decisions-trees\n:END:\n\n[[file:images/decision-tree.png]]\n\n#+begin_example\n  ┏━━━━━━━━━━━━┓\n  ┃which color?┃\n  ┗━┯━━━━━━━━━━┛\n    │     ╭──────╮\n    │  ╭──┤yellow├─▷╮good─choice╭□\n    ▽  │  ╰──────╯  ╰═══════════╯\n    ╰──●  ╭───╮    ┏━━━━━┓\n       ├──┤red├───▷┨dark?┠──╮\n       │  ╰───╯    ┗━━━━━┛  │\n       │ ╭───◁──────────────╯\n       │ │   ╭───╮\n       │ ╰─●─┤yes├▷╮regular─red╭─□\n       │   │ ╰───╯ ╰═══════════╯\n       │   │ ╭──╮\n       │   ╰─┤no├─▷╮pink╭────────□\n       │     ╰──╯  ╰════╯\n       │  ╭────╮\n       ├──┤blue├───▷╮next week╭──□\n       │  ╰────╯    ╰═════════╯\n       │  ╭─────╮\n       ╰──┤white├──▷╮available╭──□\n          ╰─────╯   ╰═════════╯\n#+end_example\n\n** Draw lines or blocks\n:PROPERTIES:\n:CUSTOM_ID: draw-lines-or-blocks\n:END:\n\n[[file:images/lines-blocks.png]]\n\n#+begin_example\n                              ╭─╮←─╮\n                         ╭╮   │ │  ╰──╴max 235\n                       ╭╮││  ╭╯ │\n                       │╰╯│╭─╯  │\n      ╭╮               │  ││    │\n   ╭─╮││╭╮   ╭──╮╭╮    │  ╰╯    ╰╮\n  ╭╯ ╰╯╰╯│  ╭╯  ╰╯╰─╮  │         │ ╭╮\n◁─╯      ╰──╯       ╰──╯         ╰─╯╰────▷\n◀════════════════════════════════════════▶\n                       ╭────────╮\n   ▲                   │all time│\n   ┃       ▄     ▗▟█ ←─┤highest │\n  Qdx      █▌   ████   ╰────────╯\n   ┃     ▗▄█▌   █████▙\n   ┃   ▟███████▄█████████▄▄▄     ▗▄\n   ┃▐▄▄████████████████████████████▄▄▖\n    ╺━━━━━━━━━━╸time╺━━━━━━━━━━━━━━━━▶\n\n#+end_example\n\n** Outline the General Relativity and the Schrödinger's equations\n:PROPERTIES:\n:CUSTOM_ID: outline-the-general-relativity-and-the-schrödingers-equations\n:END:\n\n[[file:images/general-relativity-equation.png]]\n\n#+begin_example\n\n       ╭─────────────────────╴G: Einstein tensor\n       │                ╭────╴κ: Gravitational coupling constant\n    ╭──▽───╮        ╭───▽──╮\n  ┏━┷━━━━━━┷━━━━━━━━┷━━━━━━┷━━━┓\n  ┃ R - gR/2 + Λg = (8πG/c⁴)×T ┃◁╴General Relativity equation\n  ┗━△━━━△△━━━━━△△━━━━━━△━△━━━△━┛\n    │   ││     ││      │ │  ╭╯\n    │   ││     ││      │ │  ╰╴Energy-impulsion tensor\n    │   ││     ││      │ ╰───╴Speed of light\n    │   ││     ││      ╰─────╴Gravitational constant\n    │   ││     ╰┴────────────╴Cosmological constant\n    │   │╰──────┴────────────╴Scalar curvature\n    │   ╰───────╰────────────╴Metric tensor\n    ╰────────────────────────╴Ricci tensor\n\n  #+end_example\n\n[[file:images/schrodinger-equation.png]]\n\n#+begin_example\n\n         ╭─────────────────────╴Derivative over time\n         │     ╭──────────╭────╴State of quantum system at time t\n         │     │          │     (the square of its absolute value\n        ╭▽─╮ ╭─▽──╮     ╭─▽──╮   is the probability density)\n  ┏━━━━━┷━━┷━┷━━━━┷━━━━━┷━━━━┷━┓\n  ┃ i ħ d/dt |Ψ(t)> = Ĥ |Ψ(t)> ┃◁─╴Schrödinger's equation\n  ┗━△━△━━━━△━━━━△━━━━━△━━━━△━━━┛\n    │ │    ╰────╰─────┤────╰───╴Time\n    │ │               ╰────────╴Hamiltonian\n    │ ╰────────────────────────╴Reduced Plank constant\n    ╰──────────────────────────╴Imaginary number i²=-1\n\n#+end_example\n\n** Explain the structure of a sentence in a foreign language\n:PROPERTIES:\n:CUSTOM_ID: explain-the-structure-of-a-sentence-in-a-foreign-language\n:END:\n(which language?)\n\n[[file:images/foreign-language-sentence.png]]\n\n#+begin_example\n\n   ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n   ┃ the pretty table is standing ┃\n   ┗┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n    │    ╭────┬─────┬─────╴radicals\n    ↕   ╭┴╮  ╭┴─╮  ╭┴─╮\n   ┏┷━━━┿━┿━━┿━━┿━━┿━━┿━━━┓\n   ┃ la bela tablo staras ┃\n   ┗━━━━┿━┿△━┿━━┿△━┿━━┿△━━┛\n        ╰─╯│ ╰──╯│ ╰──╯│  ┏━━━━━suffixes━━━━━┓\n           │     │     ╰──╂╴as: present tense┃\n           │     │        ┃ os: future tense ┃\n           │     │        ┃ is: past tense   ┃\n           │     ╰────────╂╴ o: noun         ┃\n           ╰──────────────╂╴ a: adjective    ┃\n                          ┃  e: adverb       ┃\n                          ┗━━━━━━━━━━━━━━━━━━┛\n\n#+end_example\n\n** Draw electronic diagrams\n:PROPERTIES:\n:CUSTOM_ID: draw-electronic-diagrams\n:END:\n\n[[file:images/electronic-circuit.png]]\n\n#+begin_example\n\n               ╭────────╭──────────╮ ┏━━┓\n               │       ╭┴╮         ╰─┨5V┃\n              ╭┴╮      │░│           ┗━━┛\n              │░│      │░│1KΩ\n              │░│10KΩ  ╰┬╯\n         5μF  ╰┬╯       ├─────────────● →\n          ╷╷   │      ┠─╯           amplified\n  → ●─────┤├───┼──────┨             output\n input    ╵╵   │      ┠▶╮  500μF    signal\n signal       ╭┴╮       │   ╷╷\n              │░│       ├───┤├──╮\n              │░│1KΩ   ╭┴╮  ╵╵  │\n              ╰┬╯      │░│      │    ╭────╮\n               │       │░│470Ω  │    │ ╺━━┷━━╸\n               │       ╰┬╯      │    │  ╺━━━╸\n               ╰────────╰───────╰────╯   ╺━╸\n\n#+end_example\n\n** Explain Lisp lists\n:PROPERTIES:\n:CUSTOM_ID: explain-lisp-lists\n:END:\n\n[[file:images/lisp-lists.png]]\n\n#+begin_example\n  '(a b c)\n     ┏━━━┳━━━┓   ┏━━━┳━━━┓   ┏━━━┳━━━┓\n●━━━▶┫ ● ┃ ●─╂──▷┨ ● ┃ ●─╂──▷┨ ● ┃nil┃\n     ┗━┿━┻━━━┛   ┗━┿━┻━━━┛   ┗━┿━┻━━━┛\n       │           ╰──────────╮╰╮\n       │  ╭─────┬───────────╮ │ │\n       ╰─▷┤\"a\\0\"│properties │ │ │\n          ├─────┼───────────┤ │ │\n          │\"b\\0\"│properties ├◁╯ │\n          ├─────┼───────────┤   │\n          │\"c\\0\"│properties ├◁──╯\n          ├─────┼───────────┤\n          │...  │...        │\n          ╵     ╵           ╵\n#+end_example\n\n** Draw sketched objects\n:PROPERTIES:\n:CUSTOM_ID: draw-sketched-objects\n:END:\n\n[[file:images/sketched-objects.png]]\n\n#+begin_example\n\n  ◀─(-)────────(+)──▶    ~╭──────╮~\n   ▗──────────────╮     ~~│ ╭~~╮ │~~\n   ▐              ╰╮     ~│ ╵  ╵ │~\n ╭□▐   1.5 volts  ╭╯□╮    ╰─╖  ╓─╯\n │ ▝▀▀▀▀▀▀▀▀▀▀▀▀▀▀▘  │      ╠━━╣\n │                   ╰──────╯  │\n ╰─────────────────────────────╯\n#+end_example\n\n[[file:images/water-sketch.png]]\n\n#+begin_example\n   ╶╮       ╭╴\n  ┏┳┥▒▒▒▒▒▒▒┝╸\n  ┃┃│▒▒eau▒▒│\n  ┃┃│▒▒▒▒▒▒▒│ ╔═════╗\n  ┃┃╰──╮▒╭──╯ ║ ╶╮  ▽           ╭╴\n  ┃┃    ▒     ║  │  ░           │\n  ┃┃    ▒     ║  │░░░░░░░░░░░░░░│\n  ┃┃    ╚═════╝  │░░░░░░░░░░░░░░╞════▷▒▒\n  ┃┃             │░░░░░akvo░░░░░│    ╶╮ ▒         ╭╴\n  ┃┃             │░░░░░░░░░░░░░░│     │  ▒        │\n  ┃┃             ╰─┲┳━━━━━━━━┳┱─╯     │▒▒▒▒▒▒▒▒▒▒▒│\n  ┃┃               ┃┃        ┃┃       │▒▒▒water▒▒▒│\n  ┃┃               ┃┃        ┃┃       │▒▒▒▒▒▒▒▒▒▒▒│\n  ┃┃               ┃┃        ┃┃       ╰───────────╯\n  ▝▀▀▀▀▀▀▘        ▝▀▘        ▝▀▘      ▀▀▀▀▀▀▀▀▀▀▀▀▀\n#+end_example\n\n** Pure text\n:PROPERTIES:\n:CUSTOM_ID: pure-text\n:END:\n\nThose diagrams are pure text. There is nothing graphic. They are\nachieved using UNICODE characters. Therefore they can be drawn within\nany text formatted document, like Org Mode, Markdown, txt, comments in\nany programming language source code (C++, Python, Rust, D,\nJavaScript, GnuPlot, LaTex, whatever).\n\nMost often, the text file will be encoded as UTF-8. This is becoming\nthe de-facto standard for text and source code files.\n\nCreating such diagrams by hand is painfully slow. Use =Uniline= to\ndraw lines while you move the cursor with keyboard arrows.\n\n** Beware!\n:PROPERTIES:\n:CUSTOM_ID: beware\n:END:\n\nIf you see those diagrams miss-aligned, most likely the font used to\ndisplay them does not support UNICODE block characters. See bellow the\nparagraph [[#which-fonts][Which fonts?]] for details.\n\nIf you get misalignment when drawing, this could come from too wide\ncharacters. Emojis are an example. Usual characters may also be\nconsidered twice as wide as normal under some \"language\nenvironments\". See the paragraph [[#language-environment][Language environment]] for details.\n\n* A minor mode for drawing\n:PROPERTIES:\n:CUSTOM_ID: a-minor-mode-for-drawing\n:END:\n\n** Minor mode\n:PROPERTIES:\n:CUSTOM_ID: minor-mode\n:END:\n=Uniline= is a minor mode. Activate it temporarily:\n\n =M-x uniline-mode=\n\nExit it with:\n\n =C-c C-c=\n\nThe current major mode is still active underneath =uniline-mode=.\n\nWhile in =uniline-mode=, overwriting is active, as well as long lines\ntruncation. Also, a hollow cursor is provided (customizable). Those\nsettings are reset to their previous state when exiting =uniline-mode=.\n\n** Draw lines by moving the cursor\n:PROPERTIES:\n:CUSTOM_ID: draw-lines-by-moving-the-cursor\n:END:\n\nUse keyboard arrows to draw lines.\n\nBy default, drawing lines only happens over empty space or over other\nlines. If there is already text, it will not be erased. However, by\nhitting the control-key while moving, lines overwrite whatever there\nis.\n\nThe usual numeric prefix is available. For instance, to draw a line 12\ncharacters wide downward, type: =M-12 <down>=\n\n** Infinite ∞ buffer\n:PROPERTIES:\n:CUSTOM_ID: infinite--buffer\n:END:\n\nThe buffer is infinite ∞ in the south and east directions. Which means\nthat when the cursor ends up outside the buffer, white space\ncharacters are automatically added.\n\nAll algorithms also make use of the infiniteness of the buffer when\nneeded. Those algorithms are: moving a rectangle, pasting a rectangle,\ndrawing the external border of a rectangular region, or drawing the\ncontour of a shape.\n\nThe buffer is also infinite ∞ in the upward direction. That is\ncustomizable through the =uniline-infinite-up↑= variable. If its value\nis =t=, then the buffer is actually infinite ∞ upward. If it is =nil=,\nthen the upper border of the buffer is a hard limit. To customize,\ntype:\n\n=M-x customize-variable uniline-infinite-up↑=\n\nThe buffer can be \"narrowed\", for instance with the =C-x n n= or =M-x\nnarrow-to-region= command. In this case, the limits are those of the\nnarrow region. When Uniline needs to bypass the up↑ or down↓ limits,\nit adds empty lines. When widening again the buffer, the region which\nwas narrow will have increased.\n\n** Brush style\n:PROPERTIES:\n:CUSTOM_ID: brush-style\n:END:\nSet the current brush with:\n\n- ~-~ single thin line\n  =╭─┬─╮=\n\n- ~+~ single thick line\n  =┏━┳━┓=\n\n- ~=~ double line\n  =╔═╦═╗=\n\n- ~#~ quarter block\n  =▙▄▟▀=\n\n- =~= toggle dotted lines\n  =┄┄┄┄=\n\n- ~<delete>~ eraser\n\n- ~<return>~ move without drawing anything\n\nThe current brush and the current text direction (see [[#text-direction][Text direction]]) are\nreflected in the mode-line (at the bottom of the =Emacs= screen). It\nlooks like this:\n\n[[file:images/mode-line.png]]\n\n#+begin_example\n\n  current text                  current\n     direction╶────╮       ╭───╴brush\n                   ▼       ▼\n ══════════════════╧═══════╧══════════════\n U:** buff    (... →Uniline┼ ...)\n ═════════════════════════════════════════\n\n#+end_example\n\nThe dotted toggle ~~~ is a modifier for the single thin and thick\nlines. It circles along 3 styles:\n- plain lines,\n- 3 dots vertical, 2 dots horizontal,\n- 4 dots both vertical & horizontal,\n- back to plain line and so on.\n\n[[file:images/dotted-lines.png]]\n\n#+begin_example\n\n            ║   thin  ╷  thick  ╷\n            ║         │         │\n  ══════════╬═════════╪═════════╡\n            ║  ╭╌╌╌╮  │  ┏╍╍╍┓  │\n   3,2 dots ║  ┆   ┆  │  ┇   ┇  │\n            ║  ╰╌╌╌╯  │  ┗╍╍╍┛  │\n  ──────────╫─────────┼─────────┤\n            ║  ╭┈┈┈╮  │  ┏┉┉┉┓  │\n   4,4 dots ║  ┊   ┊  │  ┋   ┋  │\n            ║  ╰┈┈┈╯  │  ┗┉┉┉┛  │\n  ──────────╨─────────┴─────────╯\n\n#+end_example\n\nNote that the UNICODE standard offers very limited support for dotted\nlines. Only vertical and horizontal lines are available. So, no\ncrossing of line is possible. In case a line crosses a dotted line,\nUniline falls back to a plain line crossing character (but still\npreserving thickness). There is no dotted versions of double lines\neither.\n\n** Text direction\n:PROPERTIES:\n:CUSTOM_ID: text-direction\n:END:\nUsually, inserting text in a buffer moves the cursor to the right. (And\nsometimes to the left for some locales). Any of the 4 directions can be\nselected under =Uniline=. Just type any of:\n\n  - =<insert> C-<up>=\n  - =<insert> C-<right>=\n  - =<insert> C-<down>=\n  - =<insert> C-<left>=\n\nThe current direction is reflected in the mode-line, just before the\nword =\"uniline\"=.\n\n* The =<insert>= key\n:PROPERTIES:\n:CUSTOM_ID: the-insert-key\n:END:\nThe =<insert>= key is a prefix for other keys:\n- for drawing arrows, squares, crosses, o-shapes glyphs,\n- for handling rectangles,\n- for inserting =# = - += which otherwise change the brush style,\n- for trying a choice of mono-spaced fonts.\n\nWhy =<insert>=? Because:\n- =Uniline= tries to leave their original meaning to as many keys as\n  possible,\n- the standard meaning of =<insert>= is to toggle the =overwrite-mode=;\n  but =Uniline= is already in =overwrite-mode=, and de-activating\n  overwrite would break =Uniline=.\n\nSo preempting =<insert>= does not sacrifice anything.\n\n*Customization*\n\nAnother key may be defined instead of =<insert>=. Type:\n\n#+begin_example\nM-x customize-variable uniline-key-insert\n#+end_example\n\n* Glyphs =▷ ▶ → □ ◆ ╮─= insertion & modification\n:PROPERTIES:\n:CUSTOM_ID: glyphs-------insertion--modification\n:END:\n\nIndividual character glyphs may be inserted and changed.\n- Put the cursor where a glyphs should be edited or inserted.\n- Then press =<insert>= (this key may be customized, see [[#insert-key][Insert key]]).\n\nArrows, squares, circles, crosses may be handled. Also lines may be\nfine tweaked a single character at a time.\n\n** Arrows glyphs =▷ ▶ → ▹ ▸ ↔=\n:PROPERTIES:\n:CUSTOM_ID: arrows-glyphs------\n:END:\nWhen inserting an arrow, it points in the direction that the line\ndrawing follows.\n\n=Uniline= supports 6 arrows types: =▷ ▶ → ▹ ▸ ↔=\n\n[[file:images/arrow-styles.png]]\n\n#+begin_example\n\n   □\n   ╰─◁──▷─╮       □─╮ ╭─╮ ╭─╮ ╭─□\n   ╭─◀──▶─╯         △ ▲ ↑ ▵ ▴ ↕\n   ╰─←──→─╮         │ │ │ │ │ │\n   ╭─◃──▹─╯         ▽ ▼ ↓ ▿ ▾ ↕\n   ╰─◂──▸─╮         ╰─╯ ╰─╯ ╰─╯\n   ╭─↔──↔─╯\n   □\n\n#+end_example\n\nActually, there are tons of arrows of all styles in the UNICODE\nstandard. Unfortunately, support by fonts is weak. So =Uniline=\nrestrains itself to those six safe arrows.\n\nTo insert an arrow, type: =<insert> a= or =<insert> a a= or =<insert> a a a=. (=a=\ncycles through the 6 styles, =A= cycles backward).\n\n=<insert> 4 a= is equivalent to =<insert> a a a a=, which is also equivalent to\n=<insert> A A A=. Those 3 shortcuts insert an arrow of this style: =▵▹▿◃=. The\nactual direction where the arrow points follows the last movement of\nthe cursor.\n\nTo change the direction of the arrow, use shift-arrow, for example:\n=S-<up>= will change from =→= to =↑=.\n\n** Intersection glyphs =■ ◆ ●=\n:PROPERTIES:\n:CUSTOM_ID: intersection-glyphs---\n:END:\nThere are a few UNICODE characters which are mono-space and symmetric\nin the 4 directions. They are great at line intersections:\n\nTo insert a square =□ ■ ▫ ▪ ◆ ◊= type:\n=<insert> s s s…= (=s= cycles, =S= cycles backward).\n\nTo insert a circular shape =· ∙ • ● ◦ Ø ø= type:\n=<insert> o o o…= (=o= cycles, =O= cycles backward).\n\nTo insert a cross shape =╳ ╱ ╲ ÷ × ± ¤= type:\n=<insert> x x x…= (=x= cycles, =X= cycles backward).\n\nTo insert a grey character =░▒▓█= from pure white to pure black type:\n=<insert> SPC SPC SPC…= or =<insert> DEL DEL DEL…= (space key goes from\nwhite to black, back-space key goes from black to white)\n\nTo insert a usual ASCII letter or symbol, just type it.\n\nAs the keys =- + = # ~= are preempted by =uniline-mode=, to type them,\nprefix them with =<insert>=. Example: =<insert> -= inserts a =-= and\n=<insert> += inserts a =+=.\n\n[[file:images/insert-glyphs.png]]\n\n#+begin_example\n\n <insert>\n    │\n    ├────────────────────────────╮\n    ▼        ╭─arrows──────╮     ▼        ╭───╮\n    ╰──▶─(a)─┤ ▷ ▶ → ▹ ▸ ↔ │     ╰──▶─(+)─┤ + │\n    │        ╰─────────────╯     │        ╰───╯\n    │        ╭─squares─────╮     │        ╭───╮\n    ╰──▶─(s)─┤ □ ■ ▫ ▪ ◆ ◊ │     ╰──▶─(-)─┤ - │\n    │        ╰─────────────╯     │        ╰───╯\n    │        ╭─circles───────╮   │        ╭───╮\n    ╰──▶─(o)─┤ · ∙ • ● ◦ Ø ø │   ╰──▶─(=)─┤ = │\n    │        ╰───────────────╯   │        ╰───╯\n    │        ╭─crosses───────╮   │        ╭───╮\n    ╰──▷─(x)─┤ ╳ ╱ ╲ ÷ × ± ¤ │   ╰──▶─(#)─┤ # │\n    │        ╰───────────────╯   │        ╰───╯\n    │              ╭───────╮     │        ╭───╮\n    ╰──▶─(SPC DEL)─┤  ░▒▓█ │     ╰──▶─(~)─┤ ~ │\n                   ╰───────╯              ╰───╯\n\n#+end_example\n\n** Fine tweaking of lines\n:PROPERTIES:\n:CUSTOM_ID: fine-tweaking-of-lines\n:END:\n\n[[file:images/fine-tweaking.png]]\n\n#+begin_example\n\n    convert this  ═══▶   into that\n   ╭───────────╮        ╭───────────╮\n   │╶───┬────▷ │        │╶───╮────▷ │\n   │    │      │        │    │      │\n   │           │        │           │\n   │    ▀▀▀    │        │    ▀▟▀    │\n   ╰───────────╯        ╰───────────╯\n\n#+end_example\n\nAt the crossing of lines, it may be appealing to do small\nadjustments. In the above example, we removed a segment of line which\noccupies 1/4 of a character. This cannot be achieve with line tracing\nalone. We also modified a quarter-block line in a non-obvious way.\n\n- Put the point (the cursor) on the character where lines cross each other.\n- type =INS S-<right> S-<right>=\n\n=<right>= here refers to the right part of the character under the\npoint. The 1/4 line segment will cycle through all displayable\nforms. On the second stroke, no segment will be displayed, which is\nwhat we want.\n\nCaveat! The UNICODE standard does not define all possible combinations\nincluding double line segments. (It does for all combinations of thin\nand tick lines). So sometimes, when working with double lines, the\nprocess may be frustrating.\n\nThis works also for lines made of quarter-blocks. There are 4\nquarter-blocks in a character, either on or off. Each of the 4 shifted\nkeyboard arrows flips a quarter-block on-and-off.\n\nIn the above example, the effect was achieved with:\n=INS S-<up> S-<down> S-<left>=\n\n* Rectangular actions\n:PROPERTIES:\n:CUSTOM_ID: rectangular-actions\n:END:\n\n- Drawing,\n- filling,\n- moving,\n- copying & yanking,\n- change line & glyph styles,\n\nthose actions may be performed on a rectangular selection.\n\nSelect a rectangular region with =C-SPC= or =C-x SPC= and move the cursor.\n\nYou may also use =S-<arrow>= (=<arrow>= being any of the 4\ndirections) to extend the selection. The buffer grows as needed with\nwhite spaces to accommodate the selection. Selection extension mode is\nactive when =shift-select-mode= is non-nil.\n\nOr you may use the mouse to highlight the desired region.\n\nAll those region-highlighting are standard in =Emacs=, and unrelated to\n=Uniline=.\n\nOnce you have a region highlighted, press =<insert>= (this key can be\ncustomized, see [[#insert-key][Insert key]]). The selection becomes rectangular if it\nwas not. You are offered a menu of possible actions.\n\n** Drawing a rectangle\n:PROPERTIES:\n:CUSTOM_ID: drawing-a-rectangle\n:END:\n\nTo draw a rectangle in one shot, select a region, press =<insert>=, then\nhit:\n- =r= to draw a rectangle inside the selection\n- =S-R= to draw a rectangle outside the selection\n- =C-r= to overwrite a rectangle inside the selection\n- =C-S-R= to overwrite a rectangle outside the selection\n\nIf needed, change the brush with any of =- + = # <delete>=\n\n[[file:images/draw-rectangle.png]]\n\n#+begin_example\n   ╭───────╮          r: inside╮╭───────╮\n   │ one   │          ▗▄▄▄▄▄▄▖╭┤│▛▀▀▀▀▀▜│\n   │  ┏━━━━┿━━━━━━┓   ▐╭────╮▌│╰┼▌     ▐│\n   ╰──╂────╯ two  ┃   ▐│    │▌│ │▙▄▄▄▄▄▟│\n      ┃   ╔═══════╋═╗ ▐│    ├▌╯ ╰─────┬─╯\n      ┗━━━╋━━━━━━━┛ ║ ▐╰────╯▌────────┴───╮\n          ║  three  ║ ▝▀▀▀▀▀▀▘  R: outside╯\n          ╚═════════╝\n\n                          ╭─────────╮\n   my text I              │my text I│\n   want to  ╶─<insert>R─▷ │want to  │\n   box                    │box      │\n                          ╰─────────╯\n#+end_example\n\nThe usual =C-_= or =C-/= keys may be hit to undo, even with the region\nstill active visually.\n\n** Filling a rectangle\n:PROPERTIES:\n:CUSTOM_ID: filling-a-rectangle\n:END:\n\nWhile the rectangular mode is active, press =i= to fill the\nrectangle. You will be asked to choose a character. You have those\noptions:\n\n- for a regular character like =t=, just type it.\n- =SPC= or =DEL= for a shade of grey =\" ░▒▓█\"= among the 5 available in\n  UNICODE. =SPC= to make it darker and darker. =DEL= to make the rectangle\n  lighter and lighter.\n- =C-y= to chose the first character in the top of the kill ring.\n\nThe above selection is the same as for the flood-fill action (see\n[[#flood-fill][Flood-fill]]).\n\n** Moving a rectangular region\n:PROPERTIES:\n:CUSTOM_ID: moving-a-rectangular-region\n:END:\nSelect a region, then press =<insert>=.\n\nUse arrow keys to move the rectangle around. A numeric prefix may be\nused to move the rectangle that many characters.\n- Under =Hydra=, be sure to specify the numeric prefix with just digits,\n  without the =Alt= key. Typing =15 <left>= moves the rectangle 15\n  characters to the left. =M-15 <left>= does not work.\n- Under =Transient=, use the =Alt= key, like anywhere else in =Emacs=. Type\n  =M-15 <left>= to move the selected rectangle 15 characters to the left.\n\nPress =q=, =<return>=, or =C-g= to stop moving the rectangle.\n\nThe =C-_= key may also be used to undo the previous movements, even\nthough the selection is still active.\n\n[[file:images/move-rectangle.png]]\n\n#+begin_example\n                 ▲\n                 │\n                <up>\n           ╭─────┴──────╮\n           │this is     │\n           │my rectangle│\n ◀─<left>──┤I want to   ├─<right>─▶\n           │move        │\n           ╰─────┬──────╯\n               <down>\n                 │\n                 ▼\n#+end_example\n\nWhat is leakage? When moving a rectangular region, the rectangle\nleaves behind lines oriented in the movement direction. This is not a\nbug, but a feature. Leakage allows growing a drawing without breaking\nit in two parts.\n\n[[file:images/rect-region-leak.png]]\n\n#+begin_example\n\n      ┏┯━━━┓                 ┏┯━━━┓\n    ┏━┛│   ┗━╦━━┓    with  ┏━┛│   ┗━╦━━┓\n    ┃  │     ║  ┃╶──╮leak  ┃  │     ║  ┃  leaked\n    ┃  │     ║  ┃   ╰────▶ ┃  │     ║  ┃◀──────╴\n    ┗━━┷━━━━━╩━━┛          ┃  │     ║  ┃  lines\n        │                  ┗━━┷━━━━━╩━━┛\n        │without\n        │leak\n        ╰──────╮\n               ▼\n            ┏┯━━━┓\n          ┏━┛│   ┗━╦━━┓\n          ┃  │     ║  ┃   broken\n                        ◀───────╴\n          ┃  │     ║  ┃   drawing\n          ┗━━┷━━━━━╩━━┛\n\n#+end_example\n\n** Copying, killing, yanking a rectangular region\n:PROPERTIES:\n:CUSTOM_ID: copying-killing-yanking-a-rectangular-region\n:END:\n\nA rectangle can be copied or killed, then yanked somewhere else.\n\nSelect a region, press =<insert>=, then:\n- =c= to copy\n- =k= to kill\n- =y= to yank (aka paste)\n\nThis is similar to the =Emacs= standard rectangle handling:\n- =C-x r r= copy rectangle to register\n- =C-x r k= kill rectangle\n- =C-x r y= yank killed rectangle\n\nThe first difference is that =Uniline= rectangles, when killed and\nyanked, do not move surrounding characters.\n\nThe second difference is that the white characters of the yanked\nrectangle are considered transparent. As a result, only non-blank\nparts of the yanked rectangle are over-printed.\n\n=Uniline= and =Emacs= standard rectangle share the same storage for copied\nand killed rectangles, namely the =killed-rectangle= Lisp variable. So,\na rectangle can be killed one way, and yanked another way.\n\n** Dashed lines and other styles\n:PROPERTIES:\n:CUSTOM_ID: dashed-lines-and-other-styles\n:END:\n\n[[file:images/four-styles.png]]\n\n#+begin_example\n\n   ╭────▷───╮   ┏━━━━▶━━━┓   ╔════▶═══╗\n   │ ╭─□──╮ │   ┃ ┏━■━━┓ ┃   ║ ╔═■══╗ ║\n   △ │    │ ▽   ▲ ┃    ┃ ▼   ▲ ║    ║ ▼\n   │ ╰───◦╯ │   ┃ ┗━━━•┛ ┃   ║ ╚═══•╝ ║\n   ╰───◁────╯   ┗━━━◀━━━━┛   ╚═══◀════╝\n\n   ╭╌╌╌╌▷╌╌╌╮   ┏╍╍╍╍▶╍╍╍┓\n   ┆ ╭╌□╌╌╮ ┆   ┇ ┏╍■╍╍┓ ┇\n   △ ┆    ┆ ▽   ▲ ┇    ┇ ▼\n   ┆ ╰╌╌╌◦╯ ┆   ┇ ┗╍╍╍•┛ ┇\n   ╰╌╌╌◁╌╌╌╌╯   ┗╍╍╍◀╍╍╍╍┛\n\n   ╭┈┈┈┈▷┈┈┈╮   ┏┉┉┉┉▶┉┉┉┓\n   ┊ ╭┈□┈┈╮ ┊   ┋ ┏┉■┉┉┓ ┋\n   △ ┊    ┊ ▽   ▲ ┋    ┋ ▼\n   ┊ ╰┈┈┈◦╯ ┊   ┋ ┗┉┉┉•┛ ┋\n   ╰┈┈┈◁┈┈┈┈╯   ┗┉┉┉◀┉┉┉┉┛\n\n#+end_example\n\nA base drawing can be converted to dashed lines. Moreover, lines can\nbe made either thin or thick.\n\n- Select the rectangular area you want to operate on (with mouse drag\n  or =S-<left>=, =S-<down>= and so on as described earlier).\n- Type =INS=, then =s= (as \"style\").\n\nYou will be offered a choice of styles:\n- =3=: vertical lines will become 3 dashes per character, while\n  horizontal ones will get 2 dashes per character.\n- =4=: vertical and horizontal lines will get 4 dashes per character.\n- =h=: thin lines corners, which are usually rounded, become hard angles.\n- =+=: thin lines and intersections become thick, empty glyphs get\n  filled.\n- =-=: thick lines and intersections become thin, filled glyphs are\n  emptied.\n- ~=~: thick and thin lines become double lines.\n- =0=: come back to standard base-line =Uniline= style: plain, not-dashed\n  lines, thin corner rounded, ASCII art is converted to UNICODE.\n- =a=: apply the =aa2u-rectangle= function from the unrelated\n  =ascii-art-to-unicode= package, to convert ASCII art to UNICODE (this\n  only works if =ascii-art-to-unicode= is already installed).\n\nConverting parts of a drawing from one style to another can produce\nnice looking sketches.\n\n[[file:images/same-sketch-several-styles.png]]\n\n#+begin_example\n\n   ╭───╮   ╭───╮   ╭───╮\n   │░░░│   │░░░│   │░░░┝━▶┓ ╭╌╌╌╌╌╮\n   │░░░╰───╯░░░╰───╯░░░│  ┃ ┆░░░░░╰╌╌╌╌╌╮\n   □░░░░░░░░░░░░░░░░░░░│  ┗━┥░░░░░░░░░░░┆\n   │░░░╭───╮░░░╭───╮░░░│    ┆░░░░░╭╌╌╌╌╌╯\n   ╰───╯   ╰─┰─╯   ╰─┰─╯    ╰╌╌┰╌╌╯\n             ▲       ┃         ▼\n             ┗━━━━━━━┻━━━━━━━━━┛\n\n   ┏━━━┓   ┏━━━┓   ┏━━━┓\n   ┃░░░┃   ┃░░░┃   ┃░░░┠─▷╮ ┏╍╍╍╍╍┓\n   ┃░░░┗━━━┛░░░┗━━━┛░░░┃  │ ┇░░░░░┗╍╍╍╍╍┓\n   ■░░░░░░░░░░░░░░░░░░░┃  ╰─┨░░░░░░░░░░░┇\n   ┃░░░┏━━━┓░░░┏━━━┓░░░┃    ┇░░░░░┏╍╍╍╍╍┛\n   ┗━━━┛   ┗━┯━┛   ┗━┯━┛    ┗╍╍┯╍╍┛\n             △       │         ▽\n             ╰───────┴─────────╯\n\n#+end_example\n\n** ASCII to UNICODE\n:PROPERTIES:\n:CUSTOM_ID: ascii-to-unicode\n:END:\n\nThe standard base-line =Uniline= (=INS s 0=) or =aa2u-rectangle= (=INS s a=)\nconversions may be used to convert ASCII art to UNICODE. The original\nASCII art may be drawn for instance by the =artist-mode= or the\n=picture-mode= packages.\n\nTo use =aa2u-rectangle=, install the =ascii-art-to-unicode= package by\nThien-Thi Nguyen (RIP), available on ELPA. =Uniline= does not requires a\ndependency on this package, by lazy evaluating any call to\n=aa2u-rectangle=.\nSee https://elpa.gnu.org/packages/ascii-art-to-unicode.html\n\n[[file:images/ascii-2-unicode.png]]\n\n#+begin_example\n\n  +-------------+    +--+\n  |             +-->-|  +-----+   ASCII art\n  | 1  +--------+--+ | 3      |   made by\n  +----+--------+  | +----+---+   Artist-mode\n       | 2         +-<----+\n       +-----------+\n\n  ╭─────────────╮    ╭──╮\n  │             ├──▷─│  ╰─────╮   Converted to\n  │ 1  ╭────────┼──╮ │ 3      │   Uniline base style\n  ╰────┼────────╯  │ ╰────┬───╯   INS s 0\n       │ 2         ├─◁────╯\n       ╰───────────╯\n\n  ┌─────────────┐    ┌──┐\n  │             ├──>─│  └─────┐   Converted by\n  │ 1  ┌────────┼──┐ │ 3      │   aa2u-rectangle\n  └────┼────────┘  │ └────┬───┘   INS s a\n       │ 2         ├─<────┘\n       └───────────┘\n#+end_example\n\n=INS s 0= with selection active calls the =uniline-change-style-standard=\nfunction. It converts what looks ASCII-art to UNICODE-art. Of course,\nthere are ambiguities regarding whether a character is part of a\nsketch or not.\n\nThe heuristic is to consider that a character is part of a sketch if\nit is surrounded by at least one other character which is part of a\nsketch. So, an isolated =-= minus character will be left alone, while\ntwo such characters =--= will be converted to UNICODE. Conversion will\nhappens also for =<-= for instance.\n\nHere is a fairly convoluted ASCII-art example, along with its\nconversion by =INS s 0=:\n\n[[file:images/ascii-2-unicode-b.png]]\n\n#+begin_example\n\n       ╭─↔--<-◁-◀--━+           +--->------==+\n  /----/ Rectangle1 |-----+-----+ Rectangle2 v    v\n  |    | <uni^code> ^     \"     | \"quote\"    +-\\  ▼\n  ^^   \\------------/   /-+-\\   +------------+ \"  v\n  |    \\--+------+--/   |   |   +----\\----/--+ \"  >▷▶>\n  \\>--\\   |      |      \\---/        |    |    \"\n      v   \\==<===/   a=b 1=2 a-to-b  +----+ ◁==/  >->\n\n       ╭─↔──◁─◁─◀──━┑           ╭───▷──────══╕\n  ╭────┤ Rectangle1 │─────╥─────┤ Rectangle2 ▽    ▽\n  │    │ <uni^code> △     ║     │ \"quote\"    ├─╖  ▼\n  △^   ├────────────┤   ╭─╨─╮   ├────────────┤ ║  ▽\n  │    ╰──┬──────┬──╯   │   │   ╰────┬────┬──╯ ║  ▷▷▶▷\n  ╰▷──╮   │      │      ╰───╯        │    │    ║\n      ▽   ╘══◁═══╛   a=b 1=2 a-to-b  ╰────╯ ◁══╝  ▷─▷\n\n#+end_example\n\n* Long range actions: contour and flood-fill\n:PROPERTIES:\n:CUSTOM_ID: long-range-actions-contour-and-flood-fill\n:END:\n** Tracing a contour\n:PROPERTIES:\n:CUSTOM_ID: tracing-a-contour\n:END:\n\n[[file:images/contour-tracing.png]]\n\n#+begin_example\n    ╭──────────────╮\n  ╭─╯A.written.text╰────────╮\n  │outlined by the.`contour'│\n  ╰─╮function.gets╶┬────────╯\n    ╰╮a.surrounding╰───────╮\n     ╰─╮line.in.the.current│\n       ╰─╮brush.style╭─────╯\n         ╰───────────╯\n#+end_example\n\nChoose or change the brush style with any of =-,+,=_,#,<delete>=. Put\nthe cursor anywhere on the shape or outside but touching it. Then\ntype:\n\n=<insert> c=\n\nA contour line is traced (or erased if brush style is =<delete>=)\naround the contiguous shape close to the cursor.\n\nWhen hitting capital letter: =<insert> S-C= the contour is\noverwritten. This means that if there was already a different style of\nline on the contour path, it is overwritten.\n\nThe shape is distinguished because it floats in a blank characters\nocean. For the shake of the contour function, blank characters are\nthose containing lines as drawn by =Uniline= (including true blank\ncharacters). Locations outside the buffer are also considered blank.\n\nThe algorithm has an upper limit of =10000= steps. This avoids an\ninfinite loop in which the algorithm may end up in some rare\ncases. One of those cases is when the contour crosses a new-page\ncharacter, displayed by =Emacs= as =^L=. =10000= steps require a fraction of\na second to run. For shapes really huge, you may launch the contour\ncommand once again, at the point where the previous run ended.\n\nThis =10000= steps limit is customizable. Type:\n\n#+begin_example\nM-x customize-variable uniline-contour-max-steps\n#+end_example\n\n** Flood-fill\n:PROPERTIES:\n:CUSTOM_ID: flood-fill\n:END:\n\n[[file:images/flood-fill.png]]\n\n#+begin_example\n\n this.text.surrounds      this.text.surrounds\n .                 /      .▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒/\n .                //╶───▷╴.▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒//\n ...            ////      ...▒▒▒▒▒▒▒▒▒▒▒▒////\n   ...a.hole/////           ...a.hole/////\n\n#+end_example\n\nA hollow shape is a contiguous region of identical characters (not\nnecessarily blank), surrounded by a boundary of different\ncharacters. The end of the buffer in any direction is also considered\na boundary.\n\nPut the cursor anywhere in the hole. Then type:\n\n=<insert> i=\n\nAnswer by giving a character to fill the hole.\n\nIf instead of a character, =SPC= or =DEL= is typed, then a shade of grey\ncharacter is picked. =SPC= selects a darker grey than the one the point\nis on, while =DEL= selects a lighter. There are 5 shades of grey in the\nUNICODE standard: =\" ░▒▓█\"=.  Those grey characters are well supported\nby the suggested fonts.\n\n=C-y= is also an option. The first character in the top of the kill\nring will be chosen as the filling character. (The kill ring is filled\nby functions like =C-k= or =M-w=, unrelated to =Uniline=).\n\nTyping =<return>= or =C-g= aborts the filling operation.\n\nA rectangular shape may also be filled.\n- Mark a region\n- =<insert> i=\n- answer which character should be used to fill.\n\nThere is no limit on the area to fill. Therefore, the filling\noperation may flood the entire buffer (but no more).\n\n* Macros\n:PROPERTIES:\n:CUSTOM_ID: macros\n:END:\n=Uniline= adds directional macros to the =Emacs= standard macros.\n\nRecord a macro as usual with =C-x (= … =C-x )=.\n\nThen call it with the usual =C-x e=. But then, instead of executing\nthe macro, a menu is offered to execute it in any of the 4 directions.\n\nWhen a macro is executed in a direction other than the one it was\nrecorded, it is twisted in that direction. This means that recorded\nhits on the 4 keyboard arrows are rotated. It happens also for shift\nand control variations of those keys. Direction of text insertion is\nalso rotated.\n\nThere is still the classical =e= option to call the last recorded\nmacro. So instead of the usual =C-x e=, type =C-x e e=. And of course,\nthe usual repetition typing repeatedly =e= is available.\n\nWhy are directional macros useful? To create fancy lines. For\ninstance, if we want a doted-line instead of the continuous one, we\nrecord a macro for one step:\n\n#+begin_example\nC-x (             ;; begin recording\nINS o             ;; insert a small dot\n<right> <right>   ;; draw a line over 2 characters\nC-x )             ;; stop recording\n#+end_example\n\nThen we call this macro repeatedly in any of the 4 directions:\n\n[[file:images/macro-doted-line.png]]\n\n#+begin_example\n\n   ·─·─·─·─·  ╷     ·──·\n           │  │     │  │\n           ·  ·     ·  ·\n           │  │     │  │\n           ·  ·─·─·─·  ·\n           │           │\n           ·─·─·─·─·─·─·\n\n#+end_example\n\nWe can draw complex shapes by just drawing one step. Hereafter, we\ncall a macro in 4 directions, closing a square:\n\n[[file:images/macro-fancy-squares.png]]\n\n#+begin_example\n\n   ╭╮╭╮╭╮╭╮╭╮╭╮     △ △ △ △ △ △       ╭─╮ ╭─╮ ╭─╮ ╭─╮     ╭─╮ ╭─╮ ╭─╮ ╭─╮\n ╭─╯╰╯╰╯╰╯╰╯╰╯│    ╶╯╶╯╶╯╶╯╶╯╶╯╷   ╭──╯∙╰─╯∙╰─╯∙╰─╯∙│    ▷┤□├▷┤□├▷┤□├▷┤□├▽\n ╰╮           ╰╮  ◁╮           ╰▷  │∙               │   ╭┴┼─╯ ╰─╯ ╰─╯ ╰─┼┴╮\n ╭╯           ╭╯   ╵           ╷   ╰╮               ╰╮  │□│             │□│\n ╰╮           ╰╮  ◁╮           ╰▷   │               ∙│  ╰┬╯             ╰┬╯\n ╭╯           ╭╯   ╵           ╷   ╭╯               ╭╯   △               ▽\n ╰╮           ╰╮  ◁╮           ╰▷  │∙               │   ╭┴╮             ╭┴╮\n ╭╯           ╭╯   ╵           ╷   ╰╮               ╰╮  │□│             │□│\n ╰╮           ╰╮  ◁╮           ╰▷   │               ∙│  ╰┬┼─╮ ╭─╮ ╭─╮ ╭─┼┬╯\n  │╭╮╭╮╭╮╭╮╭╮╭─╯   ╵╭╴╭╴╭╴╭╴╭╴╭╴    │∙╭─╮∙╭─╮∙╭─╮∙╭──╯   △┤□├◁┤□├◁┤□├◁┤□├◁\n  ╰╯╰╯╰╯╰╯╰╯╰╯      ▽ ▽ ▽ ▽ ▽ ▽     ╰─╯ ╰─╯ ╰─╯ ╰─╯       ╰─╯ ╰─╯ ╰─╯ ╰─╯\n\n#+end_example\n\n* Which fonts?\n:PROPERTIES:\n:CUSTOM_ID: which-fonts\n:END:\nA mono-space character font must be used. It must also support UNICODE.\n\n** Recommended fonts\n:PROPERTIES:\n:CUSTOM_ID: recommended-fonts\n:END:\n\nNot all fonts are born equal.\n\n- =(set-frame-font \"DejaVu Sans Mono\"        )=\n- =(set-frame-font \"Unifont\"                 )=\n- =(set-frame-font \"Hack\"                    )=\n- =(set-frame-font \"JetBrains Mono\"          )=\n- =(set-frame-font \"Cascadia Mono\"           )=\n- =(set-frame-font \"Agave\"                   )=\n- =(set-frame-font \"JuliaMono\"               )=\n- =(set-frame-font \"FreeMono\"                )=\n- =(set-frame-font \"Iosevka Comfy Fixed\"     )=\n- =(set-frame-font \"Iosevka Comfy Wide Fixed\")=\n- =(set-frame-font \"Aporetic Sans Mono\"      )=\n- =(set-frame-font \"Aporetic Serif Mono\"     )=\n- =(set-frame-font \"Source Code Pro\"         )=\n\nThose fonts are known to support the required UNICODE characters, AND\ndisplay them as mono-space. There are fonts advertised as mono-space\nwhich give arbitrary widths to non-ASCII characters. That is bad for\nthe kind of drawings done by =Uniline=.\n\nYou may want to try any of the suggested fonts. Just hit the\ncorresponding entry in the =Uniline= menu, or type =<insert> f=. You may\nalso execute the above Lisp commands like that:\n\n=M-: (set-frame-font \"DejaVu Sans Mono\")=\n\nThis setting is for the current session only. If you want to make it\npermanent, you may use the =Emacs= customization:\n\n=<insert> f *=\n\nor\n\n=M-x customize-face default=\n\nBeware that =Emacs= tries to compensate for missing UNICODE support by\nthe current font. =Emacs= substitutes one font for another, character\nper character. The user may not notice until the drawings done under\n=Emacs= are displayed on another text editor or on the Web. Of course,\nusing the suggested fonts and the UNICODEs drawn by =Uniline= keeps you\naway from those glitches.\n\nTo know which font =Emacs= has chosen for a given character, type:\n\n=C-u C-x ==\n\nNote that none of those commands downloads a font from the Web.\nThe font should already be available.\n\n** Use case: mixing fonts\n:PROPERTIES:\n:CUSTOM_ID: use-case-mixing-fonts\n:END:\n\nA user on GitHub, dmullis, exposed his use-case. A source-code base is\nusually edited with a font not in the Uniline list of recommended\nfonts. However, it is desirable to document the source code with\nUniline, either directly along the source or in separate files. How to\nachieve that without messing with the fonts in several Emacs buffers?\n\nSeveral solutions have emerged from the discussion.\n\n- =face-remap-add-relative=\n\nA line like this at the top of the files reserved for Uniline drawings:\n\n#+begin_example\n-*- eval: (face-remap-add-relative 'default :family \"DejaVu Sans Mono\"); -*-\n#+end_example\n\nThis confines its effect to just the one single buffer.\n\n- =uniline-mode-hook=\n\nAdd a hook (a function called when entering =uniline-mode=):\n\n#+begin_example\n(add-hook\n  'uniline-mode-hook\n  (lambda () (face-remap-add-relative 'default :family \"DejaVu Sans Mono\")))\n#+end_example\n\nThere are also =uniline-mode-on-hook= & =uniline-mode-off-hook= which can be handy.\n\n- =font-lock-comment-face=\n\nAn alternative mean of limiting the scope of the font change is the\nEmacs standard font-lock mechanism.\n\n#+begin_example\n(customize-face '(font-lock-comment-face))\n#+end_example\n\nThen check =Font Family=, type in value =\"DejaVu Sans Mono\",= and =C-x C-s=.\n\nNow any major mode that understands \"comments\" as distinct from other\ntext can safely nest a Uniline drawing within its boundaries, all text\noutside the \"comment\" unaffected (except perhaps by spacing).\n\nLook also at the =font-lock-constant-face= face.\n\n- Org Mode\n\nIn Org Mode, the usable faces could be =org-block=, =org-quote=,\n=org-verse=. But first the =org-fontify-quote-and-verse-blocks= variable\nmust be set to =t=.\n\n- Markdown\n\nIn Markdown mode, customize the =markdown-pre-face= or\n=markdown-code-face= faces.\n\n* Hydra or Transient?\n:PROPERTIES:\n:CUSTOM_ID: hydra-or-transient\n:END:\nThe basic usage of =Uniline= should be easy: just move the point, and lines\nare traced. Change brush to draw thicker lines.\n\nMore complex actions are summoned by the =<insert>= key, with or without\nselection. This is a single key to remember. Then a textual menu is\ndisplayed, giving the possible keys continuations and their\nmeaning. All that is achieved by the =Hydra= or =Transient= libraries,\nwhich are now part of =Emacs= (thanks!).\n\nThe =Hydra= and =Transient= libraries offer similar features. Some users\nmay prefer one or the other.\n\n=Uniline= was developed from day one with =Hydra=. =Transient= is a late\naddition.\n\n** Selecting Hydra or Transient\n:PROPERTIES:\n:CUSTOM_ID: selecting-hydra-or-transient\n:END:\n\nTwo files are compiled when installing =Uniline=\n- =uniline-hydra.el=\n- =uniline-transient.el=\n\nOne of them should be loaded (but not both). There are several\nways. The cleanest is =use-package=. Add those lines to your =~/.emacs=\nfile:\n\n#+begin_src elisp\n(use-package uniline-hydra\n  :bind (\"C-<insert>\" . uniline-mode))\n#+end_src\n\nor:\n\n#+begin_src elisp\n(use-package uniline-transient\n  :bind (\"C-<insert>\" . uniline-mode))\n#+end_src\n\nThe following key sequences can assist in modifying the =.emacs= file:\n- =<INS> * H=\n- =<INS> * T=\n\nNote: there used to be a customizable setting to switch between the\ntwo interfaces. This had many issues. One of them is that the\nnative-compiler is blind to all user-customized settings.\n\nThere is a third file, =uniline-code.elc=. Loading =uniline-hydra.elc= or\n=uniline-transient.elc= automatically loads =uniline-core.elc=.\n\n** Instantly selecting Hydra or Transient\n:PROPERTIES:\n:CUSTOM_ID: instantly-selecting-hydra-or-transient\n:END:\nIt is now possible to switch user interfaces on the fly.\n\nTo do so, look at the \"Customize\" entry in the Uniline menu. This\nmenu is available:\n- from the menu-bar at the top of the Emacs screen (if not made\n  invisible),\n- by left-clicking on =\"Uniline\"= in the mode-line, at the bottom of the\n  Emacs screen.\n\nNote that the changes are for the current session only. To permanently\nchoose Hydra or Transient, change your =~/.emacs=initialization file\nas describe in [[#selecting-hydra-or-transient][Selecting Hydra or Transient]].\n\nThe actions performed by the menu are:\n- =(load-library \"uniline-hydra\")=\n- =(load-library \"uniline-transient\")=\n\nYou can execute them directly or by other means.\n\n** One-liner menus\n:PROPERTIES:\n:CUSTOM_ID: one-liner-menus\n:END:\nThe multi-lines menus in Hydra and Transient are quite useful for\ncasual users. For seasoned users, those huge textual menus may\ndistract them from their workflow.\n\nIt is now possible to switch to less distracting textual menus. They\nare displayed in the echo-area on a single line.\n\nTo do so, type:\n- =C-t= within a sub-mode (glyph insertion mode, rectangle handling,\n  etc.)\n- =C-h TAB= at the top-level.\n\nThis will flip between the two sizes of textual menus. It also affects\nthe welcome message, the one displayed when entering the =Uniline= minor\nmode.\n\nThe current size is controlled by the =uniline-hint-style= variable:\n- =t= for full fledged messages over several lines\n- =1= for one-liner messages\n- =0= for no message at all\n\nThe variable is \"buffer-local\", which means that it can take distinct\nvalues on distinct buffers.\n\nIts default value can be customized and saved for future sessions:\n\n=M-x customize-variable uniline-hint-style=\n\nAfter customization it can be changed later, on a buffer per buffer\nbasis, with the =C-t= or =C-h TAB= keys.\n\nTransient natively offers a similar setting:\n=transient-show-popup=. (There is no such variable in Hydra). It can be\ncustomized with =t=, =nil=, =0= (zero), or a number. This is similar but not\nexactly the same as the Hydra behavior and the =uniline-hint-style=.\nthe Transient setting stays in effect until the =C-t= or =C-h TAB= keys\nare not used, . As soon as one of those keys is invoked,\n=transient-show-popup= is toggled (which does not happens in Transient\nalone). The change is kept in effect throughout the =Uniline= session,\nbut no longer.\n\n** The Hydra interface\n:PROPERTIES:\n:CUSTOM_ID: the-hydra-interface\n:END:\n\nPut that in your =~/.emacs= file:\n\n#+begin_src elisp\n(use-package uniline-hydra\n  :bind (\"C-<insert>\" . uniline-mode))\n#+end_src\n\nIt has been asked by =Transient=-only users to avoid installing the\n=Hydra= package. Currently, it is not possible to make dependencies\nconditional in =Melpa=. And removing the =Hydra= dependency would hurt\n=Hydra= users. Therefore, for the time being, the =Hydra= package is still\ninstalled when installing =Uniline= through =Melpa=.\n\n** The Transient interface\n:PROPERTIES:\n:CUSTOM_ID: the-transient-interface\n:END:\n\nPut that in your =~/.emacs= file:\n\n#+begin_src elisp\n(use-package uniline-transient\n  :bind (\"C-<insert>\" . uniline-mode))\n#+end_src\n\n=Transient= interface was added recently to =Uniline=. This leaded to the\nsplitting of the single =uniline.el= file into 4 source\nfiles. Hopefully, the added complexity remains hidden by the =Elpa= -\n=Melpa= packaging system.\n\n* Customization\n:PROPERTIES:\n:CUSTOM_ID: customization\n:END:\nType: =M-x customize-group uniline=.\n\nOr =Menu bar ⟶ Options ⟶ Customize Emacs ⟶ Specific Group… ⟶ \"uniline\"=.\n\nThis invokes the standard =Emacs= customization system. Your settings\nwill be saved in the file pointed to by the =custom-file= variable if\nset, or your =~/.emacs= file. (Along with all your other settings\nunrelated to =Uniline=).\n\nTwo settings are special: interface type (obsolete) & the insert\nkey. The other settings are self-explanatory\n\n** Interface type\n:PROPERTIES:\n:CUSTOM_ID: interface-type\n:END:\n\nThe =uniline-interface= variable is *obsolete*. Choosing between =Hydra= or\n=Transient= interface is done by loading one or the other\nsub-package. This is best done in the =.emacs= initialization file. See\n[[#installation][Installation]] for details.\n\nTyping either of the following key sequences can assist in modifying the =.emacs= file:\n- =<INS> * H=\n- =<INS> * T=\n\n** Insert key\n:PROPERTIES:\n:CUSTOM_ID: insert-key\n:END:\n\nBy default, the =<insert>= or =INS= key is the prefix for most of the\n=Uniline= actions. Some computers do not have an =INS= key, or it is bound\nto some other command (Apple?).\n\nThis can be changed temporarily or permanently. The customization\nallows to set several keys at the same time.\n\nDepending on whether =Emacs= is run in a graphical environment or a\ntext-only terminal, either the =<insert>= or the =<insertchar>= events are\ngenerated by the =INS= key. Therefore, by default =Uniline= defines both\nevents as the =INS= key.\n\nVariable =uniline-key-insert=.\n\n** Maximum steps when drawing a contour\n:PROPERTIES:\n:CUSTOM_ID: maximum-steps-when-drawing-a-contour\n:END:\nDefaults to =10000=.\nTo avoid an infinite loop in some rare cases.\n\nVariable =uniline-contour-max-steps=.\n\n** Cursor type\n:PROPERTIES:\n:CUSTOM_ID: cursor-type\n:END:\nHollow by default, so that what is under the cursor remains visible.\n\nThere is the option to leave the cursor as it is.\n\nVariable =uniline-cursor-type.=\n\n** Hint style\n:PROPERTIES:\n:CUSTOM_ID: hint-style\n:END:\nCurrently only applicable to the =Hydra=.\nIt defaults to \"full fledged menus\".\n\nVariable =uniline-hint-style=.\n\n=Transient= offers a similar setting: =transient-show-popup=.\n\n** Welcome message visibility\n:PROPERTIES:\n:CUSTOM_ID: welcome-message-visibility\n:END:\nDefault is \"on\". Turn it \"off\" for less distraction.\n\nEven when turned of, the welcome message can still be displayed by\npressing =C-h TAB=.\n\nVariable =uniline-show-welcome-message=.\n\n** Line spacing\n:PROPERTIES:\n:CUSTOM_ID: line-spacing\n:END:\n\nThe =line-spacing= setting in =Emacs= can change the display of a\nsketch. (This setting is unrelated to =Uniline=).\n\nThe best looking effect is given by:\n: (setq line-spacing nil)\n\nYou may want to change your current setting. =Uniline= may handle this\nvariable some day. Right now, =line-spacing= is left as a matter of\nchoice for everyone.\n\n[[file:images/line-spacing.png]]\n\n#+begin_example\n\n ╭────┬────────┬────╮   ╺┯━━━━┯┯━━┯┯━┯┯━━━━━━━━┯┯━━━━━━━┯┯━━━━━━┯╸\n │▒▒▒▒╰────────╯▒▒▒▒│    │    │╰is╯╰a╯│        ││       │╰around╯\n │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│    ╰this╯       ╰sentence╯╰hanging╯\n │▒▒▒╭─╮▒▒▒▒▒▒╭─╮▒▒▒│            △\n │▒▒▒╰─╯▒▒▒▒▒▒╰─╯▒▒▒│            │                  △\n │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│            ╰─────────┬────────╯\n ╰──────────────────╯                    verbs\n              (setq line-spacing nil)\n\n#+end_example\n\n** Font\n:PROPERTIES:\n:CUSTOM_ID: font\n:END:\n\nFace customization is unrelated to =Uniline=. However, =Uniline= can\nassist in choosing a good font and customizing the =default= face. See\n[[#which-fonts][Which fonts?]].\n\nType =<insert> f= to select a font just for the current =Uniline=\nsession. Type =*= to enter the =Emacs= customization of the =default= face\nand retain your choice for future sessions.\n\n** Upward infiniteness ∞\n:PROPERTIES:\n:CUSTOM_ID: upward-infiniteness-\n:END:\nIf the variable =uniline-infinite-up↑= is:\n\n- =t=, then the buffer grows at the top of the buffer (or at the top of\n  the narrowed region), by adding empty lines as needed.\n\n- =nil=, then the top of the buffer (or the top of the narrowed region)\n  is a non-trespassable limit. This is the default, and the behaviour\n  of previous versions of Uniline.\n\n* How Uniline behaves with its environment?\n:PROPERTIES:\n:CUSTOM_ID: how-uniline-behaves-with-its-environment\n:END:\n** Language environment\n:PROPERTIES:\n:CUSTOM_ID: language-environment\n:END:\nThe so called \"language environment\" in Emacs can cause unwanted line\nbreaks, like in this drawing:\n\n[[file:images/broken-lines-lang-env.png]]\n\n#+begin_example\n\n  ╶───────────┬─────╮\n                         │           │\n                         │           │\n                         │           │\n                         ╰───────────╯\n     unexpected broken lines\n\n  ╶─────┬───────────╮\n        │           │\n        │           │\n        │           │\n        ╰───────────╯\n     expected continuous lines\n\n#+end_example\n\nThe above example was drawn first with the =Chinese-BIG5= language\nenvironment, then with the =English= environment.\n\nThere is nothing specific about =Chinese-BIG5=. It is just an instance\npicked out from more than 100 language environments.\n\n#+begin_example\nC-x RET l Chinese-BIG5\nC-x RET l English\n#+end_example\n\nIn =Chinese-BIG5=, some characters are considered twice as wide as\nstandard characters. Whereas in =English=, all characters needed by\nUniline are 1 unit wide.\n\nThanks to *rumengling* (GitHub) for discovering and diagnosing the\nissue!\n\nTo workaround the issue, when entering =uniline-mode=, the width of all\ncharacters Uniline uses is checked. If some of them are more than 1,\nthe =char-width-table= variable is patched.\n\nWhat are the consequences of this patch? The =char-width-table= variable\nis an Emacs global. Therefore the patch by Uniline will affect all\nbuffers. As the characters touched by the patch are graphic, and have\nnothing to do with Chinese, it should not have any significance on\ntext written in Chinese.\n\nIt was pondered whether Uniline should put back =char-width-table= at\nits original value upon exiting =uniline-mode=, or leaving the\npatch. For now, it has been decided to leave it. Because anyway,\nintertwining several =uniline-mode= and changes to the language\nenvironment is intractable.\n\nIn case of something, re-setting the language environment to its same\nvalue cancels the patch to =char-width-table= by Uniline.\n\n** Compatibility with Picture-mode\n:PROPERTIES:\n:CUSTOM_ID: compatibility-with-picture-mode\n:END:\n\n=Picture-mode= and =uniline-mode= are compatible. Their features overlap\nsomehow:\n- Both implement an unlimited buffer in east and south directions.\n- Both visually truncate long lines (actual text is not truncated).\n- Both set the overwrite mode (=uniline-mode= activates\n  =overwrite-mode=, while =picture-mode= re-implements it)\n- Both are able to draw rectangles (=uniline-mode= in UNICODE,\n  =picture-mode= in ASCII), copy and yank them.\n\nThey also have features unique to each:\n- =Picture-mode= writes in 8 possible directions\n- =Picture-mode= handles TAB stops\n- =Uniline-mode= draws lines and arrows\n\n** Compatibility with Artist-mode\n:PROPERTIES:\n:CUSTOM_ID: compatibility-with-artist-mode\n:END:\n\n=Artist-mode= and =uniline-mode= are mostly incompatible. This is because\n=artist-mode= preempts the arrow keys, which give access to a large part\nof =uniline-mode= features.\n\nHowever, it is possible to use both one after the other.\n\n** Compatibility with Whitespace-mode\n:PROPERTIES:\n:CUSTOM_ID: compatibility-with-whitespace-mode\n:END:\n\n=Whitespace-mode= and =uniline-mode= are mostly compatible.\n\nWhy activate =whitespace-mode= while in =uniline-mode=? Because\n=Uniline= creates a lot of white-spaces to implement an infinite\nbuffer. And it is funny to look at this activity.\n\nTo make =uniline-mode= and =whitespace-mode= fully compatible, disable\nthe newline visualization:\n\n- =M-x customize-variable whitespace-style=\n- uncheck =(Mark) NEWLINEs=\n\nThis is due to a glitch in =move-to-column= when a visual property is\nattached to newlines. And =uniline-mode= makes heavy use of =move-to-column=.\n\n** Compatibility with Org Mode\n:PROPERTIES:\n:CUSTOM_ID: compatibility-with-org-mode\n:END:\nYou may want to customize the shift extension mode in =Org Mode=. This\nis because =Org Mode= preempts =shift-select-mode= for other useful\npurposes. Just type:\n\n#+begin_example\nM-x customize-variable org-support-shift-select\n#+end_example\n\nand choose \"when outside special context\", which sets it to =t=.\n\nYou then get the shift-selection from =Org Mode=, not from =Uniline=. The\ndifference is that the =Uniline='s one handles the infinite-ness of the\nbuffer.\n\nOther than that, =Uniline= is compatible with =Org Mode=\n\nThanks to jdtsmith (GitHub) for sharing a funny fact he discovered. If\na source block is created with the =Uniline= language (=Uniline= is\n*not* a language like =C++,= =Python=, or =Bash=), then it can be\nedited (=M-x org-edit-special=) with =uniline-mode= automatically\nactivated.\n\n[[file:images/org-src-block.png]]\n\n#+begin_example\n#+begin_src uniline\n╭───╮   ╭───╮\n│ ╷ ╰───╯ ╷ │\n│ ╰─    ╶─╯ │\n╰╮ ●     ● ╭╯\n │      ╷  │\n ╰╮ ────╯ ╭╯\n  ╰───────╯\n#+end_src\n#+end_example\n\n** Org Mode and LaTex\n:PROPERTIES:\n:CUSTOM_ID: org-mode-and-latex\n:END:\nUse the =pmboxdraw= LaTex module. This gives limited support for \"box\ndrawing\" characters in LaTex documents.\n\nExample:\n\n[[file:images/latex-block.png]]\n\n#+begin_example\n#+LATEX_HEADER: \\usepackage{pmboxdraw}\n\n#+begin_src text\n\nthis works:\n┌─────┐       ┌────────────┐\n│     ├───────┤            │\n└─────┘       │            │\n┌─────┐  ┌────┤            │\n│     ├──┘    │            │\n└─────┘  ┌────┤            │\n┌─────┐  │    │            │\n│     ├──┘    └────────────┘\n└─────┘\n\nthis does not quite work:\n   ┏━━━┓  ┏━━┓     ┏━━━━━┓\n   ┃   ┃  ┃  ┣━━━━━┫     ┃\n   ┃   ┗━━┛  ┃    ┏┛     ┃\n   ┗━━━━━━━━━┛    ┗━━━━━━┛\n\nbut that is OK:\n     ┏━━━┓\n     ┃   ┃\n     ┗━━━┛\n\nthat is OK too:\n╺════╦══╗  ╔════╗\n     ║ A║  ║ B  ╚══╗\n     ╚══╝  ╚═══════╝\n\nthis works:\n\n├── dev\n└┬┬ release\n │├── new\n │└── old\n ├── graph\n └── non-graph\n\n#+end_src\n#+end_example\n\nNote that corners of thin lines should be sharp. There is no support\nfor rounded corners.\n\nTo export this Org Mode example to PDF through LaTex, type:\n\n=C-c C-E l o=\n\n** What about =\\t= tabs?\n:PROPERTIES:\n:CUSTOM_ID: what-about-t-tabs\n:END:\nSome files may contain tabs (the character =\\t=). Those include\nprogramming code (Python, Perl, C++, D, Rust, JavaScript and so on).\n\nWhen =Uniline= draws something in the middle of a TAB, or right onto a\nTAB, it first converts it to spaces, then proceeds as usual. This\nprocess is invisible. So be cautious if TABs have a special meaning in\nthe file.\n\nAlso, rectangles are first untabified (if there are TABs) before\nmoving them. This avoids some rare instances of misalignment.\n\nOne way to see what is going on, is to activate the =whitespace-mode=.\n\n** What about =^L= page separation?\n:PROPERTIES:\n:CUSTOM_ID: what-about-l-page-separation\n:END:\n=Uniline= does not work well with =^L= (page separation)\ncharacter. Nor with similar characters, like =^T=. When trying to\ndraw a line over such a character, the cursor may get stuck. This is\nbecause those characters occupy twice the width of a normal character.\n\nJust try to get away from =^L=, =^T= and such when drawing with\n=Uniline=.\n\n** Emacs on the Linux console\n:PROPERTIES:\n:CUSTOM_ID: emacs-on-the-linux-console\n:END:\nLinux consoles are the 7 non-graphic screens which can be accessed\nusually typing =C-M-F1=, =C-M-F2=, and so on. Such a screen is also\npresented when connecting through =ssh= or =tls= into a non-graphical server.\n\nBy default they use a font named \"Fixed\" with poor support for\nUnicode. However, it supports lines of the 3 types, mixing all of them\nin thin lines though.\n\nAnother problem is that by default =S-<left>= and =C-<left>= are\nindistinguishable from =<left>=. Same problem with =<right>=, =<up>=, =<down>=\nand =<insert>=. This has nothing to do with =Emacs=. A solution can be\nfound here: https://www.emacswiki.org/emacs/MissingKeys\n\n** Emacs on a graphical terminal emulator\n:PROPERTIES:\n:CUSTOM_ID: emacs-on-a-graphical-terminal-emulator\n:END:\nThis is the =Emacs= launched from a terminal typing =emacs -nw=. In this\nenvironment, =<insert>= does not exist. It is replaced by\n=<insertchar>=. This has already been taken into account by =Uniline=\nby duplicating the key-bindings for the two flavors of this key.\n\nIf you decide to bind globally =C-<insert>= to the toggling of\n=Uniline= minor mode as suggested, then you will have to do the same\nfor =C-<insertchar>=, for example with =use-package= in your\n=~/.emacs= file:\n\n#+begin_src elisp\n(use-package uniline\n  :defer t\n  :bind (\"C-<insert>\"     . uniline-mode)\n  :bind (\"C-<insertchar>\" . uniline-mode))\n#+end_src\n\n** Emacs on Windows\n:PROPERTIES:\n:CUSTOM_ID: emacs-on-windows\n:END:\nOn Windows the only native mono-spaced fonts are =Lucida Console= and\n=Courier New=. They are not mono-spaced for the Unicodes used by\n=Uniline=.\n\nOften, the =Consolas= font is present on Windows. It supports quite well\nthe required Unicodes to draw lines. A few glyphs produce unaligned\nresult though. They should be avoided under =Consolas=: =△▶▹◆=\n\nOf course, other fonts may be installed. It is quite easy.\n\n** Compatibility with ASCIIFlow\n:PROPERTIES:\n:CUSTOM_ID: compatibility-with-asciiflow\n:END:\n\nASCIIFlow is a ASCII-UNICODE diagram drawing tool (as Uniline). It\nworks on a web browser. Just open https://asciiflow.com and start\ndrawing. There is no server, ASCIIFlow operates locally on your\nPC. Your diagrams survive web browser sessions, as they are saved\nlocally behind the scene.\n\nWhen your drawing is complete, you can export it to Emacs-Uniline:\n- Click on the download button\n- Select =\"ASCII Extended\"=\n- Paste your diagram in Emacs with =C-y=\n- Modify it with Uniline\n\nFor the other way around, a Uniline drawing can be exported to\nASCIIFlow:\n- Copy it from Emacs (with =M-w= for instance).\n- In ASCIIFlow, choose =\"Select & Move\"=\n- Type =C-v=\n- Edit with ASCIIFlow\n\n* Lisp API\n:PROPERTIES:\n:CUSTOM_ID: lisp-api\n:END:\nCould =Uniline= be programmed (versus used interactively)?\nYes!\n\nThe API is usable programmatically:\n\n** Move the cursor\n:PROPERTIES:\n:CUSTOM_ID: move-the-cursor\n:END:\n\nMove cursor while drawing lines by calling any of the 4 directions\nfunctions:\n- =uniline-write-up↑=\n- =uniline-write-ri→=\n- =uniline-write-dw↓=\n- =uniline-write-lf←=\n\nThey expect a repeat =count= (usually 1) and optionally =force=t= to\noverwrite the buffer\n\n** Brush\n:PROPERTIES:\n:CUSTOM_ID: brush\n:END:\n\nSet the current brush by calling any of the following:\n\n- =uniline--set-brush-nil=   ;; write nothing\n- =uniline--set-brush-0=     ;; eraser\n- =uniline--set-brush-1=     ;; single thin line╶─╴\n- =uniline--set-brush-2=     ;; single thick line╺━╸\n- =uniline--set-brush-3=     ;; double line╺═╸\n- =uniline--set-brush-block= ;; blocks ▙▄▟▀\n\nThose functions are equivalent to:\n\n- =(setq uniline--brush nil)=\n- =(setq uniline--brush 0)=\n- =(setq uniline--brush 1)=\n- =(setq uniline--brush 2)=\n- =(setq uniline--brush 3)=\n- =(setq uniline--brush :block)=\n\nexcept the functions also update the mode-line.\n\n** Example: Lisp function to draw a plus sign\n:PROPERTIES:\n:CUSTOM_ID: example-lisp-function-to-draw-a-plus-sign\n:END:\nFor instance, if we want to create a function to draw a \"plus\" sign,\nwe can code it as follows:\n\n#+begin_src elisp\n(defun uniline-draw-plus ()\n  (interactive)\n  (uniline-write-ri→ 1)\n  (uniline-write-dw↓ 1)\n  (uniline-write-ri→ 1)\n  (uniline-write-dw↓ 1)\n  (uniline-write-lf← 1)\n  (uniline-write-dw↓ 1)\n  (uniline-write-lf← 1)\n  (uniline-write-up↑ 1)\n  (uniline-write-lf← 1)\n  (uniline-write-up↑ 1)\n  (uniline-write-ri→ 1)\n  (uniline-write-up↑ 1))\n#+end_src\n\nCalling =M-x uniline-draw-plus= will result in this nice little\nplus-shape:\n\n[[file:images/plus-shape.png]]\n\n#+begin_example\n   ╭╮\n  ╭╯╰╮\n  ╰╮╭╯\n   ╰╯\n  generated by\n  M-x uniline-draw-plus\n#+end_example\n\nWe may modify the function to accept the size of the shape as a\nparameter:\n\n#+begin_src elisp\n(defun uniline-draw-plus (size)\n  (interactive \"Nsize? \")\n  (uniline-write-ri→ size)\n  (uniline-write-dw↓ size)\n  (uniline-write-ri→ size)\n  (uniline-write-dw↓ size)\n  (uniline-write-lf← size)\n  (uniline-write-dw↓ size)\n  (uniline-write-lf← size)\n  (uniline-write-up↑ size)\n  (uniline-write-lf← size)\n  (uniline-write-up↑ size)\n  (uniline-write-ri→ size)\n  (uniline-write-up↑ size))\n#+end_src\n\nThe =(interactive \"Nsize? \")= form prompts user for the size of the\nshape if not given as a parameter.\n\nThis API works in any mode, not only in =Uniline= minor mode. It takes\ncare of the infiniteness of the buffer in the right and down\ndirections.\n\n** Long range actions (contour, flood-fill, rectangle)\n:PROPERTIES:\n:CUSTOM_ID: long-range-actions-contour-flood-fill-rectangle\n:END:\n\nThere are other useful functions operating on many characters at\nonce. Contour tracing and flood-filling are among them:\n\n- =uniline-contour=\n- =uniline-fill=\n\nThe following functions operate on a rectangular region, which must be\nactive prior to calling them:\n\n- =uniline-draw-inner-rectangle=\n- =uniline-draw-outer-rectangle=\n- =uniline-copy-rectangle=\n- =uniline-kill-rectangle=\n- =uniline-yank-rectangle=\n- =uniline-fill-rectangle=\n- =uniline-move-rect-up↑=\n- =uniline-move-rect-ri→=\n- =uniline-move-rect-dw↓=\n- =uniline-move-rect-lf←=\n\n** Constants\n:PROPERTIES:\n:CUSTOM_ID: constants\n:END:\n\nConstants for the 4 directions:\n\n- =uniline-direction-up↑= ;; constant 0\n- =uniline-direction-ri→= ;; constant 1\n- =uniline-direction-dw↓= ;; constant 2\n- =uniline-direction-lf←= ;; constant 3\n\n** Macro and text direction\n:PROPERTIES:\n:CUSTOM_ID: macro-and-text-direction\n:END:\n\nChanging text direction:\n\n- =uniline-text-direction-up↑=\n- =uniline-text-direction-ri→=\n- =uniline-text-direction-dw↓=\n- =uniline-text-direction-lf←=\n\nor (in this case the mode-line is not updated):\n\n- =(setq uniline-text-direction uniline-direction-up↑)=\n- =(setq uniline-text-direction uniline-direction-ri→)=\n- =(setq uniline-text-direction uniline-direction-dw↓)=\n- =(setq uniline-text-direction uniline-direction-lf←)=\n\nCall macro in any direction:\n\n- =uniline-call-macro-in-direction-up↑=\n- =uniline-call-macro-in-direction-ri→=\n- =uniline-call-macro-in-direction-dw↓=\n- =uniline-call-macro-in-direction-lf←=\n\n** Insert and tweak glyphs\n:PROPERTIES:\n:CUSTOM_ID: insert-and-tweak-glyphs\n:END:\n\nInsert and cycle intersection glyphs:\n\n- =uniline-insert-fw-arrow=\n- =uniline-insert-fw-square=\n- =uniline-insert-fw-oshape=\n- =uniline-insert-fw-cross=\n- =uniline-insert-fw-grey=\n- =uniline-insert-bw-arrow=\n- =uniline-insert-bw-square=\n- =uniline-insert-bw-oshape=\n- =uniline-insert-bw-cross=\n- =uniline-insert-bw-grey=\n\nRotate arrow or tweak 4-half-lines or 4-block characters:\n\n- =uniline-rotate-up↑=\n- =uniline-rotate-ri→=\n- =uniline-rotate-dw↓=\n- =uniline-rotate-lf←=\n\nHere are the lowest level functions. Move point, possibly extending\nthe buffer in right and bottom directions:\n\n- =uniline-move-to-column=\n- =uniline-move-to-line=\n- =uniline-move-to-lin-col=\n- =uniline-move-to-delta-column=\n- =uniline-move-to-delta-line=\n\n** Change to alternate styles\n:PROPERTIES:\n:CUSTOM_ID: change-to-alternate-styles\n:END:\n\nA drawing in a rectangular selection may have its style changed:\n\n- =uniline-change-style-dot-3-2=      ;; 3 dashes vert. ┆, 2 horiz. ╌\n- =uniline-change-style-dot-4-4=      ;; 4 dashes vert. ┊ & horiz. ┈\n- =uniline-change-style-standard=     ;; back to Uniline base style\n- =uniline-change-style-hard-corners= ;; rounded corners╭╴become hard┌\n- =uniline-change-style-thin=         ;; convert to ╭╴ thin lines\n- =uniline-change-style-thick=        ;; convert to ┏╸ thick lines\n- =uniline-change-style-double=       ;; convert to ╔═ thick lines\n- =uniline-aa2u-rectangle=            ;; call aa2u to convert ASCII to Unicode\n\nThe above functions require a region to be marked.\n\n* Mouse support\n:PROPERTIES:\n:CUSTOM_ID: mouse-support\n:END:\nThe out-of-the-box mouse support of =Emacs= works perfectly. Except when\nthe mouse clicks on a position outside the buffer. This happens when\nclicking past the end of a too short line, or past the end of the buffer.\n\nTo handle those cases, a few standard =Emacs= functions have been\nextended to add blank characters or blank lines. Doing so, the\nmouse-click now falls on a valid part of the buffer. Of course, those\nextensions are only active on =uniline-mode= activated buffers.\n\nBeware that when the window is at the same time zoomed with =C-x C-+\nC--= AND horizontally scrolled with =C-x <=, the cursor positioning is\nnot accurate. This is due to =Emacs= limitations and bugs. Just click\ntwice to fix the inaccuracy.\n\n* Installation\n:PROPERTIES:\n:CUSTOM_ID: installation\n:END:\n\n** use-package, the straightforward way\n:PROPERTIES:\n:CUSTOM_ID: use-package-the-straightforward-way\n:END:\n\nThe =use-package= library became the de-facto standard to manage\npackages in your =.emacs= initialization file. The =use-package= library\ncomes along with Emacs. It can (among other services) delay loading\nexternal packages until they are used, and bind keyboard shortcuts to\nthe package's entry points.\n\nAdd the following lines to your =.emacs= file, and reload it, if not\nalready done. This says that the popular Melpa repository is one of\nthe central store of third parties packages. To day, it provides almost\n7000 packages to choose from.\n\n#+begin_src elisp\n(add-to-list 'package-archives\n             '(\"melpa\" . \"http://melpa.org/packages/\")\n             t)\n(package-initialize)\n#+end_src\n\nAlternately you may customize this variable:\n\n#+begin_example\nM-x customize-variable package-archives\n#+end_example\n\nThen add those lines in your Emacs initialization file (usually =~/.emacs=):\n\n#+begin_src elisp\n(use-package uniline-hydra\n  :bind (\"C-<insert>\" . uniline-mode))\n#+end_src\n\nor:\n\n#+begin_src elisp\n(use-package uniline-transient\n  :bind (\"C-<insert>\" . uniline-mode))\n#+end_src\n\nThis tell Emacs:\n- Be prepared to load =uniline-mode= when the user request it, but do\n  not load it now.\n- Bind the =C-<insert>= keys to the function =uniline-mode=. This shortens\n  the longer =M-x uniline-mode= command. Any other key combinations can\n  be bound, as you prefer. =<insert>= happens to also be the key used\n  inside =Uniline= (customizable).\n- Load either the =uniline-hydra= or the =uniline-transient= file, as you\n  prefer. This gives Uniline one or the other flavour of\n  user-interface.\n\nThere is an alias to =uniline-hydra=:\n\n#+begin_src elisp\n(use-package uniline\n  :bind (\"C-<insert>\" . uniline-mode))\n#+end_src\n\nIf you are using [[https://github.com/radian-software/straight.el][straight.el]] with =use-package=, and have\n=(setq straight-use-package-by-default t)=, you have the following options:\n\n#+begin_src elisp\n;; uniline-hydra using the alias\n(use-package uniline)\n\n;;; uniline-hydra explicitly requested\n(use-package uniline-hydra\n  :straight uniline)\n\n;;; install and load uniline-transient\n(use-package uniline-transient\n  :straight uniline)\n#+end_src\n\n** Without use-package\n:PROPERTIES:\n:CUSTOM_ID: without-use-package\n:END:\nDownload the package from Melpa:\n\n#+begin_src elisp\n(package-install \"uniline\")\n#+end_src\n\nAlternately, you can download the Lisp files, and load them manually:\n\n#+begin_src elisp\n(load-file \"uniline-hydra.el\")   ;; interpreted form\n(load-file \"uniline-hydra.elc\")  ;; byte-compiled form\n(load-file \"uniline-hydra.eln\")  ;; native-compiled form\n;; this automatically\n;; loads \"uniline-core.el\"\n;; or    \"uniline-core.elc\"\n;; or    \"uniline-core.eln\"\n#+end_src\n\nor if you prefer the Transient interface over the Hydra one:\n#+begin_src elisp\n(load-file \"uniline-transient.el\")   ;; interpreted form\n(load-file \"uniline-transient.elc\")  ;; byte-compiled form\n(load-file \"uniline-transient.eln\")  ;; native-compiled form\n;; this automatically\n;; loads \"uniline-core.el\"\n;; or    \"uniline-core.elc\"\n;; or    \"uniline-core.eln\"\n#+end_src\n\nYou should prefer the byte-compiled or native-compiled forms over the\ninterpreted forms, because there are a lot of optimizations performed\nat compile time.\n\nYou may want to give =uniline-mode= a key-binding. A way to do that\nwithout =use-package= is to add those lines to your initialization file\n(usually =~/.emacs=):\n\n#+begin_src elisp\n(require 'uniline-hydra)\n(bind-keys :package uniline-hydra (\"C-<insert>\" . uniline-mode))\n#+end_src\n\nThe downside is that =Uniline= will be loaded as soon as =Emacs= is\nlaunched, rather than deferred until invoked.\n\n* Related packages\n:PROPERTIES:\n:CUSTOM_ID: related-packages\n:END:\n\n- =artist-mode=: the ASCII art mode built into =Emacs=.\n\n- =ascii-art-to-unicode=: as the name suggest, converts ASCII drawings\n  to UNICODE, giving results similar to those of =Uniline=.\n\n- =picture-mode=: as in =Uniline=, the buffer is infinite in east & south\n  directions.\n\n- =ascii-art-to-unicode= ASCII art to UNICODE in =Emacs=. This is a\n  standard ELPA package by Thien-Thi Nguyen (rest in peace). =Uniline=\n  may call it to convert ASCII art drawings to equivalent\n  UNICODE. =Uniline= arranges to not require a dependency on\n  =ascii-art-to-unicode= by lazy evaluating a call to =aa2u=.\n\n- =org-pretty-table=: Org Mode tables /appear/ to be drawn in UNICODE\n  characters (actually they are still in ASCII).\n\n- =boxes=: draws artistic boxes around text, with nice looking unicorns,\n  flowers, parchments, all in ASCII art.\n\n- =org-drawio=: a bridge between the Draw.Io editor and =Emacs=, producing\n  drawing similar to those of =Uniline=, but in =.svg=.\n\n- =syntree=: draws ASCII trees on-the-fly from description.\n\n- =unicode-enbox=: create a UNICODE box around a text; input and output\n  are strings.\n\n- =unicode-fonts=: in =Emacs=, helps alleviate the lack of full UNICODE\n  coverage of most fonts.\n\n- =org-superstar=: prettify headings and plain lists in Org Mode, using\n  UNICODE glyphs.\n\n- =charmap=: UNICODE table viewer for =Emacs=.\n\n- =insert-char-preview=: insert UNICODEs with character preview in\n  completion prompt.\n\n- =list-unicode-display=: list all UNICODE characters, or a selection of\n  them.\n\n- =show-font=: show font features in a buffer.\n\n- =ob-svgbob=: convert your ascii diagram scribbles into happy little\n  SVG\n\n- =el-easydraw=: a full featured SVG editor right inside your =Emacs=\n\n- =asciiflow=: (not =Emacs=) draw on the web, then copy-paste your UNICODE text\n\n- =ascii-draw=: like =asciiflow= with Unicodes.\n\n- =dot-to-ascii.ggerganov.com:= (not =Emacs=) describe your schema in the\n  Graphviz language, and copy-past your UNICODE text.\n\n- =monosketch=: (not =Emacs=) draw on the web, then copy-paste your UNICODE text.\n\n- =ibm-box-drawing-hydra.el=: keyboard interface to insert UNICODE\n  box-drawing characters one at a time.\n\n- =excalidraw.com=: inline drawing, but not in Unicode or Ascii.\n\n- =org-excalidraw=: integrate SVG images generated by excalidraw into\n  Org Mode.\n\n- =rcd-box=: create tables surrounded by box-drawing characters from\n  Lisp descriptions.\n\n- =ob-diagram=: generate various diagrams using diagrams backend.\n\n- =ob-mermaid=: generate Mermaid diagrams within org-mode babel.\n\n- =quail-boxdrawing.el=: input method for box drawing characters.\n\n- =make-box.el=: box around part of a buffer.\n\n- =vim drawit ascii diagrams=: in Vim, in ASCII.\n\n- =MarkDeep=: (Casual Effects): write in Markdown, render on the Web on\n  the fly. Uniline may be used to author part of the Markdown source.\n\n- =org-utf-to-xetex=: export Org-Mode utf-8 documents to various formats\n  preserving smileys and other Unicode characters.\n\n- =image-to-ascii=: turns a photo into ASCII.\n\n- =ascii-maze-generator=: web-inline generator of mazes, with the same\n  lines drawn by Uniline.\n\n- =diagon.arthursonzogni.com=: web-inline drawing of diagrams similar to\n  those that Unline enables.\n\n- =cascii.html=: a single Html-JavaScript file to draw Unicode diagrams.\n\n- =elm-svgbob=: think of Ditaa for converting Ascii to SVG.\n\n- =rasciigraph=: script to plot time-series in Unicode.\n\n- =asciichart-sharp=: another script to plot time-series in Unicode.\n\n- =d2=, =ob-d2=: think of Mermaid to convert diagram textual descriptions\n  to SVG.\n\n- =dag-draw.el=: in Emacs program diagrams using Lisp, the output is\n  Unicode.\n\n- =https://mbork.pl/2025-11-10_ASCII_art_timeline_diagrams=: not a\n  package, just an example of how easy is to draw time-series in Ascii\n  with Emacs.\n\n- =pikchr=: describe objects and their relationship in Markdown, render\n  in Unicode.\n\n- =ob-pikchr.el=: integration of =pikchr= in Emacs Org-Mode.\n\n- =GoAT=: Go-based Text-Art to SVG refinement.\n\n- =SvgBob=: another script to convert Ascii to SVG (like Ditaa). Written\n  in Rust.\n\n- =Durdraw=: an Ascii, Unicode and Ansi art editor for Unix-like\n  systems. With colors.\n\n- =figlet=: makes large letters out of ordinary text, in text. There is\n  an Emacs integration by J. Kotta.\n\n* Author, contributors\n:PROPERTIES:\n:CUSTOM_ID: author-contributors\n:END:\n- Thierry Banel, author\n\nFeedback:\n\n- Chris Rayner (@riscy), gave recommendations prior to insertion in\n  MELPA\n\n- Adam Porter (@alphapapa), suggested submitting =Uniline= to =ELPA=;\n  should I?\n\n- Joost Kremers https://github.com/joostkremers found a bug in the\n  minor-mode key-binding definitions, and incompatibility with\n\n- DogLooksGood https://github.com/DogLooksGood gave feedback on\n  inserting usual characters not moving the cursor\n\n- LuciusChen & lhindir on GitHub, arthurno1 & karthink on Reddit,\n  pushed toward =Transient= as the default interface instead of =Hydra=\n\n- karthink noted that =Transient= was now built into =Emacs=, loosening\n  the dependencies conundrum, arthurno1 participated in the =Hydra= -\n  =Transient= discussion\n\n- karthink pointed to the new =Aporetic= font family, which was then\n  added to the =Uniline= supported fonts\n\n- rumengling on GitHub found and diagnosed the misaligned lines issue\n  produced by some \"language environments\" (see [[#language-environment][Language environment]]).\n\n- tpapp documented the installation using Straight, and fixed some\n  typos.\n\n- tskinner-oppfi (GitHub) repoted Elpaca packaging break due to a bad\n  version number.\n\nContributors:\n\n- JD Smith (jdtsmith on GitHub) rewrote the =:lighter= for added\n   flexibility (the information in the mode-line about the state of\n   =Uniline=)\n\n- JD Smith also pointed to =#+begin_src uniline= Org Mode block\n  suprising behavior (editing its content automatically switches to\n  =uniline-mode=)\n\nUtilities:\n\n- Oleh Krehel alias abo-abo for his package =Hydra=\n\n- The =Magit= team for the =Transient= library\n\n- Thien-Thi Nguyen (RIP) for his package =ascii-art-to-unicode=\n\n* License\n:PROPERTIES:\n:CUSTOM_ID: license\n:END:\nCopyright (C) 2024-2026  Thierry Banel\n\nUniline is free software: you can redistribute it and/or modify it under\nthe terms of the GNU General Public License as published by the Free\nSoftware Foundation, either version 3 of the License, or (at your\noption) any later version.\n\nUniline is distributed in the hope that it will be useful, but WITHOUT\nANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\nFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\nfor more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n"
  },
  {
    "path": "tests/bench01.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(let ((uniline-infinite-up↑ nil))\n  (uniline-bench\n   \"\"\n   \"b RET c\n - 3*<right> <down> 5*<left> <up>\n C-SPC <down> 6*<right> <insert> <down> 11*<right> 2*RET\n <down> 2*<right>\n + <down> 4*<left>\n <insert> 2*a\n 3*<left>\n <insert> a S-<up>\n 8*<left>\n RET 2*<up> 14*<right>\n - 3*<up>\"\n   \n   \"\\\n              │\n           bc╶┴─╮\n           ╰─┰──╯\n━━━━━━△━━◀━━━┛\n\"))\n"
  },
  {
    "path": "tests/bench02.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(let ((uniline-infinite-up↑ nil))\n  (uniline-bench\n   \"\\\ninitial text\n  good\n\"\n   \"C-SPC <down> 12*<right> <insert>\n 2*<down> 3*<right> R\n <end> <return>\n <down>\n <kp-subtract> <right> q\n <return> <left>\n <kp-subtract> <down> 2*<left>\n <return> 2*<up> 2*<right> <down>\n <kp-subtract> <up> 3*<right> 3*<up>\n <return> 3*<down> 2*<up> <right> 3*<down>\n C-SPC 19*<left> 3*<up> <insert>\n <up> 2*<down> c\n C-a 22*<right> C-SPC <insert> y\n 2*<return> 3*<down> 4*<right>\n = <down> 6*<left>\n <insert> s s s\n 5*<left>\n <insert> o\n 6*<left> <up>\"\n   \"\\\n                    ╷\n                    │\n  ╭────────────╮ ╭──╯ ╭────────────╮ ╭──╯\n  │initial text│ q    │initial text│ q   \n  │  good      ├─╯    │  good      ├─╯   \n  ╰──────╥─────╯      ╰───╥────────╯     \n         ╚═════·════▫═════╝\n\"))\n"
  },
  {
    "path": "tests/bench03.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(uniline-bench\n \"\"\n \"2*<return>\n 2*<down> 3*<right>\n # 6*<right> 3*<down> 6*<right> 3*<up> 6*<right> 3*<down> 6*<right> 4*<up> 3*<right> 6*<down> 12*<left>\n <return> <up> 15*<right> <up>\n C-SPC 4*<down> 12*<right> <insert>\n r # R\n <return> <return> <down> <right>\n <kp-subtract> 4*<down> 11*<right> 4*<up> 11*<left>\n <return> 23*<left> 2*<up>\n C-SPC 4*<down> 16*<right> <insert>\n 2*<down> 3*<right>\n <return> C-c C-c\"\n \"\\\n                                      \n                        ▗▄▄▄▄▄▄▄▄▄▄▄▄▖\n                        ▐╭──────────╮▌\n                  ▄▄    ▐│          │▌\n      ▝▀▀▌  ▛▀▀▌  ▌▐    ▐│          │▌\n         ▙▄▄▌  ▙▄▄▌▐    ▐│          │▌\n             ▗▄▄▄▄▄▟    ▐╰──────────╯▌\n                        ▝▀▀▀▀▀▀▀▀▀▀▀▀▘\n\")\n"
  },
  {
    "path": "tests/bench04.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(uniline-bench\n \"\"\n \"<return> <down> 4*<right> 2*<down> 4*<right> <down> <left>\n M\n <left>\n = 3*<up> M-6 <right> 3*<down> 2*<left>\n <insert> a\n M-7 <left>\n <insert> 3*a\n <left> 3*<down> M-4 <right> 3*<up>\n <return> 2*<up> 2*<right>\n <insert> s\n <down> 2*<right>\n <insert> 2*s\n 3*<down> M-6 <left>\n <insert> x\n <up>\n <insert> 5*o\n 4*<up> M-9 <right>\n C-SPC M-6 <down> M-12 <left>\n <insert> c\n <right> <return> M-13 <right> M-6 <up>\n C-SPC <insert> y <return>\"\n\"\\\n\n       ╔═════╗       ╔═════╗\n       ║ □   ║       ║ □   ║\n       ║   ■ ║       ║   ■ ║\n   ╔←══M═══◁═╝   ╔←══M═══◁═╝\n   ║ ◦ ║         ║ ◦ ║      \n   ║ ╳ ║         ║ ╳ ║      \n   ╚═══╝         ╚═══╝      \n                \n\")\n"
  },
  {
    "path": "tests/bench05.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(uniline-bench\n \"\\\n\n\n     aaaa aaaa   aaa   aaaaaaaa\"\n\n \"<return> M-> C-SPC M-26 <left> <insert> c\n  <down> <right> <right> C-SPC <insert> y c\n  <down> <right> <right> C-SPC <insert> y\n  <down> <up> c <down> <right> <right> C-SPC <insert> y c\n  <return> <up> <up> <up> <right>\n  C-SPC <down> <down> <down> M-9 <right> <kp-subtract> <insert>\n  C-r <return> M-7 <right> <down>\n  C-SPC <down> <down> <down> M-6 <right> <kp-add>\n  <insert> C-r = R RET\"\n\n\"\\\n\n\n     aaaa aa╭─────╦╤╤══aaaaaaaa\n       aaaa │aaa  ║┏┿━━━┓aaaaaaaa\n         aaa│ aaaa║┃│aaa┃║ aaaaaaaa\n           a╰─────╫╂╯  a┃a   aaaaaaaa\n                  ║┗━━━━┛║\n                  ╚══════╝\n\")\n"
  },
  {
    "path": "tests/bench06.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(uniline-bench\n \"\\\n\n\n     mùq\n        v\n  2iii   hu\n  1       u\n   é      u\n   au  uuuu\n    uuuu\n\"\n\n \"<return> 2*<down> 2*<right>\n  C-SPC 6*<down> 9*<right>\n  <insert> c <return>\n  M-3 <right> M-5 <up>\n  C-SPC <insert> y <return>\n  6*<left>\n  <insert> <return>\n  <kp-subtract>\n  <insert> c <return>\n  7*<right>\n  j <down> <left>\n  j 2*<down>\n  <insert> i SPC\"\n \n\"\\\n\n    ╭───╮\n    │mùq╰╮\n ╭──┴─┬╮v╰─╮    jmùq   \n │2iii│╰╮hu│    j░░░v  \n │1╶┬─╯ ╰╮u│  2iii░░░hu\n ╰╮é╰╮╭──╯u│  1░░░░░░░u\n  │au╰╯uuuu│   é░░░░░░u\n  ╰╮uuuu╭──╯   au░░uuuu\n   ╰────╯       uuuu   \n              \n\")\n"
  },
  {
    "path": "tests/bench07.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(let ((uniline-infinite-up↑ nil))\n  (uniline-bench\n   \"\\\naaaaa    aaaaa\na   a    a    \na   a    a\na   aaaaaa\na\naaaa\n   a\n   a\naaaa\na\"\n\n   \" <left> <kp-subtract> <insert> c\"\n\n   \"\\\naaaaa╷  ╷aaaaa╷\na╭─╮a│  │a╭───╯\na│ │a╰──╯a│\na│ │aaaaaa│\na╰─┴┬─────╯\naaaa│\n╶─╮a│\n╶─╯a│\naaaa│\na╭──╯\n╶╯\n\"))\n"
  },
  {
    "path": "tests/bench08.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(let ((uniline-infinite-up↑ nil))\n  (uniline-bench\n   \"\\\naaaa      hhhh      hhhh\na           hh      hh\na           hhhhhhhhhh\n\n\n\n\ngggg        oooooooo\n   g       o        o\n   g       o        o\n   g       o        o\n   g       o        o\ngggg       o        o\ngggg        o       o\n   g         o      o\n   g          o     o\n   g           ooooo\ngggg\n\"\n\n   \"<kp-add> <insert> c\n    <return> 8*<right>\n    + <insert> c\n    <return>\n    M-< 7*<down> 2*<right>\n    <kp-subtract> <insert> c\n    <return> 8*<right> <down>\n    <kp-subtract> <insert> c\n    <return> <down> <right> <insert> i *\n    <return> 2*<up> <kp-subtract> <insert> c\"\n\n   \"\\\naaaa╻    ╻hhhh╻    ╻hhhh╻\na┏━━┛    ┗━┓hh┗━━━━┛hh┏━┛\na┃         ┃hhhhhhhhhh┃\n╺┛         ┗━━━━━━━━━━┛\n\n\n╶───╮      ╭────────╮\ngggg│     ╭╯oooooooo╰╮\n╶─╮g│     │o╭──────╮o│\n  │g│     │o│******│o│\n  │g│     │o│******│o│\n╶─╯g│     │o│******│o│\ngggg│     │o╰╮*****│o│\ngggg│     ╰╮o╰╮****│o│\n╶─╮g│      ╰╮o╰╮***│o│\n  │g│       ╰╮o╰───╯o│\n╶─╯g│        ╰╮ooooo╭╯\ngggg│         ╰─────╯\n╶───╯\n\"))\n"
  },
  {
    "path": "tests/bench09.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(let ((uniline-infinite-up↑ nil))\n  (uniline-bench\n   \"\\\nggggggggggg   gg   ggggg\nggggggg   g        g   \n      g   gggggggggg   \n      g                \nggggggg pppppp         \ng       pppppp         \nggggg                  \n   gg\n   g\ngggg\ng\n\"\n\"<return> 5*<down> <kp-subtract> <insert> c\"\n\"\\\nggggggggggg╷  gg  ╷ggggg╷\nggggggg╭─╮g╰──────╯g╭───╯\n╶────╮g│ │gggggggggg│  \n╶────╯g│ ╰──────────╯  \nggggggg│pppppp         \ng╶───┬─╯pppppp         \nggggg│                 \n╶─╮gg│\n╶─╯g╭╯\ngggg│\ng╭──╯\n╶╯\n\"))\n"
  },
  {
    "path": "tests/bench10.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n;; a macro cannot be defined inside another macro,\n;; so this macro is defined outside\n(setq last-kbd-macro (kbd \"<kp-subtract> <right> C-<up> C-<right> <down>\"))\n\n(uniline-bench\n \"\\\n\n\n  aaaaaaaaaaaaaaaaaaaa\n  aaaaaaaaaaaaaaaaaaaa\n  aaaaaaaaaaaaaaaaaaaa\n\"\n\n \"<return> <right> <down>\n C-x 4*e 3*<right> 3*<down> 5*<left> 3*<up>\n <return>\"\n\n \"\\\n  ╭╮╭╮╭╮╭╮╭╮╭╮\n ╶┼┴╯╰╯╰╯╰╯╰╯│\n  ╰╴aaaaaaaaa╶╮aaaaaaa\n  ╭aaaaaaaaaaa╯aaaaaaa\n  ╰╴aaaaaaaaa╶╮aaaaaaa\n  ╭╯         ╭╯\n  ╰╮         ╰╮\n   │╭╮╭╮╭╮╭╮╭─╯\n   ╰╯╰╯╰╯╰╯╰╯\n\")\n"
  },
  {
    "path": "tests/bench11.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n;; Check if TABs are converted to SPC when drawing on them\n\n(uniline-bench\n \"\\\n\ttabs ?\n\t\t2 tabs\t\"\n\n \"<return> M-> <left> <right> <down> M-<\n  <return> 2*<down> <home>\n  <kp-subtract> 2*<right> 2*<up> 10*<right> 2*<down>\n  <insert> s <return>\"\n\n\"\\\n  ╭─────tabs╮?\n  │         │   2 tabs  \n╶─╯         □           \n\")\n"
  },
  {
    "path": "tests/bench12.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n;; Test rectangle fill\n\n(uniline-bench\n \"\"\n \"<return> 3*<down> 5*<right>\n 6*S-<down> 11*S-<right> <insert> i SPC\n <return> 3*<down> 7*<right> <down> 6*S-<down> 8*S-<right> <insert> i y\n <return> 2*<up> 5*S-<left> S-<up> <insert> i SPC\n <return>\"\n\"\\\n\n\n\n     ░░░░░░░░░░░\n     ░░▒▒▒▒▒░░░░\n     ░░▒▒▒▒▒░░░░\n     ░░░░░░░░░░░\n     ░░░░░░░yyyyyyyy\n     ░░░░░░░yyyyyyyy\n     ░░░░░░░yyyyyyyy\n            yyyyyyyy\n            yyyyyyyy\n            yyyyyyyy\n            yyyyyyyy\n\")\n"
  },
  {
    "path": "tests/bench13.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n;; Test glyph insertion\n\n(uniline-bench\n \"\"\n \"<kp-subtract> <return>\n 2*<down> 4*<right>\n <kp-subtract> 4*<right> 2*<down>\n <insert> a\n 2*<up> 3*<right> <up>\n <insert> s\n <down> 3*<right> 2*<down> 3*<right> 3*<left> 2*<down>\n <insert> 2*o\n 2*<up> <return> 2*<left> <up> 4*<left> <up>\n <insert> 2*S-<right> 3*<right>\n <insert> 2*S-<right> 3*<right> 2*<down>\n <insert> 2*S-<down> 3*<right>\n <insert> 2*S-<right>\n <return>\n 13*<left> 2*<up> <insert> 2*S-<left>\"\n\"\\\n\n           □\n    ╾───╮──╯──╮\n        │     │\n        ▽     ╰──╼\n              │\n              ∙\n\")\n"
  },
  {
    "path": "tests/bench14.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(uniline-bench\n \"\"\n \"<return> <return>\n 2*<down> 3*<right> 12*S-<right> 6*S-<down> # <insert> r\n <return> <insert> S-<down>\n <return> 4*<right>\n <return> 4*<right> <insert> S-<left> 5*<right>\n <insert> S-<up> S-<right> S-<left> S-<up> 3*<down>\n <insert> S-<left> 3*<down>\n <insert> 2*S-<right> S-<down> S-<up> 5*<left>\n <insert> S-<up> S-<right> 6*<left>\n <insert> S-<up> S-<right> S-<left>\n <return> 3*<up>\n <kp-subtract> 4*<right>\n <insert> a\n S-<up> 2*<right>\n <insert> a\n S-<down> 2*<right>\n <insert> a\n S-<left> 2*<right>\n <insert>\n 2*S-<left>\n <return> 9*<left>\n <return> <insert> 2*S-<right> 2*S-<left> S-<up> S-<right> 2*<right>\n <insert> 2*S-<right> S-<up> S-<down>\n <return>\"\n\"\\\n\n\n   █▀▀▀▀▀▛▀▀▀▀▙\n   ▌          ▐\n   ▌          ▐\n   ▌╰─┤△─▽─◁─╴▟\n   ▌          ▐\n   ▌          ▐\n   ▐▄▄▄▄▄█▄▄▄▄▛\n\")\n"
  },
  {
    "path": "tests/bench15.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(uniline-bench\n \"\"\n \"<return> 3*<down> 10*<right>\n <kp-subtract>\n 2*<up> 3*<right> <insert> a\n <right> <insert> 2*a\n <right> <insert> 3*a\n <right> <insert> 4*a\n <right> <insert> 5*a\n <right> <insert> 6*a\n 3*<right> <down> <insert> A\n <down> <insert> 2*A\n <down> <insert> a\n <down> <insert> 4*A\n <down> <insert> a S-<right>\n <down> 3*<left> <insert> A\n <left> <home>\n <return> 18*<right> <left> <insert> A S-<up>\n <left> <insert> a\n 2*<left>\n <kp-subtract> 2*<left> <insert> 4*A\n 2*<left> <up> <insert> 3*a S-<left>\n <up> <insert> a S-<right>\n <up> <insert> 3*a\n <up> <insert> 5*a\n <up> <insert> 6*a S-<right>\n 3*<right>\n <return>\n <insert> a\n 3*<right> <up> <insert> S-<down>\n 3*<down> 5*<right> <insert> S-<right>\n <return>\"\n\"\\\n\n          ╭──▷▶→▿▸↔──╮\n          ↔──▷       ↕\n          ▴          ▾\n          ↑          ▷\n          ▷          ↓\n          ←          ▷\n          ╰─←─╴ ◁↕↔──╯\n\")\n"
  },
  {
    "path": "tests/bench16.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(uniline-bench\n \"\"\n \"<return>\n <down> 5*<right> <insert> a\n 2*<right> <insert> A\n 2*<right> <insert> a S-<up>\n 2*<right> <insert> A S-<up>\n <down> C-a <up> C-k C-y\n <down> <home> C-y\n <left> <insert> a\n 2*<left> <insert> a\n 2*<left> <insert> a\n 2*<left> <insert> a\n C-a C-k C-y\n <right> <down> C-a C-y\n C-a 5*<right> <insert> 2*A\n 2*<right> <insert> A\n 2*<right> <insert> 2*A\n 2*<right> <insert> A\"\n\"\\\n\n     ▷ ↔ △ ↕\n     ▶ ◁ ▲ ◁             \n     ↔ ↔ ↕ ↔                                     \")\n"
  },
  {
    "path": "tests/bench17.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(uniline-bench\n \"\"\n \"3*<right> a b c\n C-SPC 3*C-b <insert>\n 2*<left> <return>\n <left> 2*<down> d e f\n C-SPC C-a <insert>\n <up> <right> <return>\n C-SPC 3*S-<right> S-<up> <insert> 2*<down> <left> <up> <return>\"\n\"\\\n╭     \nabc \ndef \n    \n\")\n"
  },
  {
    "path": "tests/bench18.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n;; Check # blocks contour\n\n(uniline-bench\n \"\"\n \"<return>\n <down> 3*<right> 6*t\n <down> 3*<left> 2*y\n <down> 5*<left> 4*r\n <down>\n <insert> C-<left> 4*o\n <right> <down> 5*o\n <insert> C-<right> 2*<up> 5*<right>\n # <insert> c\"\n\"\\\n  ▗▄▄▄▄▄▄▖\n  ▐tttttt▌\n  ▐███yy▛▘\n  ▐rrrr█▌\n▗▄▟█oooo▌\nooooo▛▀▀▘\n▀▀▀▀▀▘\n\")\n"
  },
  {
    "path": "tests/bench19.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(let ((uniline-infinite-up↑ nil))\n  (uniline-bench\n   \"\"\n   \"<return> 3*<right>\n <insert> C-<down>   tttt\n <insert> C-<right>  yyyyyy\n <insert> C-<up>     hhhhh\n <insert> C-<right>  gggg\n <insert> C-<down>   aaaaaaaa\n <insert> C-<left>   bbbbbbbbbbbbbb\n <insert> C-<up>     ff\n <insert> C-<right>  ffff\n <home> 5*<down> dddddd\n <home>   <down> ggggg\n <down>   <home> 4*SPC hhh\n <down>   <home> 5*SPC iiii\n <down>   <home> hh\n <down>   <left> hhhhhh\n <up>     <left> h\n <home>\n M-<   19*<right> ii\n <down>   <left>  ii\n <down>   <left>  ii\n <down>   <left>  iiii\n <up>     <left>  ii\n <up>     <left>  ii\n <up>     <left>  ii\n # <insert> c\n M-< <return> 3*<right> # <insert> c\n <return> <home> 12*<down> <end> C-a M-f <insert> c\n # <insert> c\"\n   \"\\\n  ▐t▖   ▐gggga▖   ▐ii▄▖  ▗▟ii▖\n  ▐t▌   ▐h▛▀▜a▌   ▝▜ii▙▖▗▟ii▛▘\n  ▐t▌   ▐h▌ ▐a▌    ▝▜ii▙▟ii▛▘\n  ▐t▙▄▄▄▟h▌ ▐a▌     ▝▜iiii▛▘\n  ▐yyyyyyh▌ ▐a▌      ▝▀▀▀▀▘\n▗▄▟█▛▀▀▀▀▀▘ ▐a▌\nffff▌       ▐a▌\nf███▙▄▄▄▄▄▄▄▟a▌\nfbbbbbbbbbbbbb▌\n▀▀▀▀▀▀▀▀▀▀▀▀▀▀▘\n▗▄▄▄▄▄▖\ndddddd▌\nggggg█▙▖\n▀▀▀▜hhh▙▄▖\n▗▄▖▝▜iiii▌\nhh▙▄▟█h▛▀▘\n▜hhhhhh▌\n▝▀▀▀▀▀▀▘\n\"))\n"
  },
  {
    "path": "tests/bench20.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n;; Test fine tweaking\n\n(uniline-bench\n \"\"\n \"<return>\n 2*<down> 2*<right>\n <kp-subtract> 4*<right> <down> 3*<right>\n <kp-add> 2*<up> 3*<left>\n = 4*<left> 4*<down> <right>\n <kp-add> 5*<right> 2*<up>\n <insert> 2*S-<left>\n <return>\n <up> <return> <left> <up> 2*<left>\n <insert> S-<up> S-<left>\n <end> <return> 2*<right> <down>\n # 8*<right> 3*<left>\n <insert> S-<up>\n <down> <left>\"\n\"\\\n\n  ╔══╘═━━┓  \n  ╟───╮ ╻┃  ▝▀▟▀▘\n  ║   ╰─┠┚\n  ║     ┃\n  ╚━━━━━┛\n\")\n"
  },
  {
    "path": "tests/bench21.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n;; Test rectangle tracing & moving\n\n(uniline-bench\n \"\"\n \"<return>\n 5*<down> 11*<right>\n 15*S-<down> 31*S-<right> 2*S-<left>\n # <insert> R\n 2*<return>\n <down> <right>\n <down> 3*<right> <left>\n 13*S-<down> 25*S-<right> S-<left> S-<right>\n <insert> # r\n 2*<return>\n 2*<down> 3*<right>\n 9*S-<down> 18*S-<right>\n <insert> <kp-subtract> r\n 2*<return>\n 2*<down> 3*<right>\n 5*S-<down> 11*S-<right>\n <insert> S-<right> = r\n 2*<return>\n 5*<up> 2*<down> 3*<right> 4*<up>\n 19*S-<down> 16*S-<left>\n <insert> 2*<left>\n 2*<return>\n 9*S-<down> 27*S-<right> 12*S-<right>\n <insert> 2*<up>\n 2*<return>\n 15*<down> 2*<up>\n 27*S-<right> 12*S-<right> 9*S-<down>\n <insert> 2*<down>\n 2*<return>\n 19*<right> 9*<down>\n 24*S-<up> 21*S-<right>\n <insert> 2*<right>\n <return>\"\n\"\\\n\n                                               \n        ▗▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▖    \n        ▐                                 ▌    \n        ▐  ▛▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▜  ▌    \n        ▐  ▌                           ▐  ▌    \n        ▐  ▌  ╭────────────────────╮   ▐  ▌    \n        ▐  ▌  │                    │   ▐  ▌    \n        ▐  ▌  │   ╔═════════════╗  │   ▐  ▌    \n        ▐  ▌  │   ║             ║  │   ▐  ▌    \n        ▐  ▌  │   ║             ║  │   ▐  ▌    \n        ▐  ▌  │   ║             ║  │   ▐  ▌    \n        ▐  ▌  │   ║             ║  │   ▐  ▌    \n        ▐  ▌  │   ║             ║  │   ▐  ▌    \n        ▐  ▌  │   ║             ║  │   ▐  ▌    \n        ▐  ▌  │   ║             ║  │   ▐  ▌    \n        ▐  ▌  │   ║             ║  │   ▐  ▌    \n        ▐  ▌  │   ╚═════════════╝  │   ▐  ▌    \n        ▐  ▌  │                    │   ▐  ▌    \n        ▐  ▌  ╰────────────────────╯   ▐  ▌    \n        ▐  ▌                           ▐  ▌    \n        ▐  ▙▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▟  ▌    \n        ▐                                 ▌    \n        ▝▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▘    \n                                               \n                                               \n\")\n"
  },
  {
    "path": "tests/bench22.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(uniline-bench\n \"\"\n \"<return> 2*<down> M-3 <right>\n <kp-subtract> M-9 <right> M-3 <down> M-5 <left> M-4 <up> M-11 <right> 2*<down>\n <insert> a\n <down> 3*<left> <down>\n <insert> 2*a\n <down> 3*<left>\n <insert> 3*a\n 3*<left>\n <insert> 4*a\n 4*<left> <up>\n <insert> 5*a\n 2*<up> 2*<left> <up>\n <return> <up>\n C-x SPC M-5 <down> M-16 <right>\n <insert> c <return>\n M-5 <up> M-4 <right>\n C-SPC <insert> y s h <return>\n 3*A M-3 <left>\n\n <return> M-20 <right>\n C-SPC <insert> y s 4 <return>\n 3*B M-3 <left>\n\n C-SPC <insert> y s + 4 <return>\n 3*C M-3 <left>\n\n M-15 <down> M-40 <left>\n C-x SPC <insert> y s + <return>\n 3*D M-3 <left>\n\n M-0 <right> M-8 <up>\n C-x SPC y <backspace>\n C-x SPC <insert> y s 3 <return>\n 3*E M-3 <left>\n\n M-20 <right>\n C-x SPC <insert> y s 4 <return>\n 3*F M-3 <left>\n\n M-19 <right>\n C-SPC <insert> y s + <return>\n 3*G M-3 <left>\n\n M-3 <down> <return> M-5 <down> <left>\n <return>\n\n M-18 <left>\n C-x SPC <insert> y s + 3 <return>\n 3*H M-3 <left>\n\"\n\"\\\n\n       ╭──────────╮    AAA ┌──────────┐    CCC ┏┉┉┉┉┉┉┉┉┉┉┓\n   ╭───┼────╮     │    ┌───┼────┐     │    ┏┉┉┉╋┉┉┉┉┓     ┋\n   ╰─╮ │    │     ▽    └─┐ │    │     ▽    ┗┉┓ ┋    ┋     ▼\n     │ │    │  ╭──╯      │ │    │  ┌──┘      ┋ ┋    ┋  ┏┉┉┛\n     ▴ ╰────╯  ▼         ▴ └────┘  ▼         ▴ ┗┉┉┉┉┛  ▼   \n     ╰───◃──←──╯         └───◃──←──┘         ┗┉┉┉◂┉┉←┉┉┛   \n                                           \n   EEE ╭╌╌╌╌╌╌╌╌╌╌╮    FFF ╭┈┈┈┈┈┈┈┈┈┈╮   GGG ┏━━━━━━━━━━┓\n   ╭╌╌╌┼╌╌╌╌╮     ┆    ╭┈┈┈┼┈┈┈┈╮     ┊   ┏━━━╋━━━━┓     ┃\n   ╰╌╮ ┆    ┆     ▽    ╰┈╮ ┊    ┊     ▽   ┗━┓ ┃    ┃     ▼\n     ┆ ┆    ┆  ╭╌╌╯      ┊ ┊    ┊  ╭┈┈╯     ┃ ┃    ┃  ┏━━┛\n     ▴ ╰╌╌╌╌╯  ▼         ▴ ╰┈┈┈┈╯  ▼        ▴ ┗━━━━┛  ▼   \n     ╰╌╌╌◃╌╌←╌╌╯         ╰┈┈┈◃┈┈←┈┈╯        ┗━━━◂━━←━━┛   \n                                          \n\n   DDD ┏━━━━━━━━━━┓    HHH ┏╍╍╍╍╍╍╍╍╍╍┓    \n   ┏━━━╋━━━━┓     ┃    ┏╍╍╍╋╍╍╍╍┓     ┇\n   ┗━┓ ┃    ┃     ▼    ┗╍┓ ┇    ┇     ▼\n     ┃ ┃    ┃  ┏━━┛      ┇ ┇    ┇  ┏╍╍┛\n     ▴ ┗━━━━┛  ▼         ▴ ┗╍╍╍╍┛  ▼   \n     ┗━━━◂━━←━━┛         ┗╍╍╍◂╍╍←╍╍┛   \n                       \n\")\n"
  },
  {
    "path": "tests/bench23.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(uniline-bench\n \"\"\n \"<return>\n 2*<down> 5*<right>\n M-x p i c t u r e - m o d e <return>\n 5*S-<down> 19*S-<right> 2*S-<left> C-c C-r\n 3*<up> 10*<left>\n 5*S-<down> 17*S-<right> C-c C-r\n 6*<up>\n M-x M-p <end>\n ( <backspace>\n -\n r <backspace>\n e x i t <return>\n <up> <home> 2*<right> 5*<down> 10*<right>\n <insert> <kp-add>\n 5*<up> 10*<left> <up>\n 9*S-<down> 27*S-<right> 2*S-<right>\n <insert> s <kp-0>\n <return>\n <down> 6*<right> <up>\n M-x 2*M-p <return>\n 5*S-<down> 9*S-<right> C-c C-r\n M-x 2*M-p <return>\n 6*<left>\n <insert> <kp-add>\n 5*<up> 10*<left> 5*<right>\n 7*S-<down> 7*S-<right>\n <insert> s <kp-0> <kp-add>\n <return>\n 4*<down> 8*<right>\n 5*S-<down> 10*S-<right>\n <insert> s = <return>\"\n\"\\\n\n         ┏━━━━━---+\n     ╭──━┃━━━━━───|───╮\n     │   ┃        |   │\n     │   ┃  ┏━━───|──────────╮\n     │   ┃  ┃     |   ║      │\n     │   ┗━━╋━━---+   ║      │\n     ╰──━━━━╋━━─══════╝      │\n            ┃                │\n            ╰───══════════───╯\n                          \n\")\n"
  },
  {
    "path": "tests/bench24.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n;; Test undo\n\n(uniline-bench\n \"\\\n\n\n\n     uuuuuu\n       uuuuuu\"\n\n \"M-> #\n 2*<down> 2*<left> <down> 7*<left> <up> 3*<left>\n 6*C-_\n 2*<down>\n <return>\n 4*<up>\n <kp-subtract>\n 3*<left> M-4 <down> M-6 <right> 4*<up> 4*<left>\n <return> 3*<left>\n 4*S-<down> M-8 S-<right>\n <insert>\n 2*<right>\n s <kp-add>\n <kp-3>\n 5*C-_\n 2*<return>\"\n\"\\\n\n\n      ╭─────╮\n     uuuuuu │\n      │uuuuuu▖\n      │  ▗▄▄▛▘\n      ╰──▐──╯\n\")\n"
  },
  {
    "path": "tests/bench25.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(uniline-bench\n \"\"\n \"<return>\n <down> 2*<right> 5*a\n <down> <left> 5*a\n <down> M-7 <left> 5*t\n 2*<up> M-5 <right>\n 2*S-<down> M-7 S-<right>\n <insert> <kp-subtract> r\n 2*<return>\n <down> 4*<right> <insert> 3*o\n <up> M-4 <left> \n C-SPC 2*<down> M-7 <right>\n <insert> c\n <return>\n M-5 <left> <up>\n C-x SPC <insert> y\n 2*<return>\n M-12 <left> <up> <left> <up>\n C-SPC <insert> y\n <return>\n 3*<down> 3*<right> <up>\n C-SPC <insert> y\n <return>\"\n\"\\\n   ╭─────╮\n  a│aaa• │    ╭─────╮\n   ╰──╭─────╮ │ ╭─────╮\n    tt│tt • │ ╰─│───• │\n      ╰─────╯   ╰─────╯\n                \n\")\n"
  },
  {
    "path": "tests/bench26.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n;; Test ASCII → Uniline base-line\n\n(uniline-bench\n \"\\\n\n         +-------------+             +--------------+\n   /-----/ Rectangle 1 |-------------+ Rectangle 2  |\n   |     |  <sin^gle>  |             | \\\"quote\\\"      +-\n   |     \\\\-------------/   /-----\\\\   +--------------+\n   |     \\\\--+-------+--/   | vav |   +----\\\\-----/---+\n   \\\\>--\\\\    |       |      \\\\-----/        |     |\n       v    \\\\-->----/                     +--<--+\n\"\n\n  \"<return> M-< <down> C-SPC M-> <up> <home> 54*<right> <insert> s <kp-0> <return>\"\n\n\"\\\n\n         ╭─────────────╮             ╭──────────────╮ \n   ╭─────┤ Rectangle 1 │─────────────┤ Rectangle 2  │\n   │     │  <sin^gle>  │             │ \\\"quote\\\"      ├─\n   │     ├─────────────┤   ╭─────╮   ├──────────────┤\n   │     ╰──┬───────┬──╯   │ vav │   ╰────┬─────┬───╯\n   ╰▷──╮    │       │      ╰─────╯        │     │    \n       ▽    ╰──▷────╯                     ╰──◁──╯     \n\")\n"
  },
  {
    "path": "tests/bench27.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n\n;; Test drawing right onto a TAB and also on the trail of a TAB\n;; (drawing should occur)\n;; Test writing on those 2 characters ╜┡ which are in 2-entries buckets\n;; in the uniline--char-to-4halfs hash-table\n\n(uniline-bench\n \"\"\n \"<escape> 3 0 <right>\n <escape> 2 0 <down>\n <return> 16*<up> 8*<left>\n = 2*<down> <left> <right>\n <kp-add> <down> <right> <left> <down> <return> <up>\n <insert> 2*S-<down>\n <up>\n <insert> S-<down> S-<left>\n C-x h M-x t a b i f y <return>\n <kp-subtract> 8*<right> 10*<down> 14*<right> <return> 2*<up> 2*<right>\n C-SPC 4*C-p 3*C-b\n <insert> c\n <return>\n 4*<right>\n C-SPC <insert> y\n <return>\n <right> <up> <kp-subtract> 7*<down> 4*<left>\"\n\"\\\n╶───────┬─────────────────────╮\n\t│       \t      │\n\t│       \t      │\n\t│       \t  ╷   │\n\t│             ╻\t  │   │\n\t│             ║\t  ║   │\n\t│            ╺╜\t ╺┤   │\n\t│             ┡╸  ┝╸  │\n\t│             ╹\t  │   │\n\t│                 │   │\n\t╰─────────────────╯   │\n\t\t\t      │\n\t\t\t      │\n\t\t\t      │\n\t\t\t      │\n\t\t\t      │\n\t\t\t      │\n\t\t\t      │\n\t\t\t      │\n\t\t\t      │\n\t\t\t      ╵\n\")\n"
  },
  {
    "path": "tests/bench28.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(let ((uniline-infinite-up↑ nil))\n  (uniline-bench\n   \"aa\\t\\ta\\t\\ta\\t\\t\\t\\t    \\t\\t   a\n\"\n \"2*C-k 29*C-y\n M-< 5*<right> <down> 3*<right> <down> 2*<right>\n 7*<right> 2*<down> 9*<left> <up> 16*<right> 2*<up> 12*<right> 3*<down> 10*<left>\n 5*C-n 14*r <return>\n 12*<left> <down> 16*r\n 14*<left> <down> 12*r\n 2*C-b <kp-subtract> <insert> c\n <return> 12*<right> 3*<down> 11*r\n <down> 9*<left> 12*r\n <down> 8*<left> 8*r\n <down> 2*<left> 2*r\n <down> <left> 3*r\n C-SPC 3*<left> <up> 2*<left> 3*<up> 11*<left> <insert>\n 2*<right> c\n <return> 4*<right> 6*<down> 10*<left>\n C-SPC <insert> y\n <down> <right> <return>\"\n\"\\\naa───╮  \ta\t       ╷a╷      \t\t\t    \t\t   a\naa   ╰──╮       a\t╭──────┼a┼──╮   \t\t\t    \t\t   a\naa\t╰───────a╮      │      │a│  │   \t\t\t    \t\t   a\naa\t╭───────a┼──────╯      │a│  │   \t\t\t    \t\t   a\naa\t╰───────a╯        ╶────┼a┼──╯   \t\t\t    \t\t   a\naa\t\ta\t       │a│      \t\t\t    \t\t   a\naa\t\ta\t       │a│      \t\t\t    \t\t   a\naa\t\ta\t       │a│      \t\t\t    \t\t   a\naa\t\ta      ╭───────╯a╰────╮ \t\t\t    \t\t   a\naa\t\ta      │rrrrrrrrrrrrrr╰───╮     \t\t    \t\t   a\naa\t\ta      ╰─╮rrrrrrrrrrrrrrrr│     \t\t    \t\t   a\naa\t\ta\t ╰─╮rrrrrrrrrrrr╭─╯             \t    \t\t   a\naa\t\ta\t   ╰───╮a╭──────╯               \t    \t\t   a\naa\t\ta\t       │a│      \t        \t    \t\t   a\naa              a              │a│                    rrrrrrrrrrr                  a\naa              a              │a│                      rrrrrrrrrrrr               a\naa              a              │a│                          rrrrrrrr               a\naa              a              │a│                                rr               a\naa              a              │a│                                 rrr             a\naa\t\ta\t       │a│      \t\t            \t\t   a\naa              a              │a│                                                 a\naa              a              │a│               rrrrrrrrrrr                       a\naa              a              │a│                 rrrrrrrrrrrr                    a\naa              a              │a│                     rrrrrrrr                    a\naa              a              │a│                           rr                    a\naa              a              │a│                            rrr                  a\naa\t\ta\t       │a│      \t\t\t    \t\t   a\naa\t\ta\t       │a│      \t\t\t    \t\t   a\naa\t\ta\t       │a│      \t\t\t    \t\t   a\n                               ╰─╯\n\"))\n"
  },
  {
    "path": "tests/bench29.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(uniline-bench\n \"\\\n\n\n                                            aaaaaa\n                                             aaaaaaaa\n                                               aaaa aaaa\n                                                     aaa\n                                                     aa\"\n\n \"<return> 2*<down> 3*<right>\n - 2*<right>\n ~ 3*<right> 2*<up> 2*<right> 3*<down> 5*<left> <down> 2*<right> <up> 2*<right> <down> 3*<right>\n ~ 2*<right> 2*<up> 2*<right> <down> 4*<left> 3*<down>\n <kp-add> 5*<right> 2*<up> 5*<right>\n ~ 3*<right> 2*<down>\n ~ 5*<left> 2*<down> 4*<right> <up> <left> 2*<up> 8*<right>\n <return> 3*<right> 3*<up>\n C-SPC 2*<down> 6*<right> <insert> r\n <kp-subtract> r <return>\n <return> <end> <left> <kp-add>\n <insert> c\"\n\n\"\\\n        ╭┄╮\n        ┆ ┆                                ┏┅┅┅┅┅┅┓\n   ╶─┄┄┄╯ ┆   ╭┈╮                 ╭┄┄┄┄╮   ┇aaaaaa┗┅┅┓\n     ╭┄┬┄┬╯ ╭┈┼┈╯                 ┆    ┆   ┗┓aaaaaaaa┗┅┅┓\n     ╰┄╯ ╰┄┄┼┈╯  ┏┉┉┉┉━━━┓        ╰┄┄┄┄╯    ┗┅┓aaaa╻aaaa┇\n            ┊    ┋     ┏┅╋┅┅┅┅┅╸              ┗┅┅┅┅┻┓aaa┇\n            ┕┉┉┉┉┛  ┏┅┅╋┅┛                          ┇aa┏┛\n                    ┇  ┗┓                           ┗┅┅┛\n                    ┗┅┅┅┛\n\")\n"
  },
  {
    "path": "tests/bench30.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n;; Test leakage when moving a rectangle down, from all styles of lines:\n;; plain, 3-dotted, 4-dotted, thin and thick, double, block\n\n(uniline-bench\n \"\"\n \"<return> 2*<down> 5*<right>\n <insert> s <return>\n <kp-subtract> 6*<right> 5*<down>\n <kp-add> 5*<right> 5*<up>\n = 5*<right> 5*<down>\n # 11*<right> 9*<up> <return>\n <right> C-SPC 5*<down> 23*<left> <insert> c\n 5*<up> 24*<right> C-SPC <insert> y s <kp-3> <return>\n 24*<right> C-SPC <insert> y s <kp-4> <return>\n 5*<down> 25*<right> <down> C-SPC 4*<up> 74*<left> <down> <insert>\n <down>\n ~ <down>\n <return>\"\n\"\\\n\n\n     □─────╮    ╔════╗    ▗  □╌╌╌╌╌╮    ╔╍╍╍╍╗    ▗  □┈┈┈┈┈╮    ╔┉┉┉┉╗    ▗\n           │    ┃    ║    ▐        ┆    ┇    ┇    ▐        ┊    ┋    ┋    ▐\n           │    ┃    ║    ▐        ┆    ┇    ┇    ▐        ┊    ┋    ┋    ▐  \n           │    ┃    ║    ▐        ┆    ┇    ┇    ▐        ┊    ┋    ┋    ▐  \n           │    ┃    ║    ▐        ┆    ┇    ┇    ▐        ┊    ┋    ┋    ▐  \n           │    ┃    ║    ▐        ┆    ┇    ┇    ▐        ┊    ┋    ┋    ▐  \n           │    ┃    ║    ▐        ┆    ┇    ┇    ▐        ┊    ┋    ┋    ▐  \n           ┕━━━━┛    ╹▀▀▀▀▀        ┕╍╍╍╍┛    ╹▀▀▀▀▀        ┕┉┉┉┉┛    ╹▀▀▀▀▀  \n                                                                             \n\")\n"
  },
  {
    "path": "tests/bench31.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n;; Test leakage when moving a rectangle right, and another left.\n;; Test for all styles of lines:\n;; plain, 3-dotted, 4-dotted, thin and thick, double, block\n\n(uniline-bench\n \"\"\n \"<return> 2*<down> 3*<right> <insert> 4*o <return>\n <kp-subtract> 6*<right> <down>\n <kp-add> 6*<left> <down>\n = 6*<right> <down>\n # 12*<left> 2*<down>\n <return> <down> <left>\n C-SPC 5*<up> 9*<right> <insert> c\n 8*<left> 5*<down> <left> C-SPC <insert> y s <kp-3> <return>\n 5*<down> C-SPC <insert> y s <kp-4> <return>\n 5*<down> 9*<right> C-SPC 16*<up> 4*<left> <insert>\n <right> ~ <right> <return>\n 7*<left> <right> C-SPC 16*<down> 4*<right> <left> <insert>\n <left> ~ <left> ~ <left> <return>\"\n\"\\\n\n             \n●──────────╮ \n┏━━━━━━━━━━┙ \n╚══════════╗ \n▛▀▀▀▀▀▀▀▀▀▀╹ \n▘            \n●╌╌╌╌╌╌╌╌╌╌╮ \n┏╍╍╍╍╍╍╍╍╍╍┙ \n╚╍╍╍╍╍╍╍╍╍╍╗ \n▛▀▀▀▀▀▀▀▀▀▀╹ \n▘            \n●┈┈┈┈┈┈┈┈┈┈╮ \n┏┉┉┉┉┉┉┉┉┉┉┙ \n╚┉┉┉┉┉┉┉┉┉┉╗ \n▛▀▀▀▀▀▀▀▀▀▀╹ \n▘            \n             \n  \n\")\n"
  },
  {
    "path": "tests/bench33.el",
    "content": ";;; uniline.el --- Draw lines, boxes, & arrows with the keyboard  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n;; Test leakage when moving a rectangle down, from all styles of lines:\n;; plain, 3-dotted, 4-dotted, thin and thick, double, block\n\n(let ((uniline-infinite-up↑ t))\n  (uniline-bench\n   \"\"\n   \"<insert> s <right>\n <kp-add> 2*<right> <down> 3*<right> 3*<up> 3*<right> 2*<down>\n # 6*<right> 2*<up> 3*<right> 4*<up> 5*<right> <down> 4*<right> 4*<down> 2*<right>\n <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>\n <insert> C-<left> 9*a <right>\n <kp-subtract> <insert> c\n <return> 8*<right> 2*<up> 4*r <down> 5*<right> <left> 4*r\n C-SPC 5*<right> <up>\n <insert> 3*<right> 2*<up> 3*<right>\n <insert> R\n <kp-subtract> r\n <return> 3*<down> 12*<right>\n <return> <right>\n <insert> C-<up> 5*y\n <insert> C-<right> 3*y\"\n   \"\\\n                                                            yyy\n                                                            y\n                                               ╭─────╮      y\n                                               │ rrrr│      y\n                         ╭──────╮  ╭────╮      │ rrrr│      y\n             ▐▀▀▙▄▖    ╭─╯aaaaaa│  │aaaa│      ╰─────┴─────╴y\n      ┏━━┓   ▐    ▌   ╭╯aaaaaa╶─┴──┴╴aa╭╯         \n      ┃  ┃  ▛▀    ▙▖  │aaa╭─╮aaaaaaaaa╭╯      \n□╼━┓  ┃  ╹▀▀▘         ╰───╯ ╰─────────╯\n   ┗━━┛\n\"))\n"
  },
  {
    "path": "tests/uniline-bench.el",
    "content": ";;; uniline-bench.el --- Regression tests for Uniline  -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; Package-Requires: ((emacs \"29.1\") (hydra \"0.15.0\"))\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n;;; Commentary:\n\n;; Running regression tests\n;; Just load this file:\n;;   (load \"uniline-bench.el\")\n;; or\n;;   (eval-buffer)\n;;\n;; If OK, a message is displayed\n;; If ERROR, two windows are displayed, the actual and the expected sketchs\n;; with points on the first difference.\n\n;; Creating a new bench\n;; - Eval (uniline-bench-create)\n;; - Draw a sketch\n;; - When done, type $\n;; A Lisp buffer implementing the new test is displayed\n;; - Save it permanently in a file ending in *.el along with this one\n\n;;; Code:\n\n(defvar uniline-bench-result\n  nil\n  \"Boolean where a bench result is stored.\nt if the bench ran as expected.\nnil if there was an error.\")\n\n(defun uniline-bench (initial commands result)\n  \"Run a bench.\nCOMMANDS is a string describing a sequence of keyboard strokes,\nsupposed to draw a sketch using uniline minor-mode.\nIts format is the one used to store keyboard macros.\nRESULT is a string representing the expected result.\nINITIAL is the initial content of the buffer\"\n  (ignore-errors (kill-buffer \"*uniline-interactive*\"))\n  (switch-to-buffer \"*uniline-interactive*\")\n  (insert initial)\n  (goto-char (point-min))\n  ;; (set-default 'uniline-hint-style 1)\n  (let ((uniline-show-welcome-message nil))\n    (uniline-mode 1))\n  (if (fboundp 'hydra-keyboard-quit)\n      (hydra-keyboard-quit)) ;; clear any left-over from previous bench\n  (if (fboundp 'transient-quit-all)\n      (transient-quit-all))  ;; clear any left-over from previous bench\n  (setq uniline--which-quadrant (uniline--char-to-4quadb ?▘))\n  (uniline-set-brush-1)\n  (uniline-set-brush-0dots)\n  (execute-kbd-macro (kbd commands))\n\n  (when nil\n    ;; ignore trailing spaces in resulting drawing\n    (goto-char (point-min))\n    (replace-regexp (rx (+ space) eol) \"\")\n    ;; ignore trailing spaces in expected result\n    (setq result (replace-regexp-in-string (rx (+ space) eol) \"\" result)))\n\n  (setq uniline-bench-result\n        (string-equal\n         (buffer-substring (point-min) (point-max))\n         result))\n  \n  (unless uniline-bench-result\n    (delete-other-windows)\n    (switch-to-buffer \"*uniline-interactive*\")\n    (goto-char (point-min))\n    (ignore-errors (kill-buffer \"*uniline-expected*\"))\n    (switch-to-buffer-other-window \"*uniline-expected*\")\n    (insert result)\n    (goto-char (point-min))\n    (compare-windows nil))\n\n  uniline-bench-result)\n\n(defun uniline-bench-create ()\n  \"Interactively create a bench.\nAn empty buffer is made available, with uniline mode active.\nDraw a sketch.\nWhen done, type $.\nA Lisp buffer able to automatically re-run the drawing is presented.\nSave it in a *.el file along with other benches.\"\n  (interactive)\n  (ignore-errors (kill-buffer \"*uniline-interactive*\"))\n  (switch-to-buffer \"*uniline-interactive*\")\n  (local-set-key \"$\" (lambda () (interactive) (throw 'exit nil)))\n  (message \"draw the initial state of buffer, prior to invoking Uniline, type $ when done\")\n  (recursive-edit)\n  (setq uniline-initial-text\n        (buffer-substring-no-properties (point-min) (point-max)))\n  (goto-char (point-min))\n  (uniline-mode)\n  (local-set-key \"$\" 'uniline-bench-collect)\n  (message \"draw a sketch, type $ whend done\")\n  (kmacro-start-macro nil))\n\n(defun uniline-bench-collect ()\n  \"Called when typing $ to close the interactive drawing.\nDo not call it directly.\"\n  (interactive)\n  (kmacro-end-macro 1)\n  (ignore-errors (kill-buffer \"b.el\"))\n  (switch-to-buffer \"b.el\")\n  (insert \"(uniline-bench\\n\\\"\\\\\\n\" uniline-initial-text \"\\\"\\n\\n\\\"\")\n  (insert (key-description (kmacro--keys (kmacro last-kbd-macro))))\n  (insert \"\\\"\\n\\n\\\"\\\\\\n\")\n  (insert-buffer-substring \"*uniline-interactive*\")\n  (insert \"\\\")\\n\")\n  (lisp-mode))\n\n(defmacro uniline-bench-numcompact-n (n)\n  \"If N is 3, replace in the buffer <left> <left> <left> to 3*<left>\nIt is a macro, because N is a dynamic variable which must be\ninserted into a regexp.\"\n  `(save-excursion\n     (replace-regexp\n      (rx \" \"\n          (group (+ (any \"a-zA-Z<>-\")))\n          (= ,(1- n)\n             (+ blank)\n             (backref 1)))\n      ,(format \" %d*\\\\1\" n))))\n\n(defun uniline-bench-numcompact ()\n  \"Change <left> <left> <left> to 3*<left>.\nThe `kbd' function understand this notation.\nAnd it is easier to read.\nPut the cursor inside the string to modify.\"\n  (interactive)\n  (search-backward \"\\\"\")\n  (narrow-to-region (point) (progn (forward-sexp) (point)))\n  (goto-char (point-min))\n  (uniline-bench-numcompact-n 27)\n  (uniline-bench-numcompact-n 26)\n  (uniline-bench-numcompact-n 25)\n  (uniline-bench-numcompact-n 24)\n  (uniline-bench-numcompact-n 23)\n  (uniline-bench-numcompact-n 22)\n  (uniline-bench-numcompact-n 21)\n  (uniline-bench-numcompact-n 20)\n  (uniline-bench-numcompact-n 19)\n  (uniline-bench-numcompact-n 18)\n  (uniline-bench-numcompact-n 17)\n  (uniline-bench-numcompact-n 16)\n  (uniline-bench-numcompact-n 15)\n  (uniline-bench-numcompact-n 14)\n  (uniline-bench-numcompact-n 13)\n  (uniline-bench-numcompact-n 12)\n  (uniline-bench-numcompact-n 11)\n  (uniline-bench-numcompact-n 10)\n  (uniline-bench-numcompact-n  9)\n  (uniline-bench-numcompact-n  8)\n  (uniline-bench-numcompact-n  7)\n  (uniline-bench-numcompact-n  6)\n  (uniline-bench-numcompact-n  5)\n  (uniline-bench-numcompact-n  4)\n  (uniline-bench-numcompact-n  3)\n  (uniline-bench-numcompact-n  2)\n  (widen))\n\n(defun uniline-bench-run (&rest files)\n  \"Run all benches, or a specified list.\nWhen FILES is nil, the benches are all files with *.el suffix.\nStops on the first error, presenting two buffers,\n- one with the actual drawing,\n- the other with the expected drawing,\nwith points on the first difference.\nIf there are no errors, a summary is presented.\"\n  (interactive)\n  (unless files\n    (setq files\n          (directory-files \".\" nil \"^bench.*\\\\.el$\")))\n  (let* ((buf (current-buffer))\n         (nbpassed 0)\n         (nbfailed 0)\n         (failed\n          (cl-loop\n           for file in files\n           do\n           (load (format \"%s%s\" default-directory file) nil nil t)\n           (if uniline-bench-result\n               (cl-incf nbpassed)\n             (cl-incf nbfailed)\n             (message \"%s FAILED\" file))\n           unless uniline-bench-result\n           collect file)))\n    (switch-to-buffer buf)\n    (message \"%s PASSED / %s FAILED %s\" nbpassed nbfailed failed)))\n\n(pcase 0\n  (0\n   (uniline-bench-run))\n  (1\n   (garbage-collect)\n   (profiler-start 'cpu+mem)\n   (uniline-bench-run)\n   (profiler-stop)\n   (profiler-report))\n  (2\n   (garbage-collect)\n   (elp-instrument-package \"uniline\")\n   (uniline-bench-run)\n   (elp-results)\n   (elp-restore-all)))\n\n(if nil\n    (uniline-bench-run \"bench26.el\" \"bench27.el\"))\n\n(provide 'uniline-bench)\n;;; uniline-bench.el ends here\n"
  },
  {
    "path": "uniline-core.el",
    "content": ";;; uniline-core.el --- Add▶ ■─UNICODE based diagrams─■ to▶ ■─text files─■ -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; Package-Requires: ((emacs \"29.1\"))\n;; Keywords: convenience, text\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n;;; Commentary:\n;;                ┏━━━━━━━┓\n;;    ╭──────╮    ┃ thick ┣═◁═╗\n;;    │ thin ┝◀━━━┫ box   ┃   ║\n;;    │ box  │    ┗━━━━━━━┛   ║\n;;    ╰───┬──╯         ╔══════╩═╗\n;;        ↓            ║ double ║\n;;        ╰────────────╢ box    ║\n;;                     ╚════╤═══╝\n;;      ▛▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▜   │\n;;      ▌quadrant-blocks▐─◁─╯\n;;      ▙▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▟\n;;\n;;╭─Pure text────────────────□\n;;│ UNICODE characters are available to draw nice boxes and lines.\n;;│ They come in 4 flavours: thin, thick, double, and quadrant-blocks.\n;;│ Uniline makes it easy to draw and combine all 4 flavours.\n;;│ Use the arrows on the keyboard to move around leaving a line behind.\n;;╰──────────────────────────╮\n;;╭─Minor mode───────────────╯\n;;│ Uniline is a minor mode.  Enter it with:\n;;│   M-x uniline-mode\n;;│ Leave it with:\n;;│   C-c C-c\n;;╰──────────────────────────╮\n;;╭─Fonts────────────────────╯\n;;│ A font able to displays the needed UNICODE characters have to\n;;│ be used.  It works well with the following families:\n;;│ - DejaVu Sans Mono\n;;│ - Unifont\n;;│ - Hack\n;;│ - JetBrains Mono\n;;│ - Cascadia Mono\n;;│ - Agave\n;;│ - JuliaMono\n;;│ - FreeMono\n;;│ - Iosevka Comfy Fixed, Iosevka Comfy Wide Fixed\n;;│ - Aporetic Sans Mono, Aporetic Serif Mono\n;;│ - Source Code Pro\n;;╰──────────────────────────╮\n;;╭─UTF-8────────────────────╯\n;;│ Also, the encoding of the file must support UNICODE.\n;;│ One way to do that, is to add a line like this one\n;;│ at the top of your file:\n;;│   -*- coding:utf-8; -*-\n;;╰──────────────────────────╮\n;;╭─Hydra or Transient───────╯\n;;│ Uniline comes with two flavours of user interfaces:\n;;│ Hydra and Transient.\n;;│ Both versions are compiled when installing the package.\n;;│\n;;│ Then one or the other packages must be loaded (not both)\n;;│ for example with:\n;;│   (require 'uniline-hydra)\n;;│ or\n;;│   (use-package uniline-hydra\n;;│     :bind (\"C-<insert>\" . uniline-mode))\n;;│\n;;│ This file, uniline-core.el, is the largest one, the one\n;;│ implementing all the core functions independent from\n;;│ Hydra or Transient\n;;╰──────────────────────────□\n\n;;; Requires:\n(require 'cl-lib)\n(require 'rect)\n(cl-proclaim '(optimize (speed 3) (safety 1)))\n\n;;; Code:\n\n;;;╭────────────────╮\n;;;│Coding decisions│\n;;;╰────────────────╯\n\n;; Why so much use of (defmacro)?\n;; Because we handle 4 directions: north, east, south, west.\n;; We end up writing slightly the same algorithms 4 times.\n;; It is easier to centralise the algorithms in a set\n;; of (defmacro) to write them just once.\n\n;; Isn't (defun) enough and more readable than (defmacro)?\n;; Yes, absolutely!\n;; We can centralize algorithms in a set of (defun).\n;; However a (defun) generically handling all four directions\n;; misses nice code-folding.\n;; For instance somewhere we have:\n;;   (ash 3 (* 2 dir))\n;; If the compiler sees `dir' as `uniline-direction-dw↓',\n;; for example, then it can fold this expression to just 48,\n;; which is nice©\n\n;; We call four times a (defmacro) with hard-coded directions.\n;; The hard-coded parameter must go all the way down\n;; to the last instructions needing that direction.\n;; So we have (defmacro) calling other (defmacro).\n\n;; What is the purpose `eval-when-compile'?\n;; It folds down a numerical expression to just one number\n;; for instance\n;;   (eval-when-compile (ash 3 (* 2 uniline-direction-lf←)))\n;; is converted to just 192\n;; This happens both in interpreted and byte-compiled code\n;; Otherwise the operations ash, multiplication,\n;; and retrieval from `uniline-direction-lf←'\n;; would be computed over and over at runtime,\n;; with always the same 192 result.\n;; We could put directly 192 in the source code,\n;; but this would defeat maintenance and readability.\n\n;; What is the purpose `eval-and-compile'?\n;; The byte-compiler expands all defmacro' called in defun's.\n;; In turn, those defmacro's need to access some defconst's,\n;; notably `uniline-direction-up↑' and sisters.\n;; So those defconst's are made available to the byte-compiler\n;; as well as to the runtime, by embedding them in a\n;; `eval-and-compile' declaration.\n\n;; When a decision makes the byte-compiled code better\n;; (faster to load and run), at the expense of a slower\n;; interpreted counterpart, then go ahead, bias toward\n;; byte-compiled code.\n\n;;;╭────────────────────────────────────────────────────────╮\n;;;│4 directions, infinite buffer in right & down directions│\n;;;╰────────────────────────────────────────────────────────╯\n\n(eval-and-compile\n  (defconst uniline-direction-up↑ 0) (defmacro uniline-direction-up↑ () 0)\n  (defconst uniline-direction-ri→ 1) (defmacro uniline-direction-ri→ () 1)\n  (defconst uniline-direction-dw↓ 2) (defmacro uniline-direction-dw↓ () 2)\n  (defconst uniline-direction-lf← 3) (defmacro uniline-direction-lf← () 3))\n;;                  △       △                          △\n;; usual constant╶──╯       ╰──────────────────────╮   │\n;; when we insist in having a numeric byte-code:   │   │\n;;   constant 2╶───────────────────────────────────╯   │\n;;   varref uniline-direction-dw↓╶─────────────────────╯\n\n(eval-when-compile ; not needed at runtime\n\n  (defmacro uniline--switch-with-cond (dir &rest body)\n    \"Macro to simplify `cond' applied to possible values of DIR.\nBODY is ((CASE1 EXPR1...) (CASE2 EXPR2...) ...)\nDIR is an expression which is compared to each of the CASE1, CASE2, ...\nuntil it matches one. Then the corresponding EXPRN is executed.\nIt expands to a (cond) form.\nIt is somehow similar to `cl-case', except that the cases are evaluated\nwhile `cl-case' quotes them.\"\n    (declare (indent 1))\n    `(cond\n      ,@(cl-loop\n         for c in body\n         collect\n         `((eq ,dir (eval-when-compile ,(car c)))\n           ,@(cdr c)))\n      (t (error \"direction not known\"))))\n\n  (defmacro uniline--switch-with-table (dir &rest body)\n    \"Macro to return a value by looking in a table at the DIR entry.\nBODY is ((CASE1 EXPR1) (CASE2 EXPR2) ...)\nIt is similar to `uniline--switch-with-cond', but instead of executing\nan EXPRN, it just returns it as is.\nIt is expanded into a lookup into a vector or a plist, depending on the CASES.\nIt it faster than an equivalent (cond) form.\"\n    (declare (indent 1))\n    (let* ((lambda\n            (if (eq (caar body) 'lambda)\n                (pop body)))\n           (max\n            (cl-loop\n             for c in body\n             for e = (eval (car c))\n             always (and (fixnump e) (<= 0 e 30))\n             maximize e)))\n      (if max\n          ;; create a vector for fast lookup\n          (let ((vec (make-vector (1+ max) nil)))\n            (cl-loop\n             for c in body\n             do (aset\n                 vec\n                 (eval (car c))\n                 (if lambda\n                     (funcall lambda (eval (car c)))\n                   (eval (cadr c)))))\n            (if (fixnump dir)\n                (aref vec dir)\n              `(aref ,vec ,dir)))\n        ;; create a plist for versatile lookup\n        (let ((plist\n               (cl-loop\n                for c in body\n                collect (eval (car c))\n                collect (if lambda\n                            (funcall lambda (eval (car c)))\n                          (eval (cadr c))))))\n          (if (fixnump dir)\n              (plist-get plist dir)\n            `(plist-get ',plist ,dir))))))\n  )\n\n(eval-when-compile ; not needed at runtime\n  (defmacro uniline--reverse-direction (dir)\n    \"Reverse DIR.\nDIR is any of the 4 `uniline-direction-*'.\nExchange left with right, up with down.\"\n    `(% (+ 2 ,dir) 4))\n\n  (defmacro uniline--turn-right (dir)\n    \"Return DIR turned 90° clockwise.\nDIR & returned values are in [0,1,2,3].\"\n    `(% (1+ ,dir) 4))\n\n  (defmacro uniline--turn-left (dir)\n    \"Return DIR turned 90° anti-clockwise.\nDIR & returned values are in [0,1,2,3].\"\n    `(% (+ 3 ,dir) 4))\n  )\n\n(defmacro uniline-move-to-column (x)\n  \"Move to column X staying on the same line.\nAdd blanks if line is too short.\nMove to 0 if X negative.\"\n  ;; x is never numeric in the Uniline calls\n  ;; so it is pointless to optimize this case\n  `(move-to-column (max ,x 0) t))\n\n(defmacro uniline-move-to-delta-column (x)\n  \"Move X characters, staying on the same line.\nAdd blanks if line is too short.\nX may be negative to move backward.\nMove to 0 if target is beyond the left border of buffer.\"\n  (cond\n   ((eq x  0) ()) ;; this case never happens in Uniline calls\n   ((eq x  1) `(move-to-column      (1+ (current-column)   )    t))\n   ((eq x -1) `(move-to-column (max (1- (current-column)   ) 0) t))\n   ((and\n     (numberp x)\n     (>= x  0))\n    (progn    `(move-to-column      (+  (current-column) ,x)    t)))\n   (t         `(move-to-column (max (+  (current-column) ,x) 0) t))))\n\n(defcustom uniline-infinite-up↑ nil\n  \"Is the buffer infinitely extensible in the upper direction?\nIf not, the begining of buffer is a hard limit, as usual in most\nEmacs modes.\nNote that if the buffer is narrowed to a region, for example through\nthe use of C-x n n, then the buffer the narrowed region may grow\nin both the upper and lower direction by automatic insertion of\nblank lines.\"\n  :type 'boolean\n  :group 'uniline)\n\n(defun uniline--forward-line-force (y p c)\n  \"Helper function to move cursor Y lines.\nCreate lines at the end of the buffer if there\nare not enough lines to honor Y.\nY may be negative.\nDoes not preserve current column.\nP may be (point) for a relative Y move,\nor (point-min) for an absolute Y move.\nC is the expected column number.\"\n  ;; here we fix a bug in the return of (forward-line):\n  ;; when (forward-line) cannot move enough lines\n  ;; because it is stuck at the end of buffer,\n  ;; it erroneously returns one less missing forwards\n  ;; but the bug does not appears if the end-of-buffer\n  ;; is at the beginning-of-line\n  ;; so here we get out of the corner-case of the\n  ;; (forward-line) bug, by ensuring that there is an empty\n  ;; line at the end of buffer\n  (goto-char (point-max))\n  (or (bolp) (insert ?\\n))\n  (goto-char p)\n  (let ((n (forward-line y)))\n    (if (>= n 0)\n        (insert-byte ?\\n n)\n      (when uniline-infinite-up↑\n        (insert-byte ?\\n (- n))\n        (forward-line n))))\n  (move-to-column c t))\n\n(defmacro uniline-move-to-line (y)\n  \"Move to line Y, while staying on the same column.\nCreate blank lines at the end of the buffer if needed,\nor blank characters at the end of target line.\nY=0 means first line in buffer.\"\n  `(uniline--forward-line-force ,y (point-min) (current-column)))\n\n(defmacro uniline-move-to-delta-line (y)\n  \"Move Y lines while staying on the same column.\nCreate blank lines at the end of the buffer if needed,\nor blank characters at the end of target line.\nY may be negative to move backward.\"\n  `(uniline--forward-line-force ,y (point)     (current-column)))\n\n(defmacro uniline-move-to-lin-col (y x)\n  \"Move to line Y and column X.\nCreate blank lines at the end of the buffer if needed,\nor blank characters at the end of target line if needed.\nY=0 means first line of buffer.\nX=0 means first column of buffer.\"\n  `(uniline--forward-line-force ,y (point-min) ,x))\n\n(eval-when-compile ; not needed at runtime\n  (defmacro uniline--move-in-direction (dir &optional nb)\n    \"Move NB char in direction DIR.\nNB defaults to 1.\nThis is a macro, therefore it is as if writing\ndirectly (uniline-move-to-delta-line -1) and such,\nwith no overhead.\"\n    (declare (debug (form)))\n    (unless nb (setq nb 1))\n    (let ((mnb (if (fixnump nb) (- nb) `(- ,nb))))\n      (uniline--switch-with-cond dir\n        (uniline-direction-up↑ `(uniline-move-to-delta-line   ,mnb))\n        (uniline-direction-ri→ `(uniline-move-to-delta-column , nb))\n        (uniline-direction-dw↓ `(uniline-move-to-delta-line   , nb))\n        (uniline-direction-lf← `(uniline-move-to-delta-column ,mnb))))))\n\n(eval-when-compile ; not needed at runtime\n  (defmacro uniline--at-border-p (dir)\n    \"Test if at a non-trespass-able border of buffer.\nThis happens at the first line or at the first column,\nwhen trying to go further when DIR is up or left:\n`uniline-direction-up↑' or `uniline-direction-lf←'.\nIn the bottom & right directions the buffer is infinite.\"\n    (declare (debug (form)))\n    (setq dir (eval dir))\n    (uniline--switch-with-table dir\n      (uniline-direction-up↑ '(eq (pos-bol) 1))\n      (uniline-direction-ri→ nil)\n      (uniline-direction-dw↓ nil)\n      (uniline-direction-lf← '(bolp)))))\n\n(defun uniline--char-after (&optional point)\n  \"Same as `char-after', except for right and bottom edges of buffer.\nOutside the buffer, return a blank character.\nPOINT default to `(point)', as for `char-after'\"\n  (let ((c (char-after point)))\n    (if (or (not c) (eq c ?\\n))\n        ?  ;; eol & eob are considered blank\n      c)))\n\n;;;╭───────────────────╮\n;;;│Perfect hash tables│\n;;;╰───────────────────╯\n\n;; When an hash-table is constant, with entries known at compile-time,\n;; then a perfect hash-table can be built. Perfect means that there\n;; are no collisions: each bucket contains one entry, or none, never\n;; two entries. This makes the hash-table very efficient.\n;; The usual multiple entries scanning can be entirely bypassed.\n;;\n;; Uniline has 13 constant hash-tables. We want to make them\n;; perfect, collision-less. 3 Options:\n;;\n;; 1- Define a custom hash-function like\n;;    (% key 191)\n;;    by looking for the right constant like 191, we can get a\n;;    collision-less hash-table\n;;    as shown by (internal--hash-table-histogram)\n;;  - Unfortunately, this makes the resulting hash-table roughly\n;;    3 times slower than a regular (make-hash-table), even one\n;;    with collisions.\n;;\n;; 2- Use a custom vector instead of a hash-table, along with a\n;;    hashing function like\n;;    (% key (length custom-vector))\n;;    This amounts to re-creating hash-tables with vectors.\n;;  - The result is slightly slower than (make-hash-table), even\n;;    with collisions, about 30% slower.\n;;\n;; 3- Use a standard hash-table and tweak its size\n;;    (make-hash-table :size 114)\n;;  - Unfortunately, the specified size is not retained in the\n;;    compiled *.elc file\n;;    Beside, the specified size gets rounded to the next power of 2\n;;\n;; 4- Create the constant hash-table with\n;;    (make-hash-table :size 114)\n;;    when loading the compiled *.elc file\n;;    Store in the *.elc a list of pairs (key . value)\n;;  - This bypasses the #s(hash-table …) form that was handy in the\n;;    *.elc, but at no cost in the *.elc size and almost nothing\n;;    for the hash-table creation process at load-time\n;;\n;; This 4th solution is implemented hereafter through a macro\n;; which bundles:\n;;   - (defconst …)\n;;   - (make-hash-table :size my-preferred-size)\n;;   - populating the hash-table from a list of pairs loaded from *.elc\n\n(eval-when-compile ;; not needed at runtime\n  (defmacro uniline--defconst-hash-table (name pairs size test doc)\n    \"Bundles construction of a constant hash-table in one place.\nNAME is the name of the hash-table to be passed to `defconst'\nPAIRS is a list of (key . value) pairs to populate the table\nSIZE is the desired number of buckets\nTEST is the comparison function between 2 keys, like `eq' or `equal'\"\n    (declare (indent 1) (doc-string 5))\n    `(defconst ,name\n       (let ((table (make-hash-table :size ,size :test ,test)))\n         (dolist (pair ,pairs)\n           (puthash (car pair) (cdr pair) table))\n         table)\n       ,doc)))\n\n;; Display useful metrics to tune constant hash-tables\n;; those created by `uniline--defconst-hash-table'\n;; Useful only in development cycle.\n(if nil\n    (defun uniline--hash-tables-metrics ()\n      (interactive)\n      (switch-to-buffer \"*hash-tables metrics*\")\n      (erase-buffer)\n      (local-set-key \"g\" 'uniline--hash-tables-metrics)\n      (insert \"\\\ntry to make a single element list in the last column (collision-less)\nby adjusting table size                       ╰───┬╯\n(3th parameter of `uniline--defconst-hash-table') ╰───────────╮\ntype g to refresh                                             │\n                                                          ╭───┴───╮\n               table                buckets  resize       histogram\n               ╰─┬─╯                ╰──┬──╯  ╰─┬──╯       ╰───┬───╯\n                 │             entries │ index │ threshold    │\n                 │             ╰───┬─╯ │ ╰─┬─╯ │ ╰───┬───╯    │\n╭────────────────┴───────────────╮╭┴─╮╭┴─╮╭┴─╮╭┴─╮╭──┴──╮╭────┴──────────▷\n\")\n      (cl-loop\n       for table in\n       '(uniline--char-to-4halfs\n         uniline--glyphs-reverse-hash-fw\n         uniline--glyphs-reverse-hash-bw\n         uniline--char-to-4quadb1\n         uniline--keystroke-to-dir-shift\n         uniline--char-to-dot-3-2-char\n         uniline--char-to-dot-4-4-char\n         uniline--char-to-standard-char\n         uniline--char-to-hard-corner-char\n         uniline--char-to-thin-char\n         uniline--char-to-thick-char\n         uniline--char-to-double-line)\n       do\n       (let ((hash (eval table)))\n         (insert\n          (format\n           \"│%-33s│%3d│%3d│%3d│%3g│%5g│%s\\n\"\n           table\n           (hash-table-count hash)\n           (hash-table-size hash)\n           (internal--hash-table-index-size hash)\n           (hash-table-rehash-size hash)\n           (hash-table-rehash-threshold hash)\n           (internal--hash-table-histogram hash)))))))\n\n;;;╭─────────────────────────────────────────────────────╮\n;;;│Reference tables of ┼ 4 half-lines UNICODE characters│\n;;;╰─────────────────────────────────────────────────────╯\n\n;; Hereafter `4halfs' means a representation of a UNICODE character\n;; made of half-lines, like ┖ or ┶, as a single number.\n;;  ←← ↓↓ →→ ↑↑   directions\n;;  □□ □□ □□ ■■\n;;  76 54 32 10   bits\n;; There are 4 half-lines, one in each direction\n;; (north, east, west, south).  Each half line may have one of\n;; 4 styles:\n;;         ╭────────╴style of line\n;;         │   ╭┬────two bits\n;;         ▼   ▼▼\n;; blank  ( )  □□\n;; thin   (─)  □■\n;; thick  (━)  ■□\n;; double (═)  ■■\n;; So a `4halfs' number goes from 0 to 4x4x4x4 = 256.\n;; This representation is great because it is easily handled by\n;; the bits manipulation primitives: `logior', `logand', `ash'.\n\n(eval-when-compile ; not needed at runtime\n  (defmacro uniline--shift-4half (4half to-dir &optional from-dir)\n    \"Shift 4HALF bits in TO-DIR direction.\n4HALF is a number made of 2 bits, in the range [0..3]\n  0: no line\n  1: thin line\n  2: thick line\n  3: double line\nDIR is 1 of 4 directions:\n  0: uniline-direction-up↑\n  1: uniline-direction-ri→\n  2: uniline-direction-dw↓\n  3: uniline-direction-lf←\nIf FROM-DIR is given, the 4HALF bits pattern is supposed to be\nshifted in the FROM-DIR direction. Otherwise, it is not shifted.\nThe result is the bit pattern 4HALF << (2*(TO-DIR - FROM-DIR))\"\n    (condition-case nil\n        (setq to-dir (eval to-dir))     ;; fold if to-dir is a numerical sexpr\n      (error nil))                      ;; otherwise leave dir alone\n    (condition-case nil\n        (setq from-dir (eval from-dir)) ;; fold if to-dir is a numerical sexpr\n      (error nil))                      ;; otherwise leave dir alone\n    (let ((dir\n           (if from-dir\n               (if (and (fixnump to-dir) (fixnump from-dir))\n                   (- to-dir from-dir)\n                 `(- ,to-dir ,from-dir))\n             to-dir)))\n      (cond\n       ((eq dir 0) 4half)\n       ((fixnump dir)\n        (if (fixnump 4half)\n            (ash  4half  (* 2  dir))\n          `( ash ,4half ,(* 2  dir))))\n       (t `( ash ,4half  (* 2 ,dir))))))\n\n  (defmacro uniline--extract-reverse-4half (4half dir)\n    \"Extract a 2 bits pattern from the 8 bits pattern 4HALF.\nExtract the 2 bits in reverse direction of DIR,\nand shift them in direction DIR.\"\n    (let ((odir (uniline--reverse-direction dir)))\n      `(uniline--shift-4half\n        (logand ,4half ,(uniline--shift-4half 3 odir))\n        ,dir\n        ,odir)))\n\n  (defun uniline--pack-4halfs (urld)\n    \"Encode a description of lines into a single number.\nA character contains half lines upward, right, downward,\nleft.\nEach of those half lines may have one of 4 styles:\n  0: no line\n  1: thin line\n  2: thick line\n  3: double line\nExample: ╀ has description 2 1 1 1\n                           ▲ ▲ ▲ ▲\nthick=2 upward╶────────────╯ ╰─┴─┴──╮\nand thin=1 in the other directions╶─╯\nThe parameter URLD is a list of 4 numbers in [0..3]\nfor the 4 directions.\nA single number encoding all possible combinations has a\nrange of [0..256).  It is handy to index vectors rather than\n4 dimensions matrices.\"\n    (logior\n     (uniline--shift-4half (car    urld) uniline-direction-up↑)\n     (uniline--shift-4half (cadr   urld) uniline-direction-ri→)\n     (uniline--shift-4half (caddr  urld) uniline-direction-dw↓)\n     (uniline--shift-4half (cadddr urld) uniline-direction-lf←)))\n\n  (defun uniline--unpack-4halfs (4halfs)\n    (list\n     (logand (uniline--shift-4half 4halfs 0 uniline-direction-up↑) 3)\n     (logand (uniline--shift-4half 4halfs 0 uniline-direction-ri→) 3)\n     (logand (uniline--shift-4half 4halfs 0 uniline-direction-dw↓) 3)\n     (logand (uniline--shift-4half 4halfs 0 uniline-direction-lf←) 3))))\n\n(eval-when-compile ; not used at runtime\n  (defconst uniline--list-of-available-halflines\n    '(;;╭───────────────unicode char\n      ;;│  ╭─┬─┬─┬──────4half description\n      ;;▽  ▽ ▽ ▽ ▽\n      (?\\t 0 0 0 0 ) ;; TAB character     considered as a space\n      ( ?  0 0 0 0 ) ;; NO-BREAK SPACE    considered as a space\n      (8200 0 0 0 0) ;; PUNCTUATION SPACE considered as a space\n      ( ?  0 0 0 0 ) ;; real space comes AFTER the exotic spaces\n      ( ?╵ 1 0 0 0 )\n      ( ?╹ 2 0 0 0 )\n      ( ?╶ 0 1 0 0 )\n      ( ?└ 1 1 0 0 )\n      ( ?╰ 1 1 0 0 ) ;; prefer rounded corner\n      ( ?┖ 2 1 0 0 )\n      ( ?╺ 0 2 0 0 )\n      ( ?┕ 1 2 0 0 )\n      ( ?┗ 2 2 0 0 )\n      ( ?╷ 0 0 1 0 )\n      ;;( ?| 1 0 1 0 ) ;; recognize vertical ASCII pipe\n      ( ?┆ 1 0 1 0 )\n      ( ?┊ 1 0 1 0 )\n      ( ?│ 1 0 1 0 ) ;; prefer plain lines\n      ( ?┇ 2 0 2 0 )\n      ( ?┋ 2 0 2 0 )\n      ( ?┃ 2 0 2 0 ) ;; prefer plain lines\n      ( ?╿ 2 0 1 0 )\n      ( ?┌ 0 1 1 0 )\n      ( ?╭ 0 1 1 0 ) ;; prefer rounded corner\n      ( ?├ 1 1 1 0 )\n      ( ?┞ 2 1 1 0 )\n      ( ?┍ 0 2 1 0 )\n      ( ?┝ 1 2 1 0 )\n      ( ?┡ 2 2 1 0 )\n      ( ?╻ 0 0 2 0 )\n      ( ?╽ 1 0 2 0 )\n      ( ?┎ 0 1 2 0 )\n      ( ?┟ 1 1 2 0 )\n      ( ?┠ 2 1 2 0 )\n      ( ?┏ 0 2 2 0 )\n      ( ?┢ 1 2 2 0 )\n      ( ?┣ 2 2 2 0 )\n      ( ?╴ 0 0 0 1 )\n      ( ?┘ 1 0 0 1 )\n      ( ?╯ 1 0 0 1 ) ;; prefer rounded corner\n      ( ?┚ 2 0 0 1 )\n      ;;( ?- 0 1 0 1 ) ;; recognize ASCII minus\n      ( ?┄ 0 1 0 1 )\n      ( ?┈ 0 1 0 1 )\n      ( ?╌ 0 1 0 1 )\n      ( ?─ 0 1 0 1 ) ;; prefer plain lines\n      ( ?┴ 1 1 0 1 )\n      ( ?┸ 2 1 0 1 )\n      ( ?╼ 0 2 0 1 )\n      ( ?┶ 1 2 0 1 )\n      ( ?┺ 2 2 0 1 )\n      ( ?┐ 0 0 1 1 )\n      ( ?╮ 0 0 1 1 ) ;; prefer rounded corner\n      ( ?┤ 1 0 1 1 )\n      ( ?┦ 2 0 1 1 )\n      ( ?┬ 0 1 1 1 )\n      ;;( ?+ 1 1 1 1 ) ;; recognize ASCII plus\n      ( ?┼ 1 1 1 1 )\n      ( ?╀ 2 1 1 1 )\n      ( ?┮ 0 2 1 1 )\n      ( ?┾ 1 2 1 1 )\n      ( ?╄ 2 2 1 1 )\n      ( ?┒ 0 0 2 1 )\n      ( ?┧ 1 0 2 1 )\n      ( ?┨ 2 0 2 1 )\n      ( ?┰ 0 1 2 1 )\n      ( ?╁ 1 1 2 1 )\n      ( ?╂ 2 1 2 1 )\n      ( ?┲ 0 2 2 1 )\n      ( ?╆ 1 2 2 1 )\n      ( ?╊ 2 2 2 1 )\n      ( ?╸ 0 0 0 2 )\n      ( ?┙ 1 0 0 2 )\n      ( ?┛ 2 0 0 2 )\n      ( ?╾ 0 1 0 2 )\n      ( ?┵ 1 1 0 2 )\n      ( ?┹ 2 1 0 2 )\n      ( ?┉ 0 2 0 2 )\n      ( ?┅ 0 2 0 2 )\n      ( ?╍ 0 2 0 2 )\n      ( ?━ 0 2 0 2 ) ;; prefer plain lines\n      ( ?┷ 1 2 0 2 )\n      ( ?┻ 2 2 0 2 )\n      ( ?┑ 0 0 1 2 )\n      ( ?┥ 1 0 1 2 )\n      ( ?┩ 2 0 1 2 )\n      ( ?┭ 0 1 1 2 )\n      ( ?┽ 1 1 1 2 )\n      ( ?╃ 2 1 1 2 )\n      ( ?┯ 0 2 1 2 )\n      ( ?┿ 1 2 1 2 )\n      ( ?╇ 2 2 1 2 )\n      ( ?┓ 0 0 2 2 )\n      ( ?┪ 1 0 2 2 )\n      ( ?┫ 2 0 2 2 )\n      ( ?┱ 0 1 2 2 )\n      ( ?╅ 1 1 2 2 )\n      ( ?╉ 2 1 2 2 )\n      ( ?┳ 0 2 2 2 )\n      ( ?╈ 1 2 2 2 )\n      ( ?╋ 2 2 2 2 )\n\n      ( ?╒ 0 3 1 0 )\n      ( ?╞ 1 3 1 0 )\n      ( ?║ 3 0 3 0 )\n      ( ?╓ 0 1 3 0 )\n      ( ?╟ 3 1 3 0 )\n      ( ?╔ 0 3 3 0 )\n      ( ?╠ 3 3 3 0 )\n      ( ?╜ 3 0 0 1 )\n      ( ?╨ 3 1 0 1 )\n      ( ?╖ 0 0 3 1 )\n      ( ?╢ 3 0 3 1 )\n      ( ?╥ 0 1 3 1 )\n      ( ?╫ 3 1 3 1 )\n      ( ?╛ 1 0 0 3 )\n      ( ?╝ 3 0 0 3 )\n      ( ?═ 0 3 0 3 )\n      ( ?╧ 1 3 0 3 )\n      ( ?╩ 3 3 0 3 )\n      ( ?╕ 0 0 1 3 )\n      ( ?╡ 1 0 1 3 )\n      ( ?╤ 0 3 1 3 )\n      ( ?╪ 1 3 1 3 )\n      ( ?╗ 0 0 3 3 )\n      ( ?╣ 3 0 3 3 )\n      ( ?╦ 0 3 3 3 )\n      ( ?╬ 3 3 3 3 )\n      ( ?╙ 3 1 0 0 )\n      ( ?╘ 1 3 0 0 )\n      ( ?╚ 3 3 0 0 ))))\n\n(eval-when-compile ; not used at runtime\n  (defconst uniline--list-of-double-halflines\n    '(;;   ╭─missing ╭─┬─replacement\n      ;;   │         │ ╰───────╮\n      ;;   ▽         ▽         ▽\n      ((0 0 0 3) (0 0 0 2)) ;; ╸\n      ((0 0 2 3) (0 0 3 3)) ;; ╗\n      ((0 0 3 0) (0 0 2 0)) ;; ╻\n      ((0 0 3 2) (0 0 3 3)) ;; ╗\n      ((0 1 0 3) (0 3 0 3)) ;; ═\n      ((0 1 1 3) (0 3 1 3)) ;; ╤\n      ((0 1 2 3) (0 1 2 2)) ;; ┱\n      ((0 1 3 2) (0 1 2 2)) ;; ┱\n      ((0 1 3 3) (0 3 3 3)) ;; ╦\n      ((0 2 0 3) (0 3 0 3)) ;; ═\n      ((0 2 1 3) (0 3 1 3)) ;; ╤\n      ((0 2 2 3) (0 3 3 3)) ;; ╦\n      ((0 2 3 0) (0 3 3 0)) ;; ╔\n      ((0 2 3 1) (0 1 3 1)) ;; ╥\n      ((0 2 3 2) (0 3 3 3)) ;; ╦\n      ((0 2 3 3) (0 3 3 3)) ;; ╦\n      ((0 3 0 0) (0 2 0 0)) ;; ╺\n      ((0 3 0 1) (0 3 0 3)) ;; ═\n      ((0 3 0 2) (0 3 0 3)) ;; ═\n      ((0 3 1 1) (0 3 1 3)) ;; ╤\n      ((0 3 1 2) (0 3 1 3)) ;; ╤\n      ((0 3 2 0) (0 3 3 0)) ;; ╔\n      ((0 3 2 1) (0 2 2 1)) ;; ┲\n      ((0 3 2 2) (0 3 3 3)) ;; ╦\n      ((0 3 2 3) (0 3 3 3)) ;; ╦\n      ((0 3 3 1) (0 3 3 3)) ;; ╦\n      ((0 3 3 2) (0 3 3 3)) ;; ╦\n      ((1 0 2 3) (1 0 1 3)) ;; ╡\n      ((1 0 3 0) (3 0 3 0)) ;; ║\n      ((1 0 3 1) (3 0 3 1)) ;; ╢\n      ((1 0 3 2) (1 0 2 2)) ;; ┪\n      ((1 0 3 3) (3 0 3 3)) ;; ╣\n      ((1 1 0 3) (1 3 0 3)) ;; ╧\n      ((1 1 1 3) (1 3 1 3)) ;; ╪\n      ((1 1 2 3) (1 1 2 2)) ;; ╅\n      ((1 1 3 0) (3 1 3 0)) ;; ╟\n      ((1 1 3 1) (3 1 3 1)) ;; ╫\n      ((1 1 3 2) (1 1 2 2)) ;; ╅\n      ((1 1 3 3) (3 3 3 3)) ;; ╬\n      ((1 2 0 3) (1 3 0 3)) ;; ╧\n      ((1 2 1 3) (1 3 1 3)) ;; ╪\n      ((1 2 2 3) (1 2 2 2)) ;; ╈\n      ((1 2 3 0) (1 2 2 0)) ;; ┢\n      ((1 2 3 1) (1 2 2 1)) ;; ╆\n      ((1 2 3 2) (1 2 2 2)) ;; ╈\n      ((1 2 3 3) (3 3 3 3)) ;; ╬\n      ((1 3 0 1) (1 3 0 3)) ;; ╧\n      ((1 3 0 2) (1 3 0 3)) ;; ╧\n      ((1 3 1 1) (1 3 1 3)) ;; ╪\n      ((1 3 1 2) (1 3 1 3)) ;; ╪\n      ((1 3 2 0) (1 2 2 0)) ;; ┢\n      ((1 3 2 1) (1 2 2 1)) ;; ╆\n      ((1 3 2 2) (1 2 2 2)) ;; ╈\n      ((1 3 2 3) (1 3 1 3)) ;; ╪\n      ((1 3 3 0) (3 3 3 0)) ;; ╠\n      ((1 3 3 1) (3 3 3 3)) ;; ╬\n      ((1 3 3 2) (3 3 3 3)) ;; ╬\n      ((1 3 3 3) (3 3 3 3)) ;; ╬\n      ((2 0 0 3) (3 0 0 3)) ;; ╝\n      ((2 0 1 3) (1 0 1 3)) ;; ╡\n      ((2 0 2 3) (3 0 3 3)) ;; ╣\n      ((2 0 3 0) (3 0 3 0)) ;; ║\n      ((2 0 3 1) (3 0 3 1)) ;; ╢\n      ((2 0 3 2) (3 0 3 3)) ;; ╣\n      ((2 0 3 3) (3 0 3 3)) ;; ╣\n      ((2 1 0 3) (2 1 0 2)) ;; ┹\n      ((2 1 1 3) (2 1 1 2)) ;; ╃\n      ((2 1 2 3) (2 1 2 2)) ;; ╉\n      ((2 1 3 0) (3 1 3 0)) ;; ╟\n      ((2 1 3 1) (3 1 3 1)) ;; ╫\n      ((2 1 3 2) (2 1 2 2)) ;; ╉\n      ((2 1 3 3) (3 3 3 3)) ;; ╬\n      ((2 2 0 3) (3 3 0 3)) ;; ╩\n      ((2 2 1 3) (2 2 1 2)) ;; ╇\n      ((2 2 2 3) (2 2 2 2)) ;; ╋\n      ((2 2 3 0) (3 3 3 0)) ;; ╠\n      ((2 2 3 1) (2 2 2 1)) ;; ╊\n      ((2 2 3 2) (2 2 2 2)) ;; ╋\n      ((2 2 3 3) (3 3 3 3)) ;; ╬\n      ((2 3 0 0) (3 3 0 0)) ;; ╚\n      ((2 3 0 1) (2 2 0 1)) ;; ┺\n      ((2 3 0 2) (3 3 0 3)) ;; ╩\n      ((2 3 0 3) (3 3 0 3)) ;; ╩\n      ((2 3 1 0) (1 3 1 0)) ;; ╞\n      ((2 3 1 1) (2 2 1 1)) ;; ╄\n      ((2 3 1 2) (2 2 1 2)) ;; ╇\n      ((2 3 1 3) (1 3 1 3)) ;; ╪\n      ((2 3 2 0) (3 3 3 0)) ;; ╠\n      ((2 3 2 1) (2 2 2 1)) ;; ╊\n      ((2 3 2 2) (2 2 2 2)) ;; ╋\n      ((2 3 2 3) (3 3 3 3)) ;; ╬\n      ((2 3 3 0) (3 3 3 0)) ;; ╠\n      ((2 3 3 1) (3 3 3 3)) ;; ╬\n      ((2 3 3 2) (3 3 3 3)) ;; ╬\n      ((2 3 3 3) (3 3 3 3)) ;; ╬\n      ((3 0 0 0) (2 0 0 0)) ;; ╹\n      ((3 0 0 2) (3 0 0 3)) ;; ╝\n      ((3 0 1 0) (3 0 3 0)) ;; ║\n      ((3 0 1 1) (3 0 3 1)) ;; ╢\n      ((3 0 1 2) (2 0 1 2)) ;; ┩\n      ((3 0 1 3) (3 0 3 3)) ;; ╣\n      ((3 0 2 0) (3 0 3 0)) ;; ║\n      ((3 0 2 1) (3 0 3 1)) ;; ╢\n      ((3 0 2 2) (3 0 3 3)) ;; ╣\n      ((3 0 2 3) (3 0 3 3)) ;; ╣\n      ((3 0 3 2) (3 0 3 3)) ;; ╣\n      ((3 1 0 2) (2 1 0 2)) ;; ┹\n      ((3 1 0 3) (3 3 0 3)) ;; ╩\n      ((3 1 1 0) (3 1 3 0)) ;; ╟\n      ((3 1 1 1) (3 1 3 1)) ;; ╫\n      ((3 1 1 2) (2 1 1 2)) ;; ╃\n      ((3 1 1 3) (3 3 3 3)) ;; ╬\n      ((3 1 2 0) (3 1 3 0)) ;; ╟\n      ((3 1 2 1) (3 1 3 1)) ;; ╫\n      ((3 1 2 2) (2 1 2 2)) ;; ╉\n      ((3 1 2 3) (3 3 3 3)) ;; ╬\n      ((3 1 3 2) (3 1 3 1)) ;; ╫\n      ((3 1 3 3) (3 3 3 3)) ;; ╬\n      ((3 2 0 0) (3 3 0 0)) ;; ╚\n      ((3 2 0 1) (2 2 0 1)) ;; ┺\n      ((3 2 0 2) (3 3 0 3)) ;; ╩\n      ((3 2 0 3) (3 3 0 3)) ;; ╩\n      ((3 2 1 0) (2 2 1 0)) ;; ┡\n      ((3 2 1 1) (2 2 1 1)) ;; ╄\n      ((3 2 1 2) (2 2 1 2)) ;; ╇\n      ((3 2 1 3) (3 3 3 3)) ;; ╬\n      ((3 2 2 0) (3 3 3 0)) ;; ╠\n      ((3 2 2 1) (2 2 2 1)) ;; ╊\n      ((3 2 2 2) (2 2 2 2)) ;; ╋\n      ((3 2 2 3) (3 3 3 3)) ;; ╬\n      ((3 2 3 0) (3 3 3 0)) ;; ╠\n      ((3 2 3 1) (3 1 3 1)) ;; ╫\n      ((3 2 3 2) (3 3 3 3)) ;; ╬\n      ((3 2 3 3) (3 3 3 3)) ;; ╬\n      ((3 3 0 1) (3 3 0 3)) ;; ╩\n      ((3 3 0 2) (3 3 0 3)) ;; ╩\n      ((3 3 1 0) (3 3 3 0)) ;; ╠\n      ((3 3 1 1) (3 3 3 3)) ;; ╬\n      ((3 3 1 2) (3 3 3 3)) ;; ╬\n      ((3 3 1 3) (3 3 3 3)) ;; ╬\n      ((3 3 2 0) (3 3 3 0)) ;; ╠\n      ((3 3 2 1) (3 3 3 3)) ;; ╬\n      ((3 3 2 2) (3 3 3 3)) ;; ╬\n      ((3 3 2 3) (3 3 3 3)) ;; ╬\n      ((3 3 3 1) (3 3 3 3)) ;; ╬\n      ((3 3 3 2) (3 3 3 3)) ;; ╬\n      )))\n\n(eval-and-compile\n  (defconst uniline--4halfs-to-char\n    (eval-when-compile\n      (let ((table (make-vector (* 4 4 4 4) nil)))\n        (cl-loop\n         for x in uniline--list-of-available-halflines\n         do\n         (aset table\n               (uniline--pack-4halfs (cdr x))\n               (car x)))\n        (cl-loop\n         for x in uniline--list-of-double-halflines\n         do\n         (aset table\n               (uniline--pack-4halfs (car x))\n               (aref table\n                     (uniline--pack-4halfs (cadr x)))))\n        (cl-loop\n         for x in\n         '(\n           ( ?┆ 1 0 1 0 )\n           ( ?┇ 2 0 2 0 )\n           ( ?┄ 0 1 0 1 )\n           ( ?┅ 0 2 0 2 ))\n         for i = (uniline--pack-4halfs (cdr x))\n         for c = (aref table i)\n         do\n         (if (numberp c) (aset table i (setq c (vector c nil nil))))\n         (aset c 1 (car x)))\n        (cl-loop\n         for x in\n         '(\n           ( ?┊ 1 0 1 0 )\n           ( ?┋ 2 0 2 0 )\n           ( ?┈ 0 1 0 1 )\n           ( ?┉ 0 2 0 2 ))\n         for i = (uniline--pack-4halfs (cdr x))\n         for c = (aref table i)\n         do\n         (if (numberp c) (aset table i (setq c (vector c nil nil))))\n         (aset c 2 (car x)))\n        table))\n    \"Convert a 4halfs description to a UNICODE character.\nThe 4halfs description is (UP RI DW LF)\npacked into a single integer.\nAs there are no UNICODE character for every combination,\nthe visually closest UNICODE character is retrieved.\nSo for instance\n  up=1 (thin up),\n  ri=2 (thick right),\n  dw=0 (blank down),\n  lf=0 (blank left),\nis encoded into up +4*ri +16*dw +64*lf = 9,\nwhich in turn is converted to ┕.\"))\n\n(eval-and-compile\n  (uniline--defconst-hash-table uniline--char-to-4halfs\n    (eval-when-compile\n      (cl-loop\n       for x in uniline--list-of-available-halflines\n       collect\n       (cons (car x)\n             (uniline--pack-4halfs (cdr x)))))\n    256 'eq\n    ;; the setting 256 'eq\n    ;; creates 1 collision between almost never used characters\n    ;; this is the best that can be done\n    \"Convert a UNICODE character to a 4halfs description.\nThe UNICODE character is supposed to represent\na combination of half lines in 4 directions\nand in 4 brush styles.\nThe retrieved value is a 4halfs description is (UP RI DW LF)\npacked into a single integer.\nIf the UNICODE character is not a box-drawing one, nil\nis returned.\nSo for instance, the character ┸ is converted to (2 1 0 1)\nmeaning:\n  2 = thick up\n  1 = thin right\n  0 = blank down\n  1 = thin left\nValues (2 1 0 1) are encoded into 2 + 4*1 + 0*16 + 1*64 = 70\nThis table is the reverse of `uniline--4halfs-to-char'\nwithout the fall-back characters.\"))\n\n(eval-and-compile\n  (defvar-local uniline-brush 1\n    \"Controls the style of line.\nPossible values are as follows:\nnil: no action except cursor movements\n  0: erase,\n  1: simple line     like ╰─┤\n  2: thick  line     like ┗━┫\n  3: double line     like ╚═╣\n:block block drawing like ▙▄▟▀\")\n\n  (defvar-local uniline-brush-dots 0\n    \"Sub brush to control whether lines are plain or dotted.\n0: plain lines\n1: 3 dots vertical, 2 dots horizontal\n2: 4 dots both vertical & horizontal\")\n\n  (defun uniline--4halfs-to-char-aref (4halfs)\n    \"Access the array `uniline--4halfs-to-char' through this function.\nBecause sometimes an indirection is needed to desambiguate between\nseveral versions of a glyph. For instance there are at least 3\nversions of a vertical thin line:\n- │ plain line\n- ┆ 3 dotted line\n- ┊ 4 dotted line\"\n    (let ((c (aref uniline--4halfs-to-char 4halfs)))\n      (if (not (numberp c)) ;; `not' is on purpose to make a shorter bytecode\n          (aref c uniline-brush-dots)\n        c)))\n\n)\n\n(when nil\n\n  ;; This inactive code was used to generate the\n  ;;   `uniline--list-of-double-halflines' list above.\n\n  ;; As the ╬ double line glyphs combinations were not all defined\n  ;; in the UNICODE standard, a penalty system was applied to look\n  ;; for the closest possible alternate glyph.\n\n  ;; For example, ├ and ╠ exist, but a mixture of both do no exist,\n  ;; hence a line like this one\n  ;;   ((3 3 1 0)  (3 3 3 0)) ;; ╠\n  ;; which says that, when this ├ is needed, with the upward and\n  ;; rightward branches in double line style, then fallback to ╠\n\n  ;; There is no need to re-run it, except if one wants to change\n  ;; the penalties applied.\n  ;;\n  ;; To run it re-compute `uniline--4halfs-to-char'\n  ;; - but with only the first cl-loop over\n  ;;   `uniline--list-of-available-halflines'\n  ;; - not the loop over\n  ;;   `uniline--list-of-double-halflines'\n\n  (defun uniline--penalty1 (a b)\n    (pcase a\n      (0 (if (eq b 0) 0 1000))\n      (1 (pcase b\n           (0 1000)\n           (1 0)\n           (2 3)\n           (3 2)))\n      (2 (pcase b\n           (0 1000)\n           (1 3)\n           (2 0)\n           (3 1)))\n      (3 (pcase b\n           (0 1000)\n           (1 4)\n           (2 3)\n           (3 0)))))\n\n  (defun uniline--penalty (u1 r1 d1 l1 u2 r2 d2 l2)\n    (+\n     (uniline--penalty1 u1 u2)\n     (uniline--penalty1 r1 r2)\n     (uniline--penalty1 d1 d2)\n     (uniline--penalty1 l1 l2)))\n\n  (switch-to-buffer \"replace.el\")\n  (dotimes (u1 4)\n    (dotimes (r1 4)\n      (dotimes (d1 4)\n        (dotimes (l1 4)\n          (unless (uniline--4halfs-to-char-aref\n                   (uniline--pack-4halfs (list u1 r1 d1 l1)))\n            (let ((m 9999)\n                  m0\n                  u3 r3 d3 l3)\n              (dotimes (u2 4)\n                (dotimes (r2 4)\n                  (dotimes (d2 4)\n                    (dotimes (l2 4)\n                      (when (uniline--4halfs-to-char-aref\n                             (uniline--pack-4halfs (list u2 r2 d2 l2)))\n                        (setq m0 (uniline--penalty u1 r1 d1 l1 u2 r2 d2 l2))\n                        (if (< m0 m)\n                            (setq m m0\n                                  u3 u2\n                                  r3 r2\n                                  d3 d2\n                                  l3 l2)))))))\n              (insert\n               (format \"((%d %d %d %d) (%d %d %d %d)) ;; %c\\n\"\n                       u1 r1 d1 l1\n                       u3 r3 d3 l3\n                       (uniline--4halfs-to-char-aref\n                        (uniline--pack-4halfs (list u3 r3 d3 l3))))))))))))\n\n;;;╭────────────────────────────────────────────────────────╮\n;;;│Reference tables of △▶↓□◆● arrows & other UNICODE glyphs│\n;;;╰────────────────────────────────────────────────────────╯\n\n(eval-when-compile ; helper constant not needed at runtime\n  (defconst uniline--glyphs-tmp\n    '(\n      ;; arrows\n      (a   ?△ ?▷ ?▽ ?◁)      ;; white *-pointing triangle\n      (a   ?▲ ?▶ ?▼ ?◀)      ;; black *-pointing triangle\n      (a   ?↑ ?→ ?↓ ?←)      ;; *wards arrow\n      (a   ?▵ ?▹ ?▿ ?◃)      ;; white *-pointing small triangle\n      (a   ?▴ ?▸ ?▾ ?◂)      ;; black *-pointing small triangle\n      (a   ?↕ ?↔ ?↕ ?↔)      ;; up down arrow, left right arrow\n\n      ;; Those commented-out arrows are monospaces and supported\n      ;; by the 6 fonts.  But they do not have 4 directions.\n      ;;(a   ?‹ ?› ?› ?‹)      ;; single *-pointing angle quotation mark\n\n      ;; squares\n      (s   ?□)      ;; white square\n      (s   ?■)      ;; black square\n      (s   ?▫)      ;; white small square\n      (s   ?▪)      ;; black small square\n      ;;(s   ?◇)      ;; white diamond ;; glitch with Source Code Pro\n      (s   ?◆)      ;; black diamond\n      (s   ?◊)      ;; lozenge\n\n      ;; o shapes\n      (o   ?·)      ;; middle dot\n      (o   ?∙)      ;; bullet operator\n      (o   ?•)      ;; bullet\n      (o   ?●)      ;; black circle\n      (o   ?◦)      ;; white bullet\n      (o   ?Ø)      ;; latin capital letter o with stroke\n      (o   ?ø)      ;; latin small letter o with stroke\n\n      ;; crosses\n      (x   ?╳)      ;; box drawings light diagonal cross\n      (x   ?╱)      ;; box drawings light diagonal upper right to lower left\n      (x   ?╲)      ;; box drawings light diagonal upper left to lower right\n      (x   ?÷)      ;; division sign\n      (x   ?×)      ;; multiplication sign\n      (x   ?±)      ;; plus-minus sign\n      (x   ?¤)      ;; currency sign\n\n      ;; 5 shades of grey\n      (g   ? )\n      (g   ?░)\n      (g   ?▒)\n      (g   ?▓)\n      (g   ?█)\n\n      ;; The following commented-out glyphs are possible additions\n      ;; when using the DejaVu Sans Mono font\n      ;; Other fonts either do not support those glyphs\n      ;; or do not make them monospaced\n\n      ;;(t   ?⋏ ?≻ ?⋎ ?≺) ;; precedes\n      ;;(t   ?⊥ ?⊢ ?⊤ ?⊣) ;; * tack\n      ;;(t   ?⋂ ?⊃ ?⋃ ?⊂) ;; subset of\n\n      ;;(a   ?⇡ ?⇢ ?⇣ ?⇠) ;; *wards dashed arrow\n      ;;(a   ?⇑ ?⇒ ?⇓ ?⇐) ;; *wards double arrow\n      ;;(a   ?⇧ ?⇨ ?⇩ ?⇦) ;; *wards white arrow\n\n      ;;(b   ?↥ ?↦ ?↧ ?↤) ;; *wards arrow from bar\n      ;;(b   ?↟ ?↠ ?↡ ?↞) ;; *wards two headed arrow\n      ;;(b   ?⇈ ?⇉ ?⇊ ?⇇) ;; *wards paired arrows\n      ;;(b   ?☝ ?☞ ?☟ ?☜) ;; white * pointing index\n      ;;(b   ?⇞ ?⇻ ?⇟ ?⇺) ;; *wards arrow with double vertical stroke\n\n      ;;(c   ?⬘ ?⬗ ?⬙ ?⬖) ;; diamond with * half black\n      ;;(c   ?◓ ?◑ ?◒ ?◐) ;; circle with * half black\n      ;;(b   ?⍐ ?⍈ ?⍗ ?⍇) ;; apl functional symbol quad *wards arrow\n\n      ;;(s   ?▢) ;; white square with rounded corners\n      ;;(s   ?▣) ;; white square containing black small square\n      ;;(s   ?▩) ;; square with diagonal crosshatch fill\n      ;;(s   ?▤) ;; square with horizontal fill\n      ;;(s   ?▥) ;; square with vertical fill\n      ;;(s   ?▦) ;; square with orthogonal crosshatch fill\n      ;;(s   ?▧) ;; square with upper left to lower right fill\n      ;;(s   ?▨) ;; square with upper right to lower left fill\n\n      ;;(o   ?○) ;; white circle\n      ;;(o   ?◎) ;; bullseye\n      ;;(o   ?✪) ;; circled white star\n      ;;(o   ?o) ;; latin small letter o\n      ;;(o   ?O) ;; latin capital letter o\n      ;;(o   ?◍) ;; circle with vertical fill\n      ;;(o   ?◉) ;; fisheye\n      ;;(o   ?❂) ;; circled open centre eight pointed star\n      ;;(o   ?⚙) ;; gear\n      ;;(o   ?☹) ;; white frowning face\n      ;;(o   ?☺) ;; white smiling face\n      ;;(o   ?✆) ;; telephone location sign\n      ;;(o   ?✇) ;; tape drive\n\n      ;;(x   ?☓) ;; saltire\n      ;;(x   ?+) ;; plus sign\n      ;;(x   ?✔) ;; heavy check mark\n      ;;(x   ?✖) ;; heavy multiplication x\n      ;;(x   ?✚) ;; heavy greek cross\n      ;;(x   ?✜) ;; heavy open centre cross\n      ;;(x   ?x) ;; latin small letter x\n      ;;(x   ?X) ;; latin capital letter x\n      ;;(x   ?✙) ;; outlined greek cross\n      ;;(x   ?✛) ;; open centre cross\n\n      ;;(d   ?⚀) ;; die face-1\n      ;;(d   ?⚁) ;; die face-2\n      ;;(d   ?⚂) ;; die face-3\n      ;;(d   ?⚃) ;; die face-4\n      ;;(d   ?⚄) ;; die face-5\n      ;;(d   ?⚅) ;; die face-6\n      ;;(d   ?⊡) ;; squared dot operator\n      ;;(d   ?☒) ;; ballot box with x\n      ;;(d   ?☑) ;; ballot box with check\n      ;;(d   ?⊞) ;; squared plus\n\n      ;;(l   ?◇) ;; white diamond\n      ;;(l   ?◆) ;; black diamond\n      ;;(l   ?✦) ;; black four pointed star\n      ;;(l   ?✧) ;; white four pointed star\n      ;;(l   ?◈) ;; white diamond containing black small diamond\n      ;;(l   ?♠) ;; black spade suit\n      ;;(l   ?♥) ;; black heart suit\n      ;;(l   ?♦) ;; black diamond suit\n      ;;(l   ?♣) ;; black club suit\n      ;;(l   ?♤) ;; white spade suit\n      ;;(l   ?♡) ;; white heart suit\n      ;;(l   ?♢) ;; white diamond suit\n      ;;(l   ?♧) ;; white club suit\n\n      ;;(f   ?✿) ;; black florette\n      ;;(f   ?❀) ;; white florette\n      ;;(f   ?❄) ;; snowflake\n      ;;(f   ?✾) ;; six petalled black and white florette\n      ;;(f   ?❁) ;; eight petalled outlined black florette\n      ;;(f   ?❅) ;; tight trifoliate snowflake\n      ;;(f   ?❆) ;; heavy chevron snowflake\n\n      ;;(w   ?☼) ;; white sun with rays\n      ;;(w   ?☀) ;; black sun with rays\n      ;;(w   ?✫) ;; open centre black star\n      ;;(w   ?✭) ;; outlined black star\n      ;;(w   ?✩) ;; stress outlined white star\n      ;;(w   ?✪) ;; circled white star\n      ;;(w   ?✬) ;; black centre white star\n      ;;(w   ?✮) ;; heavy outlined black star\n      ;;(w   ?✯) ;; pinwheel star\n      ;;(w   ?✱) ;; heavy asterisk\n      ;;(w   ?✲) ;; open centre asterisk\n      ;;(w   ?✳) ;; eight spoked asterisk\n      ;;(w   ?✴) ;; eight pointed black star\n      ;;(w   ?✵) ;; eight pointed pinwheel star\n      ;;(w   ?✶) ;; six pointed black star\n      ;;(w   ?✷) ;; eight pointed rectilinear black star\n      ;;(w   ?✸) ;; heavy eight pointed rectilinear black star\n      ;;(w   ?✹) ;; twelve pointed black star\n      ;;(w   ?❂) ;; circled open centre eight pointed star\n\n      ;;(k   ?✻) ;; teardrop-spoked asterisk\n      ;;(k   ?✽) ;; heavy teardrop-spoked asterisk\n      ;;(k   ?❉) ;; balloon-spoked asterisk\n      ;;(k   ?❊) ;; eight teardrop-spoked propeller asterisk\n      ;;(k   ?✺) ;; sixteen pointed asterisk\n      ;;(k   ?✼) ;; open centre teardrop-spoked asterisk\n      ;;(k   ?❃) ;; heavy teardrop-spoked pinwheel asterisk\n      ;;(k   ?❋) ;; heavy eight teardrop-spoked propeller asterisk\n      ;;(k   ?❇) ;; sparkle\n      ;;(k   ?❈) ;; heavy sparkle\n      ))\n  \"List of good looking UNICODE glyphs.\nThose are:\n- arrows in 4 directions,\n- lists of symmetric-in-4-directions characters.\nThere are hundred of UNICODEs, but most of them are not\nfixed-width or height, even in mono-spaced fonts.\nHere we selected only the fixed-size ones.\")\n\n(eval-and-compile\n  (defconst uniline--glyphs-bw\n    (eval-when-compile\n      (let ((r (reverse uniline--glyphs-tmp)))\n        ;; nconc is used to create a circular list on purpose\n        (nconc r r)))\n    \"List of good looking UNICODE glyphs.\nThose are:\n- arrows in 4 directions,\n- lists of symmetric-in-4-directions characters.\nThere are hundred of UNICODEs, but most of them are not\nfixed-width or height, even in mono-spaced fonts.\nHere we selected only the fixed-size ones.\nThis list is ciurcular in backward order.\"))\n\n(eval-and-compile\n  (defconst uniline--glyphs-fw\n    (eval-when-compile\n      (let ((r (cl-copy-list uniline--glyphs-tmp)))\n      ;; nconc is used to create a circular list on purpose\n        (nconc r r)))\n    \"List of good looking UNICODE glyphs.\nThose are:\n- arrows in 4 directions,\n- lists of symmetric-in-4-directions characters.\nThere are hundred of UNICODEs, but most of them are not\nfixed-width or height, even in mono-spaced fonts.\nHere we selected only the fixed-size ones.\nThis list is ciurcular in forward order.\"))\n\n(eval-when-compile                      ; not needed at runtime\n\n  (defun uniline--duplicate (list)\n    \"Return not-nil if LIST is duplicate-free.\nUsing `eq'.\"\n    (while\n        (and\n         (cdr list)\n         (not (memq (car list) (cdr list))))\n      (pop list))\n    (cdr list))\n\n  (defun uniline--make-glyph-hash (list)\n    \"Helper function to build `uniline--glyphs-reverse-hash-*'.\nUsed only at package initialization.\nLIST is `uniline--glyphs-fbw'.\"\n    (let ((pairs ()))\n      (cl-loop\n       for ll on list\n       do\n       (if (cddar ll)\n           ;; glyph is directional, like ▲ ▶ ▼ ◀\n           (cl-loop\n            for cc in (cdar ll)\n            for i from 0\n            do (push\n                (cons\n                 cc\n                 (cons\n                  (if (uniline--duplicate (car ll))\n                      t      ; special case ↕↔↕↔ is NOT fully directional\n                    i)       ; fully directional, i gives the direction\n                  ll))\n                pairs))\n         ;; glyph is not directional, like ■ ● ◆\n         (push (cons (cadar ll) (cons nil ll)) pairs))\n       ;; explicitly break out of circular list\n       if (eq (cdr ll) list)\n       return nil)\n      pairs)))\n\n(uniline--defconst-hash-table uniline--glyphs-reverse-hash-fw\n  (eval-when-compile\n    (uniline--make-glyph-hash uniline--glyphs-fw))\n  128 'equal ; `equal' instead of `eq' to lower table size without collisions\n  \"Same as `uniline--glyphs-fw' reversing keys & values.\")\n\n(uniline--defconst-hash-table uniline--glyphs-reverse-hash-bw\n  (eval-when-compile\n    (uniline--make-glyph-hash uniline--glyphs-bw))\n  128 'equal ; `equal' instead of `eq' to lower table size without collisions\n  \"Same as `uniline--glyphs-bw' reversing keys & values.\")\n\n;;;╭───────────────────────────────────────────────────────────╮\n;;;│Reference tables of ▙▄▟▀ quadrant-blocks UNICODE characters│\n;;;╰───────────────────────────────────────────────────────────╯\n\n;; Hereafter `4quadb' means a representation of a quadrant-block\n;; UNICODE character as a single number.  This number must hold\n;; all combinations of the 4 quarter-of-a-blocks.\n;; Each of the 4 quarters may be present or absent.\n;; Therfore `4quadb' is a number from 0 to 2x2x2x2 = 16.\n;; Hereafter, the arbitrary choosen bits allocation is as follow:\n;;\n;;  2^1: here──→───╮\n;;               ╭─┴╮\n;;  2^0: here──→─┤▘▝│\n;;  2^2: here──→─┤▖▗│\n;;               ╰─┬╯\n;;  2^3: here──→───╯\n;;\n;; For instance, the character ▚ is made of two quarter blocks\n;;  - one in the up-left corner           → constant 2^0\n;;  - the other in the down-right corner  → constant 2^3\n;; The position in the `uniline--4quadb-to-char' of ▚\n;; will be 2^0 + 2^3 = 9\n\n(eval-and-compile\n  (defconst uniline--4quadb-to-char\n    [   ?  ?▘ ?▝ ?▀\n        ?▖ ?▌ ?▞ ?▛\n        ?▗ ?▚ ?▐ ?▜\n        ?▄ ?▙ ?▟ ?█\n        ]\n    \"Convert a quadrant bitmap into a UNICODE character.\nA quadrant bitmap is a number in [0..16) made of 4 bits.\nEach bit says whether or not there is a quadrant-block\nat a position.\nThe order of characters in this table is not important,\nprovided that a particular quarter is always represented\nby the same bit, and this bit is 1 when this quarter is black.\nEverything in the code hereafter follow the choosen ordering\nof this table.\"))\n\n(eval-and-compile\n  (uniline--defconst-hash-table uniline--char-to-4quadb1\n    (eval-when-compile\n      (append\n       '((?\\t .  0)  ;; TAB considered as space character\n         (?    . 0)  ;; NO-BREAK SPACE\n         (8200 . 0)) ;; PUNCTUATION SPACE\n       (cl-loop\n        for c across uniline--4quadb-to-char\n        for i from 0\n        collect (cons c i))))\n    64 'eq\n    \"Convert a UNICODE character to a quadrant bitmap.\nReverse of `uniline--4quadb-to-char'\"))\n\n(eval-and-compile\n  (defmacro uniline--char-to-4quadb (char)\n    \"Return a bit pattern (a 4quadb).\nIt represents a UNICODE character like ?▙ in CHAR.\nReturn nil if CHAR is not a 4quadb character.\"\n    (if (fixnump char)\n        (gethash  char uniline--char-to-4quadb1)\n      `( gethash ,char uniline--char-to-4quadb1))))\n\n(eval-and-compile\n  (defconst uniline--4quadb-pushed\n    (eval-when-compile\n      (let ((table (make-vector 4 nil))) ;        ╭─╴fill with zero because many\n        (cl-loop for i from 0 to 3       ;        ▽  entries will be zero anyway\n                 do (aset table i (make-vector 16 0)))\n        (cl-flet\n            ((fill-dir (subtable &rest keyvals)\n               ;; first seed the TABLE entries for single-bit quadrant blocks\n               ;; and what they become when pushed in DIR direction\n               (cl-loop for (k v) on keyvals by #'cddr\n                        do\n                        (aset subtable\n                              (uniline--char-to-4quadb k)\n                              (uniline--char-to-4quadb v)))\n               ;; then fill in entries for all 16 quadrant blocks, by logically\n               ;; composing their bits from single-bits\n               (cl-loop\n                for i from 0 to 15\n                do                  ;  ╭╴consider each of the 4 bits\n                (aset subtable      ;  │ and if bit=1, get entry╶─╮\n                      i             ;  ╰─╮                      ╭─╯\n                      (logior       ;    ▽                      ▽\n                       (if (eq (logand i 1) 0) 0 (aref subtable 1))\n                       (if (eq (logand i 2) 0) 0 (aref subtable 2))\n                       (if (eq (logand i 4) 0) 0 (aref subtable 4))\n                       (if (eq (logand i 8) 0) 0 (aref subtable 8)))))))\n          (fill-dir (aref table uniline-direction-up↑)\n                    ?▖ ?▘  ?▗ ?▝)\n          (fill-dir (aref table uniline-direction-ri→)\n                    ?▘ ?▝  ?▖ ?▗)\n          (fill-dir (aref table uniline-direction-dw↓)\n                    ?▘ ?▖  ?▝ ?▗)\n          (fill-dir (aref table uniline-direction-lf←)\n                    ?▝ ?▘  ?▗ ?▖))\n        table))\n    \"For each of the 16 quadrant blocks, this table tells what it becomes\nwhen pushed half-a-char-width in all 4 directions.\nFor instance [▞] pushed right→ becomes [▗], pushed up↑ becomes [▘]\nAccess it with this snippet:\n(uniline--4quadb-pushed dir 4quadb)\")\n\n  (defmacro uniline--4quadb-pushed (dir 4quadb)\n    \"Accessor to the `uniline--4quadb-pushed' array.\nFolds to a single number if DIR & 4QUADB are themselves numbers.\"\n    (condition-case nil\n        (setq dir (eval dir)) ;; fold if dir is a numerical sexpr\n      (error nil))            ;; otherwise leave dir alone\n    (if (and (fixnump dir)\n             (fixnump 4quadb))\n        (aref (aref uniline--4quadb-pushed  dir)  4quadb)\n      ` (aref (aref uniline--4quadb-pushed ,dir) ,4quadb))))\n\n;;;╭──────────────────────╮\n;;;│Inserting a character │\n;;;╰──────────────────────╯\n\n;; Formerly, `self-insert-command' was used directly\n;; along with the `post-self-insert-hook' to avoid the cursor\n;; moving right.\n;;\n;; But `self-insert-command' has too many side-effects.\n;; Besides, other utilities like `electric-pair-mode' assume that\n;; `last-command-event' is equal to the parameter given to\n;; `self-insert-command'.\n;;\n;; A side effect of using `post-self-insert-hook' was that inserting\n;; an ordinary character did not move the cursor.\n;;\n;; For those reasons, direct usage of `self-insert-command' was\n;; replaced by a simple call to `insert' with `delete-char' to\n;; simulate the `overwrite-mode'.\n\n(defun uniline--insert-char (char)\n  \"Insert CHAR in overwrite mode avoiding cursor moving.\"\n  ;; automatic untabification with (move-to-column … t)\n  ;; is not enough when cursor goes right onto the beginning of a TAB\n  (if (eq (char-after) ?\\t)\n      (untabify (point) (1+ (point))))\n  ;; `insert' before `delete-char' to preserve `point-marker'\n  (insert char)\n  (or (eolp) (delete-char 1))\n  (backward-char))\n\n;;;╭────────────────────────────────────────────────────────╮\n;;;│Low level management of ┏╾╯half-lines UNICODE characters│\n;;;╰────────────────────────────────────────────────────────╯\n\n(eval-when-compile ; not needed at runtime\n  (defmacro uniline--insert-4halfs (4halfs)\n    \"Insert at (point) a UNICODE like ┬.\nThe UNICODE character is described by the 4HALFS bits pattern.\nThe (point) does not move.\"\n    `(uniline--insert-char\n      (uniline--4halfs-to-char-aref ,4halfs))))\n\n(eval-when-compile ; not needed at runtime\n  (defmacro uniline--insert-4quadb (4quadb)\n    \"Insert at (point) a UNICODE like ▙.\nThe UNICODE character is described by the 4QUADB bits pattern.\nThe (point) does not move.\"\n    `(uniline--insert-char\n      (aref uniline--4quadb-to-char ,4quadb))))\n\n;;;╭───────────────────────────────────────────────────────────────╮\n;;;│Low level management of ▙▄▟▀ quadrant-blocks UNICODE characters│\n;;;╰───────────────────────────────────────────────────────────────╯\n\n(defvar-local uniline--which-quadrant (uniline--char-to-4quadb ?▘)\n  \"Where is the quadrant cursor.\nTo draw lines with quadrant-blocks like this ▙▄▟▀,\nit is required to keep track where is the\nquadrant-cursor.  It can be at 4 sub-locations:\nnorth-east, south-west, etc.\")\n\n(defun uniline--quadrant-undo (i)\n  \"Re-position `uniline--which-quadrant' on undo.\nThis function sets the cursor to I.\nIt is called from the deep intricacies of the standard\nundo machinery.\"\n  (setq uniline--which-quadrant i))\n\n(defun uniline--store-undo-quadrant-cursor ()\n  \"Helper function to store the quandrant-cursor in the undo history.\nThe standard Emacs undo mechanism already restores the point location.\nThe quadrant cursor is 1/4 smaller than the regular cursor, and\nneeds a specific action to restore it.\"\n  (setq buffer-undo-list\n        `((apply\n           uniline--quadrant-undo\n           ,uniline--which-quadrant)\n          ,(point)\n          ,@buffer-undo-list)))\n\n(defun uniline--write-one-4quadb (force)\n  \"Helper function to write the quadrant cursor at point.\nIt adds the quadrant-block described by `uniline--which-quadrant'\nat `point', preserving already present quadrant-blocks.\nWhen FORCE is not nil, overwrite a possible non quadrant-block\ncharacter at point.\"\n  (let ((bits (uniline--char-to-4quadb (uniline--char-after))))\n    (cond\n     (bits\n      (uniline--insert-4quadb (logior bits uniline--which-quadrant)))\n     (force\n      (uniline--insert-4quadb              uniline--which-quadrant)))))\n\n;;;╭────────────────────────────────╮\n;;;│Test blanks in the neighbourhood│\n;;;╰────────────────────────────────╯\n\n(defun uniline--blank-after (p)\n  \"Return non-nil if P points to a 4halfs or 4quadb character.\nThis includes\n- a blank\n- a 4half drawing character like ┴ ┻ ╩\n- a 4quad drawing character like ▙\n- a new line\n- P is nil\nThe last two cases will be changed to an actual blank character by\nvirtue of the infinite buffer.\"\n  (or\n   (not p)\n   (let ((c (char-after p)))\n     (or\n      (memq c '(?\\n nil))\n      ;; the case c==nil never happens in Uniline calls\n      (gethash c uniline--char-to-4halfs)\n      (uniline--char-to-4quadb c)))))\n\n(eval-when-compile ; not needed at runtime\n  (defmacro uniline--neighbour-point (dir)\n    \"Return the (point) one char away from current (point) in DIR direction.\nReturn nil if no such point exists because it would fall outside the buffer.\nThe buffer is not modified.\nThis macro seems large, but actually it is a bag of 4 small algorithms.\nJust one out of those 4 algorithms is retrieved on a call.\"\n    (setq dir (eval dir))\n    (uniline--switch-with-table dir\n      (uniline-direction-ri→\n       '(unless (eolp) (1+ (point))))\n      (uniline-direction-lf←\n       '(unless (bolp) (1- (point))))\n      (uniline-direction-up↑\n       '(let ((here (point))\n              (c (current-column)))\n          (prog1\n              (and (eq (forward-line -1) 0)\n                   (eq (move-to-column c) c)\n                   (point))\n            (goto-char here))))\n      (uniline-direction-dw↓\n       '(let ((here (point))\n              (c (current-column)))\n          (prog1\n              (and (eq (forward-line 1) 0)\n                   (eq (move-to-column c) c)\n                   (point))\n            (goto-char here)))))))\n\n(defun uniline--blank-neighbour1 (dir)\n  \"Return non-nil if the neighbour of current point in DIR is blank.\nThe neighbour is one character away in the DIR direction.\nBlank include:\n- actual blank\n- 4half drawing character like ┴ ┻ ╩\n- 4quad drawing character like ▙\n- new line\n- neighbour is outside buffer.\"\n  (uniline--blank-after\n   (uniline--switch-with-cond dir\n     (uniline-direction-up↑ (uniline--neighbour-point uniline-direction-up↑))\n     (uniline-direction-ri→ (uniline--neighbour-point uniline-direction-ri→))\n     (uniline-direction-dw↓ (uniline--neighbour-point uniline-direction-dw↓))\n     (uniline-direction-lf← (uniline--neighbour-point uniline-direction-lf←)))))\n\n(eval-when-compile ;; not used at runtime\n  ;; a (defmacro) vs. a (defsubst) saves 2 bytecodes\n  (defmacro uniline--blank-neighbour4 (dir)\n  \"Return non-nil if the quarter point cursor can move in DIR\nwhile staying on the same (point).\"\n  ;; Try evaluating\n  ;;   (macroexpand-all '(uniline--blank-neighbour4 dir))\n  ;; or\n  ;;   (disassemble     '(uniline--blank-neighbour4 dir))\n  ;; You will see a short 9 byte-codes function which references\n  ;; a constant vector. This is the fastest it can be.\n  (condition-case nil\n      (setq dir (eval dir)) ;; fold if dir is a numerical sexpr\n    (error nil))            ;; otherwise leave dir alone\n  `(eq\n    (logand\n     uniline--which-quadrant\n     (uniline--switch-with-table ,dir\n       ;; This lambda computes the values in the resulting lookup table\n       ;; for each entry. It is run at compile-time, never at run-time.\n       (lambda (dir)\n         (uniline--4quadb-pushed\n          dir\n          (uniline--char-to-4quadb ?█)         ; this is constant 15 = 0b1111\n          ))\n       (uniline-direction-up↑)\n       (uniline-direction-ri→)\n       (uniline-direction-dw↓)\n       (uniline-direction-lf←)))\n    0)))\n\n(defun uniline--blank-neighbour (dir)\n  \"Return non-nil if the neighbour in DIR direction is blank.\nBlank include:\n- actual blank\n- 4half drawing character like ┴ ┻ ╩\n- 4quad drawing character like ▙\n- new line\n- neighbour is outside buffer\n- when the cursor is :block and there is still room to move in DIRection\n  while staying on the same (point).\"\n  (or\n   (and (eq uniline-brush :block)\n        (uniline--blank-neighbour4 dir))\n   (and (uniline--blank-neighbour1 dir))))\n\n;;;╭──────────────────────────────────────────────────╮\n;;;│High level drawing in half-lines & quadrant-blocks│\n;;;╰──────────────────────────────────────────────────╯\n\n(defvar-local uniline--arrow-direction\n  (uniline-direction-up↑)\n  \"Where the next arrow should point to.\nThis might be 0, 1, 2, 3, as defined by the four constants\n`uniline-direction-up↑', `uniline-direction-lf←', ...\")\n\n(defun uniline--write-one-4halfs-impl (brush force 4halfmask 4quadmask)\n  \"Draw half a line.\nIf there are too few characters on the row where the line\nwill be drawn, fill it with blank characters.\nCursor does not move.\nBRUSH is `uniline-brush' turned in the direction of drawing.\nWhen FORCE is not nil, overwrite a possible non-4halfs character.\n4HALFMASK is a bit-mask to erase 4halfs lines found at (point).\n4QUADMASK is a bit-mask to erase 4quads blocks found at (point).\"\n  (let ((bits\n         (gethash (uniline--char-after) uniline--char-to-4halfs)))\n    (cond\n     ;; 1st case: (char-after) is a line-character like ├,\n     ;; or any character if FORCE\n     ;; then change a half-line of this character\n     ;; for example changing it from ├ to ┽\n     (bits\n      (uniline--insert-4halfs\n       (logior\n        (logand bits 4halfmask)\n        brush)))\n     ;; 2nd case: (char-after) is a block character like ▜,\n     ;; and the brush is the eraser\n     ;; then clear only half of this character\n     ((eq uniline-brush 0)\n      (if (setq bits (uniline--char-to-4quadb (uniline--char-after)))\n          (uniline--insert-4quadb (logand 4quadmask bits))))\n     ;; 3th case: force\n     (force\n      (uniline--insert-4halfs brush)))))\n\n(eval-when-compile ; not needed at runtime\n  (defmacro uniline--write-one-4halfs (dir force)\n    \"Draw half a line in the direction DIR.\nIf there are too few characters on the row where the line\nwill be drawn, fill it with blank characters.\nCursor does not move.\nWhen FORCE is not nil, overwrite a possible non-4halfs character.\"\n    `(uniline--write-one-4halfs-impl\n      (uniline--shift-4half uniline-brush ,dir)\n      ,force\n      ,(lognot (uniline--shift-4half 3 dir))\n      ,(uniline--4quadb-pushed\n        (uniline--reverse-direction dir)\n        (uniline--char-to-4quadb ?█))))) ; this is constant 15 = 0b1111\n\n(eval-when-compile ; not needed at runtime\n  (defmacro uniline--write-impl (dir repeat force)\n    \"Move cursor in direction DIR drawing or erasing lines.\nOr extending region.\nThis is an implementation function for the 4 actual functions.\n- If region is already active, just extend it without drawing.\n- If the brush is set to `:block', draw one quadrant-block.\n  Then move cursor half a character.\n  This could move (point) one char, or leave it where it is.\n- Otherwise, draw or erase pairs of half-lines.\n  `uniline-brush' gives the style of line (it may be an eraser).\nREPEAT is the length of the line to draw or erase,\nor the number of quadrant-blocks to draw,\nor the length to extend region.\nREPEAT defaults to 1.\nWhen FORCE is not nil, overwrite characters which are not lines.\"\n    (let* ((dir (eval dir)) ;; to convert 'uniline-direction-dw↓ into 2\n           (odir (uniline--reverse-direction dir)))\n      `(progn\n         (unless ,repeat (setq ,repeat 1))\n         (setq uniline--arrow-direction ,dir)\n         (handle-shift-selection)\n         (cond\n          ((region-active-p)\n           ;; region is marked, continue extending it\n           (uniline--move-in-direction ,dir ,repeat)\n           (setq deactivate-mark nil))\n\n          ((eq uniline-brush :block)\n           ;; draw quadrant-blocks ▝▙▄▌\n           (uniline--store-undo-quadrant-cursor)\n           (cl-loop\n            repeat ,repeat\n            do\n            (if (uniline--blank-neighbour4 ,odir)\n                (uniline--move-in-direction ,dir))\n            (setq\n             uniline--which-quadrant\n             ;; this huge expression is evaluated only at compile time\n             ;; and folded to a mere, fast reference to a constant vector\n             ;; for instance:\n             ;; (aref [nil 4 8 nil 1 nil nil nil 2] uniline--which-quadrant)\n             ,(cond\n               ((memq dir (list uniline-direction-up↑ uniline-direction-dw↓))\n                '(uniline--switch-with-table uniline--which-quadrant\n                   ((uniline--char-to-4quadb ?▘) (uniline--char-to-4quadb ?▖))\n                   ((uniline--char-to-4quadb ?▖) (uniline--char-to-4quadb ?▘))\n                   ((uniline--char-to-4quadb ?▗) (uniline--char-to-4quadb ?▝))\n                   ((uniline--char-to-4quadb ?▝) (uniline--char-to-4quadb ?▗))))\n               ((memq dir (list uniline-direction-ri→ uniline-direction-lf←))\n                '(uniline--switch-with-table uniline--which-quadrant\n                   ((uniline--char-to-4quadb ?▘) (uniline--char-to-4quadb ?▝))\n                   ((uniline--char-to-4quadb ?▖) (uniline--char-to-4quadb ?▗))\n                   ((uniline--char-to-4quadb ?▗) (uniline--char-to-4quadb ?▖))\n                   ((uniline--char-to-4quadb ?▝) (uniline--char-to-4quadb ?▘))))))\n            (uniline--write-one-4quadb ,force)))\n\n          (uniline-brush\n           ;; draw lines ╰──╮\n           (cl-loop\n            repeat ,repeat\n            do\n            (uniline--write-one-4halfs ,dir ,force)\n            (uniline--move-in-direction ,dir)\n            (uniline--write-one-4halfs ,odir ,force)))\n          (t\n           ;; brush is nil, just move point\n           (uniline--move-in-direction ,dir ,repeat)))))))\n\n(defun uniline-write-up↑ (repeat &optional force)\n  \"Move cursor up drawing or erasing glyphs, or extending region.\n- If region is already active, just extend it without drawing.\n- If the brush is set to blocks, draw one quadrant-block.\n  Then move cursor half a character.\n- Otherwise, draw or erase pairs of half-lines.\n  `uniline-brush' gives the style of line (it may be an eraser).\nREPEAT is the length of the line to draw or erase,\nor the number of quadrant-blocks to draw,\nor the length to extend region.\nREPEAT defaults to 1.\nWhen FORCE is not nil, overwrite characters which are not lines.\"\n  (interactive \"P\")\n  (uniline--write-impl uniline-direction-up↑ repeat force))\n\n(defun uniline-write-ri→ (repeat &optional force)\n  \"Move cursor right drawing or erasing glyphs, or extending region.\n- If region is already active, just extend it without drawing.\n- If the brush is set to blocks, draw one quadrant-block.\n  Then move cursor half a character.\n- Otherwise, draw or erase pairs of half-lines.\n  `uniline-brush' gives the style of line (it may be an eraser).\nREPEAT is the length of the line to draw or erase,\nor the number of quadrant-blocks to draw,\nor the length to extend region.\nREPEAT defaults to 1.\nWhen FORCE is not nil, overwrite characters which are not lines.\"\n  (interactive \"P\")\n  (uniline--write-impl uniline-direction-ri→ repeat force))\n\n(defun uniline-write-dw↓ (repeat &optional force)\n  \"Move cursor down drawing or erasing glyphs, or extending region.\n- If region is already active, just extend it without drawing.\n- If the brush is set to blocks, draw one quadrant-block.\n  Then move cursor half a character.\n- Otherwise, draw or erase pairs of half-lines.\n  `uniline-brush' gives the style of line (it may be an eraser).\nREPEAT is the length of the line to draw or erase,\nor the number of quadrant-blocks to draw,\nor the length to extend region.\nREPEAT defaults to 1.\nWhen FORCE is not nil, overwrite characters which are not lines.\"\n  (interactive \"P\")\n  (uniline--write-impl uniline-direction-dw↓ repeat force))\n\n(defun uniline-write-lf← (repeat &optional force)\n  \"Move cursor left drawing or erasing glyphs, or extending region.\n- If region is already active, just extend it without drawing.\n- If the brush is set to blocks, draw one quadrant-block.\n  Then move cursor half a character.\n- Otherwise, draw or erase pairs of half-lines.\n  `uniline-brush' gives the style of line (it may be an eraser).\nREPEAT is the length of the line to draw or erase,\nor the number of quadrant-blocks to draw,\nor the length to extend region.\nREPEAT defaults to 1.\nWhen FORCE is not nil, overwrite characters which are not lines.\"\n  (interactive \"P\")\n  (uniline--write-impl uniline-direction-lf← repeat force))\n\n(defun uniline-overwrite-up↑ (repeat)\n  \"Like `uniline-write-up↑' but overwriting.\nREPEAT is the length of the line to draw, defaulting to 1.\"\n  (interactive \"P\")\n  (uniline-write-up↑ repeat t))\n\n(defun uniline-overwrite-ri→ (repeat)\n  \"Like `uniline-write-ri→' but overwriting.\nREPEAT is the length of the line to draw, defaulting to 1.\"\n  (interactive \"P\")\n  (uniline-write-ri→ repeat t))\n\n(defun uniline-overwrite-dw↓ (repeat)\n  \"Like `uniline-write-dw↓' but overwriting.\nREPEAT is the length of the line to draw, defaulting to 1.\"\n  (interactive \"P\")\n  (uniline-write-dw↓ repeat t))\n\n(defun uniline-overwrite-lf← (repeat)\n  \"Like `uniline-write-lf←' but overwriting.\nREPEAT is the length of the line to draw, defaulting to 1.\"\n  (interactive \"P\")\n  (uniline-write-lf← repeat t))\n\n(defun uniline--write (dir &optional force)\n  \"Move cursor one char in DIR direction.\nDoing so, draw or erase glyphs, or extend region.\n- If region is already active, just extend it without drawing.\n- If the brush is set to blocks, draw one quadrant-block.\n  Then move cursor half a character.\n- Otherwise, draw or erase pairs of half-lines.\n  `uniline-brush' gives the style of line (it may be an eraser).\nWhen FORCE is not nil, overwrite whatever is in the buffer.\"\n  (funcall\n   (uniline--switch-with-table dir\n     (uniline-direction-up↑ 'uniline-write-up↑)\n     (uniline-direction-ri→ 'uniline-write-ri→)\n     (uniline-direction-dw↓ 'uniline-write-dw↓)\n     (uniline-direction-lf← 'uniline-write-lf←))\n   1\n   force))\n\n;;;╭────╮\n;;;│Fill│\n;;;╰────╯\n\n(defun uniline--choose-fill-char ()\n  \"Interactively choose a character to fill a shape.\nThis character will replace the character the cursor is on\nthroughout the shape.\nSome values of the input character are replaced by computed values:\n- `C-y' chooses the first character in the kill ring\n- `SPC' selects a darker  shade of grey than the character the point is on.\n- `DEL' selects a lighter shade of grey than the character the point is on.\n  there are 5 shades of grey in the UNICODΕ standard: \\\" ░▒▓█\\\".\n- `RET' means abort filling.\"\n  (let ((char (read-char \"Fill with (any char, C-y, SPC, DEL, RET)? \")))\n    (cond\n     ((eq char 13) nil)              ; RET: abort filling\n     ((eq char ?\u0019)                  ; yank: 1st char of the kill ring\n      (aref (car kill-ring) 0))\n     ((eq char ? )                      ; SPC: next shade of grey\n      (or (cadr (memq (uniline--char-after) '(?  ?░ ?▒ ?▓ ?█ )))\n          ? ))\n     ((eq char ?)                     ; DEL: prev shade of grey\n      (or (cadr (memq (uniline--char-after) '(?█ ?▓ ?▒ ?░ ? )))\n          ?█))\n     (t char))))\n\n(defun uniline-fill (&optional char)\n  \"Fill a hollow shape with character CHAR.\nCHAR is optional, if not given, user is queried.\nThe hole is the set of contiguous identical characters.\nThe character at point is used as reference for other\nidentical characters.\"\n  (interactive)\n  (unless char\n    (setq char (uniline--choose-fill-char)))\n\n  ;; why is stack initialized with twice the current point?\n  ;; the first is for starting the filling process\n  ;; the second is for returning to the starting point after filling\n  (let ((currentchar (uniline--char-after))\n        (stack (list (point) (point))))\n    (unless (eq char currentchar)\n      (while stack\n        (goto-char (pop stack))\n        (when (eq (char-after) currentchar) ; not (uniline--char-after) !\n          (uniline--insert-char char)\n          (let ((p (uniline--neighbour-point uniline-direction-up↑)))\n            (if  p (push p stack)))\n          (let ((p (uniline--neighbour-point uniline-direction-ri→)))\n            (if  p (push p stack)))\n          (let ((p (uniline--neighbour-point uniline-direction-dw↓)))\n            (if  p (push p stack)))\n          (let ((p (uniline--neighbour-point uniline-direction-lf←)))\n            (if  p (push p stack))))))))\n\n;;;╭────────────────────────╮\n;;;│Undo rectangle selection│\n;;;╰────────────────────────╯\n\n;; Rectangle functions operate on a visually highlighted rectangular selection\n;; Keeping the selection highlighted\n;;  - after changing the rectangle (move, fill, contour, kill, yank…)\n;;  - when undoing a rectangle change\n;; gives a sense of confidence.\n;; To achieve that, we add the point and mark (the selection) into the\n;; regular Emacs undo machinery, so as to restore it.\n\n(defun uniline--undo-restore-selection (i p)\n  \"Function called by the Emacs undo system to restore selection.\nI is the mark,\nP is the point.\"\n  (set-mark i)\n  (goto-char p)\n  (activate-mark)\n  (rectangle-mark-mode 1)\n  (setq deactivate-mark nil))\n\n(defun uniline--record-undo-rectangle-selection ()\n  \"Add the selection (point and mark) into the Emacs undo stack.\"\n  (setq buffer-undo-list\n        `((apply\n           uniline--undo-restore-selection\n           ,(mark)\n           ,(point))\n          ,@buffer-undo-list)))\n\n;; Here is the list of all interactive functions which leave\n;; the region visually active on the rectangle they just operated on.\n;;\n;; To correclty undo such rectangle operations, the selection should\n;; first be deactivated. This is because some changes in the buffer\n;; are on the fringe of the region, and are not prpoperly undone.\n;;\n;; However deactivating the selection proved to be quite tricky to achieve.\n;;\n;; Therefore we leave the region highlighted, and we instructs `undo'\n;; to disregard the region. The `undo' mechanism provides for that,\n;; by looking at the `undo-inhibit-region' property of the `last-command'\n;;\n;; The following list was hand-compiled by looking for interactive\n;; functions which call `uniline--operate-on-rectangle', either directly\n;; or through a utility function.\n\n(dolist\n    (fun\n     '(uniline-move-rect-up↑\n       uniline-move-rect-dw↓\n       uniline-move-rect-ri→\n       uniline-move-rect-lf←\n       uniline-fill-rectangle\n       uniline-draw-inner-rectangle\n       uniline-draw-outer-rectangle\n       uniline-yank-rectangle\n       uniline-change-style-standard\n       uniline-aa2u-rectangle\n       uniline-change-style-dot-3-2\n       uniline-change-style-dot-4-4\n       uniline-change-style-hard-corners\n       uniline-change-style-thin\n       uniline-change-style-thick\n       uniline-change-style-double))\n  (put fun 'undo-inhibit-region t))\n\n;;;╭───────────────────────────────────╮\n;;;│High level management of rectangles│\n;;;╰───────────────────────────────────╯\n\n(eval-when-compile ; not needed at runtime\n  (defmacro uniline--operate-on-rectangle (&rest body)\n    \"Execute BODY with some variables describing selection.\nThis is a helper macro to write actual functions.\nAssumes that a selection is active.\nThe variables\n  BEG BEGX BEGY  (point, column, line of beginning)\n  END ENDX ENDY  (point, column, line of ending)\nare made available to BODY for easy handling of the selection.\nThe selection may be reversed in any way, the variables\nare sets as if the selection was made from\nthe upper-most, left-most to the lower-most, right-most points.\nIt works even when in `rectangle-mark-mode'.\nNote that ENDX & ENDY point outside the selection in such a way that\nENDX-BEGX is the width of the rectangle, ENDY-BEGY is the height\nAfter execution of the body, selection is activated\nfrom BEGX,BEGY inclusive to ENDX,ENDY exclusive\nin `rectangle-mark-mode'.\"\n    (declare (debug (body)))\n    `(when (region-active-p)\n       (rectangle-mark-mode -1) ; otherwise sometimes end is wrong\n       (let* ((deactivate-mark) ; kludge needed to avoid deactivating the mark\n              (beg (region-beginning))\n              (end (region-end))\n              (begx (progn (goto-char beg) (current-column)))\n              (begy (1- (line-number-at-pos)))\n              (endx (progn (goto-char end) (current-column)))\n              (endy (line-number-at-pos)))\n         (when (< endx begx)\n           (setq endx (prog1 begx (setq begx endx)))\n           (setq beg (+ beg (- begx endx)))\n           (setq end (+ end (- endx begx))))\n         ,@body\n         (uniline-move-to-lin-col (1- endy) endx)\n         (set-mark (point))\n         (uniline-move-to-lin-col begy begx)\n         (rectangle-mark-mode 1)))))\n\n(eval-when-compile ; not needed at runtime\n  (defmacro uniline--compute-leakage-4halfs (dir)\n    \"Compute lines leakage from two directions for 4halfs characters.\nWhen a rectangle moves, it leaves blank chars.\nThose chars are filled with leakage from their two neighbours,\nin DIR direction, and its opposite.\nFor instance consider a situation like this:\n   ╶┬╴\n     <<< empty space\n    ┗╸\nA space is leaved empty after translation.\nThen the leakage of the two glyphs fills in E:\n   ╶┬╴\n    ╽<<< filled with leakage\n    ┗╸\"\n    (setq dir (eval dir))\n    (let ((odir (uniline--reverse-direction dir)))\n      `(let* ((cc (uniline--char-after))\n              (4c (gethash cc uniline--char-to-4halfs 0))\n              (leak (uniline--extract-reverse-4half 4c ,dir))\n              (p (uniline--neighbour-point ,odir)))\n         (if p\n             (setq\n              leak\n              (logior\n               leak\n               (uniline--extract-reverse-4half\n                (gethash (uniline--char-after p) uniline--char-to-4halfs 0)\n                ,odir))))\n         ;; here the case of dotted line is handled:\n         ;; `leak' and `4c' may be the same code, for example 02-00-02-00\n         ;; denoting 3 possible actual characters: ┇ ┋ ┃\n         ;; in this case, we recover and return the actual character in the buffer\n         ;; not the syntetic `leak'\n         (if (and (eq leak 4c) (not (eq 4c 0)))\n             cc\n           (uniline--4halfs-to-char-aref leak))))))\n\n(eval-when-compile ; not needed at runtime\n  (defmacro uniline--compute-leakage-quadb (dir)\n    \"Compute lines leakage from two directions for 4quadb characters.\nWhen a rectangle moves, it leaves blank chars.\nThose chars are filled with leakage from their two neighbours,\nin DIR direction, and its opposite.\nFor instance consider a situation like this:\n    ▌\n     <<< empty space\n    ▙\nA space is leaved empty after translation.\nThen the leakage of the two glyphs fills in E:\n    ▌\n    ▌<<< filled with leakage\n    ▙\"\n    (setq dir (eval dir))\n    (let ((odir (uniline--reverse-direction dir)))\n      `(let ((leak ;; leak from (point)\n              (uniline--4quadb-pushed\n               ,dir\n               (or (uniline--char-to-4quadb (uniline--char-after)) 0)))\n             (lean ;; leak from neighbour of (point)\n              (let ((p (uniline--neighbour-point ,odir)))\n                (and\n                 p\n                 (uniline--char-to-4quadb (uniline--char-after p))))))\n         (aref uniline--4quadb-to-char\n               (if (not lean)\n                   leak\n                 (logior\n                  leak\n                  (uniline--4quadb-pushed ,odir lean)\n                  )))))))\n\n(eval-when-compile ; not needed at runtime\n  (defmacro uniline--compute-leakage (dir)\n    \"Compute lines leakage from two directions: DIR and its opposite.\"\n    `(let ((c (uniline--compute-leakage-4halfs ,dir)))\n       (if (eq c ? )\n           (uniline--compute-leakage-quadb ,dir)\n         c))))\n\n(eval-when-compile ; not needed at runtime\n  (defmacro uniline--compute-leakage-on-region (dir y begx endx)\n    \"Compute the leakage of all characters in a region.\nRegion is BEGX..ENDX x Y..Y, it is one char high.\nThe region just below is considered when DIR is ↑,\nthe region just above is considered when DIR is ↓.\nReturn a string which will be inserted back in the buffer.\"\n    `(cl-loop\n      with s = (make-vector (- ,endx ,begx) ? )\n      for x from ,begx below ,endx\n      for i from 0\n      do\n      (uniline-move-to-lin-col ,y x)\n      (aset s i (uniline--compute-leakage ,dir))\n      finally return (concat s))))\n\n(defun uniline--exchange-region-string (y begx endx hand)\n  \"Replace the region BEGX..ENDX x Y..Y with HAND.\nRegion is 1 character high.\nHAND is a string of the same length as the region.\nReturn the replaced region as a string.\"\n  (uniline-move-to-line y)\n  (let* ((end (progn (uniline-move-to-column endx) (point)))\n         (beg (progn (uniline-move-to-column begx) (point)))\n         (line (buffer-substring beg end)))\n    (delete-region beg end)\n    (goto-char beg)\n    (insert hand)\n    line))\n\n(defun uniline--untabify-rectangle (begy endy)\n  \"Untabify all lines over which the rectangle operates.\nBEGY and ENDY are the line-numbers of the beggining en ending\nof the rectangle.\nThe costly `untabify' function is called only if there are\nTAB characters in the region.\"\n  (let ((end\n         (progn\n           (uniline-move-to-line (1- endy))\n           (end-of-line)\n           (point)))\n        (beg\n         (progn\n           (uniline-move-to-line begy)\n           (beginning-of-line)\n           (point))))\n    ;; (point) is at beg\n    (when (search-forward \"\\t\" end t)\n      (untabify beg end))))\n\n\n(defun uniline-move-rect-up↑ (repeat)\n  \"Move the rectangle marked by selection one line upper.\nTruncate the selection if it touches the upper side of the buffer.\nREPEAT tells how many characters the rectangle should move,\ndefaulting to 1.\n    ↑ ↑ ↑ ↑\n    ░░░░░░░\n    ░░░░░░░\n    ░░░░░░░\n\"\n  (interactive \"P\")\n  (uniline--record-undo-rectangle-selection)\n  (cl-loop\n   repeat (or repeat 1)\n   do\n   (uniline--operate-on-rectangle\n    (uniline--untabify-rectangle begy endy)\n    (if (and (eq begy 0) uniline-infinite-up↑)\n        (save-excursion (uniline-move-to-line -1))\n      (setq\n       begy (max (1- begy) 0)\n       endy      (1- endy)))\n    (cl-loop\n     with hand = (uniline--compute-leakage-on-region\n                  uniline-direction-up↑ endy begx endx)\n     for y from endy downto begy\n     do (setq hand (uniline--exchange-region-string y begx endx hand))))))\n\n(defun uniline-move-rect-dw↓ (repeat)\n  \"Move the rectangle marked by selection one line down.\nThe buffer is infinite at the bottom.\nREPEAT tells how many characters the rectangle should move,\ndefaulting to 1.\n    ░░░░░░░\n    ░░░░░░░\n    ░░░░░░░\n    ↓ ↓ ↓ ↓\n\"\n  (interactive \"P\")\n  (uniline--record-undo-rectangle-selection)\n  (cl-loop\n   repeat (or repeat 1)\n   do\n   (uniline--operate-on-rectangle\n    (uniline--untabify-rectangle begy endy)\n    (cl-loop\n     with hand = (uniline--compute-leakage-on-region\n                  uniline-direction-dw↓ begy begx endx)\n     for y from begy to endy\n     do (setq hand (uniline--exchange-region-string y begx endx hand)))\n    (setq\n     begy (1+ begy)\n     endy (1+ endy)))))\n\n(defun uniline-move-rect-ri→ (repeat)\n  \"Move the rectangle marked by selection one char to the left.\nThe buffer is infinite at its right side.\nREPEAT tells how many characters the rectangle should move,\ndefaulting to 1.\n    ░░░░░░░→\n    ░░░░░░░→\n    ░░░░░░░→\n\"\n  (interactive \"P\")\n  (uniline--record-undo-rectangle-selection)\n  (cl-loop\n   repeat (or repeat 1)\n   do\n   (uniline--operate-on-rectangle\n    (uniline--untabify-rectangle begy endy)\n    (cl-loop\n     for y from begy below endy\n     do\n     (uniline-move-to-lin-col y begx)\n     (insert\n      (uniline--compute-leakage uniline-direction-ri→))\n     (uniline-move-to-column (1+ endx))\n     (or (eolp) (delete-char 1)))\n    (setq\n     begx (1+ begx)\n     endx (1+ endx)))))\n\n(defun uniline-move-rect-lf← (repeat)\n  \"Move the rectangle marked by selection one char to the left.\nTruncate the selection if it touches the left side of the buffer.\nREPEAT tells how many characters the rectangle should move,\ndefaulting to 1.\n   ←░░░░░░░\n   ←░░░░░░░\n   ←░░░░░░░\n\"\n  (interactive \"P\")\n  (uniline--record-undo-rectangle-selection)\n  (cl-loop\n   repeat (or repeat 1)\n   do\n   (uniline--operate-on-rectangle\n    (uniline--untabify-rectangle begy endy)\n    (setq\n     begx (max (1- begx) 0)\n     endx (max (1- endx) 0))\n    (cl-loop\n     for y from (1- endy) downto begy\n     do\n     (uniline-move-to-lin-col y endx)\n     (insert\n      (prog1\n          (uniline--compute-leakage uniline-direction-lf←)\n        (uniline-move-to-delta-column 1)))\n     (uniline-move-to-column begx)\n     (delete-char 1)))))\n\n(defun uniline-fill-rectangle ()\n  \"Fill the rectangle marked by selection.\nInteractively choose the filling character.\nSee `uniline--choose-fill-char'.\n    ░░░░░░░\n    ░░░░░░░\n    ░░░░░░░\n\"\n  (interactive)\n  (uniline--record-undo-rectangle-selection)\n  (uniline--operate-on-rectangle\n   (set-mark end)  ;; ensure we are inside the region before\n   (goto-char beg) ;; asking for the filling character\n   (rectangle-mark-mode 1)\n   (let ((char (uniline--choose-fill-char)))\n     (cl-loop\n      for y from begy below endy\n      do\n      (uniline-move-to-lin-col y begx)\n      (cl-loop\n       for x from begx below endx\n       do\n       (uniline--insert-char char)\n       (uniline-move-to-delta-column 1))))))\n\n(defun uniline-draw-inner-rectangle (&optional force)\n  \"Draws a rectangle inside a rectangular selection.\nUse the current brush style, which may be thin, thick,\ndouble line, block, or eraser.\nWhen FORCE is not nil, overwrite whatever is there.\"\n  (interactive)\n  (uniline--record-undo-rectangle-selection)\n  (uniline--operate-on-rectangle\n   (let ((width  (- endx begx 1))\n         (height (- endy begy 1)))\n     (goto-char beg)\n     (if (eq uniline-brush :block)\n         (setq\n          width  (+ width  width  1)\n          height (+ height height 1)\n          uniline--which-quadrant (uniline--char-to-4quadb ?▘)))\n     (let ((mark-active nil)) ;; otherwise brush would be inactive\n       (uniline-write-ri→ width  force)\n       (uniline-write-dw↓ height force)\n       (uniline-write-lf← width  force)\n       (uniline-write-up↑ height force)))))\n\n(defun uniline-overwrite-inner-rectangle ()\n  \"Draws a rectangle inside a rectangular selection.\nUse the current brush style, which may be thin, thick,\ndouble line, block, or eraser.\nOverwrite whatever is there.\"\n  (interactive)\n  (uniline-draw-inner-rectangle t))\n\n(defun uniline-draw-outer-rectangle (&optional force)\n  \"Draws a rectangle outside a rectangular selection.\nUse the current brush style, which may be thin, thick,\ndouble line, block, or eraser.\nWhen FORCE is not nil, overwrite whatever is there.\"\n  (interactive)\n  (uniline--record-undo-rectangle-selection)\n  (uniline--operate-on-rectangle\n   (let ((width  (- endx begx -1))\n         (height (- endy begy -1))\n         (mark-active nil))             ; otherwise brush is inactive\n     (goto-char beg)\n     (if (<= begx 0)\n         (setq width (1- width))         ; at leftmost side of buffer\n       (uniline-move-to-delta-column -1))\n     (let ((uniline-infinite-up↑ t))         ; forcefully add a line\n       (uniline-move-to-delta-line -1))        ; above the rectangular region\n     (if (eq uniline-brush :block)\n         (setq\n          width  (+ width  width  -1)\n          height (+ height height -1)\n          uniline--which-quadrant (uniline--char-to-4quadb ?▗)))\n     (uniline-move-to-column (1- begx))\n     (uniline-write-ri→ width  force)\n     (uniline-write-dw↓ height force)\n     (uniline-write-lf← width  force)\n     (when (> begx 0)\n       (uniline-write-up↑ height force)\n       (setq begx (1- begx)))\n     (setq endx (1+ endx))\n     (if (eq begy 0)                    ; an additional line was needed\n         (if uniline-infinite-up↑\n             (setq endy (+ 2 endy))\n           (setq endy (1+ endy))\n           (goto-char (point-min))\n           (delete-line))\n       (setq begy (1- begy))            ; no additionl line\n       (setq endy (1+ endy))))))\n\n(defun uniline-overwrite-outer-rectangle ()\n  \"Draws a rectangle outside a rectangular selection.\nUse the current brush style, which may be thin, thick,\ndouble line, block, or eraser.\nOverwrite whatever is there.\"\n  (interactive)\n  (uniline-draw-outer-rectangle t))\n\n(defun uniline-copy-rectangle ()\n  \"Copy the selected rectangle in the kill storage.\"\n  (interactive)\n  (rectangle-mark-mode -1)\n  (copy-rectangle-as-kill (region-beginning) (region-end))\n  (deactivate-mark))\n\n(defun uniline-kill-rectangle ()\n  \"Kill the selected rectangle.\nIt differs from the standard Emacs `kill-rectangle'\nin that it leaves a rectangle of space characters.\"\n  (interactive)\n  (uniline--record-undo-rectangle-selection)\n  (copy-rectangle-as-kill (region-beginning) (region-end))\n  (clear-rectangle        (region-beginning) (region-end))\n  (deactivate-mark))\n\n(defun uniline-yank-rectangle ()\n  \"Insert a previously cut rectangle.\nIt differs from the standard Emacs `yank-rectangle'\nin that it overwrites the rectangle.\"\n  (interactive)\n  (uniline--record-undo-rectangle-selection)\n  (uniline--operate-on-rectangle\n   (goto-char beg)\n   (cl-loop\n    for line in killed-rectangle\n    do\n    (cl-loop\n     for char across line\n     do\n     (unless (eq char ? )\n       (uniline--insert-char char))\n     (uniline-move-to-delta-column 1))\n    (uniline-move-to-column begx)\n    (uniline-move-to-delta-line 1))\n   (setq endx (+ begx (length (car killed-rectangle))))\n   (setq endy (+ begy (length killed-rectangle)))))\n\n;;;╭──────────────╮\n;;;│Text direction│\n;;;╰──────────────╯\n\n(defvar-local uniline-text-direction\n    (uniline-direction-ri→)\n  \"Direction of text insertion.\nIt can be any of the 4 values of\n`uniline-direction-up↑' `-ri→' `-dw↓' `-lf←'\nwhich means that typing a key on the keyboard moves the cursor\nin this direction.\nIt can also be nil, which means that uniline makes no tweaking of\nthe natural cursor movement upon insertion.\")\n\n(defvar-local uniline--mode-line-brush nil\n  \"The current brush suitable for display in the `:lighter'.\")\n\n(defvar-local uniline--mode-line-dir nil\n  \"The current text direction suitable for ddisplay in the `:lighter'.\")\n\n(defun uniline--update-mode-line ()\n  \"Computes the string which appears in the mode-line.\"\n  (setq uniline--mode-line-dir\n\t(uniline--switch-with-table uniline-text-direction\n\t  (nil                   \" \")\n\t  (uniline-direction-up↑ \"↑\")\n\t  (uniline-direction-ri→ \"→\")\n\t  (uniline-direction-dw↓ \"↓\")\n\t  (uniline-direction-lf← \"←\"))\n        uniline--mode-line-brush\n\t(uniline--switch-with-cond uniline-brush\n\t  (nil    \" \")\n\t  (0      \"/\")\n\t  (1\n           (uniline--switch-with-table uniline-brush-dots\n             (0   \"┼\")\n             (1   \"┆\")\n             (2   \"┊\")))\n\t  (2\n           (uniline--switch-with-table uniline-brush-dots\n             (0   \"╋\")\n             (1   \"┇\")\n             (2   \"┋\")))\n\t  (3      \"╬\")\n\t  (:block \"▞\")))\n  (force-mode-line-update))\n\n(defun uniline--post-self-insert ()\n  \"Change the cursor movement after `self-insert-command'.\nUsually the cursor moves to the right.\nSometimes to the left for some locales, but this is not currently handled.\nThis hook fixes the cursor movement according to `uniline-text-direction'\"\n  (let* ((pn\n          (cond\n           ((numberp current-prefix-arg)\n            current-prefix-arg)\n           ((and\n             (consp current-prefix-arg)\n             (numberp (car current-prefix-arg)))\n            (car current-prefix-arg))\n           ((null current-prefix-arg)\n            1)\n           (t (error \"current-prefix-arg = %S\" current-prefix-arg))))\n         (mn (- pn)))\n    (uniline--switch-with-cond uniline-text-direction\n      (uniline-direction-ri→ ()) ;; nothing to fix\n      (uniline-direction-lf←\n       (forward-char mn)\n       (uniline-move-to-delta-column mn))\n      (uniline-direction-dw↓\n       (forward-char mn)\n       (uniline-move-to-delta-line pn))\n      (uniline-direction-up↑\n       (forward-char mn)\n       (uniline-move-to-delta-line mn))\n      (nil))))\n\n(defun uniline-text-direction-up↑ ()\n  \"Set text insertion direction up↑.\"\n  (interactive)\n  (setq uniline-text-direction (uniline-direction-up↑))\n  (uniline--update-mode-line))\n(defun uniline-text-direction-ri→ ()\n  \"Set text insertion direction right→.\"\n  (interactive)\n  (setq uniline-text-direction (uniline-direction-ri→))\n  (uniline--update-mode-line))\n(defun uniline-text-direction-dw↓ ()\n  \"Set text insertion direction down↓.\"\n  (interactive)\n  (setq uniline-text-direction (uniline-direction-dw↓))\n  (uniline--update-mode-line))\n(defun uniline-text-direction-lf← ()\n  \"Set text insertion direction left←.\"\n  (interactive)\n  (setq uniline-text-direction (uniline-direction-lf←))\n  (uniline--update-mode-line))\n\n;;;╭───────────────────────────╮\n;;;│Macro calls in 4 directions│\n;;;╰───────────────────────────╯\n\n(defvar-local uniline--directional-macros\n    (make-vector 8 nil)\n  \"A cache handling 4 versions of the current macro in 4 directions.\nIt is needed to avoid repeatidly re-creating a new directional macro\nfrom the recorded macro.\nThere are 4 entries indexed by\n`uniline-direction-up↑' `-ri→' `-dw↓' `-lf←'\nEach entry has 2 slots:\n- the recorded macro (a vector of key strokes)\n- the twisted macro (the same vector with <up> <right> and sisters twisted).\")\n\n(eval-when-compile ; not used at runtime\n  (defconst uniline--directional-keystrokes-table\n    `(;; ╭───keystroke as used in keyboard macros\n      ;; │           ╭─direction of the keystroke\n      ;; │           │   ╭─shift-control modifiers\n      ;; │           │   ╰─────────────────╮\n      ;; ▽           ▽                     ▽\n      (  up    . ,(+ uniline-direction-up↑ 0))\n      (  right . ,(+ uniline-direction-ri→ 0))\n      (  down  . ,(+ uniline-direction-dw↓ 0))\n      (  left  . ,(+ uniline-direction-lf← 0))\n      (S-up    . ,(+ uniline-direction-up↑ 4))\n      (S-right . ,(+ uniline-direction-ri→ 4))\n      (S-down  . ,(+ uniline-direction-dw↓ 4))\n      (S-left  . ,(+ uniline-direction-lf← 4))\n      (C-up    . ,(+ uniline-direction-up↑ 8))\n      (C-right . ,(+ uniline-direction-ri→ 8))\n      (C-down  . ,(+ uniline-direction-dw↓ 8))\n      (C-left  . ,(+ uniline-direction-lf← 8)))))\n\n(uniline--defconst-hash-table uniline--keystroke-to-dir-shift\n  (eval-when-compile uniline--directional-keystrokes-table)\n  16 'eq\n  \"Hashtable to convert a directional keystroke into Uniline constants.\")\n;; it is impossible to guaranty that the table will stay collision-less\n;; because keys are symbols, whose hash-values may change from\n;; one Emacs session to another\n\n(defconst uniline--dir-shift-to-keystroke\n  (eval-when-compile ;; not needed at runtime\n    (cl-loop\n     with vec = (make-vector\n                 (1+\n                  (cl-loop\n                   for x in uniline--directional-keystrokes-table\n                   maximize (cdr x)))\n                 nil)\n     for r in uniline--directional-keystrokes-table\n     do (aset vec (cdr r) (car r))\n     finally return vec))\n  \"Convert Uniline directional constants back into keystrokes.\")\n\n(defun uniline-call-macro-in-direction (dir)\n  \"Call last keybord macro twisted in DIR direction.\nA twisted version of the last keybord macro is created, unless\nit is already present in the `uniline--directional-macros' cache\"\n  (interactive)\n  (let*\n      ((uniline-text-direction dir)\n       (dir2 (* 2 dir))\n       (last-kbd-macro\n        (or (and (eq (aref uniline--directional-macros dir2) last-kbd-macro)\n                 (aref uniline--directional-macros (1+ dir2)))\n            (progn\n              (aset uniline--directional-macros dir2 last-kbd-macro)\n              (aset uniline--directional-macros (1+ dir2)\n                    (cl-loop\n                     with result = (vconcat last-kbd-macro)\n                     for r across-ref result\n                     for y = (gethash r uniline--keystroke-to-dir-shift)\n                     if y\n                     do (setf\n                         r\n                         (aref\n                          uniline--dir-shift-to-keystroke\n                          (logior\n                           (logand (+ y dir 3) 3)\n                           (logand y (eval-when-compile (lognot 3))))))\n                     finally return result))))))\n    (execute-kbd-macro last-kbd-macro 1)))\n\n;; Run the following cl-loop to automatically write a bunch\n;; of 4 interactive functions\n;; They have little value, except to be an interface between\n;; `easy-menu-define', `defhydra', `transient-define-prefix',\n;; and the real function `uniline-call-macro-in-direction'\n\n(when nil\n  (insert \"\\n;; BEGIN -- Automatically generated\\n\")\n  (cl-loop\n   for dir in '(\"up↑\" \"ri→\" \"dw↓\" \"lf←\")\n   do\n    (cl-prettyprint\n     `(defun ,(intern (format \"uniline-call-macro-in-direction-%s\" dir)) ()\n        ,(format \"Call macro in direction %s.\" dir)\n        (interactive)\n        (uniline-call-macro-in-direction (,(intern (format \"uniline-direction-%s\" dir)))))))\n  (insert \"\\n;; END -- Automatically generated\\n\"))\n\n;; BEGIN -- Automatically generated\n\n(defun uniline-call-macro-in-direction-up↑ nil\n  \"Call macro in direction up↑.\"\n  (interactive)\n  (uniline-call-macro-in-direction (uniline-direction-up↑)))\n(defun uniline-call-macro-in-direction-ri→ nil\n  \"Call macro in direction ri→.\"\n  (interactive)\n  (uniline-call-macro-in-direction (uniline-direction-ri→)))\n(defun uniline-call-macro-in-direction-dw↓ nil\n  \"Call macro in direction dw↓.\"\n  (interactive)\n  (uniline-call-macro-in-direction (uniline-direction-dw↓)))\n(defun uniline-call-macro-in-direction-lf← nil\n  \"Call macro in direction lf←.\"\n  (interactive)\n  (uniline-call-macro-in-direction (uniline-direction-lf←)))\n;; END -- Automatically generated\n\n;;;╭───────────────────────────╮\n;;;│High level brush management│\n;;;╰───────────────────────────╯\n\n(defun uniline-set-brush-nil ()\n  \"Change the current style of line to nothing.\nIt means that cursor movements do not trace anything.\"\n  (interactive)\n  (setq uniline-brush nil)\n  (uniline--update-mode-line))\n\n(defun uniline-set-brush-0 ()\n  \"Change the current style of line to the eraser.\nIt means that moving the cursor horizontally erase horizontal\nlines, and moving vertically erase vertical lines.  Characters\nother than lines or arrows are not touched.\"\n  (interactive)\n  (setq uniline-brush 0)\n  (uniline--update-mode-line))\n\n(defun uniline-set-brush-0dots ()\n  \"Change the current style of line plain.\"\n  (interactive)\n  (setq uniline-brush-dots 0)\n  (uniline--update-mode-line))\n\n(defun uniline-set-brush-3dots ()\n  \"Change the current style of line 3 dots vertical, 2 dots horizontal.\"\n  (interactive)\n  (setq uniline-brush-dots 1)\n  (uniline--update-mode-line))\n\n(defun uniline-set-brush-4dots ()\n  \"Change the current style of line 4 dots vertical & horizontal.\"\n  (interactive)\n  (setq uniline-brush-dots 2)\n  (uniline--update-mode-line))\n\n(defun uniline-set-brush-dot-toggle ()\n  \"Toggle dotted lines: plain → 3-2-dots → 4-4-dots → plain.\"\n  (interactive)\n  (setq uniline-brush-dots (% (1+ uniline-brush-dots) 3))\n  (uniline--update-mode-line))\n\n(defun uniline-set-brush-1 ()\n  \"Change the current style of line to a single thin line╶─╴.\"\n  (interactive)\n  (setq uniline-brush 1)\n  (uniline--update-mode-line))\n\n(defun uniline-set-brush-2 ()\n  \"Change the current style of line to a single thick line╺━╸.\"\n  (interactive)\n  (setq uniline-brush 2)\n  (uniline--update-mode-line))\n\n(defun uniline-set-brush-3 ()\n  \"Change the current style of line to a double line╺═╸.\"\n  (interactive)\n  (setq uniline-brush 3)\n  (uniline--update-mode-line))\n\n(defun uniline-set-brush-block ()\n  \"Change the current style of line to blocks ▙▄▟▀.\"\n  (interactive)\n  (setq uniline-brush :block)\n  (uniline--update-mode-line))\n\n;;;╭─────────────────────────────────────╮\n;;;│High level arrows & glyphs management│\n;;;╰─────────────────────────────────────╯\n\n(defun uniline--insert-glyph (letter back repeat)\n  \"Insert or modify a glyph.\nLETTER is any of\n `a' for arrows\n `s' for squares\n `x' for crosses\n `o' for o-shapes\n `g' for shades of grey\nIf a glyph of that category is already there under (point),\nit is modified with the next glyph in the same category.\nIf not, whatever character under (point) is overwritten\nwith the first glyph in the required category.\nBACK is t for backward scan,\nnil for forward scan in the same category.\nREPEAT is the usual universal argument for repetition\nof the same command.\"\n  (if (and repeat (< repeat 0))\n      (setq repeat (- repeat)\n            back (not back)))\n  (let ((line\n         ;; line is something like:\n         ;; (3 ((a ?↑ ?→ ?↓ ?←) (a ?▲ ?▶ ?▼ ?◀) …))\n         ;;  △      △  △  △  △  current character is\n         ;;  │      ╰──┴──┴──┴─╴one of those arrows\n         ;;  ╰─────────────────╴oriented in this direction\n         ;;\n         ;; line can also be like:\n         ;; (t ((a ?↕ ?↔ ?↕ ?↔) (a ?↑ ?→ ?↓ ?←) …))\n         ;;  △      △  △  △  △  current character is\n         ;;  │      ╰──┴──┴──┴─╴one of those arrows\n         ;;  ╰─────────────────╴with no definite orientation\n         ;;\n         ;; or line may be like:\n         ;; (nil ((s ?■) (s ?▫) …))\n         ;;   △       △         current character is\n         ;;   │       ╰────────╴this one\n         ;;   ╰────────────────╴and it has NO orientation\n         (gethash\n          (uniline--char-after)\n          (cond\n           (back uniline--glyphs-reverse-hash-bw)\n           (t    uniline--glyphs-reverse-hash-fw)))))\n    (if (and\n         line                  ; current character is one the known glyphs\n         (fixnump (car line))) ; it has a north-south-east-west orientation\n        (setq uniline--arrow-direction (car line)))\n    (setq line\n          (if line\n              (cddr line)\n            (if back uniline--glyphs-bw uniline--glyphs-fw)))\n    (setq line\n          (cl-loop\n           for line2 on line\n           if (or (null (caar line2)) ; currently useless\n                  (eq (caar line2) letter))\n           return line2\n           if (eq (cdr line2) line) return nil))\n    (when line\n      (setq line (nthcdr (1- (or repeat 1)) line))\n      (uniline--insert-char\n       (if (cddar line)\n           (nth uniline--arrow-direction (cdar line))\n         (cadar line))))))\n\n;; Run the following cl-loop to automatically write a bunch\n;; of 8 interactive functions\n;; They have little value, except to be an interface between\n;; `easy-menu-define', `defhydra', `transient-define-prefix',\n;; and the real function `uniline--insert-glyph'\n\n(when nil\n  (insert \"\\n;; BEGIN -- Automatically generated\\n\")\n  (cl-loop\n   for fb in '(f b)\n   do\n   (cl-loop\n    for shape in\n    '( (a . \"arrow\" )\n       (s . \"square\")\n       (o . \"oshape\")\n       (x . \"cross\" )\n       (g . \"grey\"  ))\n    do\n    (cl-prettyprint\n     `(defun ,(intern (format \"uniline-insert-%sw-%s\" fb (cdr shape))) (repeat)\n        ,(format \"Insert or modify a glyph of type %s in %s mode.\nREPEAT cycles through glyphs list that many times, default to 1.\nSee `uniline--insert-glyph'.\"\n                 (cdr shape)\n                 (pcase fb ('f \"forward\") ('b \"backward\")))\n        (interactive \"P\")\n        (uniline--insert-glyph ',(car shape) ,(eq fb 'b) repeat)))))\n  (insert \"\\n;; END -- Automatically generated\\n\"))\n\n;; BEGIN -- Automatically generated\n\n(defun uniline-insert-fw-arrow (repeat)\n  \"Insert or modify a glyph of type arrow in forward mode.\nREPEAT cycles through glyphs list that many times, default to 1.\nSee `uniline--insert-glyph'.\"\n  (interactive \"P\")\n  (uniline--insert-glyph 'a nil repeat))\n(defun uniline-insert-fw-square (repeat)\n  \"Insert or modify a glyph of type square in forward mode.\nREPEAT cycles through glyphs list that many times, default to 1.\nSee `uniline--insert-glyph'.\"\n  (interactive \"P\")\n  (uniline--insert-glyph 's nil repeat))\n(defun uniline-insert-fw-oshape (repeat)\n  \"Insert or modify a glyph of type oshape in forward mode.\nREPEAT cycles through glyphs list that many times, default to 1.\nSee `uniline--insert-glyph'.\"\n  (interactive \"P\")\n  (uniline--insert-glyph 'o nil repeat))\n(defun uniline-insert-fw-cross (repeat)\n  \"Insert or modify a glyph of type cross in forward mode.\nREPEAT cycles through glyphs list that many times, default to 1.\nSee `uniline--insert-glyph'.\"\n  (interactive \"P\")\n  (uniline--insert-glyph 'x nil repeat))\n(defun uniline-insert-fw-grey (repeat)\n  \"Insert or modify a glyph of type grey in forward mode.\nREPEAT cycles through glyphs list that many times, default to 1.\nSee `uniline--insert-glyph'.\"\n  (interactive \"P\")\n  (uniline--insert-glyph 'g nil repeat))\n(defun uniline-insert-bw-arrow (repeat)\n  \"Insert or modify a glyph of type arrow in backward mode.\nREPEAT cycles through glyphs list that many times, default to 1.\nSee `uniline--insert-glyph'.\"\n  (interactive \"P\")\n  (uniline--insert-glyph 'a t repeat))\n(defun uniline-insert-bw-square (repeat)\n  \"Insert or modify a glyph of type square in backward mode.\nREPEAT cycles through glyphs list that many times, default to 1.\nSee `uniline--insert-glyph'.\"\n  (interactive \"P\")\n  (uniline--insert-glyph 's t repeat))\n(defun uniline-insert-bw-oshape (repeat)\n  \"Insert or modify a glyph of type oshape in backward mode.\nREPEAT cycles through glyphs list that many times, default to 1.\nSee `uniline--insert-glyph'.\"\n  (interactive \"P\")\n  (uniline--insert-glyph 'o t repeat))\n(defun uniline-insert-bw-cross (repeat)\n  \"Insert or modify a glyph of type cross in backward mode.\nREPEAT cycles through glyphs list that many times, default to 1.\nSee `uniline--insert-glyph'.\"\n  (interactive \"P\")\n  (uniline--insert-glyph 'x t repeat))\n(defun uniline-insert-bw-grey (repeat)\n  \"Insert or modify a glyph of type grey in backward mode.\nREPEAT cycles through glyphs list that many times, default to 1.\nSee `uniline--insert-glyph'.\"\n  (interactive \"P\")\n  (uniline--insert-glyph 'g t repeat))\n;; END -- Automatically generated\n\n(eval-when-compile ; not needed at runtime\n  (defmacro uniline--rotate-arrow (dir)\n    \"Rotate an arrow, or tweak a 4halfs or a 4quadb.\n- If character under point is a known arrow, turn it in DIR direction.\n- If character under point is a combination of 4halfs lines,\nthen change the 4half segment pointing in the DIR direction.\nRepeateadly changing in the same direction cycles through at most\n4 characters (with thin, thick, double, or no line at all in DIR\ndirection). Sometimes, the cycle is shorter than 4, because UNICODE\ndoes not define all combinations of 4halfs when one of them is a\ndouble line.\n- If character under point is a combination of 4quadb blocks,\nthen flip the block in DIR direction on-and-off. As the blocks are\nin the 4 corners of the character, DIR cannot point exactly to a block.\nInstead DIR is twisted 45° from the actual direction of the block.\"\n    (setq dir (eval dir)) ; turn symbol like --direction-dw↓ to number like 2\n    (let* ((dir1 (1+ dir))\n           (ash3dir2 (uniline--shift-4half 3 dir)) ;; 3 is a bit-mask\n           (ash1dir2 (uniline--shift-4half 1 dir)) ;; 1 is an increment\n           (notash3dir2 (lognot ash3dir2))\n           (dir4\n            (uniline--4quadb-pushed\n             (uniline--turn-left dir)\n             (uniline--4quadb-pushed\n              dir\n              (uniline--char-to-4quadb ?█) ; this is constant 15 = 0b1111\n              ))))\n      `(let ((pat                       ; pattern\n              (gethash (uniline--char-after)\n                       uniline--glyphs-reverse-hash-fw)))\n         (cond\n          ;; If (point) is on a directional arrow\n          ((car pat)\n           (uniline--insert-char        ; then change its direction\n            (nth ,dir1 (cadr pat)))\n           (setq uniline--arrow-direction ,dir))\n\n          ;; If point is on lines crossing\n          ((setq pat (gethash (uniline--char-after) uniline--char-to-4halfs))\n           (let ((patdir (logand pat ,   ash3dir2)) ; pattern in DIR\n                 (patnot (logand pat ,notash3dir2)) ; pattern not in DIR\n                 patnew)                            ; new pattern to insert\n             (while               ; search for valid UNICODE\n                 (progn           ; with one 4half segment changed\n                   (setq patdir (logand (+ patdir ,ash1dir2) ,ash3dir2))\n                   (setq patnew (logior patnot patdir))\n                   (not\n                    (eq\n                     (gethash\n                      (uniline--4halfs-to-char-aref patnew)\n                      uniline--char-to-4halfs)\n                     patnew))))\n             (uniline--insert-4halfs patnew)))\n\n          ;; If point is on a quarter-char\n          ((setq pat (uniline--char-to-4quadb (uniline--char-after)))\n           (uniline--insert-4quadb (logxor pat ,dir4))))))))\n\n;; Run the following cl-loop to automatically write a bunch\n;; of 4 interactive functions\n;; They have little value, except to be an interface between\n;; `easy-menu-define', `defhydra', `transient-define-prefix',\n;; and the real function `uniline--rotate-arrow'\n\n(when nil\n  (insert \"\\n;; BEGIN -- Automatically generated\\n\")\n  (cl-loop\n   for dir in '(\"up↑\" \"ri→\" \"dw↓\" \"lf←\")\n   do\n    (cl-prettyprint\n     `(defun ,(intern (format \"uniline-rotate-%s\" dir)) ()\n        ,(format \"Rotate an arrow or tweak 4halfs.\nIf character under point is an arrow, turn it %s.\nIf character under point is a combination of 4halfs lines,\nthen change the 4half segment pointing %s.\"\n                 dir dir)\n        (interactive)\n        (uniline--rotate-arrow (,(intern (format \"uniline-direction-%s\" dir)))))))\n  (insert \"\\n;; END -- Automatically generated\\n\"))\n;; BEGIN -- Automatically generated\n\n(defun uniline-rotate-up↑ nil\n  \"Rotate an arrow or tweak 4halfs.\nIf character under point is an arrow, turn it up↑.\nIf character under point is a combination of 4halfs lines,\nthen change the 4half segment pointing up↑.\"\n  (interactive)\n  (uniline--rotate-arrow (uniline-direction-up↑)))\n(defun uniline-rotate-ri→ nil\n  \"Rotate an arrow or tweak 4halfs.\nIf character under point is an arrow, turn it ri→.\nIf character under point is a combination of 4halfs lines,\nthen change the 4half segment pointing ri→.\"\n  (interactive)\n  (uniline--rotate-arrow (uniline-direction-ri→)))\n(defun uniline-rotate-dw↓ nil\n  \"Rotate an arrow or tweak 4halfs.\nIf character under point is an arrow, turn it dw↓.\nIf character under point is a combination of 4halfs lines,\nthen change the 4half segment pointing dw↓.\"\n  (interactive)\n  (uniline--rotate-arrow (uniline-direction-dw↓)))\n(defun uniline-rotate-lf← nil\n  \"Rotate an arrow or tweak 4halfs.\nIf character under point is an arrow, turn it lf←.\nIf character under point is a combination of 4halfs lines,\nthen change the 4half segment pointing lf←.\"\n  (interactive)\n  (uniline--rotate-arrow (uniline-direction-lf←)))\n;; END -- Automatically generated\n\n;;;╭───────╮\n;;;│Contour│\n;;;╰───────╯\n\n(defcustom uniline-contour-max-steps 10000\n  \"Maximum number of steps before stopping the contour algorithm.\nThis is the number of characters drawn with lines╶─┬─══╦══━━┳━╸,\nor twice the number of characters drawn with block-characters ▝▙.\nThis limit may be reached if the text is huge. Of course, the\nalgorithm may be started again for an additional 10000 steps.\"\n  :type 'natnum\n  :group 'uniline)\n\n(defun uniline-contour (&optional force)\n  \"Draw a contour arround a block of characters.\nA block of characters is a contiguous set of non-blank characters.\nFor the sake of the contour, a non-blank character is any character\nnot in the 4halfs set.\nThe style of the contour is determined by the current brush.\nThis includes possibly the eraser, which erases an actual contour.\nWhen FORCE is not nil, overwrite whatever is in the buffer.\n ╭──────╮\n │AAAAAA╰─╮\n ╰─╮AAAAAA│\n   │AA╭───╯\n   ╰──╯\n\"\n  (interactive)\n  (while (not (uniline--blank-after (point)))\n    (uniline-move-to-delta-column 1))\n\n  (let ((dir)\n        (start (point-marker))\n        (q uniline--which-quadrant)\n        (n 0))\n    ;; look for a surrounding wall successively in directions lf← up↑ ri→ dw↓.\n    ;; stop as soon as hitting a wall.\n    ;; then initialize dir-ection turning left from the wall.\n    ;; so for instance, if there is empty space toward west lf←,\n    ;; and a wall toward north up↑, then initial dir will be set to west lf←.\n    ;;      \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n    ;;      ╺━━━━━━╳━━━━━━┓ \\\n    ;;             ↑      ┃ \\\n    ;;          ╭──╯──╮   ┃ \\\n    ;;          │     │   ┃ \\\n    ;;        ←─╮  ●  ╰──→╳ \\\n    ;;          ╰──╯  │   ┃ \\\n    ;;           ╭─╭──╯   ┃ \\\n    ;;   error╶──╯ ↓      ┃ \\\n    ;;                    ╹ \\\n    (and\n     (progn\n       (setq dir (uniline--turn-left (uniline-direction-lf←)))\n       (uniline--blank-neighbour1    (uniline-direction-lf←)))\n     (progn\n       (setq dir (uniline--turn-left (uniline-direction-up↑)))\n       (uniline--blank-neighbour1    (uniline-direction-up↑)))\n     (progn\n       (setq dir (uniline--turn-left (uniline-direction-ri→)))\n       (uniline--blank-neighbour1    (uniline-direction-ri→)))\n     (progn\n       (setq dir (uniline--turn-left (uniline-direction-dw↓)))\n       (uniline--blank-neighbour1    (uniline-direction-dw↓)))\n     (error \"No border of any shape found around point\"))\n    (if (eq uniline-brush :block)\n        (setq\n         q\n         (setq\n          uniline--which-quadrant\n          ;; this huge expression folds to just:\n          ;; (aref [8 4 1 2] dir)\n          ;; which is as fast as can be at runtime\n          (uniline--switch-with-table dir\n            (lambda (dir)\n              (uniline--4quadb-pushed\n               (uniline--reverse-direction dir)\n               (uniline--4quadb-pushed\n                (uniline--turn-right dir)\n                (uniline--char-to-4quadb ?█))))\n            (uniline-direction-up↑)\n            (uniline-direction-ri→)\n            (uniline-direction-dw↓)\n            (uniline-direction-lf←)))))\n    (while\n        (progn\n          ;; change current dir-ection in whichever direction there is no wall.\n          ;; in this example dir pointed north up↑, and was changed to west lf←\n          ;;       \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n          ;;       ╺━━━━━━━━╳━━━━━━┓\\\\\\\\\\\n          ;;                ↑      ┃\\\\\\\\\\\n          ;;            ╭───╰───╮  ┃\\\\\\\\\\\n          ;;            │   ╭─╮ │  ┃\\\\\\\\\\\n          ;;  no wall ←─╯   ● │ ╭─→╳\\\\\\\\\\\n          ;;            │     ╰─╯  ╹\\\\\\\\\\\n          ;;            ╰───╮───╮     \\\\\\\n          ;;                ↓   ╰error  \\\n          (let ((d dir))\n            (or\n             (uniline--blank-neighbour (setq dir (uniline--turn-right dir)))\n             (uniline--blank-neighbour (setq dir d))\n             (uniline--blank-neighbour (setq dir (uniline--turn-left dir)))\n             (uniline--blank-neighbour (setq dir (uniline--turn-left dir)))\n             (error \"Cursor is surrounded by walls\")))\n          ;; bumping into the left or upper borders?\n          ;; move the (point) silently as if drawing outside the buffer\n          ;; until finding a blank (or eolp)) which allows the (point)\n          ;; to re-enter the actual buffer.\n          (cond\n           ;; bump into the left border\n           ((and\n             (eq dir (uniline-direction-lf←))\n             (uniline--at-border-p uniline-direction-lf←)\n             (or (not (eq uniline-brush :block))\n                 (uniline--blank-neighbour4 uniline-direction-ri→)))\n            (while\n                (and\n                 (eq (forward-line -1) 0)\n                 (not (uniline--blank-after (point)))))\n            (if (uniline--at-border-p uniline-direction-up↑)\n                (setq dir (uniline-direction-up↑)\n                      uniline--which-quadrant (uniline--char-to-4quadb ?▝))\n              (setq dir (uniline-direction-ri→)\n                    uniline--which-quadrant (uniline--char-to-4quadb ?▖))))\n           ;; bump into the upper border\n           ((and\n             (not uniline-infinite-up↑)\n             (eq dir (uniline-direction-up↑))\n             (uniline--at-border-p uniline-direction-up↑)\n             (or (not (eq uniline-brush :block))\n                 (uniline--blank-neighbour4 uniline-direction-dw↓)))\n            (while\n                (progn\n                  (forward-char 1)\n                  (not (uniline--blank-after (point)))))\n            (setq dir (uniline-direction-dw↓))\n            (setq uniline--which-quadrant (uniline--char-to-4quadb ?▘)))\n           ;; not bumping into a border, so just draw\n           (t\n            (uniline--write dir force)\n            (setq n (1+ n))))\n          (and\n           (not\n            (and (eq (point) (marker-position start))\n                 (or (not (eq uniline-brush :block))\n                     (eq uniline--which-quadrant q))))\n           (< n uniline-contour-max-steps))))\n    (set-marker start nil)\n    (message \"drew a %s steps contour\" n)))\n\n;;;╭─────────────────────────────╮\n;;;│Dashed lines and other styles│\n;;;╰─────────────────────────────╯\n\n(defun uniline--change-style-hash (fromto)\n  \"Change some characters to similar characters in a rectangular selection.\nFROMTO is a hash-table telling what are the characters replacements.\nThe changes are reversible in a single undo command.\"\n    (interactive)\n    (uniline--operate-on-rectangle\n     (cl-loop\n      for y from begy below endy\n      do\n      (uniline-move-to-line y)\n      (cl-loop\n       for x from begx below endx\n       do\n       (uniline-move-to-column x)\n       (let ((rep (gethash (uniline--char-after) fromto)))\n         (if rep\n             (uniline--insert-char rep)))))))\n\n(uniline--defconst-hash-table uniline--char-to-dot-3-2-char\n  '((?─ . ?╌)\n    (?┄ . ?╌)\n    (?┈ . ?╌)\n    (?━ . ?╍)\n    (?┅ . ?╍)\n    (?┉ . ?╍)\n    (?═ . ?╍)\n    (?│ . ?┆)\n    (?╎ . ?┆)\n    (?┊ . ?┆)\n    (?┃ . ?┇)\n    (?╏ . ?┇)\n    (?┋ . ?┇)\n    (?║ . ?┇))\n  16 'eq\n  \"Convert to 3 vertical & 2 horizontal dashes\")\n\n(defun uniline-change-style-dot-3-2 ()\n  \"Change plain lines to dashed lines in a rectangular selection.\nIt retains thickness of the lines.\nVertical lines will be 3-dots, while horizontal will be 2-dots.\"\n  (interactive)\n  (uniline--record-undo-rectangle-selection)\n  (uniline--change-style-hash uniline--char-to-dot-3-2-char))\n\n(uniline--defconst-hash-table uniline--char-to-dot-4-4-char\n  '((?─ . ?┈)\n    (?╌ . ?┈)\n    (?┄ . ?┈)\n    (?━ . ?┉)\n    (?╍ . ?┉)\n    (?┅ . ?┉)\n    (?═ . ?┉)\n    (?│ . ?┊)\n    (?╎ . ?┊)\n    (?┆ . ?┊)\n    (?┃ . ?┋)\n    (?╏ . ?┋)\n    (?┇ . ?┋)\n    (?║ . ?┋))\n  32 'eq\n  \"Convert to 4 vertical & 4 horizontal dashes\")\n\n(defun uniline-change-style-dot-4-4 ()\n  \"Change plain lines to dashed lines in a rectangular selection.\nIt retains thickness of the lines.\nVertical and horizontzl lines will be 4-dots.\"\n  (interactive)\n  (uniline--record-undo-rectangle-selection)\n  (uniline--change-style-hash uniline--char-to-dot-4-4-char))\n\n(uniline--defconst-hash-table uniline--char-to-standard-char\n  '((?╌ . ?─)\n    (?┄ . ?─)\n    (?┈ . ?─)\n    (?╍ . ?━)\n    (?┅ . ?━)\n    (?┉ . ?━)\n    (?╎ . ?│)\n    (?┆ . ?│)\n    (?┊ . ?│)\n    (?╏ . ?┃)\n    (?┇ . ?┃)\n    (?┋ . ?┃)\n    (?┐ . ?╮)\n    (?└ . ?╰)\n    (?┌ . ?╭)\n    (?┘ . ?╯))\n  64 'equal ; `equal' instead of `eq' to achieve collision-less table\n  \"Convert back to base-line style\")\n\n(eval-when-compile ;; not used at runtime\n  (defmacro uniline--infer-neighbour-4half (dir)\n    \"Return a 4half to seamlessly connect with neighbour in DIR direction.\nReturn 0 if there is no neighbour, or if neighbour does not look like\na connecting line or glyph.\"\n    (setq dir (eval dir))\n    (let\n        ((isvert (memq dir `(,uniline-direction-up↑ ,uniline-direction-dw↓)))\n         (ishorz (memq dir `(,uniline-direction-lf← ,uniline-direction-ri→)))\n         ;; 1 is thin   half-line in direction DIR ──▷\n         (shift1 (uniline--shift-4half 1 dir))\n         ;; 1 is double half-line in direction DIR ══▷\n         (shift3 (uniline--shift-4half 3 dir)))\n\n      `(let ((neighbour (uniline--neighbour-point ,dir)))\n         (if (not neighbour)\n             0\n           (setq neighbour (uniline--char-after neighbour))\n           (let ((b (gethash neighbour uniline--char-to-4halfs)))\n             (cond\n              (b\n               (uniline--extract-reverse-4half b ,dir))\n              ((memq neighbour '(?+ ?\\\\ ?/ ?' ?`))\n               ,shift1)\n              ((eq neighbour ?#)\n               ,shift3)\n              ,@(when isvert\n                  `(((memq neighbour\n                           '(?^ ?v ?V ?| ?△ ?▽ ?▲ ?▼ ?↑ ?↓ ?▵ ?▿ ?▴ ?▾ ?↕))\n                     ,shift1)\n                    ((eq neighbour ?\\\")\n                     ,shift3)))\n              ,@(when ishorz\n                  `(((memq neighbour\n                           '(?> ?< ?- ?_ ?▷ ?◁ ?▶ ?◀ ?→ ?← ?▹ ?◃ ?▸ ?◂ ?↔))\n                     ,shift1)\n                    ((eq neighbour ?=)\n                     ,shift3)))\n              (t 0))))))))\n\n(defun uniline-change-style-standard ()\n  \"Change fancy lines styles to standard ones in a rectangular selection.\nThis includes dashed lines, which become plain while preserving thickness,\nand hard corners which become rounded.\nAlso ASCII-art is converted to UNICODE-art.\"\n  (interactive)\n  (uniline--record-undo-rectangle-selection)\n  (uniline--change-style-hash uniline--char-to-standard-char)\n  ;; Hereafter, we handle ASCII characters used to draw sketches\n  ;; We distinguish two cases for those characters\n  ;; - when they should be left alone as ASCII chars\n  ;; - when they are part of a future UNICODE line\n  ;; ASCII chars are converted to UNICODE glyphs only when they\n  ;; are surrounded by UNICODE or ASCII characters that are\n  ;; themselves part of a sketch or will be.\n  ;; Surrounding is considered depending on the orientation of the\n  ;; char: Vertical, horizontal, or both.\n  ;; So for instance:\n  ;;   -  surrounding is left and right\n  ;;   |  surrounding is up and down\n  ;;   +  surrounding is in the 4 directions\n  ;; For UNICODE box drawing characters, an attempt is made to\n  ;; complete them with half lines so that they will connect\n  ;; seamlessly with their surrounding.\n  (uniline--operate-on-rectangle\n   (cl-loop\n    for y from begy below endy\n    do\n    (uniline-move-to-line y)\n    (cl-loop\n     for x from begx below endx\n     do\n     (uniline-move-to-column x)\n     (let ((char (uniline--char-after)))\n       (if (memq char\n                 '(?^ ?v ?V ?| ?\\\" ?- ?_ ?> ?< ?=\n                      ?+ ?/ ?\\\\ ?' ?` ?# ?o ?O ?* ?.))\n           (let*\n               ((4halfvert\n                 (logior\n                  (uniline--infer-neighbour-4half uniline-direction-up↑)\n                  (uniline--infer-neighbour-4half uniline-direction-dw↓)))\n                (4halfhorz\n                 (logior\n                  (uniline--infer-neighbour-4half uniline-direction-lf←)\n                  (uniline--infer-neighbour-4half uniline-direction-ri→)))\n                (4half (logior 4halfvert 4halfhorz))\n                (newchar\n                 (or\n                  (unless (eq 4halfvert 0)\n                    (uniline--switch-with-table char\n                      (?^ ?△)\n                      (?v ?▽)\n                      (?V ?▽)\n                      (?| ?│)\n                      (?\\\" ?║)))\n                  (unless (eq 4halfhorz 0)\n                    (uniline--switch-with-table char\n                      (?- ?─)\n                      (?_ ?─)\n                      (?> ?▷)\n                      (?< ?◁)\n                      (?= ?═)))\n                  (unless (eq 4half 0)\n                    (uniline--switch-with-table char\n                      (?O ?●)\n                      (?o ?◦)\n                      (?* ?●)\n                      (?. ?∙))))))\n             (if newchar\n                 (uniline--insert-char newchar)\n               (unless (eq 4half 0)\n                 (if (memq char '(?+ ?# ?/ ?\\\\ ?' ?`))\n                     (uniline--insert-4halfs 4half)))))))))))\n\n(uniline--defconst-hash-table uniline--char-to-hard-corner-char\n  '((?╮ . ?┐)\n    (?╰ . ?└)\n    (?╭ . ?┌)\n    (?╯ . ?┘))\n  4 'eq\n  \"Convert rounded corners to hard ones\")\n\n(defun uniline-change-style-hard-corners ()\n  \"Change rounded corners to hard corners in a rectangular selection.\nThis happens only for thin-lines corners, as UNICODE does not define\nthick-line or double-line rounded corners.\"\n  (interactive)\n  (uniline--record-undo-rectangle-selection)\n  (uniline--change-style-hash uniline--char-to-hard-corner-char))\n\n(uniline--defconst-hash-table uniline--char-to-thin-char\n  (eval-when-compile\n    (append\n     (cl-loop\n      for c being hash-keys of uniline--char-to-4halfs\n      using (hash-values v)\n      for 4halfs\n      = (uniline--pack-4halfs\n         (cl-loop\n          for u in (uniline--unpack-4halfs v)\n          collect (if (or (eq u 2) (eq u 3)) 1 u)))\n      unless (eq 4halfs v)\n      collect (cons c (uniline--4halfs-to-char-aref 4halfs)))\n     '((?┅ . ?┄)\n       (?┉ . ?┈)\n       (?╍ . ?╌)\n       (?┇ . ?┆)\n       (?┋ . ?┊)\n       (?▲ . ?△)\n       (?▶ . ?▷)\n       (?▼ . ?▽)\n       (?◀ . ?◁)\n       (?▴ . ?▵)\n       (?▸ . ?▹)\n       (?▾ . ?▿)\n       (?◂ . ?◃)\n       (?■ . ?□)\n       (?▪ . ?▫)\n       (?• . ?◦))))\n  256 'eq\n  \"Convert black or heavy characters to white or light ones\")\n\n(defun uniline-change-style-thin ()\n  \"Change bold lines and glyphs to thin ones.\nThis includes plain and dashed lines (e.g. ┴ to ┻, or ┅ to ┄)\nas well as glyphs (e.g. ■ to □ or ▼ to ▽).\"\n  (interactive)\n  (uniline--record-undo-rectangle-selection)\n  (uniline--change-style-hash uniline--char-to-thin-char))\n\n(uniline--defconst-hash-table uniline--char-to-thick-char\n  (eval-when-compile\n    (append\n     (cl-loop\n      for c being hash-keys of uniline--char-to-4halfs\n      using (hash-values v)\n      for 4halfs\n      = (uniline--pack-4halfs\n         (cl-loop\n          for u in (uniline--unpack-4halfs v)\n          collect (if (or (eq u 1) (eq u 3)) 2 u)))\n      unless (eq 4halfs v)\n      collect (cons c (uniline--4halfs-to-char-aref 4halfs)))\n     '((?┄ . ?┅)\n       (?┈ . ?┉)\n       (?╌ . ?╍)\n       (?┆ . ?┇)\n       (?┊ . ?┋)\n       (?△ . ?▲)\n       (?▷ . ?▶)\n       (?▽ . ?▼)\n       (?◁ . ?◀)\n       (?▵ . ?▴)\n       (?▹ . ?▸)\n       (?▿ . ?▾)\n       (?◃ . ?◂)\n       (?□ . ?■)\n       (?▫ . ?▪)\n       (?◦ . ?•))))\n  128 'equal ; `equal' instead of `eq' to achieve a small collision-less table\n  \"Convert white or light characters to black or heavy ones\")\n\n(defun uniline-change-style-thick ()\n  \"Change thin lines and glyphs to bold ones.\nThis includes plain and dashed lines (e.g. ┴ to ┻, or ┄ to ┅)\nas well as glyphs (e.g. □ to ■ or ▽ to ▼).\"\n  (interactive)\n  (uniline--record-undo-rectangle-selection)\n  (uniline--change-style-hash uniline--char-to-thick-char))\n\n(uniline--defconst-hash-table uniline--char-to-double-line\n  (eval-when-compile\n    (append\n     (cl-loop\n      for c being hash-keys of uniline--char-to-4halfs\n      using (hash-values v)\n      for 4halfs\n      = (uniline--pack-4halfs\n         (cl-loop\n          for u in (uniline--unpack-4halfs v)\n          collect (if (or (eq u 1) (eq u 2)) 3 u)))\n      unless (eq 4halfs v)\n      collect (cons c (uniline--4halfs-to-char-aref 4halfs)))\n     '((?┄ . ?═)\n       (?┅ . ?═)\n       (?┈ . ?═)\n       (?┉ . ?═)\n       (?╌ . ?═)\n       (?╍ . ?═)\n       (?┆ . ?║)\n       (?┇ . ?║)\n       (?┊ . ?║)\n       (?┋ . ?║))))\n  128 'eq\n  \"Convert any line to double line.\")\n\n(defun uniline-change-style-double ()\n  \"Change thin lines and bold lines to double ones.\nThis includes plain and dashed lines (e.g. ┻ to ╩, or ┄ to ═).\"\n  (interactive)\n  (uniline--record-undo-rectangle-selection)\n  (uniline--change-style-hash uniline--char-to-double-line))\n\n(defun uniline-aa2u-rectangle ()\n  \"Wrapper arround `aa2u-rectangle'.\"\n  (interactive)\n  (uniline--record-undo-rectangle-selection)\n  (if (functionp 'aa2u-rectangle)\n      (uniline--operate-on-rectangle\n       ;; here we use `eval' on purpose, to get a loose coupling\n       ;; with the `ascii-art-to-unicode' package; if not installed\n       ;; the native compiler may complain that `aa2u-rectangle'\n       ;; is not defined; no longer with `eval'.\n       ;; but long after compiling `uniline', if the\n       ;; `ascii-art-to-unicode' package is eventually installed,\n       ;; the `aa2u-rectangle' will be called without the need to\n       ;; recompile or native-recompile `uniline'.\n       (eval `(aa2u-rectangle ,beg ,end)))\n    (message \"Install the ascii-art-to-unicode package prior to using aa2u.\nIt is available on ELPA.\nOr use the '0 standard' style transformer instead.\")))\n\n;;;╭───────────────────────────╮\n;;;│Common to Hydra & Transient│\n;;;╰───────────────────────────╯\n\n(defun uniline-customize-face ()\n  \"Customize a temporary font to may-be set it for future sessions.\"\n  (interactive)\n  (customize-face-other-window 'default))\n\n(defun uniline--is-font (letter)\n  \"Check if current font is the one presented by LETTER.\"\n  (let ((name\n         (uniline--switch-with-table letter\n          (?d \"DejaVu\"                  )\n          (?u \"Unifont\"                 )\n          (?h \"Hack\"                    )\n          (?b \"JetBrain\"                )\n          (?c \"Cascadia\"                )\n          (?a \"Agave\"                   )\n          (?j \"Julia\"                   )\n          (?f \"FreeMono\"                )\n          (?i \"Iosevka Comfy Fixed\"     )\n          (?I \"Iosevka Comfy Wide Fixed\")\n          (?p \"Aporetic Sans Mono\"      )\n          (?P \"Aporetic Serif Mono\"     )\n          (?s \"Source Code\"             ))))\n    (and name (string-match name (frame-parameter nil 'font)))))\n\n(defun uniline--font-name-ticked (letter)\n  \"Return the name of the font presented by LETTER,\nwith a tick-glyph ▶ if current.\"\n  (funcall (if (uniline--is-font letter) #'cdr #'car)\n           (uniline--switch-with-table letter\n             (?d '(\" DejaVu\"          . \"▶DejaVu\"         ))\n             (?u '(\" Unifont\"         . \"▶Unifont\"        ))\n             (?h '(\" Hack\"            . \"▶Hack\"           ))\n             (?b '(\" JetBrains\"       . \"▶JetBrains\"      ))\n             (?c '(\" Cascadia\"        . \"▶Cascadia\"       ))\n             (?a '(\" Agave\"           . \"▶Agave\"          ))\n             (?j '(\" Julia\"           . \"▶Julia\"          ))\n             (?f '(\" FreeMono\"        . \"▶FreeMono\"       ))\n             (?i '(\" Iosevka\"         . \"▶Iosevka\"        ))\n             (?I '(\" Iosevka Wide\"    . \"▶Iosevka Wide\"   ))\n             (?p '(\" Aporetic Sans\"   . \"▶Aporetic Sans\"  ))\n             (?P '(\" Aporetic Serif\"  . \"▶Aporetic Serif\" ))\n             (?s '(\" Source Code Pro\" . \"▶Source Code Pro\")))))\n\n(when nil\n  ;; Those low-added-value functions are automatically generated.\n  ;; Their purpose is to avoid lambdas in the definition\n  ;; of Hydras and Tansients.\n  (insert \"\\n;; BEGIN -- Automatically generated\\n\")\n  (cl-loop\n   for f in\n   '((?d . \"DejaVu Sans Mono\"        )\n     (?u . \"Unifont\"                 )\n     (?h . \"Hack\"                    )\n     (?b . \"JetBrains Mono\"          )\n     (?c . \"Cascadia Mono\"           )\n     (?a . \"Agave\"                   )\n     (?j . \"JuliaMono\"               )\n     (?f . \"FreeMono\"                )\n     (?i . \"Iosevka Comfy Fixed\"     )\n     (?I . \"Iosevka Comfy Wide Fixed\")\n     (?p . \"Aporetic Sans Mono\"      )\n     (?P . \"Aporetic Serif Mono\"     )\n     (?s . \"Source Code Pro\"         ))\n   do\n   (insert\n    (format \"(defun uniline--set-font-%c ()\\n\" (car f)))\n   (insert \"  (interactive)\\n\")\n   (insert\n    (format \"  (set-frame-font \\\"%s\\\"))\\n\" (cdr f))))\n  (insert \"\\n;; END -- Automatically generated\\n\"))\n\n;; BEGIN -- Automatically generated\n(defun uniline--set-font-d ()\n  (interactive)\n  (set-frame-font \"DejaVu Sans Mono\"))\n(defun uniline--set-font-u ()\n  (interactive)\n  (set-frame-font \"Unifont\"))\n(defun uniline--set-font-h ()\n  (interactive)\n  (set-frame-font \"Hack\"))\n(defun uniline--set-font-b ()\n  (interactive)\n  (set-frame-font \"JetBrains Mono\"))\n(defun uniline--set-font-c ()\n  (interactive)\n  (set-frame-font \"Cascadia Mono\"))\n(defun uniline--set-font-a ()\n  (interactive)\n  (set-frame-font \"Agave\"))\n(defun uniline--set-font-j ()\n  (interactive)\n  (set-frame-font \"JuliaMono\"))\n(defun uniline--set-font-f ()\n  (interactive)\n  (set-frame-font \"FreeMono\"))\n(defun uniline--set-font-i ()\n  (interactive)\n  (set-frame-font \"Iosevka Comfy Fixed\"))\n(defun uniline--set-font-I ()\n  (interactive)\n  (set-frame-font \"Iosevka Comfy Wide Fixed\"))\n(defun uniline--set-font-p ()\n  (interactive)\n  (set-frame-font \"Aporetic Sans Mono\"))\n(defun uniline--set-font-P ()\n  (interactive)\n  (set-frame-font \"Aporetic Serif Mono\"))\n(defun uniline--set-font-s ()\n  (interactive)\n  (set-frame-font \"Source Code Pro\"))\n\n;; END -- Automatically generated\n\n(defun uniline--self-insert-+ ()\n  \"Wrapper over `self-insert-command' <kp-add>.\"\n  (interactive)\n  (self-insert-command 1 ?+))\n\n(defun uniline--self-insert-- ()\n  \"Wrapper over `self-insert-command' <kp-subtract>.\"\n  (interactive)\n  (self-insert-command 1 ?-))\n\n(defun uniline-text-direction-str ()\n  \"Return a textual representation of current text direction.\"\n  (interactive)\n  (uniline--switch-with-table uniline-text-direction\n    (uniline-direction-up↑ \"↑\")\n    (uniline-direction-ri→ \"→\")\n    (uniline-direction-dw↓ \"↓\")\n    (uniline-direction-lf← \"←\")))\n\n(defun uniline--rect-quit ()\n  \"Quit this hydra or transient.\"\n  (interactive)\n  (deactivate-mark))\n\n;;;╭────────────────────╮\n;;;│Language environment│\n;;;╰────────────────────╯\n\n;; Some language environments give a double width to some characters\n;; used by Uniline. For instance\n;;   M-x set-language-environment Chinese-BIG5\n;; gives a width of 2 to ─\n;; It does so through char-width-table, which is a global Emacs variable.\n;; So we fix that by patching (setq char-width-table …)\n;; to a new table which inherit from the original char-width-table,\n;; and set the witdh of all needed characters to 1.\n;; No attempt is done to revert the change on exiting uniline-mode,\n;; because this would create more problems than it solves.\n;; To revert char-width-table, just re-set the language environments:\n;;   C-x RET l Chinese-BIG5\n\n(defun uniline--fix-char-width-table ()\n  \"Create a descendent of char-width-table with characters widths set to 1.\nIt does so for all characters Uniline creates.\nThe process is lazy in the sense that if a character already has a width of 1,\nits entry in the table is left as is.\"\n  (let ((allchars))\n    (maphash ;; list characters like ╶─┴╮╶╼━┻━┳━═══╩╦╸\n     (lambda (key _val) (push key allchars))\n     uniline--char-to-4halfs)\n    (cl-loop ;; list characters like ▝▙▄█ ▗▄▖ ▖ ▘\n     for e across uniline--4quadb-to-char\n     do (push e allchars))\n    (let ((x uniline--glyphs-fw)) ;; warning: circular list\n      (while ;; list characters like ◁▲→□■·●╳\n          (progn\n            (setq allchars (append (cdr (car x)) allchars))\n            (not (eq (setq x (cdr x)) uniline--glyphs-fw)))))\n    (setq allchars (sort allchars #'<=))\n    (cl-loop ;; filter out characters already 1 in width, and duplicates\n     for  iter on allchars\n     for  curr  = (car iter)\n     with prev  = nil\n     do\n     (if (or\n          (eq prev curr)                       ;; duplicate\n          (eq (aref char-width-table curr) 1)) ;; width already 1\n         (setcar iter nil))\n     (setq prev curr))\n    (setq allchars (delq nil allchars))\n\n    (if allchars ;; patch only if there are characters to patch\n      (let ((ct (make-char-table nil)))\n        (nconc allchars (list nil))     ;; a last entry to close the algorithm\n        (cl-loop\n         for  curr in (cdr allchars)\n         with prev  = (car allchars)\n         with start = (car allchars)\n         do\n         (if (eq curr prev) (message \"duplicate cannot happen\"))\n         (unless (eq (1+ prev) curr)\n           (set-char-table-range ct (cons start prev) 1)\n           (setq start curr))\n         (setq prev curr))\n        (set-char-table-parent ct char-width-table)\n        (setq char-width-table ct)))))\n\n;;;╭─────────────╮\n;;;│Mouse support│\n;;;╰─────────────╯\n\n;; How it works?\n;; The 3 mouse-button-1 handling functions of Emacs are\n;; fairly complex. They are named:\n;;   mouse-set-point, mouse-drag-region, mouse-set-region.\n;;\n;; Here we just let them do whatever they want to. But first\n;; we intercept them, so that Uniline can add blank\n;; characters or lines if the mouse event falls outside the\n;; buffer.\n;;\n;; The mouse events provide a (point) position, which is\n;; wrong when outside the buffer. But they also provide a\n;; position in pixels along with the width and height in\n;; pixels of a typical character. This allows to reconstruct\n;; a hopefully accurate (point) position, which we re-inject\n;; in the mouse event in place of the wrong (point) position.\n;;\n;; Of course, things are tricky because the pixel-position is\n;; relative to the upper-left corner of the displayed window,\n;; while we need the buffer-position. We use the (window-start)\n;; function to get the line number under the upper-left corner.\n;; And the (scroll-left 0) function to get the column number\n;; of this corner.\n;;\n;; But things get even trickier when the window is zoomed with\n;; C-x C-+ C-- and it is scrolled with C-x <. In this case,\n;; the result of (scroll-left 0) is wrong. It does not\n;; account for the zoom. So we have to reconstruct the actual\n;; value using the text-scale-mode-amount variable, which\n;; contains the number of x1.2 zooms (1.2 is stored in the\n;; text-scale-mode-step variable).\n;;\n;; But reconstruction trying to reverse a computation which\n;; mixes floating point numbers along with rounding to\n;; integers is impossible to do accurately. Therefore, the\n;; mouse placement when the window is zoomed AND scrolled\n;; horizontally is not perfect.\n;;\n;; Moreover, each click generates 1, 2, or 3 events. The\n;; first one is used by Uniline to add blank characters if\n;; needed, and adjust the point. The following events also\n;; need their point being adjusted as well. But the following\n;; events do not have the information that blanks have been\n;; added. We do not want to adjust the point if the buffer\n;; have not received additional blanks, because in this case\n;; the point store in the event is right and accurate. We do\n;; not want to ruin it with an unreliable approximation.\n;; Therefore we put in place a variable used by the 1st event\n;; to communicate information the the 2nd and 3th events.\n;;\n;; The Uniline intercepting functions are attached to the\n;; uniline-mode keymap. Therefore they are active only in\n;; uniline-mode.\n;;\n;; Nothing happens in a non-graphical environment, although\n;; the interceptions and keymap-bindings are still present.\n;;\n;; Picture-mode & Artist-mode also handle the mouse. The\n;; picture-mode way, unfortunately, breaks down when the\n;; buffer is zoomed (C-x C-+).\n;;\n;; The artist-mode way smoothly handles zooming. But it is\n;; completely off when window is zoomed and horizontally\n;; scrolled. Artist-mode re-implements parts of the standard\n;; Emacs event handling. This is because it needs to draw\n;; while moving the mouse in many different styles. The\n;; uniline-mode does not need this complexity.\n\n(require 'face-remap)\n\n(defun uniline--scroll-horiz ()\n  \"Compute the actual horizontal scroll.\nThere is a bug in Emacs that this function tries to fix.\nThe scroll is returned by Emacs as a number of character,\nwhich is fine. But Emacs assumes that the window is not\nzoomed. When it is, the result is wrong. Here the scroll\nis zoomed back. Unfortunately, this is not reliable no matter\nwhat is attempted. Zoom is a floating point as powers of 1.2.\nScroll is an integer number of characters.\"\n  (let ((scroll (scroll-left 0)))\n    (cond\n     ((> text-scale-mode-amount 0)\n      (cl-loop\n       repeat text-scale-mode-amount\n       do\n       (setq scroll (ceiling (/ scroll text-scale-mode-step)))))\n     ((< text-scale-mode-amount 0)\n      (cl-loop\n       repeat (- text-scale-mode-amount)\n       do\n       (setq scroll (ceiling (* scroll text-scale-mode-step))))))\n    scroll))\n\n(defun uniline--intercept-mouse-1 (position)\n  \"Add blanks characters if mouse click falls outside buffer.\nAlso adjust the buffer position coded in POSITION, so that\nit will be located right under the mouse event.\"\n  ;; make the clicked window the selected one\n  (set-frame-selected-window nil (posn-window position) t)\n\n  (let* ((firstcol (uniline--scroll-horiz))\n         (firstlin (1- (line-number-at-pos (window-start))))\n         (last (point-max))\n         (pxy (posn-x-y position))\n         (wxy (posn-object-width-height position))\n         (x (+ (/ (car pxy) (car wxy)) firstcol))\n         (y (+ (/ (cdr pxy) (cdr wxy)) firstlin)))\n\n    ;; possibly add blank characters\n    (uniline-move-to-lin-col y x)\n    (if (<= (point-max) last)\n        ;; case where no blank characters where added\n        ;; the point in the event can be trusted\n        () ;; nothing to change\n      \n      ;; blanks were added\n      (when (eobp) (insert \"\\n\") (forward-char -1))\n      (setf (nth 1 position) (point))\n      (setf (nth 5 position) (point)))))\n\n(defun uniline-mouse-set-point (event &optional promote-to-region)\n  \"Drop-in replacement for the base mouse-set-point.\nIt adds blank characters if necessary.\"\n  (interactive \"e\\np\")\n  (uniline--intercept-mouse-1 (event-end event))\n  (mouse-set-point event promote-to-region))\n\n;;;╭──────────────╮\n;;;│Customizations│\n;;;╰──────────────╯\n\n(defun uniline-customize-hydra-or-transient (type)\n  \"Attempt to tweak .emacs to setup the type of interface.\nTYPE is \\\"hydra\\\" or \\\"transient\\\".\"\n  (interactive)\n  (let ((message))\n    (find-file user-init-file)\n    (goto-char (point-min))\n    (if (re-search-forward\n         (rx bol (* (not \";\")) \"uniline-\" (group (+ word)))\n         nil\n         t)\n        (let ((current (match-string 1)))\n          (beginning-of-line)\n          (cond\n           ((string= current type)\n            (setq message (format \"Already configured as uniline-%s\" type)))\n           ((or (string= current \"hydra\") (string= current \"transient\"))\n            (setq message \"It seems your current configuration is here.\"))\n           (t\n            (setq message\n                  (format \"It seems your current configuration is the unknown uniline-%s\"\n                          current)))))\n      (setq message \"It seems that nothing is currently configured in .emacs.\")\n      (goto-char (point-max)))\n    (kill-new\n     (format\n      \"(use-package uniline-%s\\n  :bind (\\\"C-<insert>\\\" . uniline-mode))\\n\"\n      type))\n    (message \"%s\\nType C-y to insert the suggested new configuration.\"\n             message)))\n\n;;;╭──────────────────╮\n;;;│Uniline minor mode│\n;;;╰──────────────────╯\n\n(defgroup uniline nil\n  \"Draw Unicode lines\"\n  :group 'text\n  :link '(url-link :tag \"GitHub\" \"https://github.com/tbanel/uniline\"))\n\n(defcustom uniline-cursor-type 'hollow\n  \"The suggested cursor in Uniline is a the hollow one,\nbecause it has no prefered direction (up, down, right, left),\nand the character under the cursor remains visible.\nYet, the cursor style is a matter of preference,\nso any possible choice is available.\"\n  :type '(choice\n          (const :tag \"Frame default\" t)\n          (const :tag \"Filled box\" box)\n          (cons  :tag \"Box with specified size\"\n                 (const box) integer)\n          (const :tag \"Hollow cursor\" hollow)\n          (const :tag \"Vertical bar\" bar)\n          (cons  :tag \"Vertical bar with specified height\"\n                 (const bar) integer)\n          (const :tag \"Horizontal bar\" hbar)\n          (cons  :tag \"Horizontal bar with specified width\"\n                 (const hbar) integer)\n          (const :tag \"None \"nil))\n  :local t\n  :group 'uniline)\n\n;; toggle between normal hydra hints, and one-liners\n(defcustom uniline-hint-style t\n  \"Which kind of hint message should the Hydras menus display?\nt: large and detailed menus\n1: one-line non-disturbing menus in the echo area\n0: no menus\nThose values are loosely in sync with those defined by the\n`:verbosity' Hydra property.\"\n  :type '(choice\n          (const :tag \"full fledged hints\" t)\n          (const :tag \"one liner hints\"    1)\n          (const :tag \"no hint\"            0))\n  :local t\n  :group 'uniline)\n\n(eval-and-compile\n  (defun uniline--color-hint (face hint)\n    \"Return a colored message mimicking the Hydra way.\nHINT is the message string. It  contains pairs of ^xxx^\ncarets which are to be removed from the message, while the\ntext within will be colored.\nFACE is the face used to color text.\"\n    (interactive)\n    (replace-regexp-in-string\n     \"\\\\^.*?\\\\^\"\n     (lambda (x)\n       (setq x (substring x 1 (1- (length x))))\n       (add-face-text-property 0 (length x) face nil x)\n       x)\n     hint\n     t)))\n\n(defcustom uniline-show-welcome-message t\n  \"Whether to show the welcome message upon activating uniline-mode.\"\n  :type 'boolean\n  :group 'uniline)\n\n(defun uniline-dismiss-welcome-message ()\n  (interactive)\n  (customize-variable 'uniline-show-welcome-message))\n\n(defun uniline--welcome-message ()\n  \"Display a message giving the main key-bindings of the minor mode.\"\n  (interactive)\n  (let ((message-log-max))\n    (message\n     (cond\n      ((eq uniline-hint-style t)\n       (eval-when-compile\n         (uniline--color-hint\n          'error\n          \"\\\n ╭─^^────────────╴Uniline╶╴mode╶─────────────────────────────╮\n │^(Ctrl) → ↓ ← ↑^  (overwrite)/draw lines with current brush│\n │^Shift  → ↓ ← ↑^         extend selection                  │\n │^- + = # DEL RET^        change brush style                │\n │^INS^ without selection  insert glyphs, change font        │\n │^INS^ with    selection  handle rectangles                 │\n │^C-h TAB^                switch small/large hints          │\n │^C-h DEL^                dismiss this message in the future│\n │^C-c C-c^                quit uniline                      │\n ╰─^^────────────────────────────────────────────────────────╯\")))\n      ((eq uniline-hint-style 1)\n       (eval-when-compile\n         (uniline--color-hint\n          'error\n          \"trace: ^←→↑↓^  ovwr: ^C-←→↑↓^  selec: ^C-←→↑↓^  brush: ^-+=# DEL RET^  menu: (sel)^INS^  hint: ^C-h TAB^\")))\n      (t nil)))))\n\n(declare-function uniline-toggle-hints \"\" (&optional notoggle))\n\n(defun uniline-toggle-hints-welcome ()\n  \"Toggle between styles of hydra hints, and display welcome message.\"\n  (interactive)\n  (uniline-toggle-hints)\n  (uniline--welcome-message))\n\n(defvar-local uniline--remember-settings\n    nil\n  \"Remember settings before entering uniline minor-mode.\nIt is a list containing:\n  - `overwrite-mode'\n  - `indent-tabs-mode'\n  - `truncate-lines'\n  - `cursor-type'\n  - `post-self-insert-hook'\")\n\n(defun uniline--mode-pre ()\n  \"Change settings when entering uniline mode.\nAnd backup previous settings.\"\n  (setq uniline--remember-settings\n        (list\n         overwrite-mode\n         indent-tabs-mode\n         truncate-lines\n         cursor-type\n         post-self-insert-hook))\n  (overwrite-mode 1)\n  (indent-tabs-mode 0)\n  (setq truncate-lines t)\n  (setq cursor-type uniline-cursor-type)\n  (add-hook\n   'post-self-insert-hook\n   #'uniline--post-self-insert\n   nil 'local)\n  (uniline--fix-char-width-table)\n  (uniline-toggle-hints t)\n  (uniline--update-mode-line)\n  (if uniline-show-welcome-message\n      (uniline--welcome-message)))\n\n(defun uniline--mode-post ()\n  \"Restore settings when exiting uniline mode.\"\n  (overwrite-mode    (if (nth 0 uniline--remember-settings) 1 0))\n  (indent-tabs-mode  (if (nth 1 uniline--remember-settings) 1 0))\n  (setq\n   truncate-lines        (nth 2 uniline--remember-settings)\n   cursor-type           (nth 3 uniline--remember-settings)\n   post-self-insert-hook (nth 4 uniline--remember-settings)))\n\n;; This `unintern' instruction is useful during development\n;; to ensure that M-x eval-buffer reloads 100% of the Lisp code\n;; (unintern 'uniline-mode-map nil)\n\n(define-minor-mode uniline-mode\n  \"Minor mode to draw lines, boxes, & arrows using UNICODE characters.\n\n                ┏━━━━━━━┓\n        ┏━━━━◀━━┫ thick ┣═◁═╗\n    ╭───┸──╮    ┃ box   ┃   ║\n    │ thin │    ┗━━┯━━━━┛   ║\n    │ box  ├───●───╯ ╔══════╩═╗\n    ╰───┬──╯   ╰─────╢ double ║\n        ╰───────▷────╢ box    ║\n                     ╚════════╝\n     here╶──────────────╮\n                        ↓\n     △ ╭────────╮ ┏━━━━━┷━━━━━┓\n   A │ │ A+X    │ ┃ A+Y       ┃\n     │ │        │ ┃           ┃\n     ▽ ╰────────╯ ┗━━━━━━━━━━━┛\n     △ ╭────────╮ ╭───────────╮\n   B │ │ B+X    │ │ B+Y       │\n     ▽ ╰────────╯ ╰───────────╯\n       ◀━━━━━━━━▶◀━━━━━━━━━━━━▶\n           X           Y\n\n    v △      ▗▖         ▗\n    a │   ▗▟▄▟██▖   ▗▄▄▟█\n    l │ ▐▄███████▄ ▟█████▙ ▄▖\n    u─┴╴▝▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀\n    e   ├───time────────────▷\n\n╭─Keyboard arrows────────────╴\n│ Use keyboard arrows to draw lines ╭─┲━╦═╗\n│ Use control-arrows to overwrite whatever was there\n│ Use shift-arrows to extend the selection (or start a selection)\n╰────────────────────────────╴\n╭─Brush style────────────────╴\\\\<uniline-mode-map>\n│ \\\\[uniline-set-brush-1]\tfor thin   lines\t╭─┬─╮\n│ \\\\[uniline-set-brush-2]\tfor thick  lines\t┏━┳━┓\n│ \\\\[uniline-set-brush-3]\tfor double lines\t╔═╦═╗\n│ \\\\[uniline-set-brush-block]\tfor blocks\t\t▙▄▟▀\n│ \\\\[uniline-set-brush-0]\tto erase lines\n│ \\\\[uniline-set-brush-nil]\tto move cursor without drawing\n╰────────────────────────────╴\n╭─Glyphs (region inactive)───╴\n│ \\\\[uniline-launch-interface] when there is NO region highlighted,\n│ enter a sub-mode to draw a single character glyph,\n│ and change its orientation.\n├─Intersection glyphs────────╴\n│ \\\\`a' or \\\\`A' arrows ▷ ▶ → ▹ ▸ ↔\n│ \\\\`s' or \\\\`S' squares  □ ■ ◆ ◊\n│ \\\\`o' or \\\\`O' circles  · ● ◦ Ø ø\n│ \\\\`x' or \\\\`X' crosses  ╳ ╱ ╲ ÷ × ± ¤\n│ Shifting the key cycles backward\n├─Arrow direction────────────╴\n│ \\\\`S-<right>' point arrow → right\n│ \\\\`S-<left>'  point arrow ← left\n│ \\\\`S-<up>'    point arrow ↑ up\n│ \\\\`S-<down>'  point arrow ↓ down\n├─Tweak 1/4 line─────────────╴\n│ \\\\`S-<right>' change ¼ line → right\n│ \\\\`S-<left>'  change ¼ line ← left\n│ \\\\`S-<up>'    change ¼ line ↑ up\n│ \\\\`S-<down>'  change ¼ line ↓ down\n├─Text direction─────────────╴\n│ Usually when typing text, cursor moves to the right.\n│ \\\\`C-<right>' text goes right→\n│ \\\\`C-<left>'  text goes left ←\n│ \\\\`C-<up>'    text goes up   ↑\n│ \\\\`C-<down>'  text goes down ↓\n├─Insert characters──────────╴\n│ In this sub-mode, the keys \\\\`-' \\\\`+' \\\\`=' \\\\`#' recover their\n│ basic meaning, which is to insert this character.\n├─Other──────────────────────╴\n│ \\\\`f' enter the fonts sub-menu\n│ \\\\`RET' or \\\\`q' exits the sub-mode\n│ Any other key exits the sub-mode and do whatever they\n│ are intended for.\n╰────────────────────────────╴\n╭─Rectangles (region active)─╴\n│ \\\\[uniline-launch-interface] when region IS highlighted,\n│ enter a sub-mode to handle rectangles,\n│ marked by the highlighted region.\n├─Move rectangle─────────────╴\n│ \\\\`S-<right>' move rectangle → right\n│ \\\\`S-<left>'  move rectangle ← left\n│ \\\\`S-<up>'    move rectangle ↑ up\n│ \\\\`S-<down>'  move rectangle ↓ down\n├─Draw rectangle─────────────╴\n│ \\\\`r'     draw      an inner rectangle\n│ \\\\`R'     draw      an outer rectangle\n│ \\\\`C-r'   overwrite an inner rectangle\n│ \\\\`C-S-R' overwrite an outer rectangle\n├─Fill───────────────────────╴\n│ \\\\`i'\tfill region with a character\n├─Other──────────────────────╴\n│ \\\\`C-_', \\\\`C-/', \\\\`C-x u' undo works outside selection\n│ \\\\`RET', \\\\`q' exit the rectangle sub-mode\n│ Any other key exits the sub-mode and do whatever they\n│ are intended for.\n╰────────────────────────────╴\n╭╴Macros─────────────────────╴\n│ Usual Emacs macros recording works as usual\n│ Last keybord macro can be twisted in any of the 4 directions\n│ \\\\[uniline-macro-exec] then \\\\`→' \\\\`←' \\\\`↑' \\\\`↓': directional call of last keyboard macro\n╰────────────────────────────╴\n╭╴Alternate styles───────────╴\n│ Highlight a region (a rectangle) then \\\\[uniline-launch-interface] \\\\`s'\n│ This enters a menu where alternative styles are applied\n│ to the rectangular selection\n│ \\\\`3' make 3 dots vertical, 2 dots horizontal lines\n│ \\\\`4' make 4 dots vertical and horizontal lines\n│ \\\\`h' convert round corners to hard ones\n│ \\\\`-' make thin lines\n│ \\\\`+' make thick lines\n│ \\\\`=' make double lines\n│ \\\\`0' come back to standard base line style, including from ASCII art\n│ \\\\`a' apply external package aa2u conversion from ASCII art to UNICODE\n╰────────────────────────────╴\n╭─Fonts──────────────────────╴\n│ Try out some mono-spaced fonts with support for the\n│ required UNICODE characters.\n│ \\\\[uniline-launch-interface] \\\\`f' enters a sub-menu to change the font\n│ type the first letter of the font name.\n│ This setting is just for the current Emacs session.\n│ \\\\`*' customize default font for future sessions.\n╰────────────────────────────╴\n╭─Toggle hint sizes──────────╴\n│ This is for changing the height of Hydra menus,\n│ between multiline to single-line and back,\n│ \\\\[uniline-toggle-hints-welcome] in base Uniline mode\n│ \\\\`TAB' in a \\\\[uniline-launch-interface]-activated menu\n╰────────────────────────────╴\n╭─Quit───────────────────────╴\\\\<uniline-mode-map>\n│ \\\\[uniline-mode] quit the Uniline minor mode.\n│ The state of the buffer (ex: `overwrite-mode' and cursor shape)\n│ will return to what it was prior to entering `uniline-mode'\n╰────────────────────────────╴\n\n Documentation here: (info \\\"uniline\\\")\"\n  :init-value nil\n  ;;         ╭───╴without that, mouse-1 on mode-line does not display the menu\n  ;;         ▽\n  :lighter (:eval (format \" %sUniline%s\" uniline--mode-line-dir uniline--mode-line-brush))\n  :keymap  ;; defines uniline-mode-map\n  '(([right]   . uniline-write-ri→)\n    ([left ]   . uniline-write-lf←)\n    ([up   ]   . uniline-write-up↑)\n    ([down ]   . uniline-write-dw↓)\n    ([C-right] . uniline-overwrite-ri→)\n    ([C-left ] . uniline-overwrite-lf←)\n    ([C-up   ] . uniline-overwrite-up↑)\n    ([C-down ] . uniline-overwrite-dw↓)\n    ([insert]      . uniline-launch-interface)\n    ([insertchar]  . uniline-launch-interface)\n    ([?\\r]           . uniline-set-brush-nil)\n    ([delete]        . uniline-set-brush-0)\n    ([deletechar]    . uniline-set-brush-0)\n    (\"-\"             . uniline-set-brush-1)\n    ([kp-subtract]   . uniline-set-brush-1)\n    (\"+\"             . uniline-set-brush-2)\n    ([kp-add]        . uniline-set-brush-2)\n    (\"=\"             . uniline-set-brush-3)\n    (\"#\"             . uniline-set-brush-block)\n    (\"~\"             . uniline-set-brush-dot-toggle)\n    ([?\\C-x ?e]      . uniline-macro-exec)\n    ([?\\C-h ?\\t]        . uniline-toggle-hints-welcome)\n    ([?\\C-h delete]     . uniline-dismiss-welcome-message)\n    ([?\\C-h deletechar] . uniline-dismiss-welcome-message)\n    ([mouse-1] . uniline-mouse-set-point  )\n    ([?\\C-c ?\\C-c] . uniline-mode))\n  :after-hook (if uniline-mode (uniline--mode-pre) (uniline--mode-post)))\n\n(defun uniline--keymap-remove-launch-interface (keymap)\n  \"Remove key-bindings in KEYMAP whose action is `uniline-launch-interface'.\nDo that recursively in child keymaps too.\"\n  (cl-loop\n   for on on keymap\n   for bind = (car on)\n   if (consp bind)\n   do\n   (cond ((eq (cdr bind) 'uniline-launch-interface)\n          (setcar on nil))\n         ((and (consp (cdr bind))\n               (eq (cadr bind) 'keymap))\n          (uniline--keymap-remove-launch-interface (cdr bind)))))\n  (delq nil keymap))\n\n(defun uniline--set-insert-key (symbol keys)\n  \"Replace all key-bindings pointing to `uniline-launch-interface'\nwith custom bindings to each key in KEYS.\n_SYMBOL is not used.\"\n  (uniline--keymap-remove-launch-interface uniline-mode-map)\n  (cl-loop\n   for key in keys\n   do (keymap-set uniline-mode-map key 'uniline-launch-interface))\n  (set-default-toplevel-value symbol keys))\n\n(defcustom uniline-key-insert\n  '(\"<insert>\" \"<insertchar>\")\n  \"Prefix key (or keys) to invoke all Uniline minor mode functions.\n<insert> and <insertchar> by default.\nUse C-h k, then type a key (or key combination) to see the exact syntax.\nDo not confuse this key (which is active inside Uniline-mode)\nwith the one used to invoke Uniline-mode.\"\n  :type '(repeat (key))\n  :set 'uniline--set-insert-key\n  :group 'uniline)\n\n(defun uniline-about ()\n  \"Print a message containing the MELPA version and the repository URL.\"\n  (interactive)\n  (let ((path\n         (cl-loop\n          for p in load-path\n          if (string-match (rx \"/uniline-\" (group (+ (any \"0-9.\"))) eos) p)\n          collect (match-string 1 p))))\n    (setq path\n          (if (listp path)\n              (car path)\n            \"\"))\n    (message \"Uniline %s, https://github.com/tbanel/uniline\" path)))\n\n(defvar uniline--current-interface nil\n  \"Remember whether Hydra or Transient is loaded.\nIts value is ?h or ?t\")\n\n(easy-menu-define\n  uniline-menu\n  uniline-mode-map\n  ;; ╭─that makes this menu appear upon clicking on the mode-line\n  ;; ╰────────────────────────╮\n  ;;                          ▽\n  \"Uniline mode menu. \\\\{uniline-mode-map}\"\n  '(\"Uniline\"\n    :visible t\n    :active t\n    [\"Write right\" uniline-write-ri→ t]\n    [\"Write left\"  uniline-write-lf← t]\n    [\"Write up\"    uniline-write-up↑ t]\n    [\"Write down\"  uniline-write-dw↓ t]\n    (\"Overwrite\"\n     [\"Overwrite right\" uniline-overwrite-ri→ t]\n     [\"Overwrite left\"  uniline-overwrite-lf← t]\n     [\"Overwrite up\"    uniline-overwrite-up↑ t]\n     [\"Overwrite down\"  uniline-overwrite-dw↓ t])\n    \"----\"\n    [\"─ light brush\"    uniline-set-brush-1     :style radio :selected (eq uniline-brush 1     )]\n    [\"━ heavy brush\"    uniline-set-brush-2     :style radio :selected (eq uniline-brush 2     )]\n    [\"═ double brush\"   uniline-set-brush-3     :style radio :selected (eq uniline-brush 3     )]\n    [\"▞ blocks brush\"   uniline-set-brush-block :style radio :selected (eq uniline-brush :block)]\n    [\"eraser brush\"     uniline-set-brush-0     :style radio :selected (eq uniline-brush 0     )]\n    [\"inactive brush\"   uniline-set-brush-nil   :style radio :selected (eq uniline-brush nil   )]\n    \"----\"\n    [\"┄ 3-2 dots brush\" uniline-set-brush-3dots :style radio :selected (eq uniline-brush-dots 1) :keys \"~\"  ]\n    [\"┄ 4-4 dots brush\" uniline-set-brush-4dots :style radio :selected (eq uniline-brush-dots 2) :keys \"~~\" ]\n    [\"┄ no dots brush\"  uniline-set-brush-0dots :style radio :selected (eq uniline-brush-dots 0) :keys \"~~~\"]\n    \"----\"\n    (\"Insert glyph\"\n     [\"Insert arrow ▷ ▶ → ▹ ▸ ↔\"   uniline-insert-fw-arrow  :keys \"INS a\"]\n     [\"Insert square □ ■ ◆ ◊\"      uniline-insert-fw-square :keys \"INS s\"]\n     [\"Insert oshape · ● ◦ Ø ø\"    uniline-insert-fw-oshape :keys \"INS o\"]\n     [\"Insert cross ╳ ╱ ╲ ÷ × ± ¤\" uniline-insert-fw-cross  :keys \"INS x\"])\n    (\"Rotate arrow, tweak ¼ line\"\n     [\"Rotate arrow, tweak ¼ line → right\" uniline-rotate-ri→ :keys \"INS S-<right>\"]\n     [\"Rotate arrow, tweak ¼ line ← left\"  uniline-rotate-lf← :keys \"INS S-<left>\" ]\n     [\"Rotate arrow, tweak ¼ line ↑ up\"    uniline-rotate-up↑ :keys \"INS S-<up>\"   ]\n     [\"Rotate arrow, tweak ¼ line ↓ down\"  uniline-rotate-dw↓ :keys \"INS S-<down>\" ])\n    (\"Rectangular region\" :active (region-active-p)\n     [\"Move selection right\" uniline-move-rect-ri→ :keys \"INS <right>\"]\n     [\"Move selection left\"  uniline-move-rect-lf← :keys \"INS <left>\" ]\n     [\"Move selection up\"    uniline-move-rect-up↑ :keys \"INS <up>\"   ]\n     [\"Move selection down\"  uniline-move-rect-dw↓ :keys \"INS <down>\" ]\n     \"----\"\n     [\"Copy\"        uniline-copy-rectangle :keys \"INS c\"]\n     [\"Kill\"        uniline-kill-rectangle :keys \"INS k\"]\n     [\"Yank, paste\" uniline-yank-rectangle :keys \"INS y\"]\n     \"----\"\n     [\"Trace rectangle inside selection\"     uniline-draw-inner-rectangle      :keys \"INS r\"  ]\n     [\"Trace rectangle around selection\"     uniline-draw-outer-rectangle      :keys \"INS R\"  ]\n     [\"Overwrite rectangle inside selection\" uniline-overwrite-inner-rectangle :keys \"INS C-r\"]\n     [\"Overwrite rectangle around selection\" uniline-overwrite-outer-rectangle :keys \"INS C-R\"]\n     [\"Fill\"                                 uniline-fill-rectangle            :keys \"INS i\"  ])\n    (\"Alternate styles\" :active (region-active-p)\n     [\"─ thin lines\"          uniline-change-style-thin     :keys \"INS s -\"]\n     [\"━ thick lines\"         uniline-change-style-thick    :keys \"INS s +\"]\n     [\"═ double lines\"        uniline-change-style-double   :keys \"INS s =\"]\n     [\"╌ 3 dots vert, 2 dots horiz\" uniline-change-style-dot-3-2 :keys \"INS s 3\"]\n     [\"┈ 4 dots vert & horiz\" uniline-change-style-dot-4-4  :keys \"INS s 4\"]\n     [\"┌ hard corners\"    uniline-change-style-hard-corners :keys \"INS s h\"]\n     [\"╭ back to standard\"    uniline-change-style-standard :keys \"INS s 0\"]\n     [\"aa2u (ext. package)\" uniline-aa2u-rectangle        :keys \"INS s a\"])\n    (\"Fill & contour\"\n     [\"Contour\"        uniline-contour    :keys \"INS c\"]\n     [\"Contour overw\" (uniline-contour t) :keys \"INS C\"]\n     [\"Fill\"           uniline-fill       :keys \"INS i\"])\n    (\"Text insertion direction\"\n     [\"→ right\" uniline-text-direction-ri→ :keys \"INS C-<right>\" :style radio :selected (eq uniline-text-direction (uniline-direction-ri→))]\n     [\"← left\"  uniline-text-direction-lf← :keys \"INS C-<left> \" :style radio :selected (eq uniline-text-direction (uniline-direction-lf←))]\n     [\"↑ up\"    uniline-text-direction-up↑ :keys \"INS C-<up>   \" :style radio :selected (eq uniline-text-direction (uniline-direction-up↑))]\n     [\"↓ down\"  uniline-text-direction-dw↓ :keys \"INS C-<down> \" :style radio :selected (eq uniline-text-direction (uniline-direction-dw↓))])\n    \"----\"\n    (\"Font\"\n     [\"DejaVu Sans Mono\"         (set-frame-font \"DejaVu Sans Mono\"        ) :keys \"INS f d\" :style radio :selected (uniline--is-font ?d)]\n     [\"Hack\"                     (set-frame-font \"Hack\"                    ) :keys \"INS f h\" :style radio :selected (uniline--is-font ?h)]\n     [\"Cascadia Mono\"            (set-frame-font \"Cascadia Mono\"           ) :keys \"INS f c\" :style radio :selected (uniline--is-font ?c)]\n     [\"JuliaMono\"                (set-frame-font \"JuliaMono\"               ) :keys \"INS f j\" :style radio :selected (uniline--is-font ?j)]\n     [\"JetBrains Mono\"           (set-frame-font \"JetBrains Mono\"          ) :keys \"INS f b\" :style radio :selected (uniline--is-font ?b)]\n     [\"FreeMono\"                 (set-frame-font \"FreeMono\"                ) :keys \"INS f f\" :style radio :selected (uniline--is-font ?f)]\n     [\"Source Code Pro\"          (set-frame-font \"Source Code Pro\"         ) :keys \"INS f s\" :style radio :selected (uniline--is-font ?s)]\n     [\"Iosevka Comfy Fixed\"      (set-frame-font \"Iosevka Comfy Fixed\"     ) :keys \"INS f i\" :style radio :selected (uniline--is-font ?i)]\n     [\"Iosevka Comfy Wide Fixed\" (set-frame-font \"Iosevka Comfy Wide Fixed\") :keys \"INS f I\" :style radio :selected (uniline--is-font ?I)]\n     [\"Aporetic Sans Mono\"       (set-frame-font \"Aporetic Sans Mono\"      ) :keys \"INS f p\" :style radio :selected (uniline--is-font ?p)]\n     [\"Aporetic Serif Mono\"      (set-frame-font \"Aporetic Serif Mono\"     ) :keys \"INS f P\" :style radio :selected (uniline--is-font ?P)]\n     [\"Unifont\"                  (set-frame-font \"Unifont\"                 ) :keys \"INS f u\" :style radio :selected (uniline--is-font ?u)]\n     [\"Agave\"                    (set-frame-font \"Agave\"                   ) :keys \"INS f a\" :style radio :selected (uniline--is-font ?a)]\n     [\"Configure permanently\"    uniline-customize-face                      :keys \"INS f *\"])\n    (\"Customize\"\n     [\"Current session only:\" :selected nil]\n     [\"Large hints sizes\" uniline-toggle-hints :keys \"C-t or C-h C-t\" :style toggle :selected (eq uniline-hint-style t)]\n     [\"Hydra\"     (load-library \"uniline-hydra\"    ) :style radio :selected (eq uniline--current-interface ?h)]\n     [\"Transient\" (load-library \"uniline-transient\") :style radio :selected (eq uniline--current-interface ?t)]\n     \"----\"\n     [\"Future sessions:\" :selected nil]\n     [\"Uniline Group\" (customize-group 'uniline)]\n     [\"Hydra     (change .emacs)\" (uniline-customize-hydra-or-transient 'hydra    ) ]\n     [\"Transient (change .emacs)\" (uniline-customize-hydra-or-transient 'transient) ]\n     [\"Line spacing\" (customize-variable 'line-spacing)]\n     )\n    [\"Info\" (info \"uniline\") :keys \"M-: (info \\\"uniline\\\")\"]\n    [\"Quit Uniline Mode\" uniline-mode t]\n    [\"About\" uniline-about t]))\n\n(provide 'uniline-core)\n;;; uniline-core.el ends here\n"
  },
  {
    "path": "uniline-hydra.el",
    "content": ";;; uniline-hydra.el --- Add▶ ■─UNICODE based diagrams─■ to▶ ■─text files─■ -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; Package-Requires: ((emacs \"29.1\") (hydra \"0.15.0\"))\n;; Keywords: convenience, text\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n;;; Commentary:\n;;                ┏━━━━━━━┓\n;;    ╭──────╮    ┃ thick ┣═◁═╗\n;;    │ thin ┝◀━━━┫ box   ┃   ║\n;;    │ box  │    ┗━━━━━━━┛   ║\n;;    ╰───┬──╯         ╔══════╩═╗\n;;        ↓            ║ double ║\n;;        ╰────────────╢ box    ║\n;;                     ╚════╤═══╝\n;;      ▛▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▜   │\n;;      ▌quadrant-blocks▐─◁─╯\n;;      ▙▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▟\n;;\n;;╭─Pure text────────────────□\n;;│ UNICODE characters are available to draw nice boxes and lines.\n;;│ They come in 4 flavours: thin, thick, double, and quadrant-blocks.\n;;│ Uniline makes it easy to draw and combine all 4 flavours.\n;;│ Use the arrows on the keyboard to move around leaving a line behind.\n;;╰──────────────────────────╮\n;;╭─Minor mode───────────────╯\n;;│ Uniline is a minor mode.  Enter it with:\n;;│   M-x uniline-mode\n;;│ Leave it with:\n;;│   C-c C-c\n;;╰──────────────────────────╮\n;;╭─Fonts────────────────────╯\n;;│ A font able to displays the needed UNICODE characters have to\n;;│ be used.  It works well with the following families:\n;;│ - DejaVu Sans Mono\n;;│ - Unifont\n;;│ - Hack\n;;│ - JetBrains Mono\n;;│ - Cascadia Mono\n;;│ - Agave\n;;│ - JuliaMono\n;;│ - FreeMono\n;;│ - Iosevka Comfy Fixed, Iosevka Comfy Wide Fixed\n;;│ - Aporetic Sans Mono, Aporetic Serif Mono\n;;│ - Source Code Pro\n;;╰──────────────────────────╮\n;;╭─UTF-8────────────────────╯\n;;│ Also, the encoding of the file must support UNICODE.\n;;│ One way to do that, is to add a line like this one\n;;│ at the top of your file:\n;;│   -*- coding:utf-8; -*-\n;;╰──────────────────────────╮\n;;╭─Hydra or Transient───────╯\n;;│ Uniline comes with two flavours of user interfaces:\n;;│ Hydra and Transient.\n;;│ Both versions are compiled when installing the package.\n;;│\n;;│ Then one or the other packages must be loaded (not both)\n;;│ for example with:\n;;│   (require 'uniline-hydra)\n;;│ or\n;;│   (use-package uniline-hydra\n;;│     :bind (\"C-<insert>\" . uniline-mode))\n;;│\n;;│ This file, uniline-hydra.el, implements the Hydra interface\n;;│ and calls the functions defined by uniline-core.el\n;;╰──────────────────────────□\n\n;;; Requires:\n(require 'uniline-core)\n;; (require 'hydra) ;; no hard dependency\n\n;;; Code:\n\n(eval-when-compile\n  ;; temporarily fix a bug about Hydra generating too long docstrings\n  (setq byte-compile-docstring-max-column 2000))\n\n;;;╭────────────────╮\n;;;│Hydra interfaces│\n;;;╰────────────────╯\n\n(require 'hydra nil t)\n\n(unless (featurep 'hydra)\n  (eval-and-compile\n    (defun uniline-launch-interface ()\n      \"Fake function only when Hydra requested but not installed\"\n      (interactive)\n      (warn \"Uniline-Hydra requested, but Hydra is not installed.\"))))\n\n(eval-and-compile\n  (declare-function uniline-transient-customize nil ())\n  (put 'uniline-transient-customize 'interactive-only nil) ;; to avoid a warning\n  (declare-function uniline-customize-hydra-or-transient (type)))\n\n(when (featurep 'hydra)\n  (eval-and-compile\n\n    (defun uniline--is-font-str (letter)\n      \"Return a tick-glyph ▶ if current font is the one presented by LETTER.\"\n      (if (uniline--is-font letter) \"▶\" \" \"))\n\n    (defhydra uniline-hydra-fonts\n      (:hint nil :exit nil)\n      ;; Docstring MUST begin with an empty line to benefit from substitutions\n      (concat\n       (replace-regexp-in-string\n        \"_\\\\([dhcjbfsiIuapP]\\\\)_ \"\n        \"_\\\\1_%s(uniline--is-font-str ?\\\\1)\"\n        \"\\\n╭^─Try a font^──^─^───────────^─^───────────────────╮╭^─^───^─^──────╮\n│_d_ DejaVu     _b_ JetBrains _i_ Iosevka Comfy     ││_*_ ^^configure│\n│_h_ Hack       _f_ FreeMono  _I_ Iosevka Comfy Wide││_C-t_^^ tg hint│\n│_c_ Cascadia   _a_ Agave     _p_ Aporetic Sans     ││_?_ ^^info-mode│\n│_j_ JuliaMono  _u_ Unifont   _P_ Aporetic Serif    ││_RET_ _q_  exit│\n│_s_ Source Code Pro^^╭───────^─^───────────────────╯╰^─^───^─^──────╯\n╰^─^────────────^─^───╯\"))\n      (\"d\" uniline--set-font-d)\n      (\"u\" uniline--set-font-u)\n      (\"h\" uniline--set-font-h)\n      (\"b\" uniline--set-font-b)\n      (\"c\" uniline--set-font-c)\n      (\"a\" uniline--set-font-a)\n      (\"j\" uniline--set-font-j)\n      (\"f\" uniline--set-font-f)\n      (\"i\" uniline--set-font-i)\n      (\"I\" uniline--set-font-I)\n      (\"p\" uniline--set-font-p)\n      (\"P\" uniline--set-font-P)\n      (\"s\" uniline--set-font-s)\n      (\"*\" uniline-customize-face :exit t)\n      (\"C-t\" uniline-toggle-hints)\n      (\"TAB\" uniline-toggle-hints)\n      (\"?\"  (info \"(uniline) Which fonts?\"))\n      (\"q\"   () :exit t)\n      (\"RET\" () :exit t))\n\n    (defhydra uniline-hydra-customize\n      (:hint nil :exit t)\n      \"\n╭^^╴current session╶╮╭^^╴future sessions╶───────────╮\n│_f_  fonts         ││_g_ Uniline group (settings)  │\n│_t_  transient     ││_H_ Hydra     (change .emacs) │\n│_?_  info          ││_T_ Transient (change .emacs) │\n│_C-t_ large hints  ││_l_ line spacing              │\n╰^^─────────────────╯╰^^────────────────────────────╯\"\n      (\"C-t\" uniline-toggle-hints :exit nil)\n      (\"TAB\" uniline-toggle-hints :exit nil)\n      (\"f\"   uniline-hydra-fonts/body)\n      (\"t\" (progn (load-library \"uniline-transient\") (uniline-transient-customize)))\n      (\"?\" (info \"(uniline) Customization\"))\n      (\"g\" (customize-group \"uniline\"))\n      (\"H\" (uniline-customize-hydra-or-transient \"hydra\"    ))\n      (\"T\" (uniline-customize-hydra-or-transient \"transient\"))\n      (\"l\" (customize-variable (intern \"line-spacing\")))) ;; intern to avoid a quote\n\n    (defhydra uniline-hydra-arrows\n      (:hint nil :exit nil)\n      ;; Docstring MUST begin with an empty line to benefit from substitutions\n      (concat\n       (string-replace\n        \"Text dir────\"\n        \"Text dir─╴%s(uniline-text-direction-str)╶\"\n        \"\\\n╭^─^─^Insert glyph^^^^^─^─^───╮╭─╮╭^Rotate arrow^╮╭^Contour^╮╭^Text dir────^╮╭^─^─^─^─────────╮\n│_a_,_A_rrow ▷ ▶ → ▹ ▸ ↔^^^^^^││-│╭^Tweak glyph─^╮│_c_ draw ││_C-<left>_  ← ││_*_^^ customize │\n│_s_,_S_quare  □ ■ ◆ ◊  ^^^^^^││+││_S-<left>_  ← ││_C_ ovwrt││_C-<right>_ → ││_f_^^      font │\n│_o_,_O_-shape · ● ◦ Ø ø^^^^^^││=││_S-<right>_ → │╰^───────^╯│_C-<up>_    ↑ ││_?_^^      info │\n│_x_,_X_-cross ╳ ÷ × ± ¤^^^^^^││#││_S-<up>_    ↑ │╭^─Fill──^╮│_C-<down>_  ↓ ││_q_ _RET_  exit │\n│_SPC_,_DEL_ grey  ░▒▓█ ^^^^^^││~││_S-<down>_  ↓ ││_i_ fill │╰^─^───────────╯╰^─^─^─^─────────╯\n╰^─^─^─^─^─^─^─^─^─^──────────╯╰─╯╰^────────────^╯╰^───────^╯\"))\n      (\"a\" uniline-insert-fw-arrow )\n      (\"A\" uniline-insert-bw-arrow )\n      (\"s\" uniline-insert-fw-square)\n      (\"S\" uniline-insert-bw-square)\n      (\"o\" uniline-insert-fw-oshape)\n      (\"O\" uniline-insert-bw-oshape)\n      (\"x\" uniline-insert-fw-cross )\n      (\"X\" uniline-insert-bw-cross )\n      (\"SPC\" uniline-insert-fw-grey)\n      (\"DEL\" uniline-insert-bw-grey)\n      (\"S-<left>\"  uniline-rotate-lf←)\n      (\"S-<right>\" uniline-rotate-ri→)\n      (\"S-<up>\"    uniline-rotate-up↑)\n      (\"S-<down>\"  uniline-rotate-dw↓)\n      (\"C-<right>\" uniline-text-direction-ri→ :exit t)\n      (\"C-<left>\"  uniline-text-direction-lf← :exit t)\n      (\"C-<up>\"    uniline-text-direction-up↑ :exit t)\n      (\"C-<down>\"  uniline-text-direction-dw↓ :exit t)\n      (\"<kp-subtract>\" uniline--self-insert--)\n      (\"<kp-add>\"      uniline--self-insert-+)\n      (\"-\" self-insert-command)\n      (\"+\" self-insert-command)\n      (\"=\" self-insert-command)\n      (\"#\" self-insert-command)\n      (\"~\" self-insert-command)\n      (\"f\" uniline-hydra-fonts/body :exit t)\n      (\"c\" uniline-contour          :exit t)\n      (\"C\" (uniline-contour t)      :exit t)\n      (\"i\" uniline-fill             :exit t)\n      (\"C-t\" uniline-toggle-hints)\n      (\"TAB\" uniline-toggle-hints)\n      (\"*\" uniline-hydra-customize/body :exit t)\n      (\"?\"  (info \"uniline\") :exit t)\n      (\"q\"   ()              :exit t)\n      (\"RET\" ()              :exit t))\n\n    (defhydra uniline-hydra-alt-styles\n      (:pre (rectangle-mark-mode 1) :hint nil :exit nil)\n      ;; Docstring MUST begin with an empty line to benefit from substitutions\n      \"\n╭^Thickness^╮╭^─Alt styles^──╮╭^Base style^╮╭^─^─^─^──────────────╮\n│_-_ thin   ││_3_ 3x2 dots   ││_0_ standard││_f_    ^^ choose font│\n│_+_ thick  ││_4_ 4x4 dots   ││_a_ aa2u    ││_C-t_  ^^ short hint │\n│_=_ double ││_h_ hard corner│╰─^─^────────╯│_?_    ^^ info-mode  │\n╰^─^────────╯╰^─^────────────╯ ^ ^          │_q_ _RET_ exit       │\n ^ ^          ^ ^              ^ ^          ╰^─^─^─^──────────────╯\"\n      (\"3\"             uniline-change-style-dot-3-2)\n      (\"<kp-3>\"        uniline-change-style-dot-3-2)\n      (\"4\"             uniline-change-style-dot-4-4)\n      (\"<kp-4>\"        uniline-change-style-dot-4-4)\n      (\"h\"             uniline-change-style-hard-corners)\n      (\"0\"             uniline-change-style-standard)\n      (\"<kp-0>\"        uniline-change-style-standard)\n      (\"-\"             uniline-change-style-thin)\n      (\"<kp-subtract>\" uniline-change-style-thin)\n      (\"+\"             uniline-change-style-thick)\n      (\"<kp-add>\"      uniline-change-style-thick)\n      (\"=\"             uniline-change-style-double)\n      (\"a\"             uniline-aa2u-rectangle)\n      ;; copy here the bindings for handling rectangles\n      (\"<right>\" uniline-move-rect-ri→)\n      (\"<left>\"  uniline-move-rect-lf←)\n      (\"<up>\"    uniline-move-rect-up↑)\n      (\"<down>\"  uniline-move-rect-dw↓)\n      (\"r\"       uniline-draw-inner-rectangle)\n      (\"R\"       uniline-draw-outer-rectangle)\n      (\"C-r\"     uniline-overwrite-inner-rectangle)\n      (\"C-S-R\"   uniline-overwrite-outer-rectangle)\n      (\"i\"       uniline-fill-rectangle)\n      (\"f\"       uniline-hydra-fonts/body :exit t)\n      (\"s\"       uniline-hydra-moverect/body :exit t)\n      ;; misc.\n      (\"C-x C-x\" rectangle-exchange-point-and-mark)\n      (\"C-t\"     uniline-toggle-hints)\n      (\"TAB\"     uniline-toggle-hints)\n      (\"?\"      (info \"(uniline) Rectangular actions\"))\n      (\"q\"       uniline--rect-quit :exit t)\n      (\"RET\"     uniline--rect-quit :exit t))\n\n    (defhydra uniline-hydra-moverect\n      (:pre (rectangle-mark-mode 1) :hint nil :exit nil)\n      ;; Docstring MUST begin with an empty line to benefit from substitutions\n      \"\n╭^Move ^rect╮╭────^Draw^ rect────╮╭^─Rect^─╮╭^Brush^╮╭──^Misc^─────────╮\n│_<right>_ →││_r_     trace inner││_c_ copy││_-_ ╭─╯││_s_   alt styles │\n│_<left>_  ←││_R_     trace outer││_k_ kill││_+_ ┏━┛││_f_   choose font│\n│_<up>_    ↑││_C-r_   ovewr inner││_y_ yank││_=_ ╔═╝││_C-t_ short hints│\n│_<down>_  ↓││_C-S-R_ ovewr outer│╰^^──────╯│_#_ ▄▄▟││_?_   info       │\n╰^─────^────╯│_i_     fill       │ ^^╭──────╯_~_ ┄┄┄││_RET_ exit       │\n ^     ^     ╰^────^─────────────╯ ^^│_<delete>_ DEL│╰^───^────────────╯\n ^     ^      ^    ^               ^^╰^────────^────╯\"\n      (\"<right>\" uniline-move-rect-ri→)\n      (\"<left>\"  uniline-move-rect-lf←)\n      (\"<up>\"    uniline-move-rect-up↑)\n      (\"<down>\"  uniline-move-rect-dw↓)\n\n      (\"r\"     uniline-draw-inner-rectangle)\n      (\"R\"     uniline-draw-outer-rectangle)\n      (\"C-r\"   uniline-overwrite-inner-rectangle)\n      (\"C-S-R\" uniline-overwrite-outer-rectangle)\n      (\"i\"     uniline-fill-rectangle)\n\n      (\"c\"   uniline-copy-rectangle :exit t)\n      (\"k\"   uniline-kill-rectangle :exit t)\n      (\"y\"   uniline-yank-rectangle)\n\n      (\"<delete>\"       uniline-set-brush-0)\n      (\"<deletechar>\"   uniline-set-brush-0)\n      (\"C-<delete>\"     uniline-set-brush-0)\n      (\"C-<deletechar>\" uniline-set-brush-0)\n      (\"-\"              uniline-set-brush-1)\n      (\"<kp-subtract>\"  uniline-set-brush-1)\n      (\"+\"              uniline-set-brush-2)\n      (\"<kp-add>\"       uniline-set-brush-2)\n      (\"=\"              uniline-set-brush-3)\n      (\"#\"              uniline-set-brush-block)\n      (\"~\"              uniline-set-brush-dot-toggle)\n\n      (\"C-t\" uniline-toggle-hints)\n      (\"TAB\" uniline-toggle-hints)\n      (\"?\"  (info \"(uniline) Rectangular actions\"))\n      (\"f\"     uniline-hydra-fonts/body      :exit t)\n      (\"s\"     uniline-hydra-alt-styles/body :exit t)\n      (\"C-x C-x\" rectangle-exchange-point-and-mark)\n      (\"RET\"   uniline--rect-quit :exit t))\n\n    (defun uniline-launch-interface ()\n      \"Choose between two Hydras based on selection.\nWhen selection is active, most likely user wants to act\non a rectangle.\nTherefore the rectangle hydra is launched.\nOtherwise, the arrows & shapes hydra is invoked.\"\n      (interactive)\n      (let ((message-log-max))       ; avoid hint copied in *Messages*\n        (if (region-active-p)\n            (uniline-hydra-moverect/body)\n          (uniline-hydra-arrows/body))))\n\n    (defhydra uniline-hydra-macro-exec\n      (:hint nil :exit nil)\n      ;; Docstring MUST begin with an empty line to benefit from substitutions\n      \"\n╭^╴Call macro╶^───╮╭^^^^──────────────╮\n│_e_ usual call   ││_C-t_^^ short hint│\n│_<right>_ call → ││_?_ ^^  info-mode │\n│_<left>_  call ← ││_q_ _RET_ exit    │\n│_<up>_    call ↑ │╰^─^─^───^─────────╯\n│_<down>_  call ↓ │\n╰^^───────────────╯\"\n      (\"e\"       (kmacro-end-and-call-macro 1))\n      (\"<right>\" uniline-call-macro-in-direction-ri→)\n      (\"<left>\"  uniline-call-macro-in-direction-lf←)\n      (\"<up>\"    uniline-call-macro-in-direction-up↑)\n      (\"<down>\"  uniline-call-macro-in-direction-dw↓)\n      (\"C-t\" uniline-toggle-hints)\n      (\"TAB\" uniline-toggle-hints)\n      (\"?\"  (info \"(uniline) Macros\"))\n      (\"q\"   () :exit t)\n      (\"RET\" () :exit t))\n\n    (defun uniline-macro-exec ()\n      (interactive)\n      (uniline-hydra-macro-exec/body))\n\n;;;╭───────────────────╮\n;;;│Smaller hydra hints│\n;;;╰───────────────────╯\n\n    ;; Pack 2 hints in the usual uniline-hydra-*/hint variables\n    ;; one is the standard hint created by `defhydra'\n    ;; the other is a one-liner\n    (setq\n     uniline-hydra-arrows/hint\n     `(if (eq uniline-hint-style t)\n          ,uniline-hydra-arrows/hint\n        ,(eval-when-compile\n           (uniline--color-hint\n            'hydra-face-red\n            \"glyph:^aAsSoOxX SPC DEL-+=#~^ arr&tweak:^S-→←↑↓^ txt-dir:^C-→←↑↓^ ^c^ontour f^i^ll ^f^ont ^*^ ^C-t^\")))\n     uniline-hydra-fonts/hint\n     `(if (eq uniline-hint-style t)\n          ,uniline-hydra-fonts/hint\n        ,(eval-when-compile\n           (uniline--color-hint\n            'hydra-face-red\n            \"font:^dhcjbfsiIpPua^ config:^*^ hint:^C-t^\")))\n     uniline-hydra-customize/hint\n     `(if (eq uniline-hint-style t)\n          ,uniline-hydra-customize/hint\n        ,(eval-when-compile\n           (uniline--color-hint\n            'hydra-face-red\n            \"customize: ^t^ransient uniline-^g^roup .emacs:^H^ydra-^T^ransient ^l^ine-spc ^f^ont ^C-t^\")))\n     uniline-hydra-moverect/hint\n     `(if (eq uniline-hint-style t)\n          ,uniline-hydra-moverect/hint\n        ,(eval-when-compile\n           (uniline--color-hint\n            'hydra-face-red\n            \"move:^→←↑↓^ trace:^rR C-rR^ copy-paste:^cky^ f^i^ll brush:^-+=# DEL^ ^s^tyle ^f^onts ^C-t^\")))\n     uniline-hydra-macro-exec/hint\n     `(if (eq uniline-hint-style t)\n          ,uniline-hydra-macro-exec/hint\n        ,(eval-when-compile\n           (uniline--color-hint\n            'hydra-face-red\n            \"macro exec, usual:^e^ directional:^→←↑↓^ hint:^C-t^\")))\n     uniline-hydra-alt-styles/hint\n     `(if (eq uniline-hint-style t)\n          ,uniline-hydra-alt-styles/hint\n        ,(eval-when-compile\n           (uniline--color-hint\n            'hydra-face-red\n            \"alt styles, thick:^-+=^ dashed:^34^ corners:^h^ standard:^0^ ^a^a2u ^C-t^\"))))\n\n    (defun uniline-toggle-hints (&optional notoggle)\n      \"Toggle between styles of hydra hints.\nWhen NOTOGGLE is t, do not toggle `uniline-hint-style',\njust put everything in sync.\"\n      (interactive)\n      (unless notoggle\n        (setq uniline-hint-style\n              (if (eq uniline-hint-style t) 1 t)))\n      (cl-loop\n       for hydra in\n       '(uniline-hydra-arrows\n         uniline-hydra-fonts\n         uniline-hydra-customize\n         uniline-hydra-moverect\n         uniline-hydra-macro-exec\n         uniline-hydra-alt-styles)\n       do\n       (hydra-set-property\n        hydra :verbosity uniline-hint-style)))\n\n    ))\n\n(defvar uniline--current-interface)\n(setq uniline--current-interface ?h)\n\n(provide 'uniline-hydra)\n;;; uniline-hydra.el ends here\n"
  },
  {
    "path": "uniline-transient.el",
    "content": ";;; uniline-transient.el --- Add▶ ■─UNICODE based diagrams─■ to▶ ■─text files─■ -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; Package-Requires: ((emacs \"29.1\") (transient \"0.12.0\"))\n;; Keywords: convenience, text\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n;;; Commentary:\n;;                ┏━━━━━━━┓\n;;    ╭──────╮    ┃ thick ┣═◁═╗\n;;    │ thin ┝◀━━━┫ box   ┃   ║\n;;    │ box  │    ┗━━━━━━━┛   ║\n;;    ╰───┬──╯         ╔══════╩═╗\n;;        ↓            ║ double ║\n;;        ╰────────────╢ box    ║\n;;                     ╚════╤═══╝\n;;      ▛▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▜   │\n;;      ▌quadrant-blocks▐─◁─╯\n;;      ▙▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▟\n;;\n;;╭─Pure text────────────────□\n;;│ UNICODE characters are available to draw nice boxes and lines.\n;;│ They come in 4 flavours: thin, thick, double, and quadrant-blocks.\n;;│ Uniline makes it easy to draw and combine all 4 flavours.\n;;│ Use the arrows on the keyboard to move around leaving a line behind.\n;;╰──────────────────────────╮\n;;╭─Minor mode───────────────╯\n;;│ Uniline is a minor mode.  Enter it with:\n;;│   M-x uniline-mode\n;;│ Leave it with:\n;;│   C-c C-c\n;;╰──────────────────────────╮\n;;╭─Fonts────────────────────╯\n;;│ A font able to displays the needed UNICODE characters have to\n;;│ be used.  It works well with the following families:\n;;│ - DejaVu Sans Mono\n;;│ - Unifont\n;;│ - Hack\n;;│ - JetBrains Mono\n;;│ - Cascadia Mono\n;;│ - Agave\n;;│ - JuliaMono\n;;│ - FreeMono\n;;│ - Iosevka Comfy Fixed, Iosevka Comfy Wide Fixed\n;;│ - Aporetic Sans Mono, Aporetic Serif Mono\n;;│ - Source Code Pro\n;;╰──────────────────────────╮\n;;╭─UTF-8────────────────────╯\n;;│ Also, the encoding of the file must support UNICODE.\n;;│ One way to do that, is to add a line like this one\n;;│ at the top of your file:\n;;│   -*- coding:utf-8; -*-\n;;╰──────────────────────────╮\n;;╭─Hydra or Transient───────╯\n;;│ Uniline comes with two flavours of user interfaces:\n;;│ Hydra and Transient.\n;;│ Both versions are compiled when installing the package.\n;;│\n;;│ Then one or the other packages must be loaded (not both)\n;;│ for example with:\n;;│   (require 'uniline-hydra)\n;;│ or\n;;│   (use-package uniline-hydra\n;;│     :bind (\"C-<insert>\" . uniline-mode))\n;;│\n;;│ This file, uniline-transient.el, implements the Transient interface\n;;│ and calls the functions defined by uniline-core.el\n;;╰──────────────────────────□\n\n;;; Requires:\n(require 'uniline-core)\n\n;;; Code:\n\n;;;╭───────────────────╮\n;;;│Transient interface│\n;;;╰───────────────────╯\n\n(require 'transient)\n\n(defun uniline--self-insert-command (N)\n  \"To fool transient into thinking this is NOT self-insert-command.\"\n  (interactive)\n  (self-insert-command N))\n\n(eval-and-compile\n  (declare-function uniline-customize-hydra-or-transient (type)))\n\n(eval-and-compile\n  ;; a kludge so that Uniline works both with transient 0.13.0\n  ;; or earlier, without generating warnings\n  (if (and (boundp 'transient-show-popup)\n           (not (boundp 'transient-show-menu)))\n      (defvaralias 'transient-show-menu 'transient-show-popup)))\n\n;; make this transient setting buffer local, so that Uniline can\n;; tweak it without touching other usages like Magit for instance\n(make-variable-buffer-local 'transient-show-menu)\n\n(defun uniline-toggle-hints (&optional notoggle)\n  \"Toggle between styles of transient hints.\nWhen NOTOGGLE is t, do not toggle `uniline-hint-style',\njust put everything in sync.\"\n  (interactive)\n  (unless notoggle\n    (setq transient-show-menu\n          (cond\n           ((eq transient-show-menu   t) nil)\n           ((eq transient-show-menu nil)   t)\n           ((numberp transient-show-menu)  t))))\n  (setq uniline-hint-style\n        (cond\n         ((eq transient-show-menu   t)   t)\n         ((eq transient-show-menu nil)   1)\n         ((numberp transient-show-menu)  1))))\n\n(transient-define-suffix uniline-toggle-transient-hints-suffix ()\n  \"Toggle between full and one-liner menus.\nAssociated with C-t, which does half the work natively in Transient:\none-liner → full menu.\nAdditionally, modify transient-show-menu so that the choice is remembered\nfor later menu invocation in the same Uniline session.\"\n  :transient 'transient--do-exit\n  (interactive)\n  (uniline-toggle-hints)\n  (setq transient--showp nil)\n  (eval-when-compile ;; suppress compilation warning \"slot :command unknown\"\n    (put :command 'slot-name t))\n  (transient-setup (eieio-oref (transient-prefix-object) 'command )))\n\n;; Define common command classes to control state transitions\n\n(transient-define-suffix uniline--persistent-command (&rest args)\n  \"Base class for commands that should keep the transient state active.\"\n  :transient t\n  (interactive)\n  args) ;; to avoid warnings\n\n(transient-define-suffix uniline--exit-command ()\n  \"Base class for commands that should exit the transient state.\"\n  :transient nil)\n\n(transient-define-prefix uniline-transient-customize ()\n  \"Preferences.\"\n  :info-manual \"(uniline) Customization\"\n  :transient-non-suffix 'transient-quit-one\n  [:class\n   transient-columns\n   :pad-keys t\n   [\"Current session\"\n    (\"f\" \"choose font\" uniline-transient-fonts :transient nil)\n    (\"h\" \"Hydra\"\n     (lambda () (interactive)\n       (load-library \"uniline-hydra\")\n       (declare-function uniline-hydra-customize/body \"uniline-hydra\" ())\n       (uniline-hydra-customize/body))\n     :transient nil)\n    (\"C-t\" \"large hints\" uniline-toggle-transient-hints-suffix)\n    ]\n   [\"Future sessions\"\n    (\"g\" \"uniline group\" (lambda () (interactive) (customize-group 'uniline)))\n    (\"H\" \"Hydra     (change .emacs)\"\n     (lambda () (interactive) (uniline-customize-hydra-or-transient \"hydra\"    ))\n     :transient nil)\n    (\"T\" \"Transient (change .emacs)\"\n     (lambda () (interactive) (uniline-customize-hydra-or-transient \"transient\"))\n     :transient nil)\n    (\"l\" \"line spacing\" (lambda () (interactive) (customize-variable 'line-spacing)))\n    ]]\n  (interactive)\n  (transient-setup 'uniline-transient-customize))\n\n(transient-define-prefix uniline-transient-fonts ()\n  \"Font selection menu.\"\n  :info-manual \"(uniline) Which fonts?\"\n  :transient-non-suffix 'transient-quit-one\n  [:class\n   transient-columns\n   :pad-keys t\n   [\"Try a font\"\n    (\"d\" (lambda () (uniline--font-name-ticked ?d)) uniline--set-font-d :transient t)\n    (\"h\" (lambda () (uniline--font-name-ticked ?h)) uniline--set-font-h :transient t)\n    (\"c\" (lambda () (uniline--font-name-ticked ?c)) uniline--set-font-c :transient t)\n    (\"j\" (lambda () (uniline--font-name-ticked ?j)) uniline--set-font-j :transient t)\n    (\"s\" (lambda () (uniline--font-name-ticked ?s)) uniline--set-font-s :transient t)]\n   [\"\"\n    (\"b\" (lambda () (uniline--font-name-ticked ?b)) uniline--set-font-b :transient t)\n    (\"f\" (lambda () (uniline--font-name-ticked ?f)) uniline--set-font-f :transient t)\n    (\"a\" (lambda () (uniline--font-name-ticked ?a)) uniline--set-font-a :transient t)\n    (\"u\" (lambda () (uniline--font-name-ticked ?u)) uniline--set-font-u :transient t)]\n   [\"\"\n    (\"i\" (lambda () (uniline--font-name-ticked ?i)) uniline--set-font-i :transient t)\n    (\"I\" (lambda () (uniline--font-name-ticked ?I)) uniline--set-font-I :transient t)\n    (\"p\" (lambda () (uniline--font-name-ticked ?p)) uniline--set-font-p :transient t)\n    (\"P\" (lambda () (uniline--font-name-ticked ?P)) uniline--set-font-P :transient t)]\n   [\"Actions\"\n    (\"*\"   \"Configure\"  uniline-customize-face)\n    (\"C-t\" \"Togg hints\" uniline-toggle-transient-hints-suffix)\n    (\"q\"   \"Quit\"       transient-quit-one)\n    (\"RET\" \"Quit\"       (lambda () (interactive)) :transient nil)]]\n  (interactive)\n  (transient-setup 'uniline-transient-fonts))\n\n(transient-define-prefix uniline-transient-arrows ()\n  \"Arrows and shapes interface.\"\n  :info-manual \"uniline\"\n  :transient-suffix 'transient--do-leave\n  :transient-non-suffix 'transient--do-leave\n  [:class\n   transient-columns\n   :pad-keys t\n   [\"Insert\"\n    (\"a\" \"▷▶→▹▸↔\"  uniline-insert-fw-arrow  :transient t)\n    (\"s\" \"□■◆◊\"    uniline-insert-fw-square :transient t)\n    (\"o\" \"·●◦Øø\"   uniline-insert-fw-oshape :transient t)\n    (\"x\" \"╳╱╲÷×±¤\" uniline-insert-fw-cross  :transient t)\n    (\"SPC\" \" ░▒▓█\" uniline-insert-fw-grey   :transient t)]\n   [\"\"\n    (\"A\" \"↔▸▹→▶▷\"  uniline-insert-bw-arrow  :transient t)\n    (\"S\" \"◊◆■□\"    uniline-insert-bw-square :transient t)\n    (\"O\" \"øØ◦●·\"   uniline-insert-bw-oshape :transient t)\n    (\"X\" \"¤±×÷╲╱╳\" uniline-insert-bw-cross  :transient t)\n    (\"DEL\" \"█▓▒░ \" uniline-insert-bw-grey   :transient t)]\n   [\"\"\n    (\"-\" \"\" uniline--self-insert--     :transient t)\n    (\"+\" \"\" uniline--self-insert-+     :transient t)\n    (\"=\" \"\" self-insert-command :transient t)\n    (\"#\" \"\" self-insert-command :transient t)\n    (\"~\" \"\" self-insert-command :transient t)]\n   [\"Rotate,tweak\"\n    (\"S-<up>\"    \"↑\" uniline-rotate-up↑ :transient t)\n    (\"S-<right>\" \"→\" uniline-rotate-ri→ :transient t)\n    (\"S-<down>\"  \"↓\" uniline-rotate-dw↓ :transient t)\n    (\"S-<left>\"  \"←\" uniline-rotate-lf← :transient t)]\n   [\"Text dir\"\n    (\"C-<up>\"    \"↑\" uniline-text-direction-up↑ :transient nil)\n    (\"C-<right>\" \"→\" uniline-text-direction-ri→ :transient nil)\n    (\"C-<down>\"  \"↓\" uniline-text-direction-dw↓ :transient nil)\n    (\"C-<left>\"  \"←\" uniline-text-direction-lf← :transient nil)]\n   [\"Contour,fill\"\n    (\"c\" \"Draw  cnt\" uniline-contour)\n    (\"C\" \"Ovwrt cnt\" (lambda () (interactive) (uniline-contour t)))\n    (\"i\" \"Fill area\" uniline-fill)]\n   [\"Navigation\"\n    (\"*\"  \"Customize\" uniline-transient-customize)\n    (\"f\"  \"Font\"   uniline-transient-fonts)\n    (\"C-t\" \"Hints\" uniline-toggle-transient-hints-suffix)\n    (\"RET\" \"Quit\" (lambda () (interactive)) :transient nil)]]\n  (interactive)\n  ;; the purpose of this keymap handling is to regain the basic behavior\n  ;; of <up> & <down>\n  ;; those keys were captured by Transient to navigate in the transient menu\n  ;; the desired behavior is to exit this Transient menu and trace lines\n  (let ((transient-popup-navigation-map\n         (define-keymap\n           \"<down-mouse-1>\" #'transient-noop\n           \"C-r\"    #'transient-isearch-backward\n           \"C-s\"    #'transient-isearch-forward\n           \"M-RET\"  #'transient-push-button)))\n    (transient-setup 'uniline-transient-arrows)))\n\n(transient-define-prefix uniline-transient-alt-styles ()\n  \"Change lines style interface.\"\n  :info-manual \"(uniline) Rectangular actions\"\n  :transient-non-suffix 'transient-quit-one\n  [:class\n   transient-columns\n   :pad-keys t\n   [\"Dashes\"\n    (\"3\"    \"3x2 dots\" uniline-change-style-dot-3-2      :transient t)\n    (\"4\"    \"4x4 dots\" uniline-change-style-dot-4-4      :transient t)\n    (\"h\" \"hard corner\" uniline-change-style-hard-corners :transient t)]\n   [\"Thickness\"\n    (\"-\"        \"thin\" uniline-change-style-thin         :transient t)\n    (\"+\"       \"thick\" uniline-change-style-thick        :transient t)\n    (\"=\"      \"double\" uniline-change-style-double       :transient t)]\n   [\"Base style\"\n    (\"0\"    \"standard\" uniline-change-style-standard     :transient t)\n    (\"a\"        \"aa2u\" uniline-aa2u-rectangle            :transient t)]\n   ;;[\"Move rectangle\"\n   ;; (\"<right>\" \"→\" uniline-move-rect-ri→ :transient t)\n   ;; (\"<left>\"  \"←\" uniline-move-rect-lf← :transient t)\n   ;; (\"<up>\"    \"↑\" uniline-move-rect-up↑ :transient t)\n   ;; (\"<down>\"  \"↓\" uniline-move-rect-dw↓ :transient t)]\n   [\"Misc\"\n    (\"f\"        \"fonts\" uniline-transient-fonts)\n    (\"C-t\" \"Togg hints\" uniline-toggle-transient-hints-suffix)\n    (\"s\"         \"back\" uniline-transient-moverect)\n    (\"RET\"       \"exit\" uniline--rect-quit)]\n   ]\n  (interactive)\n  (rectangle-mark-mode 1)\n  (transient-setup 'uniline-transient-alt-styles))\n\n(transient-define-prefix uniline-transient-moverect ()\n  \"Rectangle manipulation interface.\"\n  :info-manual \"(uniline) Rectangular actions\"\n  :transient-non-suffix 'transient-quit-one\n  [:class\n   transient-columns\n   :pad-keys t\n   [\"Move\"\n    (\"<left>\"  \"←\" uniline-move-rect-lf← :transient t)\n    (\"<right>\" \"→\" uniline-move-rect-ri→ :transient t)\n    (\"<up>\"    \"↑\" uniline-move-rect-up↑ :transient t)\n    (\"<down>\"  \"↓\" uniline-move-rect-dw↓ :transient t)]\n   [\"Draw\"\n    (\"r\"     \"Trace inner\" uniline-draw-inner-rectangle      :transient t)\n    (\"R\"     \"Trace outer\" uniline-draw-outer-rectangle      :transient t)\n    (\"C-r\"   \"Ovwrt inner\" uniline-overwrite-inner-rectangle :transient t)\n    (\"C-S-R\" \"Ovwrt outer\" uniline-overwrite-outer-rectangle :transient t)\n    (\"i\"     \"Fill\"        uniline-fill-rectangle            :transient t)]\n   [\"Copy-paste\"\n    (\"c\" \"Copy\" uniline-copy-rectangle :transient nil)\n    (\"k\" \"Kill\" uniline-kill-rectangle :transient nil)\n    (\"y\" \"Yank\" uniline-yank-rectangle :transient t)]\n   [\"Brush\"\n    (\"-\"   \"╭─╯\" uniline-set-brush-1          :transient t)\n    (\"+\"   \"┏━┛\" uniline-set-brush-2          :transient t)\n    (\"=\"   \"╔═╝\" uniline-set-brush-3          :transient t)\n    (\"#\"   \"▄▄▟\" uniline-set-brush-block      :transient t)\n    (\"~\"   \"┄┄┄\" uniline-set-brush-dot-toggle :transient t)\n    (\"DEL\" \"DEL\" uniline-set-brush-0          :transient t)]\n   [\"Misc\"\n    (\"s\"   \"Line styles\" uniline-transient-alt-styles)\n    (\"f\"   \"Choose font\" uniline-transient-fonts)\n    ;;(\"C-x C-x\" \"Exchg point-mark\" rectangle-exchange-point-and-mark :transient t)\n    (\"C-t\" \"Togg hints\"  uniline-toggle-transient-hints-suffix)\n    (\"RET\" \"Exit\"        uniline--rect-quit)]\n   ]\n  (interactive)\n  (rectangle-mark-mode 1)\n  (transient-setup 'uniline-transient-moverect))\n\n;; those low-value helper-functions are needed because for an unknown reason\n;; calling a macro exits a transient menu, so we have to re-enter it\n(defun uniline--transient-call-macro-in-direction-up↑ ()\n  (interactive)\n  (uniline-call-macro-in-direction-up↑)\n  (transient-setup 'uniline-transient-macro-exec))\n(defun uniline--transient-call-macro-in-direction-ri→ ()\n  (interactive)\n  (uniline-call-macro-in-direction-ri→)\n  (transient-setup 'uniline-transient-macro-exec))\n(defun uniline--transient-call-macro-in-direction-dw↓ ()\n  (interactive)\n  (uniline-call-macro-in-direction-dw↓)\n  (transient-setup 'uniline-transient-macro-exec))\n(defun uniline--transient-call-macro-in-direction-lf← ()\n  (interactive)\n  (uniline-call-macro-in-direction-lf←)\n  (transient-setup 'uniline-transient-macro-exec))\n(defun uniline--transient-call-macro ()\n  (interactive)\n  (kmacro-end-and-call-macro 1)\n  (transient-setup 'uniline-transient-macro-exec))\n\n(defun uniline-macro-exec ()\n  (interactive)\n  (transient-setup 'uniline-transient-macro-exec))\n\n(transient-define-prefix uniline-transient-macro-exec ()\n  \"Macro execution interface.\"\n  :info-manual \"(uniline) Macros\"\n  :transient-non-suffix 'transient-quit-one\n  [:class\n   transient-columns\n   :pad-keys t\n   [\"Call macro in direction\"\n    (\"<right>\"   \"→\" uniline--transient-call-macro-in-direction-ri→)\n    (\"<up>\"      \"↑\" uniline--transient-call-macro-in-direction-up↑)\n    (\"<down>\"    \"↓\" uniline--transient-call-macro-in-direction-dw↓)\n    (\"<left>\"    \"←\" uniline--transient-call-macro-in-direction-lf←)]\n   [\"\"\n    (\"e\"   \"Normal call\" uniline--transient-call-macro)\n    (\"C-t\" \"Togg hints\"  uniline-toggle-transient-hints-suffix)\n    (\"RET\" \"Quit\"        transient-quit-one)\n    (\"q\"   \"Quit\"        transient-quit-one)]\n   ]\n  (interactive)\n  (transient-setup 'uniline-transient-macro-exec))\n\n(eval-when-compile\n  ;; this ugly patch removes dumb compilation warnings.\n  ;; they appear when loading this file, then byte-compiling it.\n  (dolist\n      (s '(uniline-transient-moverect\n           uniline-transient-arrows))\n    (plist-put (symbol-plist s) 'interactive-only nil)))\n\n(defun uniline-launch-interface ()\n  \"Choose between rectangle and arrows interface based on selection.\"\n  (interactive)\n  (if (region-active-p)\n      (uniline-transient-moverect)\n    (uniline-transient-arrows)))\n\n(defvar uniline--current-interface)\n(setq uniline--current-interface ?t)\n\n(provide 'uniline-transient)\n;;; uniline-transient.el ends here\n"
  },
  {
    "path": "uniline.el",
    "content": ";;; uniline.el --- Add▶ ■─UNICODE based diagrams─■ to▶ ■─text files─■ -*- coding:utf-8; lexical-binding: t; -*-\n\n;; Copyright (C) 2024-2026  Thierry Banel\n\n;; Author: Thierry Banel tbanelwebmin at free dot fr\n;; Version: 1.0\n;; Package-Requires: ((emacs \"29.1\") (hydra \"0.15.0\"))\n;; Keywords: convenience, text\n;; URL: https://github.com/tbanel/uniline\n\n;; Uniline is free software: you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation, either version 3 of the License, or\n;; (at your option) any later version.\n\n;; Uniline is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n;;; Commentary:\n;;                ┏━━━━━━━┓\n;;    ╭──────╮    ┃ thick ┣═◁═╗\n;;    │ thin ┝◀━━━┫ box   ┃   ║\n;;    │ box  │    ┗━━━━━━━┛   ║\n;;    ╰───┬──╯         ╔══════╩═╗\n;;        ↓            ║ double ║\n;;        ╰────────────╢ box    ║\n;;                     ╚════╤═══╝\n;;      ▛▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▜   │\n;;      ▌quadrant-blocks▐─◁─╯\n;;      ▙▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▟\n;;\n;;╭─Pure text────────────────□\n;;│ UNICODE characters are available to draw nice boxes and lines.\n;;│ They come in 4 flavours: thin, thick, double, and quadrant-blocks.\n;;│ Uniline makes it easy to draw and combine all 4 flavours.\n;;│ Use the arrows on the keyboard to move around leaving a line behind.\n;;╰──────────────────────────╮\n;;╭─Minor mode───────────────╯\n;;│ Uniline is a minor mode.  Enter it with:\n;;│   M-x uniline-mode\n;;│ Leave it with:\n;;│   C-c C-c\n;;╰──────────────────────────╮\n;;╭─Fonts────────────────────╯\n;;│ A font able to displays the needed UNICODE characters have to\n;;│ be used.  It works well with the following families:\n;;│ - DejaVu Sans Mono\n;;│ - Unifont\n;;│ - Hack\n;;│ - JetBrains Mono\n;;│ - Cascadia Mono\n;;│ - Agave\n;;│ - JuliaMono\n;;│ - FreeMono\n;;│ - Iosevka Comfy Fixed, Iosevka Comfy Wide Fixed\n;;│ - Aporetic Sans Mono, Aporetic Serif Mono\n;;│ - Source Code Pro\n;;╰──────────────────────────╮\n;;╭─UTF-8────────────────────╯\n;;│ Also, the encoding of the file must support UNICODE.\n;;│ One way to do that, is to add a line like this one\n;;│ at the top of your file:\n;;│   -*- coding:utf-8; -*-\n;;╰──────────────────────────╮\n;;╭─Hydra or Transient───────╯\n;;│ Uniline comes with two flavours of user interfaces:\n;;│ Hydra and Transient.\n;;│ Both versions are compiled when installing the package.\n;;│\n;;│ Then one or the other packages must be loaded (not both)\n;;│ for example with:\n;;│   (require 'uniline-hydra)\n;;│ or\n;;│   (use-package uniline-hydra\n;;│     :bind (\"C-<insert>\" . uniline-mode))\n;;│\n;;│ This file, uniline-core.el, is the largest one, the one\n;;│ implementing all the core functions independent from\n;;│ Hydra or Transient\n;;╰──────────────────────────□\n\n;;; Requires:\n(require 'uniline-hydra)\n\n;; this is an alias for uniline-hydra\n\n(provide 'uniline)\n;;; uniline.el ends here\n"
  },
  {
    "path": "uniline.info",
    "content": "This is uniline.info, produced by makeinfo version 6.8 from\nuniline.texi.\n\n\nINFO-DIR-SECTION Emacs\nSTART-INFO-DIR-ENTRY\n* Uniline: (uniline).     Draw UNICODE diagrams\nEND-INFO-DIR-ENTRY\n\nINFO-DIR-SECTION Misc\nSTART-INFO-DIR-ENTRY\n* (uniline).            Uniline.\nEND-INFO-DIR-ENTRY\n\n\u001f\nFile: uniline.info,  Node: Top,  Next: Getting started in 10 seconds,  Up: (dir)\n\nUniline\n*******\n\n* Menu:\n\n* Getting started in 10 seconds::\n* New::\n* Gallery pure UNICODE diagrams in Emacs::\n* A minor mode for drawing::\n* The <insert> key::\n* Glyphs ▷ ▶ → □ ◆ ╮─ insertion & modification::\n* Rectangular actions::\n* Long range actions contour and flood-fill::\n* Macros::\n* Which fonts?::\n* Hydra or Transient?::\n* Customization::\n* How Uniline behaves with its environment?::\n* Lisp API::\n* Mouse support::\n* Installation::\n* Related packages::\n* Author, contributors: Author contributors.\n* License::\n\n— The Detailed Node Listing —\n\nGallery: pure UNICODE diagrams in Emacs\n\n* Document a command::\n* Connect boxes with arrows::\n* Explain decisions trees::\n* Draw lines or blocks::\n* Outline the General Relativity and the Schrödinger's equations::\n* Explain the structure of a sentence in a foreign language::\n* Draw electronic diagrams::\n* Explain Lisp lists::\n* Draw sketched objects::\n* Pure text::\n* Beware!::\n\nA minor mode for drawing\n\n* Minor mode::\n* Draw lines by moving the cursor::\n* Infinite ∞ buffer::\n* Brush style::\n* Text direction::\n\nGlyphs ‘▷ ▶ → □ ◆ ╮─’ insertion & modification\n\n* Arrows glyphs ▷ ▶ → ▹ ▸ ↔::\n* Intersection glyphs ■ ◆ ●::\n* Fine tweaking of lines::\n\nRectangular actions\n\n* Drawing a rectangle::\n* Filling a rectangle::\n* Moving a rectangular region::\n* Copying, killing, yanking a rectangular region: Copying killing yanking a rectangular region.\n* Dashed lines and other styles::\n* ASCII to UNICODE::\n\nLong range actions: contour and flood-fill\n\n* Tracing a contour::\n* Flood-fill::\n\nWhich fonts?\n\n* Recommended fonts::\n* Use case mixing fonts::\n\nHydra or Transient?\n\n* Selecting Hydra or Transient::\n* Instantly selecting Hydra or Transient::\n* One-liner menus::\n* The Hydra interface::\n* The Transient interface::\n\nCustomization\n\n* Interface type::\n* Insert key::\n* Maximum steps when drawing a contour::\n* Cursor type::\n* Hint style::\n* Welcome message visibility::\n* Line spacing::\n* Font::\n* Upward infiniteness ∞::\n\nHow Uniline behaves with its environment?\n\n* Language environment::\n* Compatibility with Picture-mode::\n* Compatibility with Artist-mode::\n* Compatibility with Whitespace-mode::\n* Compatibility with Org Mode::\n* Org Mode and LaTex::\n* What about \\t tabs?::\n* What about ^L page separation?::\n* Emacs on the Linux console::\n* Emacs on a graphical terminal emulator::\n* Emacs on Windows::\n* Compatibility with ASCIIFlow::\n\nLisp API\n\n* Move the cursor::\n* Brush::\n* Example Lisp function to draw a plus sign::\n* Long range actions (contour, flood-fill, rectangle): Long range actions (contour flood-fill rectangle).\n* Constants::\n* Macro and text direction::\n* Insert and tweak glyphs::\n* Change to alternate styles::\n\nInstallation\n\n* use-package, the straightforward way: use-package the straightforward way.\n* Without use-package::\n\n\n\u001f\nFile: uniline.info,  Node: Getting started in 10 seconds,  Next: New,  Prev: Top,  Up: Top\n\n1 Getting started in 10 seconds\n*******************************\n\n   • Type ‘M-x uniline-mode’\n   • Move cursor with the arrow-keys on the keyboard ‘→ ← ↑ ↓’\n   • Quit ‘C-c C-c’\n\n\n\n     ╷   ╭─────────╮\n     ╰───┤my first ├─╮\n         │drawing  │ ╰───╮\n         ╰─────────╯     │\n            ╭────┬───────╯\n            ╰────╯\n\n\u001f\nFile: uniline.info,  Node: New,  Next: Gallery pure UNICODE diagrams in Emacs,  Prev: Getting started in 10 seconds,  Up: Top\n\n2 New\n*****\n\nCustomization & settings now consistently available from the application\nmenu, from Hydra, and from Transient.\n\n   Type ‘<INS> *’.\n\n\u001f\nFile: uniline.info,  Node: Gallery pure UNICODE diagrams in Emacs,  Next: A minor mode for drawing,  Prev: New,  Up: Top\n\n3 Gallery: pure UNICODE diagrams in Emacs\n*****************************************\n\nDraw diagrams like those:\n\n* Menu:\n\n* Document a command::\n* Connect boxes with arrows::\n* Explain decisions trees::\n* Draw lines or blocks::\n* Outline the General Relativity and the Schrödinger's equations::\n* Explain the structure of a sentence in a foreign language::\n* Draw electronic diagrams::\n* Explain Lisp lists::\n* Draw sketched objects::\n* Pure text::\n* Beware!::\n\n\u001f\nFile: uniline.info,  Node: Document a command,  Next: Connect boxes with arrows,  Up: Gallery pure UNICODE diagrams in Emacs\n\n3.1 Document a command\n======================\n\n\n\n        pdfjam source.pdf 3-5,9\n       ╶─────▲────▲────────▲──▲╴\n     command╶╯    │        │  │\n     input file╶──╯        │  │\n     select pages 3,4,5╶───╯  │\n     and page 9╶──────────────╯\n\n\u001f\nFile: uniline.info,  Node: Connect boxes with arrows,  Next: Explain decisions trees,  Prev: Document a command,  Up: Gallery pure UNICODE diagrams in Emacs\n\n3.2 Connect boxes with arrows\n=============================\n\n\n\n                 ╭───────────────────────╮\n       ╷123╭────▶┤ hundred and something │\n       ╰───╯     ╰───────────────────────╯\n                                  ╭────▶──╮A╷\n         ╭───╮    ┏━━━┓    ╔═══╗  │       ╰─╯\n     0╶─→┤ 1 ┝━━━▶┫ 2 ┣═══▷╣ 3 ╟──●────▶──╮B╷\n         ╰───╯    ┗━┯━┛    ╚═╤═╝  │       ╰─╯\n                    ╰────←───╯    ╰────▶──╮C╷\n                                          ╰─╯\n        ╔══════════╗\n        ║ 1        ║          ▐▀▀▀▀▀▀▀▀▜\n        ║    ╭─────╫───╮ ◁──▷ ▐ 3      ▐\n        ╚════╪═════╝ 2 │      ▐▄▄▄▄▄▄▄▄▟\n             ╰─────────╯\n\n\u001f\nFile: uniline.info,  Node: Explain decisions trees,  Next: Draw lines or blocks,  Prev: Connect boxes with arrows,  Up: Gallery pure UNICODE diagrams in Emacs\n\n3.3 Explain decisions trees\n===========================\n\n\n\n     ┏━━━━━━━━━━━━┓\n     ┃which color?┃\n     ┗━┯━━━━━━━━━━┛\n       │     ╭──────╮\n       │  ╭──┤yellow├─▷╮good─choice╭□\n       ▽  │  ╰──────╯  ╰═══════════╯\n       ╰──●  ╭───╮    ┏━━━━━┓\n          ├──┤red├───▷┨dark?┠──╮\n          │  ╰───╯    ┗━━━━━┛  │\n          │ ╭───◁──────────────╯\n          │ │   ╭───╮\n          │ ╰─●─┤yes├▷╮regular─red╭─□\n          │   │ ╰───╯ ╰═══════════╯\n          │   │ ╭──╮\n          │   ╰─┤no├─▷╮pink╭────────□\n          │     ╰──╯  ╰════╯\n          │  ╭────╮\n          ├──┤blue├───▷╮next week╭──□\n          │  ╰────╯    ╰═════════╯\n          │  ╭─────╮\n          ╰──┤white├──▷╮available╭──□\n             ╰─────╯   ╰═════════╯\n\n\u001f\nFile: uniline.info,  Node: Draw lines or blocks,  Next: Outline the General Relativity and the Schrödinger's equations,  Prev: Explain decisions trees,  Up: Gallery pure UNICODE diagrams in Emacs\n\n3.4 Draw lines or blocks\n========================\n\n\n\n                                   ╭─╮←─╮\n                              ╭╮   │ │  ╰──╴max 235\n                            ╭╮││  ╭╯ │\n                            │╰╯│╭─╯  │\n           ╭╮               │  ││    │\n        ╭─╮││╭╮   ╭──╮╭╮    │  ╰╯    ╰╮\n       ╭╯ ╰╯╰╯│  ╭╯  ╰╯╰─╮  │         │ ╭╮\n     ◁─╯      ╰──╯       ╰──╯         ╰─╯╰────▷\n     ◀════════════════════════════════════════▶\n                            ╭────────╮\n        ▲                   │all time│\n        ┃       ▄     ▗▟█ ←─┤highest │\n       Qdx      █▌   ████   ╰────────╯\n        ┃     ▗▄█▌   █████▙\n        ┃   ▟███████▄█████████▄▄▄     ▗▄\n        ┃▐▄▄████████████████████████████▄▄▖\n         ╺━━━━━━━━━━╸time╺━━━━━━━━━━━━━━━━▶\n\n\n\u001f\nFile: uniline.info,  Node: Outline the General Relativity and the Schrödinger's equations,  Next: Explain the structure of a sentence in a foreign language,  Prev: Draw lines or blocks,  Up: Gallery pure UNICODE diagrams in Emacs\n\n3.5 Outline the General Relativity and the Schrödinger’s equations\n==================================================================\n\n\n\n\n          ╭─────────────────────╴G: Einstein tensor\n          │                ╭────╴κ: Gravitational coupling constant\n       ╭──▽───╮        ╭───▽──╮\n     ┏━┷━━━━━━┷━━━━━━━━┷━━━━━━┷━━━┓\n     ┃ R - gR/2 + Λg = (8πG/c⁴)×T ┃◁╴General Relativity equation\n     ┗━△━━━△△━━━━━△△━━━━━━△━△━━━△━┛\n       │   ││     ││      │ │  ╭╯\n       │   ││     ││      │ │  ╰╴Energy-impulsion tensor\n       │   ││     ││      │ ╰───╴Speed of light\n       │   ││     ││      ╰─────╴Gravitational constant\n       │   ││     ╰┴────────────╴Cosmological constant\n       │   │╰──────┴────────────╴Scalar curvature\n       │   ╰───────╰────────────╴Metric tensor\n       ╰────────────────────────╴Ricci tensor\n\n\n\n\n\n            ╭─────────────────────╴Derivative over time\n            │     ╭──────────╭────╴State of quantum system at time t\n            │     │          │     (the square of its absolute value\n           ╭▽─╮ ╭─▽──╮     ╭─▽──╮   is the probability density)\n     ┏━━━━━┷━━┷━┷━━━━┷━━━━━┷━━━━┷━┓\n     ┃ i ħ d/dt |Ψ(t)> = Ĥ |Ψ(t)> ┃◁─╴Schrödinger's equation\n     ┗━△━△━━━━△━━━━△━━━━━△━━━━△━━━┛\n       │ │    ╰────╰─────┤────╰───╴Time\n       │ │               ╰────────╴Hamiltonian\n       │ ╰────────────────────────╴Reduced Plank constant\n       ╰──────────────────────────╴Imaginary number i²=-1\n\n\n\u001f\nFile: uniline.info,  Node: Explain the structure of a sentence in a foreign language,  Next: Draw electronic diagrams,  Prev: Outline the General Relativity and the Schrödinger's equations,  Up: Gallery pure UNICODE diagrams in Emacs\n\n3.6 Explain the structure of a sentence in a foreign language\n=============================================================\n\n(which language?)\n\n\n\n\n     ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n     ┃ the pretty table is standing ┃\n     ┗┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n      │    ╭────┬─────┬─────╴radicals\n      ↕   ╭┴╮  ╭┴─╮  ╭┴─╮\n     ┏┷━━━┿━┿━━┿━━┿━━┿━━┿━━━┓\n     ┃ la bela tablo staras ┃\n     ┗━━━━┿━┿△━┿━━┿△━┿━━┿△━━┛\n          ╰─╯│ ╰──╯│ ╰──╯│  ┏━━━━━suffixes━━━━━┓\n             │     │     ╰──╂╴as: present tense┃\n             │     │        ┃ os: future tense ┃\n             │     │        ┃ is: past tense   ┃\n             │     ╰────────╂╴ o: noun         ┃\n             ╰──────────────╂╴ a: adjective    ┃\n                            ┃  e: adverb       ┃\n                            ┗━━━━━━━━━━━━━━━━━━┛\n\n\n\u001f\nFile: uniline.info,  Node: Draw electronic diagrams,  Next: Explain Lisp lists,  Prev: Explain the structure of a sentence in a foreign language,  Up: Gallery pure UNICODE diagrams in Emacs\n\n3.7 Draw electronic diagrams\n============================\n\n\n\n\n                   ╭────────╭──────────╮ ┏━━┓\n                   │       ╭┴╮         ╰─┨5V┃\n                  ╭┴╮      │░│           ┗━━┛\n                  │░│      │░│1KΩ\n                  │░│10KΩ  ╰┬╯\n             5μF  ╰┬╯       ├─────────────● →\n              ╷╷   │      ┠─╯           amplified\n      → ●─────┤├───┼──────┨             output\n     input    ╵╵   │      ┠▶╮  500μF    signal\n     signal       ╭┴╮       │   ╷╷\n                  │░│       ├───┤├──╮\n                  │░│1KΩ   ╭┴╮  ╵╵  │\n                  ╰┬╯      │░│      │    ╭────╮\n                   │       │░│470Ω  │    │ ╺━━┷━━╸\n                   │       ╰┬╯      │    │  ╺━━━╸\n                   ╰────────╰───────╰────╯   ╺━╸\n\n\n\u001f\nFile: uniline.info,  Node: Explain Lisp lists,  Next: Draw sketched objects,  Prev: Draw electronic diagrams,  Up: Gallery pure UNICODE diagrams in Emacs\n\n3.8 Explain Lisp lists\n======================\n\n\n\n       '(a b c)\n          ┏━━━┳━━━┓   ┏━━━┳━━━┓   ┏━━━┳━━━┓\n     ●━━━▶┫ ● ┃ ●─╂──▷┨ ● ┃ ●─╂──▷┨ ● ┃nil┃\n          ┗━┿━┻━━━┛   ┗━┿━┻━━━┛   ┗━┿━┻━━━┛\n            │           ╰──────────╮╰╮\n            │  ╭─────┬───────────╮ │ │\n            ╰─▷┤\"a\\0\"│properties │ │ │\n               ├─────┼───────────┤ │ │\n               │\"b\\0\"│properties ├◁╯ │\n               ├─────┼───────────┤   │\n               │\"c\\0\"│properties ├◁──╯\n               ├─────┼───────────┤\n               │...  │...        │\n               ╵     ╵           ╵\n\n\u001f\nFile: uniline.info,  Node: Draw sketched objects,  Next: Pure text,  Prev: Explain Lisp lists,  Up: Gallery pure UNICODE diagrams in Emacs\n\n3.9 Draw sketched objects\n=========================\n\n\n\n\n      ◀─(-)────────(+)──▶    ~╭──────╮~\n       ▗──────────────╮     ~~│ ╭~~╮ │~~\n       ▐              ╰╮     ~│ ╵  ╵ │~\n     ╭□▐   1.5 volts  ╭╯□╮    ╰─╖  ╓─╯\n     │ ▝▀▀▀▀▀▀▀▀▀▀▀▀▀▀▘  │      ╠━━╣\n     │                   ╰──────╯  │\n     ╰─────────────────────────────╯\n\n\n\n      ╶╮       ╭╴\n     ┏┳┥▒▒▒▒▒▒▒┝╸\n     ┃┃│▒▒eau▒▒│\n     ┃┃│▒▒▒▒▒▒▒│ ╔═════╗\n     ┃┃╰──╮▒╭──╯ ║ ╶╮  ▽           ╭╴\n     ┃┃    ▒     ║  │  ░           │\n     ┃┃    ▒     ║  │░░░░░░░░░░░░░░│\n     ┃┃    ╚═════╝  │░░░░░░░░░░░░░░╞════▷▒▒\n     ┃┃             │░░░░░akvo░░░░░│    ╶╮ ▒         ╭╴\n     ┃┃             │░░░░░░░░░░░░░░│     │  ▒        │\n     ┃┃             ╰─┲┳━━━━━━━━┳┱─╯     │▒▒▒▒▒▒▒▒▒▒▒│\n     ┃┃               ┃┃        ┃┃       │▒▒▒water▒▒▒│\n     ┃┃               ┃┃        ┃┃       │▒▒▒▒▒▒▒▒▒▒▒│\n     ┃┃               ┃┃        ┃┃       ╰───────────╯\n     ▝▀▀▀▀▀▀▘        ▝▀▘        ▝▀▘      ▀▀▀▀▀▀▀▀▀▀▀▀▀\n\n\u001f\nFile: uniline.info,  Node: Pure text,  Next: Beware!,  Prev: Draw sketched objects,  Up: Gallery pure UNICODE diagrams in Emacs\n\n3.10 Pure text\n==============\n\nThose diagrams are pure text.  There is nothing graphic.  They are\nachieved using UNICODE characters.  Therefore they can be drawn within\nany text formatted document, like Org Mode, Markdown, txt, comments in\nany programming language source code (C++, Python, Rust, D, JavaScript,\nGnuPlot, LaTex, whatever).\n\n   Most often, the text file will be encoded as UTF-8.  This is becoming\nthe de-facto standard for text and source code files.\n\n   Creating such diagrams by hand is painfully slow.  Use ‘Uniline’ to\ndraw lines while you move the cursor with keyboard arrows.\n\n\u001f\nFile: uniline.info,  Node: Beware!,  Prev: Pure text,  Up: Gallery pure UNICODE diagrams in Emacs\n\n3.11 Beware!\n============\n\nIf you see those diagrams miss-aligned, most likely the font used to\ndisplay them does not support UNICODE block characters.  See bellow the\nparagraph *note Which fonts?:: for details.\n\n   If you get misalignment when drawing, this could come from too wide\ncharacters.  Emojis are an example.  Usual characters may also be\nconsidered twice as wide as normal under some \"language environments\".\nSee the paragraph *note Language environment:: for details.\n\n\u001f\nFile: uniline.info,  Node: A minor mode for drawing,  Next: The <insert> key,  Prev: Gallery pure UNICODE diagrams in Emacs,  Up: Top\n\n4 A minor mode for drawing\n**************************\n\n* Menu:\n\n* Minor mode::\n* Draw lines by moving the cursor::\n* Infinite ∞ buffer::\n* Brush style::\n* Text direction::\n\n\u001f\nFile: uniline.info,  Node: Minor mode,  Next: Draw lines by moving the cursor,  Up: A minor mode for drawing\n\n4.1 Minor mode\n==============\n\n‘Uniline’ is a minor mode.  Activate it temporarily:\n\n   ‘M-x uniline-mode’\n\n   Exit it with:\n\n   ‘C-c C-c’\n\n   The current major mode is still active underneath ‘uniline-mode’.\n\n   While in ‘uniline-mode’, overwriting is active, as well as long lines\ntruncation.  Also, a hollow cursor is provided (customizable).  Those\nsettings are reset to their previous state when exiting ‘uniline-mode’.\n\n\u001f\nFile: uniline.info,  Node: Draw lines by moving the cursor,  Next: Infinite ∞ buffer,  Prev: Minor mode,  Up: A minor mode for drawing\n\n4.2 Draw lines by moving the cursor\n===================================\n\nUse keyboard arrows to draw lines.\n\n   By default, drawing lines only happens over empty space or over other\nlines.  If there is already text, it will not be erased.  However, by\nhitting the control-key while moving, lines overwrite whatever there is.\n\n   The usual numeric prefix is available.  For instance, to draw a line\n12 characters wide downward, type: ‘M-12 <down>’\n\n\u001f\nFile: uniline.info,  Node: Infinite ∞ buffer,  Next: Brush style,  Prev: Draw lines by moving the cursor,  Up: A minor mode for drawing\n\n4.3 Infinite ∞ buffer\n=====================\n\nThe buffer is infinite ∞ in the south and east directions.  Which means\nthat when the cursor ends up outside the buffer, white space characters\nare automatically added.\n\n   All algorithms also make use of the infiniteness of the buffer when\nneeded.  Those algorithms are: moving a rectangle, pasting a rectangle,\ndrawing the external border of a rectangular region, or drawing the\ncontour of a shape.\n\n   The buffer is also infinite ∞ in the upward direction.  That is\ncustomizable through the ‘uniline-infinite-up↑’ variable.  If its value\nis ‘t’, then the buffer is actually infinite ∞ upward.  If it is ‘nil’,\nthen the upper border of the buffer is a hard limit.  To customize,\ntype:\n\n   ‘M-x customize-variable uniline-infinite-up↑’\n\n   The buffer can be \"narrowed\", for instance with the ‘C-x n n’ or ‘M-x\nnarrow-to-region’ command.  In this case, the limits are those of the\nnarrow region.  When Uniline needs to bypass the up↑ or down↓ limits, it\nadds empty lines.  When widening again the buffer, the region which was\nnarrow will have increased.\n\n\u001f\nFile: uniline.info,  Node: Brush style,  Next: Text direction,  Prev: Infinite ∞ buffer,  Up: A minor mode for drawing\n\n4.4 Brush style\n===============\n\nSet the current brush with:\n\n   • ‘-’ single thin line ‘╭─┬─╮’\n\n   • ‘+’ single thick line ‘┏━┳━┓’\n\n   • ‘=’ double line ‘╔═╦═╗’\n\n   • ‘#’ quarter block ‘▙▄▟▀’\n\n   • ‘~’ toggle dotted lines ‘┄┄┄┄’\n\n   • ‘<delete>’ eraser\n\n   • ‘<return>’ move without drawing anything\n\n   The current brush and the current text direction (see *note Text\ndirection::) are reflected in the mode-line (at the bottom of the\n‘Emacs’ screen).  It looks like this:\n\n\n\n\n      current text                  current\n         direction╶────╮       ╭───╴brush\n                       ▼       ▼\n     ══════════════════╧═══════╧══════════════\n     U:** buff    (... →Uniline┼ ...)\n     ═════════════════════════════════════════\n\n\n   The dotted toggle ‘~’ is a modifier for the single thin and thick\nlines.  It circles along 3 styles:\n   • plain lines,\n   • 3 dots vertical, 2 dots horizontal,\n   • 4 dots both vertical & horizontal,\n   • back to plain line and so on.\n\n\n\n\n               ║   thin  ╷  thick  ╷\n               ║         │         │\n     ══════════╬═════════╪═════════╡\n               ║  ╭╌╌╌╮  │  ┏╍╍╍┓  │\n      3,2 dots ║  ┆   ┆  │  ┇   ┇  │\n               ║  ╰╌╌╌╯  │  ┗╍╍╍┛  │\n     ──────────╫─────────┼─────────┤\n               ║  ╭┈┈┈╮  │  ┏┉┉┉┓  │\n      4,4 dots ║  ┊   ┊  │  ┋   ┋  │\n               ║  ╰┈┈┈╯  │  ┗┉┉┉┛  │\n     ──────────╨─────────┴─────────╯\n\n\n   Note that the UNICODE standard offers very limited support for dotted\nlines.  Only vertical and horizontal lines are available.  So, no\ncrossing of line is possible.  In case a line crosses a dotted line,\nUniline falls back to a plain line crossing character (but still\npreserving thickness).  There is no dotted versions of double lines\neither.\n\n\u001f\nFile: uniline.info,  Node: Text direction,  Prev: Brush style,  Up: A minor mode for drawing\n\n4.5 Text direction\n==================\n\nUsually, inserting text in a buffer moves the cursor to the right.  (And\nsometimes to the left for some locales).  Any of the 4 directions can be\nselected under ‘Uniline’.  Just type any of:\n\n   • ‘<insert> C-<up>’\n   • ‘<insert> C-<right>’\n   • ‘<insert> C-<down>’\n   • ‘<insert> C-<left>’\n\n   The current direction is reflected in the mode-line, just before the\nword ‘\"uniline\"’.\n\n\u001f\nFile: uniline.info,  Node: The <insert> key,  Next: Glyphs ▷ ▶ → □ ◆ ╮─ insertion & modification,  Prev: A minor mode for drawing,  Up: Top\n\n5 The ‘<insert>’ key\n********************\n\nThe ‘<insert>’ key is a prefix for other keys:\n   • for drawing arrows, squares, crosses, o-shapes glyphs,\n   • for handling rectangles,\n   • for inserting ‘# = - +’ which otherwise change the brush style,\n   • for trying a choice of mono-spaced fonts.\n\n   Why ‘<insert>’?  Because:\n   • ‘Uniline’ tries to leave their original meaning to as many keys as\n     possible,\n   • the standard meaning of ‘<insert>’ is to toggle the\n     ‘overwrite-mode’; but ‘Uniline’ is already in ‘overwrite-mode’, and\n     de-activating overwrite would break ‘Uniline’.\n\n   So preempting ‘<insert>’ does not sacrifice anything.\n\n   *Customization*\n\n   Another key may be defined instead of ‘<insert>’.  Type:\n\n     M-x customize-variable uniline-key-insert\n\n\u001f\nFile: uniline.info,  Node: Glyphs ▷ ▶ → □ ◆ ╮─ insertion & modification,  Next: Rectangular actions,  Prev: The <insert> key,  Up: Top\n\n6 Glyphs ‘▷ ▶ → □ ◆ ╮─’ insertion & modification\n************************************************\n\nIndividual character glyphs may be inserted and changed.\n   • Put the cursor where a glyphs should be edited or inserted.\n   • Then press ‘<insert>’ (this key may be customized, see *note Insert\n     key::).\n\n   Arrows, squares, circles, crosses may be handled.  Also lines may be\nfine tweaked a single character at a time.\n\n* Menu:\n\n* Arrows glyphs ▷ ▶ → ▹ ▸ ↔::\n* Intersection glyphs ■ ◆ ●::\n* Fine tweaking of lines::\n\n\u001f\nFile: uniline.info,  Node: Arrows glyphs ▷ ▶ → ▹ ▸ ↔,  Next: Intersection glyphs ■ ◆ ●,  Up: Glyphs ▷ ▶ → □ ◆ ╮─ insertion & modification\n\n6.1 Arrows glyphs ‘▷ ▶ → ▹ ▸ ↔’\n===============================\n\nWhen inserting an arrow, it points in the direction that the line\ndrawing follows.\n\n   ‘Uniline’ supports 6 arrows types: ‘▷ ▶ → ▹ ▸ ↔’\n\n\n\n\n     □\n     ╰─◁──▷─╮       □─╮ ╭─╮ ╭─╮ ╭─□\n     ╭─◀──▶─╯         △ ▲ ↑ ▵ ▴ ↕\n     ╰─←──→─╮         │ │ │ │ │ │\n     ╭─◃──▹─╯         ▽ ▼ ↓ ▿ ▾ ↕\n     ╰─◂──▸─╮         ╰─╯ ╰─╯ ╰─╯\n     ╭─↔──↔─╯\n     □\n\n\n   Actually, there are tons of arrows of all styles in the UNICODE\nstandard.  Unfortunately, support by fonts is weak.  So ‘Uniline’\nrestrains itself to those six safe arrows.\n\n   To insert an arrow, type: ‘<insert> a’ or ‘<insert> a a’ or ‘<insert>\na a a’.  (‘a’ cycles through the 6 styles, ‘A’ cycles backward).\n\n   ‘<insert> 4 a’ is equivalent to ‘<insert> a a a a’, which is also\nequivalent to ‘<insert> A A A’.  Those 3 shortcuts insert an arrow of\nthis style: ‘▵▹▿◃’.  The actual direction where the arrow points follows\nthe last movement of the cursor.\n\n   To change the direction of the arrow, use shift-arrow, for example:\n‘S-<up>’ will change from ‘→’ to ‘↑’.\n\n\u001f\nFile: uniline.info,  Node: Intersection glyphs ■ ◆ ●,  Next: Fine tweaking of lines,  Prev: Arrows glyphs ▷ ▶ → ▹ ▸ ↔,  Up: Glyphs ▷ ▶ → □ ◆ ╮─ insertion & modification\n\n6.2 Intersection glyphs ‘■ ◆ ●’\n===============================\n\nThere are a few UNICODE characters which are mono-space and symmetric in\nthe 4 directions.  They are great at line intersections:\n\n   To insert a square ‘□ ■ ▫ ▪ ◆ ◊’ type: ‘<insert> s s s…’ (‘s’ cycles,\n‘S’ cycles backward).\n\n   To insert a circular shape ‘· ∙ • ● ◦ Ø ø’ type: ‘<insert> o o o…’\n(‘o’ cycles, ‘O’ cycles backward).\n\n   To insert a cross shape ‘╳ ╱ ╲ ÷ × ± ¤’ type: ‘<insert> x x x…’ (‘x’\ncycles, ‘X’ cycles backward).\n\n   To insert a grey character ‘░▒▓█’ from pure white to pure black type:\n‘<insert> SPC SPC SPC…’ or ‘<insert> DEL DEL DEL…’ (space key goes from\nwhite to black, back-space key goes from black to white)\n\n   To insert a usual ASCII letter or symbol, just type it.\n\n   As the keys ‘- + = # ~’ are preempted by ‘uniline-mode’, to type\nthem, prefix them with ‘<insert>’.  Example: ‘<insert> -’ inserts a ‘-’\nand ‘<insert> +’ inserts a ‘+’.\n\n\n\n\n     <insert>\n        │\n        ├────────────────────────────╮\n        ▼        ╭─arrows──────╮     ▼        ╭───╮\n        ╰──▶─(a)─┤ ▷ ▶ → ▹ ▸ ↔ │     ╰──▶─(+)─┤ + │\n        │        ╰─────────────╯     │        ╰───╯\n        │        ╭─squares─────╮     │        ╭───╮\n        ╰──▶─(s)─┤ □ ■ ▫ ▪ ◆ ◊ │     ╰──▶─(-)─┤ - │\n        │        ╰─────────────╯     │        ╰───╯\n        │        ╭─circles───────╮   │        ╭───╮\n        ╰──▶─(o)─┤ · ∙ • ● ◦ Ø ø │   ╰──▶─(=)─┤ = │\n        │        ╰───────────────╯   │        ╰───╯\n        │        ╭─crosses───────╮   │        ╭───╮\n        ╰──▷─(x)─┤ ╳ ╱ ╲ ÷ × ± ¤ │   ╰──▶─(#)─┤ # │\n        │        ╰───────────────╯   │        ╰───╯\n        │              ╭───────╮     │        ╭───╮\n        ╰──▶─(SPC DEL)─┤  ░▒▓█ │     ╰──▶─(~)─┤ ~ │\n                       ╰───────╯              ╰───╯\n\n\n\u001f\nFile: uniline.info,  Node: Fine tweaking of lines,  Prev: Intersection glyphs ■ ◆ ●,  Up: Glyphs ▷ ▶ → □ ◆ ╮─ insertion & modification\n\n6.3 Fine tweaking of lines\n==========================\n\n\n\n\n      convert this  ═══▶   into that\n     ╭───────────╮        ╭───────────╮\n     │╶───┬────▷ │        │╶───╮────▷ │\n     │    │      │        │    │      │\n     │           │        │           │\n     │    ▀▀▀    │        │    ▀▟▀    │\n     ╰───────────╯        ╰───────────╯\n\n\n   At the crossing of lines, it may be appealing to do small\nadjustments.  In the above example, we removed a segment of line which\noccupies 1/4 of a character.  This cannot be achieve with line tracing\nalone.  We also modified a quarter-block line in a non-obvious way.\n\n   • Put the point (the cursor) on the character where lines cross each\n     other.\n   • type ‘INS S-<right> S-<right>’\n\n   ‘<right>’ here refers to the right part of the character under the\npoint.  The 1/4 line segment will cycle through all displayable forms.\nOn the second stroke, no segment will be displayed, which is what we\nwant.\n\n   Caveat!  The UNICODE standard does not define all possible\ncombinations including double line segments.  (It does for all\ncombinations of thin and tick lines).  So sometimes, when working with\ndouble lines, the process may be frustrating.\n\n   This works also for lines made of quarter-blocks.  There are 4\nquarter-blocks in a character, either on or off.  Each of the 4 shifted\nkeyboard arrows flips a quarter-block on-and-off.\n\n   In the above example, the effect was achieved with: ‘INS S-<up>\nS-<down> S-<left>’\n\n\u001f\nFile: uniline.info,  Node: Rectangular actions,  Next: Long range actions contour and flood-fill,  Prev: Glyphs ▷ ▶ → □ ◆ ╮─ insertion & modification,  Up: Top\n\n7 Rectangular actions\n*********************\n\n   • Drawing,\n   • filling,\n   • moving,\n   • copying & yanking,\n   • change line & glyph styles,\n\n   those actions may be performed on a rectangular selection.\n\n   Select a rectangular region with ‘C-SPC’ or ‘C-x SPC’ and move the\ncursor.\n\n   You may also use ‘S-<arrow>’ (‘<arrow>’ being any of the 4\ndirections) to extend the selection.  The buffer grows as needed with\nwhite spaces to accommodate the selection.  Selection extension mode is\nactive when ‘shift-select-mode’ is non-nil.\n\n   Or you may use the mouse to highlight the desired region.\n\n   All those region-highlighting are standard in ‘Emacs’, and unrelated\nto ‘Uniline’.\n\n   Once you have a region highlighted, press ‘<insert>’ (this key can be\ncustomized, see *note Insert key::).  The selection becomes rectangular\nif it was not.  You are offered a menu of possible actions.\n\n* Menu:\n\n* Drawing a rectangle::\n* Filling a rectangle::\n* Moving a rectangular region::\n* Copying, killing, yanking a rectangular region: Copying killing yanking a rectangular region.\n* Dashed lines and other styles::\n* ASCII to UNICODE::\n\n\u001f\nFile: uniline.info,  Node: Drawing a rectangle,  Next: Filling a rectangle,  Up: Rectangular actions\n\n7.1 Drawing a rectangle\n=======================\n\nTo draw a rectangle in one shot, select a region, press ‘<insert>’, then\nhit:\n   • ‘r’ to draw a rectangle inside the selection\n   • ‘S-R’ to draw a rectangle outside the selection\n   • ‘C-r’ to overwrite a rectangle inside the selection\n   • ‘C-S-R’ to overwrite a rectangle outside the selection\n\n   If needed, change the brush with any of ‘- + = # <delete>’\n\n\n\n     ╭───────╮          r: inside╮╭───────╮\n     │ one   │          ▗▄▄▄▄▄▄▖╭┤│▛▀▀▀▀▀▜│\n     │  ┏━━━━┿━━━━━━┓   ▐╭────╮▌│╰┼▌     ▐│\n     ╰──╂────╯ two  ┃   ▐│    │▌│ │▙▄▄▄▄▄▟│\n        ┃   ╔═══════╋═╗ ▐│    ├▌╯ ╰─────┬─╯\n        ┗━━━╋━━━━━━━┛ ║ ▐╰────╯▌────────┴───╮\n            ║  three  ║ ▝▀▀▀▀▀▀▘  R: outside╯\n            ╚═════════╝\n\n                            ╭─────────╮\n     my text I              │my text I│\n     want to  ╶─<insert>R─▷ │want to  │\n     box                    │box      │\n                            ╰─────────╯\n\n   The usual ‘C-_’ or ‘C-/’ keys may be hit to undo, even with the\nregion still active visually.\n\n\u001f\nFile: uniline.info,  Node: Filling a rectangle,  Next: Moving a rectangular region,  Prev: Drawing a rectangle,  Up: Rectangular actions\n\n7.2 Filling a rectangle\n=======================\n\nWhile the rectangular mode is active, press ‘i’ to fill the rectangle.\nYou will be asked to choose a character.  You have those options:\n\n   • for a regular character like ‘t’, just type it.\n   • ‘SPC’ or ‘DEL’ for a shade of grey ‘\" ░▒▓█\"’ among the 5 available\n     in UNICODE.  ‘SPC’ to make it darker and darker.  ‘DEL’ to make the\n     rectangle lighter and lighter.\n   • ‘C-y’ to chose the first character in the top of the kill ring.\n\n   The above selection is the same as for the flood-fill action (see\n*note Flood-fill::).\n\n\u001f\nFile: uniline.info,  Node: Moving a rectangular region,  Next: Copying killing yanking a rectangular region,  Prev: Filling a rectangle,  Up: Rectangular actions\n\n7.3 Moving a rectangular region\n===============================\n\nSelect a region, then press ‘<insert>’.\n\n   Use arrow keys to move the rectangle around.  A numeric prefix may be\nused to move the rectangle that many characters.\n   • Under ‘Hydra’, be sure to specify the numeric prefix with just\n     digits, without the ‘Alt’ key.  Typing ‘15 <left>’ moves the\n     rectangle 15 characters to the left.  ‘M-15 <left>’ does not work.\n   • Under ‘Transient’, use the ‘Alt’ key, like anywhere else in\n     ‘Emacs’.  Type ‘M-15 <left>’ to move the selected rectangle 15\n     characters to the left.\n\n   Press ‘q’, ‘<return>’, or ‘C-g’ to stop moving the rectangle.\n\n   The ‘C-_’ key may also be used to undo the previous movements, even\nthough the selection is still active.\n\n\n\n                     ▲\n                     │\n                    <up>\n               ╭─────┴──────╮\n               │this is     │\n               │my rectangle│\n     ◀─<left>──┤I want to   ├─<right>─▶\n               │move        │\n               ╰─────┬──────╯\n                   <down>\n                     │\n                     ▼\n\n   What is leakage?  When moving a rectangular region, the rectangle\nleaves behind lines oriented in the movement direction.  This is not a\nbug, but a feature.  Leakage allows growing a drawing without breaking\nit in two parts.\n\n\n\n\n       ┏┯━━━┓                 ┏┯━━━┓\n     ┏━┛│   ┗━╦━━┓    with  ┏━┛│   ┗━╦━━┓\n     ┃  │     ║  ┃╶──╮leak  ┃  │     ║  ┃  leaked\n     ┃  │     ║  ┃   ╰────▶ ┃  │     ║  ┃◀──────╴\n     ┗━━┷━━━━━╩━━┛          ┃  │     ║  ┃  lines\n         │                  ┗━━┷━━━━━╩━━┛\n         │without\n         │leak\n         ╰──────╮\n                ▼\n             ┏┯━━━┓\n           ┏━┛│   ┗━╦━━┓\n           ┃  │     ║  ┃   broken\n                         ◀───────╴\n           ┃  │     ║  ┃   drawing\n           ┗━━┷━━━━━╩━━┛\n\n\n\u001f\nFile: uniline.info,  Node: Copying killing yanking a rectangular region,  Next: Dashed lines and other styles,  Prev: Moving a rectangular region,  Up: Rectangular actions\n\n7.4 Copying, killing, yanking a rectangular region\n==================================================\n\nA rectangle can be copied or killed, then yanked somewhere else.\n\n   Select a region, press ‘<insert>’, then:\n   • ‘c’ to copy\n   • ‘k’ to kill\n   • ‘y’ to yank (aka paste)\n\n   This is similar to the ‘Emacs’ standard rectangle handling:\n   • ‘C-x r r’ copy rectangle to register\n   • ‘C-x r k’ kill rectangle\n   • ‘C-x r y’ yank killed rectangle\n\n   The first difference is that ‘Uniline’ rectangles, when killed and\nyanked, do not move surrounding characters.\n\n   The second difference is that the white characters of the yanked\nrectangle are considered transparent.  As a result, only non-blank parts\nof the yanked rectangle are over-printed.\n\n   ‘Uniline’ and ‘Emacs’ standard rectangle share the same storage for\ncopied and killed rectangles, namely the ‘killed-rectangle’ Lisp\nvariable.  So, a rectangle can be killed one way, and yanked another\nway.\n\n\u001f\nFile: uniline.info,  Node: Dashed lines and other styles,  Next: ASCII to UNICODE,  Prev: Copying killing yanking a rectangular region,  Up: Rectangular actions\n\n7.5 Dashed lines and other styles\n=================================\n\n\n\n\n     ╭────▷───╮   ┏━━━━▶━━━┓   ╔════▶═══╗\n     │ ╭─□──╮ │   ┃ ┏━■━━┓ ┃   ║ ╔═■══╗ ║\n     △ │    │ ▽   ▲ ┃    ┃ ▼   ▲ ║    ║ ▼\n     │ ╰───◦╯ │   ┃ ┗━━━•┛ ┃   ║ ╚═══•╝ ║\n     ╰───◁────╯   ┗━━━◀━━━━┛   ╚═══◀════╝\n\n     ╭╌╌╌╌▷╌╌╌╮   ┏╍╍╍╍▶╍╍╍┓\n     ┆ ╭╌□╌╌╮ ┆   ┇ ┏╍■╍╍┓ ┇\n     △ ┆    ┆ ▽   ▲ ┇    ┇ ▼\n     ┆ ╰╌╌╌◦╯ ┆   ┇ ┗╍╍╍•┛ ┇\n     ╰╌╌╌◁╌╌╌╌╯   ┗╍╍╍◀╍╍╍╍┛\n\n     ╭┈┈┈┈▷┈┈┈╮   ┏┉┉┉┉▶┉┉┉┓\n     ┊ ╭┈□┈┈╮ ┊   ┋ ┏┉■┉┉┓ ┋\n     △ ┊    ┊ ▽   ▲ ┋    ┋ ▼\n     ┊ ╰┈┈┈◦╯ ┊   ┋ ┗┉┉┉•┛ ┋\n     ╰┈┈┈◁┈┈┈┈╯   ┗┉┉┉◀┉┉┉┉┛\n\n\n   A base drawing can be converted to dashed lines.  Moreover, lines can\nbe made either thin or thick.\n\n   • Select the rectangular area you want to operate on (with mouse drag\n     or ‘S-<left>’, ‘S-<down>’ and so on as described earlier).\n   • Type ‘INS’, then ‘s’ (as \"style\").\n\n   You will be offered a choice of styles:\n   • ‘3’: vertical lines will become 3 dashes per character, while\n     horizontal ones will get 2 dashes per character.\n   • ‘4’: vertical and horizontal lines will get 4 dashes per character.\n   • ‘h’: thin lines corners, which are usually rounded, become hard\n     angles.\n   • ‘+’: thin lines and intersections become thick, empty glyphs get\n     filled.\n   • ‘-’: thick lines and intersections become thin, filled glyphs are\n     emptied.\n   • ‘=’: thick and thin lines become double lines.\n   • ‘0’: come back to standard base-line ‘Uniline’ style: plain,\n     not-dashed lines, thin corner rounded, ASCII art is converted to\n     UNICODE.\n   • ‘a’: apply the ‘aa2u-rectangle’ function from the unrelated\n     ‘ascii-art-to-unicode’ package, to convert ASCII art to UNICODE\n     (this only works if ‘ascii-art-to-unicode’ is already installed).\n\n   Converting parts of a drawing from one style to another can produce\nnice looking sketches.\n\n\n\n\n     ╭───╮   ╭───╮   ╭───╮\n     │░░░│   │░░░│   │░░░┝━▶┓ ╭╌╌╌╌╌╮\n     │░░░╰───╯░░░╰───╯░░░│  ┃ ┆░░░░░╰╌╌╌╌╌╮\n     □░░░░░░░░░░░░░░░░░░░│  ┗━┥░░░░░░░░░░░┆\n     │░░░╭───╮░░░╭───╮░░░│    ┆░░░░░╭╌╌╌╌╌╯\n     ╰───╯   ╰─┰─╯   ╰─┰─╯    ╰╌╌┰╌╌╯\n               ▲       ┃         ▼\n               ┗━━━━━━━┻━━━━━━━━━┛\n\n     ┏━━━┓   ┏━━━┓   ┏━━━┓\n     ┃░░░┃   ┃░░░┃   ┃░░░┠─▷╮ ┏╍╍╍╍╍┓\n     ┃░░░┗━━━┛░░░┗━━━┛░░░┃  │ ┇░░░░░┗╍╍╍╍╍┓\n     ■░░░░░░░░░░░░░░░░░░░┃  ╰─┨░░░░░░░░░░░┇\n     ┃░░░┏━━━┓░░░┏━━━┓░░░┃    ┇░░░░░┏╍╍╍╍╍┛\n     ┗━━━┛   ┗━┯━┛   ┗━┯━┛    ┗╍╍┯╍╍┛\n               △       │         ▽\n               ╰───────┴─────────╯\n\n\n\u001f\nFile: uniline.info,  Node: ASCII to UNICODE,  Prev: Dashed lines and other styles,  Up: Rectangular actions\n\n7.6 ASCII to UNICODE\n====================\n\nThe standard base-line ‘Uniline’ (‘INS s 0’) or ‘aa2u-rectangle’ (‘INS s\na’) conversions may be used to convert ASCII art to UNICODE.  The\noriginal ASCII art may be drawn for instance by the ‘artist-mode’ or the\n‘picture-mode’ packages.\n\n   To use ‘aa2u-rectangle’, install the ‘ascii-art-to-unicode’ package\nby Thien-Thi Nguyen (RIP), available on ELPA.  ‘Uniline’ does not\nrequires a dependency on this package, by lazy evaluating any call to\n‘aa2u-rectangle’.  See\n<https://elpa.gnu.org/packages/ascii-art-to-unicode.html>\n\n\n\n\n     +-------------+    +--+\n     |             +-->-|  +-----+   ASCII art\n     | 1  +--------+--+ | 3      |   made by\n     +----+--------+  | +----+---+   Artist-mode\n          | 2         +-<----+\n          +-----------+\n\n     ╭─────────────╮    ╭──╮\n     │             ├──▷─│  ╰─────╮   Converted to\n     │ 1  ╭────────┼──╮ │ 3      │   Uniline base style\n     ╰────┼────────╯  │ ╰────┬───╯   INS s 0\n          │ 2         ├─◁────╯\n          ╰───────────╯\n\n     ┌─────────────┐    ┌──┐\n     │             ├──>─│  └─────┐   Converted by\n     │ 1  ┌────────┼──┐ │ 3      │   aa2u-rectangle\n     └────┼────────┘  │ └────┬───┘   INS s a\n          │ 2         ├─<────┘\n          └───────────┘\n\n   ‘INS s 0’ with selection active calls the\n‘uniline-change-style-standard’ function.  It converts what looks\nASCII-art to UNICODE-art.  Of course, there are ambiguities regarding\nwhether a character is part of a sketch or not.\n\n   The heuristic is to consider that a character is part of a sketch if\nit is surrounded by at least one other character which is part of a\nsketch.  So, an isolated ‘-’ minus character will be left alone, while\ntwo such characters ‘--’ will be converted to UNICODE.  Conversion will\nhappens also for ‘<-’ for instance.\n\n   Here is a fairly convoluted ASCII-art example, along with its\nconversion by ‘INS s 0’:\n\n\n\n\n          ╭─↔--<-◁-◀--━+           +--->------==+\n     /----/ Rectangle1 |-----+-----+ Rectangle2 v    v\n     |    | <uni^code> ^     \"     | \"quote\"    +-\\  ▼\n     ^^   \\------------/   /-+-\\   +------------+ \"  v\n     |    \\--+------+--/   |   |   +----\\----/--+ \"  >▷▶>\n     \\>--\\   |      |      \\---/        |    |    \"\n         v   \\==<===/   a=b 1=2 a-to-b  +----+ ◁==/  >->\n\n          ╭─↔──◁─◁─◀──━┑           ╭───▷──────══╕\n     ╭────┤ Rectangle1 │─────╥─────┤ Rectangle2 ▽    ▽\n     │    │ <uni^code> △     ║     │ \"quote\"    ├─╖  ▼\n     △^   ├────────────┤   ╭─╨─╮   ├────────────┤ ║  ▽\n     │    ╰──┬──────┬──╯   │   │   ╰────┬────┬──╯ ║  ▷▷▶▷\n     ╰▷──╮   │      │      ╰───╯        │    │    ║\n         ▽   ╘══◁═══╛   a=b 1=2 a-to-b  ╰────╯ ◁══╝  ▷─▷\n\n\n\u001f\nFile: uniline.info,  Node: Long range actions contour and flood-fill,  Next: Macros,  Prev: Rectangular actions,  Up: Top\n\n8 Long range actions: contour and flood-fill\n********************************************\n\n* Menu:\n\n* Tracing a contour::\n* Flood-fill::\n\n\u001f\nFile: uniline.info,  Node: Tracing a contour,  Next: Flood-fill,  Up: Long range actions contour and flood-fill\n\n8.1 Tracing a contour\n=====================\n\n\n\n       ╭──────────────╮\n     ╭─╯A.written.text╰────────╮\n     │outlined by the.`contour'│\n     ╰─╮function.gets╶┬────────╯\n       ╰╮a.surrounding╰───────╮\n        ╰─╮line.in.the.current│\n          ╰─╮brush.style╭─────╯\n            ╰───────────╯\n\n   Choose or change the brush style with any of ‘-,+,=_,#,<delete>’.\nPut the cursor anywhere on the shape or outside but touching it.  Then\ntype:\n\n   ‘<insert> c’\n\n   A contour line is traced (or erased if brush style is ‘<delete>’)\naround the contiguous shape close to the cursor.\n\n   When hitting capital letter: ‘<insert> S-C’ the contour is\noverwritten.  This means that if there was already a different style of\nline on the contour path, it is overwritten.\n\n   The shape is distinguished because it floats in a blank characters\nocean.  For the shake of the contour function, blank characters are\nthose containing lines as drawn by ‘Uniline’ (including true blank\ncharacters).  Locations outside the buffer are also considered blank.\n\n   The algorithm has an upper limit of ‘10000’ steps.  This avoids an\ninfinite loop in which the algorithm may end up in some rare cases.  One\nof those cases is when the contour crosses a new-page character,\ndisplayed by ‘Emacs’ as ‘^L’.  ‘10000’ steps require a fraction of a\nsecond to run.  For shapes really huge, you may launch the contour\ncommand once again, at the point where the previous run ended.\n\n   This ‘10000’ steps limit is customizable.  Type:\n\n     M-x customize-variable uniline-contour-max-steps\n\n\u001f\nFile: uniline.info,  Node: Flood-fill,  Prev: Tracing a contour,  Up: Long range actions contour and flood-fill\n\n8.2 Flood-fill\n==============\n\n\n\n\n     this.text.surrounds      this.text.surrounds\n     .                 /      .▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒/\n     .                //╶───▷╴.▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒//\n     ...            ////      ...▒▒▒▒▒▒▒▒▒▒▒▒////\n       ...a.hole/////           ...a.hole/////\n\n\n   A hollow shape is a contiguous region of identical characters (not\nnecessarily blank), surrounded by a boundary of different characters.\nThe end of the buffer in any direction is also considered a boundary.\n\n   Put the cursor anywhere in the hole.  Then type:\n\n   ‘<insert> i’\n\n   Answer by giving a character to fill the hole.\n\n   If instead of a character, ‘SPC’ or ‘DEL’ is typed, then a shade of\ngrey character is picked.  ‘SPC’ selects a darker grey than the one the\npoint is on, while ‘DEL’ selects a lighter.  There are 5 shades of grey\nin the UNICODE standard: ‘\" ░▒▓█\"’.  Those grey characters are well\nsupported by the suggested fonts.\n\n   ‘C-y’ is also an option.  The first character in the top of the kill\nring will be chosen as the filling character.  (The kill ring is filled\nby functions like ‘C-k’ or ‘M-w’, unrelated to ‘Uniline’).\n\n   Typing ‘<return>’ or ‘C-g’ aborts the filling operation.\n\n   A rectangular shape may also be filled.\n   • Mark a region\n   • ‘<insert> i’\n   • answer which character should be used to fill.\n\n   There is no limit on the area to fill.  Therefore, the filling\noperation may flood the entire buffer (but no more).\n\n\u001f\nFile: uniline.info,  Node: Macros,  Next: Which fonts?,  Prev: Long range actions contour and flood-fill,  Up: Top\n\n9 Macros\n********\n\n‘Uniline’ adds directional macros to the ‘Emacs’ standard macros.\n\n   Record a macro as usual with ‘C-x (’ … ‘C-x )’.\n\n   Then call it with the usual ‘C-x e’.  But then, instead of executing\nthe macro, a menu is offered to execute it in any of the 4 directions.\n\n   When a macro is executed in a direction other than the one it was\nrecorded, it is twisted in that direction.  This means that recorded\nhits on the 4 keyboard arrows are rotated.  It happens also for shift\nand control variations of those keys.  Direction of text insertion is\nalso rotated.\n\n   There is still the classical ‘e’ option to call the last recorded\nmacro.  So instead of the usual ‘C-x e’, type ‘C-x e e’.  And of course,\nthe usual repetition typing repeatedly ‘e’ is available.\n\n   Why are directional macros useful?  To create fancy lines.  For\ninstance, if we want a doted-line instead of the continuous one, we\nrecord a macro for one step:\n\n     C-x (             ;; begin recording\n     INS o             ;; insert a small dot\n     <right> <right>   ;; draw a line over 2 characters\n     C-x )             ;; stop recording\n\n   Then we call this macro repeatedly in any of the 4 directions:\n\n\n\n\n     ·─·─·─·─·  ╷     ·──·\n             │  │     │  │\n             ·  ·     ·  ·\n             │  │     │  │\n             ·  ·─·─·─·  ·\n             │           │\n             ·─·─·─·─·─·─·\n\n\n   We can draw complex shapes by just drawing one step.  Hereafter, we\ncall a macro in 4 directions, closing a square:\n\n\n\n\n       ╭╮╭╮╭╮╭╮╭╮╭╮     △ △ △ △ △ △       ╭─╮ ╭─╮ ╭─╮ ╭─╮     ╭─╮ ╭─╮ ╭─╮ ╭─╮\n     ╭─╯╰╯╰╯╰╯╰╯╰╯│    ╶╯╶╯╶╯╶╯╶╯╶╯╷   ╭──╯∙╰─╯∙╰─╯∙╰─╯∙│    ▷┤□├▷┤□├▷┤□├▷┤□├▽\n     ╰╮           ╰╮  ◁╮           ╰▷  │∙               │   ╭┴┼─╯ ╰─╯ ╰─╯ ╰─┼┴╮\n     ╭╯           ╭╯   ╵           ╷   ╰╮               ╰╮  │□│             │□│\n     ╰╮           ╰╮  ◁╮           ╰▷   │               ∙│  ╰┬╯             ╰┬╯\n     ╭╯           ╭╯   ╵           ╷   ╭╯               ╭╯   △               ▽\n     ╰╮           ╰╮  ◁╮           ╰▷  │∙               │   ╭┴╮             ╭┴╮\n     ╭╯           ╭╯   ╵           ╷   ╰╮               ╰╮  │□│             │□│\n     ╰╮           ╰╮  ◁╮           ╰▷   │               ∙│  ╰┬┼─╮ ╭─╮ ╭─╮ ╭─┼┬╯\n      │╭╮╭╮╭╮╭╮╭╮╭─╯   ╵╭╴╭╴╭╴╭╴╭╴╭╴    │∙╭─╮∙╭─╮∙╭─╮∙╭──╯   △┤□├◁┤□├◁┤□├◁┤□├◁\n      ╰╯╰╯╰╯╰╯╰╯╰╯      ▽ ▽ ▽ ▽ ▽ ▽     ╰─╯ ╰─╯ ╰─╯ ╰─╯       ╰─╯ ╰─╯ ╰─╯ ╰─╯\n\n\n\u001f\nFile: uniline.info,  Node: Which fonts?,  Next: Hydra or Transient?,  Prev: Macros,  Up: Top\n\n10 Which fonts?\n***************\n\nA mono-space character font must be used.  It must also support UNICODE.\n\n* Menu:\n\n* Recommended fonts::\n* Use case mixing fonts::\n\n\u001f\nFile: uniline.info,  Node: Recommended fonts,  Next: Use case mixing fonts,  Up: Which fonts?\n\n10.1 Recommended fonts\n======================\n\nNot all fonts are born equal.\n\n   • ‘(set-frame-font \"DejaVu Sans Mono\" )’\n   • ‘(set-frame-font \"Unifont\" )’\n   • ‘(set-frame-font \"Hack\" )’\n   • ‘(set-frame-font \"JetBrains Mono\" )’\n   • ‘(set-frame-font \"Cascadia Mono\" )’\n   • ‘(set-frame-font \"Agave\" )’\n   • ‘(set-frame-font \"JuliaMono\" )’\n   • ‘(set-frame-font \"FreeMono\" )’\n   • ‘(set-frame-font \"Iosevka Comfy Fixed\" )’\n   • ‘(set-frame-font \"Iosevka Comfy Wide Fixed\")’\n   • ‘(set-frame-font \"Aporetic Sans Mono\" )’\n   • ‘(set-frame-font \"Aporetic Serif Mono\" )’\n   • ‘(set-frame-font \"Source Code Pro\" )’\n\n   Those fonts are known to support the required UNICODE characters, AND\ndisplay them as mono-space.  There are fonts advertised as mono-space\nwhich give arbitrary widths to non-ASCII characters.  That is bad for\nthe kind of drawings done by ‘Uniline’.\n\n   You may want to try any of the suggested fonts.  Just hit the\ncorresponding entry in the ‘Uniline’ menu, or type ‘<insert> f’.  You\nmay also execute the above Lisp commands like that:\n\n   ‘M-: (set-frame-font \"DejaVu Sans Mono\")’\n\n   This setting is for the current session only.  If you want to make it\npermanent, you may use the ‘Emacs’ customization:\n\n   ‘<insert> f *’\n\n   or\n\n   ‘M-x customize-face default’\n\n   Beware that ‘Emacs’ tries to compensate for missing UNICODE support\nby the current font.  ‘Emacs’ substitutes one font for another,\ncharacter per character.  The user may not notice until the drawings\ndone under ‘Emacs’ are displayed on another text editor or on the Web.\nOf course, using the suggested fonts and the UNICODEs drawn by ‘Uniline’\nkeeps you away from those glitches.\n\n   To know which font ‘Emacs’ has chosen for a given character, type:\n\n   ‘C-u C-x =’\n\n   Note that none of those commands downloads a font from the Web.  The\nfont should already be available.\n\n\u001f\nFile: uniline.info,  Node: Use case mixing fonts,  Prev: Recommended fonts,  Up: Which fonts?\n\n10.2 Use case: mixing fonts\n===========================\n\nA user on GitHub, dmullis, exposed his use-case.  A source-code base is\nusually edited with a font not in the Uniline list of recommended fonts.\nHowever, it is desirable to document the source code with Uniline,\neither directly along the source or in separate files.  How to achieve\nthat without messing with the fonts in several Emacs buffers?\n\n   Several solutions have emerged from the discussion.\n\n   • ‘face-remap-add-relative’\n\n   A line like this at the top of the files reserved for Uniline\ndrawings:\n\n     -*- eval: (face-remap-add-relative 'default :family \"DejaVu Sans Mono\"); -*-\n\n   This confines its effect to just the one single buffer.\n\n   • ‘uniline-mode-hook’\n\n   Add a hook (a function called when entering ‘uniline-mode’):\n\n     (add-hook\n       'uniline-mode-hook\n       (lambda () (face-remap-add-relative 'default :family \"DejaVu Sans Mono\")))\n\n   There are also ‘uniline-mode-on-hook’ & ‘uniline-mode-off-hook’ which\ncan be handy.\n\n   • ‘font-lock-comment-face’\n\n   An alternative mean of limiting the scope of the font change is the\nEmacs standard font-lock mechanism.\n\n     (customize-face '(font-lock-comment-face))\n\n   Then check ‘Font Family’, type in value ‘\"DejaVu Sans Mono\",’ and\n‘C-x C-s’.\n\n   Now any major mode that understands \"comments\" as distinct from other\ntext can safely nest a Uniline drawing within its boundaries, all text\noutside the \"comment\" unaffected (except perhaps by spacing).\n\n   Look also at the ‘font-lock-constant-face’ face.\n\n   • Org Mode\n\n   In Org Mode, the usable faces could be ‘org-block’, ‘org-quote’,\n‘org-verse’.  But first the ‘org-fontify-quote-and-verse-blocks’\nvariable must be set to ‘t’.\n\n   • Markdown\n\n   In Markdown mode, customize the ‘markdown-pre-face’ or\n‘markdown-code-face’ faces.\n\n\u001f\nFile: uniline.info,  Node: Hydra or Transient?,  Next: Customization,  Prev: Which fonts?,  Up: Top\n\n11 Hydra or Transient?\n**********************\n\nThe basic usage of ‘Uniline’ should be easy: just move the point, and\nlines are traced.  Change brush to draw thicker lines.\n\n   More complex actions are summoned by the ‘<insert>’ key, with or\nwithout selection.  This is a single key to remember.  Then a textual\nmenu is displayed, giving the possible keys continuations and their\nmeaning.  All that is achieved by the ‘Hydra’ or ‘Transient’ libraries,\nwhich are now part of ‘Emacs’ (thanks!).\n\n   The ‘Hydra’ and ‘Transient’ libraries offer similar features.  Some\nusers may prefer one or the other.\n\n   ‘Uniline’ was developed from day one with ‘Hydra’.  ‘Transient’ is a\nlate addition.\n\n* Menu:\n\n* Selecting Hydra or Transient::\n* Instantly selecting Hydra or Transient::\n* One-liner menus::\n* The Hydra interface::\n* The Transient interface::\n\n\u001f\nFile: uniline.info,  Node: Selecting Hydra or Transient,  Next: Instantly selecting Hydra or Transient,  Up: Hydra or Transient?\n\n11.1 Selecting Hydra or Transient\n=================================\n\nTwo files are compiled when installing ‘Uniline’\n   • ‘uniline-hydra.el’\n   • ‘uniline-transient.el’\n\n   One of them should be loaded (but not both).  There are several ways.\nThe cleanest is ‘use-package’.  Add those lines to your ‘~/.emacs’ file:\n\n     (use-package uniline-hydra\n       :bind (\"C-<insert>\" . uniline-mode))\n\n   or:\n\n     (use-package uniline-transient\n       :bind (\"C-<insert>\" . uniline-mode))\n\n   The following key sequences can assist in modifying the ‘.emacs’\nfile:\n   • ‘<INS> * H’\n   • ‘<INS> * T’\n\n   Note: there used to be a customizable setting to switch between the\ntwo interfaces.  This had many issues.  One of them is that the\nnative-compiler is blind to all user-customized settings.\n\n   There is a third file, ‘uniline-code.elc’.  Loading\n‘uniline-hydra.elc’ or ‘uniline-transient.elc’ automatically loads\n‘uniline-core.elc’.\n\n\u001f\nFile: uniline.info,  Node: Instantly selecting Hydra or Transient,  Next: One-liner menus,  Prev: Selecting Hydra or Transient,  Up: Hydra or Transient?\n\n11.2 Instantly selecting Hydra or Transient\n===========================================\n\nIt is now possible to switch user interfaces on the fly.\n\n   To do so, look at the \"Customize\" entry in the Uniline menu.  This\nmenu is available:\n   • from the menu-bar at the top of the Emacs screen (if not made\n     invisible),\n   • by left-clicking on ‘\"Uniline\"’ in the mode-line, at the bottom of\n     the Emacs screen.\n\n   Note that the changes are for the current session only.  To\npermanently choose Hydra or Transient, change your\n=~/.emacs=initialization file as describe in *note Selecting Hydra or\nTransient::.\n\n   The actions performed by the menu are:\n   • ‘(load-library \"uniline-hydra\")’\n   • ‘(load-library \"uniline-transient\")’\n\n   You can execute them directly or by other means.\n\n\u001f\nFile: uniline.info,  Node: One-liner menus,  Next: The Hydra interface,  Prev: Instantly selecting Hydra or Transient,  Up: Hydra or Transient?\n\n11.3 One-liner menus\n====================\n\nThe multi-lines menus in Hydra and Transient are quite useful for casual\nusers.  For seasoned users, those huge textual menus may distract them\nfrom their workflow.\n\n   It is now possible to switch to less distracting textual menus.  They\nare displayed in the echo-area on a single line.\n\n   To do so, type:\n   • ‘C-t’ within a sub-mode (glyph insertion mode, rectangle handling,\n     etc.)\n   • ‘C-h TAB’ at the top-level.\n\n   This will flip between the two sizes of textual menus.  It also\naffects the welcome message, the one displayed when entering the\n‘Uniline’ minor mode.\n\n   The current size is controlled by the ‘uniline-hint-style’ variable:\n   • ‘t’ for full fledged messages over several lines\n   • ‘1’ for one-liner messages\n   • ‘0’ for no message at all\n\n   The variable is \"buffer-local\", which means that it can take distinct\nvalues on distinct buffers.\n\n   Its default value can be customized and saved for future sessions:\n\n   ‘M-x customize-variable uniline-hint-style’\n\n   After customization it can be changed later, on a buffer per buffer\nbasis, with the ‘C-t’ or ‘C-h TAB’ keys.\n\n   Transient natively offers a similar setting: ‘transient-show-popup’.\n(There is no such variable in Hydra).  It can be customized with ‘t’,\n‘nil’, ‘0’ (zero), or a number.  This is similar but not exactly the\nsame as the Hydra behavior and the ‘uniline-hint-style’.  the Transient\nsetting stays in effect until the ‘C-t’ or ‘C-h TAB’ keys are not used,\n.  As soon as one of those keys is invoked, ‘transient-show-popup’ is\ntoggled (which does not happens in Transient alone).  The change is kept\nin effect throughout the ‘Uniline’ session, but no longer.\n\n\u001f\nFile: uniline.info,  Node: The Hydra interface,  Next: The Transient interface,  Prev: One-liner menus,  Up: Hydra or Transient?\n\n11.4 The Hydra interface\n========================\n\nPut that in your ‘~/.emacs’ file:\n\n     (use-package uniline-hydra\n       :bind (\"C-<insert>\" . uniline-mode))\n\n   It has been asked by ‘Transient’-only users to avoid installing the\n‘Hydra’ package.  Currently, it is not possible to make dependencies\nconditional in ‘Melpa’.  And removing the ‘Hydra’ dependency would hurt\n‘Hydra’ users.  Therefore, for the time being, the ‘Hydra’ package is\nstill installed when installing ‘Uniline’ through ‘Melpa’.\n\n\u001f\nFile: uniline.info,  Node: The Transient interface,  Prev: The Hydra interface,  Up: Hydra or Transient?\n\n11.5 The Transient interface\n============================\n\nPut that in your ‘~/.emacs’ file:\n\n     (use-package uniline-transient\n       :bind (\"C-<insert>\" . uniline-mode))\n\n   ‘Transient’ interface was added recently to ‘Uniline’.  This leaded\nto the splitting of the single ‘uniline.el’ file into 4 source files.\nHopefully, the added complexity remains hidden by the ‘Elpa’ - ‘Melpa’\npackaging system.\n\n\u001f\nFile: uniline.info,  Node: Customization,  Next: How Uniline behaves with its environment?,  Prev: Hydra or Transient?,  Up: Top\n\n12 Customization\n****************\n\nType: ‘M-x customize-group uniline’.\n\n   Or ‘Menu bar ⟶ Options ⟶ Customize Emacs ⟶ Specific Group… ⟶\n\"uniline\"’.\n\n   This invokes the standard ‘Emacs’ customization system.  Your\nsettings will be saved in the file pointed to by the ‘custom-file’\nvariable if set, or your ‘~/.emacs’ file.  (Along with all your other\nsettings unrelated to ‘Uniline’).\n\n   Two settings are special: interface type (obsolete) & the insert key.\nThe other settings are self-explanatory\n\n* Menu:\n\n* Interface type::\n* Insert key::\n* Maximum steps when drawing a contour::\n* Cursor type::\n* Hint style::\n* Welcome message visibility::\n* Line spacing::\n* Font::\n* Upward infiniteness ∞::\n\n\u001f\nFile: uniline.info,  Node: Interface type,  Next: Insert key,  Up: Customization\n\n12.1 Interface type\n===================\n\nThe ‘uniline-interface’ variable is *obsolete*.  Choosing between\n‘Hydra’ or ‘Transient’ interface is done by loading one or the other\nsub-package.  This is best done in the ‘.emacs’ initialization file.\nSee *note Installation:: for details.\n\n   Typing either of the following key sequences can assist in modifying\nthe ‘.emacs’ file:\n   • ‘<INS> * H’\n   • ‘<INS> * T’\n\n\u001f\nFile: uniline.info,  Node: Insert key,  Next: Maximum steps when drawing a contour,  Prev: Interface type,  Up: Customization\n\n12.2 Insert key\n===============\n\nBy default, the ‘<insert>’ or ‘INS’ key is the prefix for most of the\n‘Uniline’ actions.  Some computers do not have an ‘INS’ key, or it is\nbound to some other command (Apple?).\n\n   This can be changed temporarily or permanently.  The customization\nallows to set several keys at the same time.\n\n   Depending on whether ‘Emacs’ is run in a graphical environment or a\ntext-only terminal, either the ‘<insert>’ or the ‘<insertchar>’ events\nare generated by the ‘INS’ key.  Therefore, by default ‘Uniline’ defines\nboth events as the ‘INS’ key.\n\n   Variable ‘uniline-key-insert’.\n\n\u001f\nFile: uniline.info,  Node: Maximum steps when drawing a contour,  Next: Cursor type,  Prev: Insert key,  Up: Customization\n\n12.3 Maximum steps when drawing a contour\n=========================================\n\nDefaults to ‘10000’.  To avoid an infinite loop in some rare cases.\n\n   Variable ‘uniline-contour-max-steps’.\n\n\u001f\nFile: uniline.info,  Node: Cursor type,  Next: Hint style,  Prev: Maximum steps when drawing a contour,  Up: Customization\n\n12.4 Cursor type\n================\n\nHollow by default, so that what is under the cursor remains visible.\n\n   There is the option to leave the cursor as it is.\n\n   Variable ‘uniline-cursor-type.’\n\n\u001f\nFile: uniline.info,  Node: Hint style,  Next: Welcome message visibility,  Prev: Cursor type,  Up: Customization\n\n12.5 Hint style\n===============\n\nCurrently only applicable to the ‘Hydra’.  It defaults to \"full fledged\nmenus\".\n\n   Variable ‘uniline-hint-style’.\n\n   ‘Transient’ offers a similar setting: ‘transient-show-popup’.\n\n\u001f\nFile: uniline.info,  Node: Welcome message visibility,  Next: Line spacing,  Prev: Hint style,  Up: Customization\n\n12.6 Welcome message visibility\n===============================\n\nDefault is \"on\".  Turn it \"off\" for less distraction.\n\n   Even when turned of, the welcome message can still be displayed by\npressing ‘C-h TAB’.\n\n   Variable ‘uniline-show-welcome-message’.\n\n\u001f\nFile: uniline.info,  Node: Line spacing,  Next: Font,  Prev: Welcome message visibility,  Up: Customization\n\n12.7 Line spacing\n=================\n\nThe ‘line-spacing’ setting in ‘Emacs’ can change the display of a\nsketch.  (This setting is unrelated to ‘Uniline’).\n\n   The best looking effect is given by:\n     (setq line-spacing nil)\n\n   You may want to change your current setting.  ‘Uniline’ may handle\nthis variable some day.  Right now, ‘line-spacing’ is left as a matter\nof choice for everyone.\n\n\n\n\n     ╭────┬────────┬────╮   ╺┯━━━━┯┯━━┯┯━┯┯━━━━━━━━┯┯━━━━━━━┯┯━━━━━━┯╸\n     │▒▒▒▒╰────────╯▒▒▒▒│    │    │╰is╯╰a╯│        ││       │╰around╯\n     │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│    ╰this╯       ╰sentence╯╰hanging╯\n     │▒▒▒╭─╮▒▒▒▒▒▒╭─╮▒▒▒│            △\n     │▒▒▒╰─╯▒▒▒▒▒▒╰─╯▒▒▒│            │                  △\n     │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│            ╰─────────┬────────╯\n     ╰──────────────────╯                    verbs\n                  (setq line-spacing nil)\n\n\n\u001f\nFile: uniline.info,  Node: Font,  Next: Upward infiniteness ∞,  Prev: Line spacing,  Up: Customization\n\n12.8 Font\n=========\n\nFace customization is unrelated to ‘Uniline’.  However, ‘Uniline’ can\nassist in choosing a good font and customizing the ‘default’ face.  See\n*note Which fonts?::.\n\n   Type ‘<insert> f’ to select a font just for the current ‘Uniline’\nsession.  Type ‘*’ to enter the ‘Emacs’ customization of the ‘default’\nface and retain your choice for future sessions.\n\n\u001f\nFile: uniline.info,  Node: Upward infiniteness ∞,  Prev: Font,  Up: Customization\n\n12.9 Upward infiniteness ∞\n==========================\n\nIf the variable ‘uniline-infinite-up↑’ is:\n\n   • ‘t’, then the buffer grows at the top of the buffer (or at the top\n     of the narrowed region), by adding empty lines as needed.\n\n   • ‘nil’, then the top of the buffer (or the top of the narrowed\n     region) is a non-trespassable limit.  This is the default, and the\n     behaviour of previous versions of Uniline.\n\n\u001f\nFile: uniline.info,  Node: How Uniline behaves with its environment?,  Next: Lisp API,  Prev: Customization,  Up: Top\n\n13 How Uniline behaves with its environment?\n********************************************\n\n* Menu:\n\n* Language environment::\n* Compatibility with Picture-mode::\n* Compatibility with Artist-mode::\n* Compatibility with Whitespace-mode::\n* Compatibility with Org Mode::\n* Org Mode and LaTex::\n* What about \\t tabs?::\n* What about ^L page separation?::\n* Emacs on the Linux console::\n* Emacs on a graphical terminal emulator::\n* Emacs on Windows::\n* Compatibility with ASCIIFlow::\n\n\u001f\nFile: uniline.info,  Node: Language environment,  Next: Compatibility with Picture-mode,  Up: How Uniline behaves with its environment?\n\n13.1 Language environment\n=========================\n\nThe so called \"language environment\" in Emacs can cause unwanted line\nbreaks, like in this drawing:\n\n\n\n\n     ╶───────────┬─────╮\n                            │           │\n                            │           │\n                            │           │\n                            ╰───────────╯\n        unexpected broken lines\n\n     ╶─────┬───────────╮\n           │           │\n           │           │\n           │           │\n           ╰───────────╯\n        expected continuous lines\n\n\n   The above example was drawn first with the ‘Chinese-BIG5’ language\nenvironment, then with the ‘English’ environment.\n\n   There is nothing specific about ‘Chinese-BIG5’.  It is just an\ninstance picked out from more than 100 language environments.\n\n     C-x RET l Chinese-BIG5\n     C-x RET l English\n\n   In ‘Chinese-BIG5’, some characters are considered twice as wide as\nstandard characters.  Whereas in ‘English’, all characters needed by\nUniline are 1 unit wide.\n\n   Thanks to *rumengling* (GitHub) for discovering and diagnosing the\nissue!\n\n   To workaround the issue, when entering ‘uniline-mode’, the width of\nall characters Uniline uses is checked.  If some of them are more than\n1, the ‘char-width-table’ variable is patched.\n\n   What are the consequences of this patch?  The ‘char-width-table’\nvariable is an Emacs global.  Therefore the patch by Uniline will affect\nall buffers.  As the characters touched by the patch are graphic, and\nhave nothing to do with Chinese, it should not have any significance on\ntext written in Chinese.\n\n   It was pondered whether Uniline should put back ‘char-width-table’ at\nits original value upon exiting ‘uniline-mode’, or leaving the patch.\nFor now, it has been decided to leave it.  Because anyway, intertwining\nseveral ‘uniline-mode’ and changes to the language environment is\nintractable.\n\n   In case of something, re-setting the language environment to its same\nvalue cancels the patch to ‘char-width-table’ by Uniline.\n\n\u001f\nFile: uniline.info,  Node: Compatibility with Picture-mode,  Next: Compatibility with Artist-mode,  Prev: Language environment,  Up: How Uniline behaves with its environment?\n\n13.2 Compatibility with Picture-mode\n====================================\n\n‘Picture-mode’ and ‘uniline-mode’ are compatible.  Their features\noverlap somehow:\n   • Both implement an unlimited buffer in east and south directions.\n   • Both visually truncate long lines (actual text is not truncated).\n   • Both set the overwrite mode (‘uniline-mode’ activates\n     ‘overwrite-mode’, while ‘picture-mode’ re-implements it)\n   • Both are able to draw rectangles (‘uniline-mode’ in UNICODE,\n     ‘picture-mode’ in ASCII), copy and yank them.\n\n   They also have features unique to each:\n   • ‘Picture-mode’ writes in 8 possible directions\n   • ‘Picture-mode’ handles TAB stops\n   • ‘Uniline-mode’ draws lines and arrows\n\n\u001f\nFile: uniline.info,  Node: Compatibility with Artist-mode,  Next: Compatibility with Whitespace-mode,  Prev: Compatibility with Picture-mode,  Up: How Uniline behaves with its environment?\n\n13.3 Compatibility with Artist-mode\n===================================\n\n‘Artist-mode’ and ‘uniline-mode’ are mostly incompatible.  This is\nbecause ‘artist-mode’ preempts the arrow keys, which give access to a\nlarge part of ‘uniline-mode’ features.\n\n   However, it is possible to use both one after the other.\n\n\u001f\nFile: uniline.info,  Node: Compatibility with Whitespace-mode,  Next: Compatibility with Org Mode,  Prev: Compatibility with Artist-mode,  Up: How Uniline behaves with its environment?\n\n13.4 Compatibility with Whitespace-mode\n=======================================\n\n‘Whitespace-mode’ and ‘uniline-mode’ are mostly compatible.\n\n   Why activate ‘whitespace-mode’ while in ‘uniline-mode’?  Because\n‘Uniline’ creates a lot of white-spaces to implement an infinite buffer.\nAnd it is funny to look at this activity.\n\n   To make ‘uniline-mode’ and ‘whitespace-mode’ fully compatible,\ndisable the newline visualization:\n\n   • ‘M-x customize-variable whitespace-style’\n   • uncheck ‘(Mark) NEWLINEs’\n\n   This is due to a glitch in ‘move-to-column’ when a visual property is\nattached to newlines.  And ‘uniline-mode’ makes heavy use of\n‘move-to-column’.\n\n\u001f\nFile: uniline.info,  Node: Compatibility with Org Mode,  Next: Org Mode and LaTex,  Prev: Compatibility with Whitespace-mode,  Up: How Uniline behaves with its environment?\n\n13.5 Compatibility with Org Mode\n================================\n\nYou may want to customize the shift extension mode in ‘Org Mode’.  This\nis because ‘Org Mode’ preempts ‘shift-select-mode’ for other useful\npurposes.  Just type:\n\n     M-x customize-variable org-support-shift-select\n\n   and choose \"when outside special context\", which sets it to ‘t’.\n\n   You then get the shift-selection from ‘Org Mode’, not from ‘Uniline’.\nThe difference is that the ‘Uniline’’s one handles the infinite-ness of\nthe buffer.\n\n   Other than that, ‘Uniline’ is compatible with ‘Org Mode’\n\n   Thanks to jdtsmith (GitHub) for sharing a funny fact he discovered.\nIf a source block is created with the ‘Uniline’ language (‘Uniline’ is\n*not* a language like ‘C++,’ ‘Python’, or ‘Bash’), then it can be edited\n(‘M-x org-edit-special’) with ‘uniline-mode’ automatically activated.\n\n\n\n     #+begin_src uniline\n     ╭───╮   ╭───╮\n     │ ╷ ╰───╯ ╷ │\n     │ ╰─    ╶─╯ │\n     ╰╮ ●     ● ╭╯\n      │      ╷  │\n      ╰╮ ────╯ ╭╯\n       ╰───────╯\n     #+end_src\n\n\u001f\nFile: uniline.info,  Node: Org Mode and LaTex,  Next: What about \\t tabs?,  Prev: Compatibility with Org Mode,  Up: How Uniline behaves with its environment?\n\n13.6 Org Mode and LaTex\n=======================\n\nUse the ‘pmboxdraw’ LaTex module.  This gives limited support for \"box\ndrawing\" characters in LaTex documents.\n\n   Example:\n\n\n\n     #+LATEX_HEADER: \\usepackage{pmboxdraw}\n\n     #+begin_src text\n\n     this works:\n     ┌─────┐       ┌────────────┐\n     │     ├───────┤            │\n     └─────┘       │            │\n     ┌─────┐  ┌────┤            │\n     │     ├──┘    │            │\n     └─────┘  ┌────┤            │\n     ┌─────┐  │    │            │\n     │     ├──┘    └────────────┘\n     └─────┘\n\n     this does not quite work:\n        ┏━━━┓  ┏━━┓     ┏━━━━━┓\n        ┃   ┃  ┃  ┣━━━━━┫     ┃\n        ┃   ┗━━┛  ┃    ┏┛     ┃\n        ┗━━━━━━━━━┛    ┗━━━━━━┛\n\n     but that is OK:\n          ┏━━━┓\n          ┃   ┃\n          ┗━━━┛\n\n     that is OK too:\n     ╺════╦══╗  ╔════╗\n          ║ A║  ║ B  ╚══╗\n          ╚══╝  ╚═══════╝\n\n     this works:\n\n     ├── dev\n     └┬┬ release\n      │├── new\n      │└── old\n      ├── graph\n      └── non-graph\n\n     #+end_src\n\n   Note that corners of thin lines should be sharp.  There is no support\nfor rounded corners.\n\n   To export this Org Mode example to PDF through LaTex, type:\n\n   ‘C-c C-E l o’\n\n\u001f\nFile: uniline.info,  Node: What about \\t tabs?,  Next: What about ^L page separation?,  Prev: Org Mode and LaTex,  Up: How Uniline behaves with its environment?\n\n13.7 What about ‘\\t’ tabs?\n==========================\n\nSome files may contain tabs (the character ‘\\t’).  Those include\nprogramming code (Python, Perl, C++, D, Rust, JavaScript and so on).\n\n   When ‘Uniline’ draws something in the middle of a TAB, or right onto\na TAB, it first converts it to spaces, then proceeds as usual.  This\nprocess is invisible.  So be cautious if TABs have a special meaning in\nthe file.\n\n   Also, rectangles are first untabified (if there are TABs) before\nmoving them.  This avoids some rare instances of misalignment.\n\n   One way to see what is going on, is to activate the\n‘whitespace-mode’.\n\n\u001f\nFile: uniline.info,  Node: What about ^L page separation?,  Next: Emacs on the Linux console,  Prev: What about \\t tabs?,  Up: How Uniline behaves with its environment?\n\n13.8 What about ‘^L’ page separation?\n=====================================\n\n‘Uniline’ does not work well with ‘^L’ (page separation) character.  Nor\nwith similar characters, like ‘^T’.  When trying to draw a line over\nsuch a character, the cursor may get stuck.  This is because those\ncharacters occupy twice the width of a normal character.\n\n   Just try to get away from ‘^L’, ‘^T’ and such when drawing with\n‘Uniline’.\n\n\u001f\nFile: uniline.info,  Node: Emacs on the Linux console,  Next: Emacs on a graphical terminal emulator,  Prev: What about ^L page separation?,  Up: How Uniline behaves with its environment?\n\n13.9 Emacs on the Linux console\n===============================\n\nLinux consoles are the 7 non-graphic screens which can be accessed\nusually typing ‘C-M-F1’, ‘C-M-F2’, and so on.  Such a screen is also\npresented when connecting through ‘ssh’ or ‘tls’ into a non-graphical\nserver.\n\n   By default they use a font named \"Fixed\" with poor support for\nUnicode.  However, it supports lines of the 3 types, mixing all of them\nin thin lines though.\n\n   Another problem is that by default ‘S-<left>’ and ‘C-<left>’ are\nindistinguishable from ‘<left>’.  Same problem with ‘<right>’, ‘<up>’,\n‘<down>’ and ‘<insert>’.  This has nothing to do with ‘Emacs’.  A\nsolution can be found here:\n<https://www.emacswiki.org/emacs/MissingKeys>\n\n\u001f\nFile: uniline.info,  Node: Emacs on a graphical terminal emulator,  Next: Emacs on Windows,  Prev: Emacs on the Linux console,  Up: How Uniline behaves with its environment?\n\n13.10 Emacs on a graphical terminal emulator\n============================================\n\nThis is the ‘Emacs’ launched from a terminal typing ‘emacs -nw’.  In\nthis environment, ‘<insert>’ does not exist.  It is replaced by\n‘<insertchar>’.  This has already been taken into account by ‘Uniline’\nby duplicating the key-bindings for the two flavors of this key.\n\n   If you decide to bind globally ‘C-<insert>’ to the toggling of\n‘Uniline’ minor mode as suggested, then you will have to do the same for\n‘C-<insertchar>’, for example with ‘use-package’ in your ‘~/.emacs’\nfile:\n\n     (use-package uniline\n       :defer t\n       :bind (\"C-<insert>\"     . uniline-mode)\n       :bind (\"C-<insertchar>\" . uniline-mode))\n\n\u001f\nFile: uniline.info,  Node: Emacs on Windows,  Next: Compatibility with ASCIIFlow,  Prev: Emacs on a graphical terminal emulator,  Up: How Uniline behaves with its environment?\n\n13.11 Emacs on Windows\n======================\n\nOn Windows the only native mono-spaced fonts are ‘Lucida Console’ and\n‘Courier New’.  They are not mono-spaced for the Unicodes used by\n‘Uniline’.\n\n   Often, the ‘Consolas’ font is present on Windows.  It supports quite\nwell the required Unicodes to draw lines.  A few glyphs produce\nunaligned result though.  They should be avoided under ‘Consolas’:\n‘△▶▹◆’\n\n   Of course, other fonts may be installed.  It is quite easy.\n\n\u001f\nFile: uniline.info,  Node: Compatibility with ASCIIFlow,  Prev: Emacs on Windows,  Up: How Uniline behaves with its environment?\n\n13.12 Compatibility with ASCIIFlow\n==================================\n\nASCIIFlow is a ASCII-UNICODE diagram drawing tool (as Uniline).  It\nworks on a web browser.  Just open <https://asciiflow.com> and start\ndrawing.  There is no server, ASCIIFlow operates locally on your PC.\nYour diagrams survive web browser sessions, as they are saved locally\nbehind the scene.\n\n   When your drawing is complete, you can export it to Emacs-Uniline:\n   • Click on the download button\n   • Select ‘\"ASCII Extended\"’\n   • Paste your diagram in Emacs with ‘C-y’\n   • Modify it with Uniline\n\n   For the other way around, a Uniline drawing can be exported to\nASCIIFlow:\n   • Copy it from Emacs (with ‘M-w’ for instance).\n   • In ASCIIFlow, choose ‘\"Select & Move\"’\n   • Type ‘C-v’\n   • Edit with ASCIIFlow\n\n\u001f\nFile: uniline.info,  Node: Lisp API,  Next: Mouse support,  Prev: How Uniline behaves with its environment?,  Up: Top\n\n14 Lisp API\n***********\n\nCould ‘Uniline’ be programmed (versus used interactively)?  Yes!\n\n   The API is usable programmatically:\n\n* Menu:\n\n* Move the cursor::\n* Brush::\n* Example Lisp function to draw a plus sign::\n* Long range actions (contour, flood-fill, rectangle): Long range actions (contour flood-fill rectangle).\n* Constants::\n* Macro and text direction::\n* Insert and tweak glyphs::\n* Change to alternate styles::\n\n\u001f\nFile: uniline.info,  Node: Move the cursor,  Next: Brush,  Up: Lisp API\n\n14.1 Move the cursor\n====================\n\nMove cursor while drawing lines by calling any of the 4 directions\nfunctions:\n   • ‘uniline-write-up↑’\n   • ‘uniline-write-ri→’\n   • ‘uniline-write-dw↓’\n   • ‘uniline-write-lf←’\n\n   They expect a repeat ‘count’ (usually 1) and optionally ‘force=t’ to\noverwrite the buffer\n\n\u001f\nFile: uniline.info,  Node: Brush,  Next: Example Lisp function to draw a plus sign,  Prev: Move the cursor,  Up: Lisp API\n\n14.2 Brush\n==========\n\nSet the current brush by calling any of the following:\n\n   • ‘uniline--set-brush-nil’ ;; write nothing\n   • ‘uniline--set-brush-0’ ;; eraser\n   • ‘uniline--set-brush-1’ ;; single thin line╶─╴\n   • ‘uniline--set-brush-2’ ;; single thick line╺━╸\n   • ‘uniline--set-brush-3’ ;; double line╺═╸\n   • ‘uniline--set-brush-block’ ;; blocks ▙▄▟▀\n\n   Those functions are equivalent to:\n\n   • ‘(setq uniline--brush nil)’\n   • ‘(setq uniline--brush 0)’\n   • ‘(setq uniline--brush 1)’\n   • ‘(setq uniline--brush 2)’\n   • ‘(setq uniline--brush 3)’\n   • ‘(setq uniline--brush :block)’\n\n   except the functions also update the mode-line.\n\n\u001f\nFile: uniline.info,  Node: Example Lisp function to draw a plus sign,  Next: Long range actions (contour flood-fill rectangle),  Prev: Brush,  Up: Lisp API\n\n14.3 Example: Lisp function to draw a plus sign\n===============================================\n\nFor instance, if we want to create a function to draw a \"plus\" sign, we\ncan code it as follows:\n\n     (defun uniline-draw-plus ()\n       (interactive)\n       (uniline-write-ri→ 1)\n       (uniline-write-dw↓ 1)\n       (uniline-write-ri→ 1)\n       (uniline-write-dw↓ 1)\n       (uniline-write-lf← 1)\n       (uniline-write-dw↓ 1)\n       (uniline-write-lf← 1)\n       (uniline-write-up↑ 1)\n       (uniline-write-lf← 1)\n       (uniline-write-up↑ 1)\n       (uniline-write-ri→ 1)\n       (uniline-write-up↑ 1))\n\n   Calling ‘M-x uniline-draw-plus’ will result in this nice little\nplus-shape:\n\n\n\n      ╭╮\n     ╭╯╰╮\n     ╰╮╭╯\n      ╰╯\n     generated by\n     M-x uniline-draw-plus\n\n   We may modify the function to accept the size of the shape as a\nparameter:\n\n     (defun uniline-draw-plus (size)\n       (interactive \"Nsize? \")\n       (uniline-write-ri→ size)\n       (uniline-write-dw↓ size)\n       (uniline-write-ri→ size)\n       (uniline-write-dw↓ size)\n       (uniline-write-lf← size)\n       (uniline-write-dw↓ size)\n       (uniline-write-lf← size)\n       (uniline-write-up↑ size)\n       (uniline-write-lf← size)\n       (uniline-write-up↑ size)\n       (uniline-write-ri→ size)\n       (uniline-write-up↑ size))\n\n   The ‘(interactive \"Nsize? \")’ form prompts user for the size of the\nshape if not given as a parameter.\n\n   This API works in any mode, not only in ‘Uniline’ minor mode.  It\ntakes care of the infiniteness of the buffer in the right and down\ndirections.\n\n\u001f\nFile: uniline.info,  Node: Long range actions (contour flood-fill rectangle),  Next: Constants,  Prev: Example Lisp function to draw a plus sign,  Up: Lisp API\n\n14.4 Long range actions (contour, flood-fill, rectangle)\n========================================================\n\nThere are other useful functions operating on many characters at once.\nContour tracing and flood-filling are among them:\n\n   • ‘uniline-contour’\n   • ‘uniline-fill’\n\n   The following functions operate on a rectangular region, which must\nbe active prior to calling them:\n\n   • ‘uniline-draw-inner-rectangle’\n   • ‘uniline-draw-outer-rectangle’\n   • ‘uniline-copy-rectangle’\n   • ‘uniline-kill-rectangle’\n   • ‘uniline-yank-rectangle’\n   • ‘uniline-fill-rectangle’\n   • ‘uniline-move-rect-up↑’\n   • ‘uniline-move-rect-ri→’\n   • ‘uniline-move-rect-dw↓’\n   • ‘uniline-move-rect-lf←’\n\n\u001f\nFile: uniline.info,  Node: Constants,  Next: Macro and text direction,  Prev: Long range actions (contour flood-fill rectangle),  Up: Lisp API\n\n14.5 Constants\n==============\n\nConstants for the 4 directions:\n\n   • ‘uniline-direction-up↑’ ;; constant 0\n   • ‘uniline-direction-ri→’ ;; constant 1\n   • ‘uniline-direction-dw↓’ ;; constant 2\n   • ‘uniline-direction-lf←’ ;; constant 3\n\n\u001f\nFile: uniline.info,  Node: Macro and text direction,  Next: Insert and tweak glyphs,  Prev: Constants,  Up: Lisp API\n\n14.6 Macro and text direction\n=============================\n\nChanging text direction:\n\n   • ‘uniline-text-direction-up↑’\n   • ‘uniline-text-direction-ri→’\n   • ‘uniline-text-direction-dw↓’\n   • ‘uniline-text-direction-lf←’\n\n   or (in this case the mode-line is not updated):\n\n   • ‘(setq uniline-text-direction uniline-direction-up↑)’\n   • ‘(setq uniline-text-direction uniline-direction-ri→)’\n   • ‘(setq uniline-text-direction uniline-direction-dw↓)’\n   • ‘(setq uniline-text-direction uniline-direction-lf←)’\n\n   Call macro in any direction:\n\n   • ‘uniline-call-macro-in-direction-up↑’\n   • ‘uniline-call-macro-in-direction-ri→’\n   • ‘uniline-call-macro-in-direction-dw↓’\n   • ‘uniline-call-macro-in-direction-lf←’\n\n\u001f\nFile: uniline.info,  Node: Insert and tweak glyphs,  Next: Change to alternate styles,  Prev: Macro and text direction,  Up: Lisp API\n\n14.7 Insert and tweak glyphs\n============================\n\nInsert and cycle intersection glyphs:\n\n   • ‘uniline-insert-fw-arrow’\n   • ‘uniline-insert-fw-square’\n   • ‘uniline-insert-fw-oshape’\n   • ‘uniline-insert-fw-cross’\n   • ‘uniline-insert-fw-grey’\n   • ‘uniline-insert-bw-arrow’\n   • ‘uniline-insert-bw-square’\n   • ‘uniline-insert-bw-oshape’\n   • ‘uniline-insert-bw-cross’\n   • ‘uniline-insert-bw-grey’\n\n   Rotate arrow or tweak 4-half-lines or 4-block characters:\n\n   • ‘uniline-rotate-up↑’\n   • ‘uniline-rotate-ri→’\n   • ‘uniline-rotate-dw↓’\n   • ‘uniline-rotate-lf←’\n\n   Here are the lowest level functions.  Move point, possibly extending\nthe buffer in right and bottom directions:\n\n   • ‘uniline-move-to-column’\n   • ‘uniline-move-to-line’\n   • ‘uniline-move-to-lin-col’\n   • ‘uniline-move-to-delta-column’\n   • ‘uniline-move-to-delta-line’\n\n\u001f\nFile: uniline.info,  Node: Change to alternate styles,  Prev: Insert and tweak glyphs,  Up: Lisp API\n\n14.8 Change to alternate styles\n===============================\n\nA drawing in a rectangular selection may have its style changed:\n\n   • ‘uniline-change-style-dot-3-2’ ;; 3 dashes vert.  ┆, 2 horiz.  ╌\n   • ‘uniline-change-style-dot-4-4’ ;; 4 dashes vert.  ┊ & horiz.  ┈\n   • ‘uniline-change-style-standard’ ;; back to Uniline base style\n   • ‘uniline-change-style-hard-corners’ ;; rounded corners╭╴become\n     hard┌\n   • ‘uniline-change-style-thin’ ;; convert to ╭╴ thin lines\n   • ‘uniline-change-style-thick’ ;; convert to ┏╸ thick lines\n   • ‘uniline-change-style-double’ ;; convert to ╔═ thick lines\n   • ‘uniline-aa2u-rectangle’ ;; call aa2u to convert ASCII to Unicode\n\n   The above functions require a region to be marked.\n\n\u001f\nFile: uniline.info,  Node: Mouse support,  Next: Installation,  Prev: Lisp API,  Up: Top\n\n15 Mouse support\n****************\n\nThe out-of-the-box mouse support of ‘Emacs’ works perfectly.  Except\nwhen the mouse clicks on a position outside the buffer.  This happens\nwhen clicking past the end of a too short line, or past the end of the\nbuffer.\n\n   To handle those cases, a few standard ‘Emacs’ functions have been\nextended to add blank characters or blank lines.  Doing so, the\nmouse-click now falls on a valid part of the buffer.  Of course, those\nextensions are only active on ‘uniline-mode’ activated buffers.\n\n   Beware that when the window is at the same time zoomed with ‘C-x C-+\nC--’ AND horizontally scrolled with ‘C-x <’, the cursor positioning is\nnot accurate.  This is due to ‘Emacs’ limitations and bugs.  Just click\ntwice to fix the inaccuracy.\n\n\u001f\nFile: uniline.info,  Node: Installation,  Next: Related packages,  Prev: Mouse support,  Up: Top\n\n16 Installation\n***************\n\n* Menu:\n\n* use-package, the straightforward way: use-package the straightforward way.\n* Without use-package::\n\n\u001f\nFile: uniline.info,  Node: use-package the straightforward way,  Next: Without use-package,  Up: Installation\n\n16.1 use-package, the straightforward way\n=========================================\n\nThe ‘use-package’ library became the de-facto standard to manage\npackages in your ‘.emacs’ initialization file.  The ‘use-package’\nlibrary comes along with Emacs.  It can (among other services) delay\nloading external packages until they are used, and bind keyboard\nshortcuts to the package’s entry points.\n\n   Add the following lines to your ‘.emacs’ file, and reload it, if not\nalready done.  This says that the popular Melpa repository is one of the\ncentral store of third parties packages.  To day, it provides almost\n7000 packages to choose from.\n\n     (add-to-list 'package-archives\n                  '(\"melpa\" . \"http://melpa.org/packages/\")\n                  t)\n     (package-initialize)\n\n   Alternately you may customize this variable:\n\n     M-x customize-variable package-archives\n\n   Then add those lines in your Emacs initialization file (usually\n‘~/.emacs’):\n\n     (use-package uniline-hydra\n       :bind (\"C-<insert>\" . uniline-mode))\n\n   or:\n\n     (use-package uniline-transient\n       :bind (\"C-<insert>\" . uniline-mode))\n\n   This tell Emacs:\n   • Be prepared to load ‘uniline-mode’ when the user request it, but do\n     not load it now.\n   • Bind the ‘C-<insert>’ keys to the function ‘uniline-mode’.  This\n     shortens the longer ‘M-x uniline-mode’ command.  Any other key\n     combinations can be bound, as you prefer.  ‘<insert>’ happens to\n     also be the key used inside ‘Uniline’ (customizable).\n   • Load either the ‘uniline-hydra’ or the ‘uniline-transient’ file, as\n     you prefer.  This gives Uniline one or the other flavour of\n     user-interface.\n\n   There is an alias to ‘uniline-hydra’:\n\n     (use-package uniline\n       :bind (\"C-<insert>\" . uniline-mode))\n\n   If you are using *note straight.el:: with ‘use-package’, and have\n‘(setq straight-use-package-by-default t)’, you have the following\noptions:\n\n     ;; uniline-hydra using the alias\n     (use-package uniline)\n\n     ;;; uniline-hydra explicitly requested\n     (use-package uniline-hydra\n       :straight uniline)\n\n     ;;; install and load uniline-transient\n     (use-package uniline-transient\n       :straight uniline)\n\n\u001f\nFile: uniline.info,  Node: Without use-package,  Prev: use-package the straightforward way,  Up: Installation\n\n16.2 Without use-package\n========================\n\nDownload the package from Melpa:\n\n     (package-install \"uniline\")\n\n   Alternately, you can download the Lisp files, and load them manually:\n\n     (load-file \"uniline-hydra.el\")   ;; interpreted form\n     (load-file \"uniline-hydra.elc\")  ;; byte-compiled form\n     (load-file \"uniline-hydra.eln\")  ;; native-compiled form\n     ;; this automatically\n     ;; loads \"uniline-core.el\"\n     ;; or    \"uniline-core.elc\"\n     ;; or    \"uniline-core.eln\"\n\n   or if you prefer the Transient interface over the Hydra one:\n     (load-file \"uniline-transient.el\")   ;; interpreted form\n     (load-file \"uniline-transient.elc\")  ;; byte-compiled form\n     (load-file \"uniline-transient.eln\")  ;; native-compiled form\n     ;; this automatically\n     ;; loads \"uniline-core.el\"\n     ;; or    \"uniline-core.elc\"\n     ;; or    \"uniline-core.eln\"\n\n   You should prefer the byte-compiled or native-compiled forms over the\ninterpreted forms, because there are a lot of optimizations performed at\ncompile time.\n\n   You may want to give ‘uniline-mode’ a key-binding.  A way to do that\nwithout ‘use-package’ is to add those lines to your initialization file\n(usually ‘~/.emacs’):\n\n     (require 'uniline-hydra)\n     (bind-keys :package uniline-hydra (\"C-<insert>\" . uniline-mode))\n\n   The downside is that ‘Uniline’ will be loaded as soon as ‘Emacs’ is\nlaunched, rather than deferred until invoked.\n\n\u001f\nFile: uniline.info,  Node: Related packages,  Next: Author contributors,  Prev: Installation,  Up: Top\n\n17 Related packages\n*******************\n\n   • ‘artist-mode’: the ASCII art mode built into ‘Emacs’.\n\n   • ‘ascii-art-to-unicode’: as the name suggest, converts ASCII\n     drawings to UNICODE, giving results similar to those of ‘Uniline’.\n\n   • ‘picture-mode’: as in ‘Uniline’, the buffer is infinite in east &\n     south directions.\n\n   • ‘ascii-art-to-unicode’ ASCII art to UNICODE in ‘Emacs’.  This is a\n     standard ELPA package by Thien-Thi Nguyen (rest in peace).\n     ‘Uniline’ may call it to convert ASCII art drawings to equivalent\n     UNICODE.  ‘Uniline’ arranges to not require a dependency on\n     ‘ascii-art-to-unicode’ by lazy evaluating a call to ‘aa2u’.\n\n   • ‘org-pretty-table’: Org Mode tables _appear_ to be drawn in UNICODE\n     characters (actually they are still in ASCII).\n\n   • ‘boxes’: draws artistic boxes around text, with nice looking\n     unicorns, flowers, parchments, all in ASCII art.\n\n   • ‘org-drawio’: a bridge between the Draw.Io editor and ‘Emacs’,\n     producing drawing similar to those of ‘Uniline’, but in ‘.svg’.\n\n   • ‘syntree’: draws ASCII trees on-the-fly from description.\n\n   • ‘unicode-enbox’: create a UNICODE box around a text; input and\n     output are strings.\n\n   • ‘unicode-fonts’: in ‘Emacs’, helps alleviate the lack of full\n     UNICODE coverage of most fonts.\n\n   • ‘org-superstar’: prettify headings and plain lists in Org Mode,\n     using UNICODE glyphs.\n\n   • ‘charmap’: UNICODE table viewer for ‘Emacs’.\n\n   • ‘insert-char-preview’: insert UNICODEs with character preview in\n     completion prompt.\n\n   • ‘list-unicode-display’: list all UNICODE characters, or a selection\n     of them.\n\n   • ‘show-font’: show font features in a buffer.\n\n   • ‘ob-svgbob’: convert your ascii diagram scribbles into happy little\n     SVG\n\n   • ‘el-easydraw’: a full featured SVG editor right inside your ‘Emacs’\n\n   • ‘asciiflow’: (not ‘Emacs’) draw on the web, then copy-paste your\n     UNICODE text\n\n   • ‘ascii-draw’: like ‘asciiflow’ with Unicodes.\n\n   • ‘dot-to-ascii.ggerganov.com:’ (not ‘Emacs’) describe your schema in\n     the Graphviz language, and copy-past your UNICODE text.\n\n   • ‘monosketch’: (not ‘Emacs’) draw on the web, then copy-paste your\n     UNICODE text.\n\n   • ‘ibm-box-drawing-hydra.el’: keyboard interface to insert UNICODE\n     box-drawing characters one at a time.\n\n   • ‘excalidraw.com’: inline drawing, but not in Unicode or Ascii.\n\n   • ‘org-excalidraw’: integrate SVG images generated by excalidraw into\n     Org Mode.\n\n   • ‘rcd-box’: create tables surrounded by box-drawing characters from\n     Lisp descriptions.\n\n   • ‘ob-diagram’: generate various diagrams using diagrams backend.\n\n   • ‘ob-mermaid’: generate Mermaid diagrams within org-mode babel.\n\n   • ‘quail-boxdrawing.el’: input method for box drawing characters.\n\n   • ‘make-box.el’: box around part of a buffer.\n\n   • ‘vim drawit ascii diagrams’: in Vim, in ASCII.\n\n   • ‘MarkDeep’: (Casual Effects): write in Markdown, render on the Web\n     on the fly.  Uniline may be used to author part of the Markdown\n     source.\n\n   • ‘org-utf-to-xetex’: export Org-Mode utf-8 documents to various\n     formats preserving smileys and other Unicode characters.\n\n   • ‘image-to-ascii’: turns a photo into ASCII.\n\n   • ‘ascii-maze-generator’: web-inline generator of mazes, with the\n     same lines drawn by Uniline.\n\n   • ‘diagon.arthursonzogni.com’: web-inline drawing of diagrams similar\n     to those that Unline enables.\n\n   • ‘cascii.html’: a single Html-JavaScript file to draw Unicode\n     diagrams.\n\n   • ‘elm-svgbob’: think of Ditaa for converting Ascii to SVG.\n\n   • ‘rasciigraph’: script to plot time-series in Unicode.\n\n   • ‘asciichart-sharp’: another script to plot time-series in Unicode.\n\n   • ‘d2’, ‘ob-d2’: think of Mermaid to convert diagram textual\n     descriptions to SVG.\n\n   • ‘dag-draw.el’: in Emacs program diagrams using Lisp, the output is\n     Unicode.\n\n   • ‘https://mbork.pl/2025-11-10_ASCII_art_timeline_diagrams’: not a\n     package, just an example of how easy is to draw time-series in\n     Ascii with Emacs.\n\n   • ‘pikchr’: describe objects and their relationship in Markdown,\n     render in Unicode.\n\n   • ‘ob-pikchr.el’: integration of ‘pikchr’ in Emacs Org-Mode.\n\n   • ‘GoAT’: Go-based Text-Art to SVG refinement.\n\n   • ‘SvgBob’: another script to convert Ascii to SVG (like Ditaa).\n     Written in Rust.\n\n   • ‘Durdraw’: an Ascii, Unicode and Ansi art editor for Unix-like\n     systems.  With colors.\n\n   • ‘figlet’: makes large letters out of ordinary text, in text.  There\n     is an Emacs integration by J.  Kotta.\n\n\u001f\nFile: uniline.info,  Node: Author contributors,  Next: License,  Prev: Related packages,  Up: Top\n\n18 Author, contributors\n***********************\n\n   • Thierry Banel, author\n\n   Feedback:\n\n   • Chris Rayner (@riscy), gave recommendations prior to insertion in\n     MELPA\n\n   • Adam Porter (@alphapapa), suggested submitting ‘Uniline’ to ‘ELPA’;\n     should I?\n\n   • Joost Kremers <https://github.com/joostkremers> found a bug in the\n     minor-mode key-binding definitions, and incompatibility with\n\n   • DogLooksGood <https://github.com/DogLooksGood> gave feedback on\n     inserting usual characters not moving the cursor\n\n   • LuciusChen & lhindir on GitHub, arthurno1 & karthink on Reddit,\n     pushed toward ‘Transient’ as the default interface instead of\n     ‘Hydra’\n\n   • karthink noted that ‘Transient’ was now built into ‘Emacs’,\n     loosening the dependencies conundrum, arthurno1 participated in the\n     ‘Hydra’ - ‘Transient’ discussion\n\n   • karthink pointed to the new ‘Aporetic’ font family, which was then\n     added to the ‘Uniline’ supported fonts\n\n   • rumengling on GitHub found and diagnosed the misaligned lines issue\n     produced by some \"language environments\" (see *note Language\n     environment::).\n\n   • tpapp documented the installation using Straight, and fixed some\n     typos.\n\n   • tskinner-oppfi (GitHub) repoted Elpaca packaging break due to a bad\n     version number.\n\n   Contributors:\n\n   • JD Smith (jdtsmith on GitHub) rewrote the ‘:lighter’ for added\n     flexibility (the information in the mode-line about the state of\n     ‘Uniline’)\n\n   • JD Smith also pointed to ‘#+begin_src uniline’ Org Mode block\n     suprising behavior (editing its content automatically switches to\n     ‘uniline-mode’)\n\n   Utilities:\n\n   • Oleh Krehel alias abo-abo for his package ‘Hydra’\n\n   • The ‘Magit’ team for the ‘Transient’ library\n\n   • Thien-Thi Nguyen (RIP) for his package ‘ascii-art-to-unicode’\n\n\u001f\nFile: uniline.info,  Node: License,  Prev: Author contributors,  Up: Top\n\n19 License\n**********\n\nCopyright (C) 2024-2026 Thierry Banel\n\n   Uniline is free software: you can redistribute it and/or modify it\nunder the terms of the GNU General Public License as published by the\nFree Software Foundation, either version 3 of the License, or (at your\noption) any later version.\n\n   Uniline is distributed in the hope that it will be useful, but\nWITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\nGeneral Public License for more details.\n\n   You should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n\n\u001f\nTag Table:\nNode: Top171\nNode: Getting started in 10 seconds3126\nNode: New3732\nNode: Gallery pure UNICODE diagrams in Emacs4010\nNode: Document a command4596\nNode: Connect boxes with arrows5129\nNode: Explain decisions trees6449\nNode: Draw lines or blocks7955\nNode: Outline the General Relativity and the Schrödinger's equations9497\nNode: Explain the structure of a sentence in a foreign language12186\nNode: Draw electronic diagrams13772\nNode: Explain Lisp lists15177\nNode: Draw sketched objects16360\nNode: Pure text18325\nNode: Beware!19059\nNode: A minor mode for drawing19642\nNode: Minor mode19954\nNode: Draw lines by moving the cursor20516\nNode: Infinite ∞ buffer21108\nNode: Brush style22389\nNode: Text direction24974\nNode: The <insert> key25524\nNode: Glyphs ▷ ▶ → □ ◆ ╮─ insertion & modification26524\nNode: Arrows glyphs ▷ ▶ → ▹ ▸ ↔27241\nNode: Intersection glyphs ■ ◆ ●28813\nNode: Fine tweaking of lines31643\nNode: Rectangular actions33526\nNode: Drawing a rectangle34876\nNode: Filling a rectangle36527\nNode: Moving a rectangular region37295\nNode: Copying killing yanking a rectangular region39864\nNode: Dashed lines and other styles41059\nNode: ASCII to UNICODE45184\nNode: Long range actions contour and flood-fill48867\nNode: Tracing a contour49130\nNode: Flood-fill51044\nNode: Macros52818\nNode: Which fonts?56209\nNode: Recommended fonts56470\nNode: Use case mixing fonts58560\nNode: Hydra or Transient?60557\nNode: Selecting Hydra or Transient61546\nNode: Instantly selecting Hydra or Transient62664\nNode: One-liner menus63630\nNode: The Hydra interface65568\nNode: The Transient interface66241\nNode: Customization66779\nNode: Interface type67647\nNode: Insert key68173\nNode: Maximum steps when drawing a contour68955\nNode: Cursor type69285\nNode: Hint style69610\nNode: Welcome message visibility69957\nNode: Line spacing70338\nNode: Font71804\nNode: Upward infiniteness ∞72320\nNode: How Uniline behaves with its environment?72850\nNode: Language environment73449\nNode: Compatibility with Picture-mode75854\nNode: Compatibility with Artist-mode76801\nNode: Compatibility with Whitespace-mode77320\nNode: Compatibility with Org Mode78220\nNode: Org Mode and LaTex79641\nNode: What about \\t tabs?81501\nNode: What about ^L page separation?82302\nNode: Emacs on the Linux console82925\nNode: Emacs on a graphical terminal emulator83885\nNode: Emacs on Windows84815\nNode: Compatibility with ASCIIFlow85496\nNode: Lisp API86452\nNode: Move the cursor87002\nNode: Brush87433\nNode: Example Lisp function to draw a plus sign88302\nNode: Long range actions (contour flood-fill rectangle)90138\nNode: Constants91076\nNode: Macro and text direction91491\nNode: Insert and tweak glyphs92426\nNode: Change to alternate styles93539\nNode: Mouse support94448\nNode: Installation95332\nNode: use-package the straightforward way95576\nNode: Without use-package97961\nNode: Related packages99522\nNode: Author contributors104585\nNode: License106622\n\u001f\nEnd Tag Table\n\n\u001f\nLocal Variables:\ncoding: utf-8\nEnd:\n"
  }
]