[
  {
    "path": ".emacs/28.2/straight/versions/default.el",
    "content": "((\"dash.el\" . \"721436b04da4e2795387cb48a98ac6de37ece0fd\")\n (\"el-get\" . \"f220df34333fdb363b84b28f4ed4a5575341bf45\")\n (\"emacs-buttercup\" . \"e4fb7cd560d27d8879a2c7739ee96946adec2df8\")\n (\"emacsmirror-mirror\" . \"d9919dfe6eede6ff668614b23f64cfef0f954c48\")\n (\"gnu-elpa-mirror\" . \"e59499eeb86979ef2b41f004b11c0e712f6354b3\")\n (\"melpa\" . \"fd3bb4b191bf416dd419c5c76d510c7f5890e673\")\n (\"nongnu-elpa\" . \"c7b774608a8b17b5e95a096317d12fae7dc31b68\")\n (\"org\" . \"233a0ced97366090c31ef94562879bb2f729b120\")\n (\"s.el\" . \"dda84d38fffdaf0c9b12837b504b402af910d01d\")\n (\"straight.el\" . \"b3760f5829dba37e855add7323304561eb57a3d4\"))\n:gamma\n"
  },
  {
    "path": ".emacs/29.3/straight/versions/default.el",
    "content": "((\"dash.el\" . \"721436b04da4e2795387cb48a98ac6de37ece0fd\")\n (\"el-get\" . \"f220df34333fdb363b84b28f4ed4a5575341bf45\")\n (\"emacs-buttercup\" . \"e4fb7cd560d27d8879a2c7739ee96946adec2df8\")\n (\"emacsmirror-mirror\" . \"d9919dfe6eede6ff668614b23f64cfef0f954c48\")\n (\"gnu-elpa-mirror\" . \"e59499eeb86979ef2b41f004b11c0e712f6354b3\")\n (\"melpa\" . \"fd3bb4b191bf416dd419c5c76d510c7f5890e673\")\n (\"nongnu-elpa\" . \"c7b774608a8b17b5e95a096317d12fae7dc31b68\")\n (\"org\" . \"233a0ced97366090c31ef94562879bb2f729b120\")\n (\"s.el\" . \"dda84d38fffdaf0c9b12837b504b402af910d01d\")\n (\"straight.el\" . \"b3760f5829dba37e855add7323304561eb57a3d4\"))\n:gamma\n"
  },
  {
    "path": ".emacs/30.1/straight/versions/default.el",
    "content": "((\"dash.el\" . \"721436b04da4e2795387cb48a98ac6de37ece0fd\")\n (\"el-get\" . \"f220df34333fdb363b84b28f4ed4a5575341bf45\")\n (\"emacs-buttercup\" . \"c0764a764cf088dcb5132c44d5864b22d7723765\")\n (\"emacsmirror-mirror\" . \"d9919dfe6eede6ff668614b23f64cfef0f954c48\")\n (\"gnu-elpa-mirror\" . \"e59499eeb86979ef2b41f004b11c0e712f6354b3\")\n (\"melpa\" . \"fd3bb4b191bf416dd419c5c76d510c7f5890e673\")\n (\"nongnu-elpa\" . \"c7b774608a8b17b5e95a096317d12fae7dc31b68\")\n (\"org\" . \"233a0ced97366090c31ef94562879bb2f729b120\")\n (\"s.el\" . \"dda84d38fffdaf0c9b12837b504b402af910d01d\")\n (\"straight.el\" . \"b3760f5829dba37e855add7323304561eb57a3d4\"))\n:gamma\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: CI\non: [push]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        emacs_version:\n          - '28.2'\n          - '29.3'\n          - '30.1'\n    steps:\n    - uses: actions/checkout@v2\n    - uses: purcell/setup-emacs@master\n      with:\n        version: ${{ matrix.emacs_version }}\n\n    - name: Run tests\n      run: |\n        make install\n        make\n"
  },
  {
    "path": ".gitignore",
    "content": "*.elc\n.emacs/*/straight/build\n.emacs/*/straight/repos\n.emacs/*/straight/build-cache.el"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n## 6.0.2\n\n- fix org-ml-match-do* form bug\n\n## 6.0.1\n\n- add tests for emacs 30.1\n\n## 6.0.0\n\nThis is a major update for org 9.7, which has been heavily optimized with a new\nsyntax tree API. This new version of org-ml takes advantage of this new API\n(which is much faster) but also breaks several things.\n\nsummary of breaking changes\n\n- `org-ml-planning-*` functions have been removed (they are no longer necessary)\n- the supercontents data structure (for\n  `org-ml-headline-get/set/map-supercontents) has been updated and rewritten.\n  See docstring for `org-ml-headline-get-supercontents` for details. TLDR is\n  that it now handles planning and node properties. This was done partly to\n  better handle whitespace (which was not done correctly previously) and also to\n  take advantage of performance improvements in the headline node type (see\n  below for `org-ml-update-supercontents`)\n- all previous depreciated functions have been removed\n  - and `org-ml-timestamp-get/set-range` have been renamed to\n    `org-ml-timestamp-get/set-length`\n- `org-ml-parse-habits` has been removed, this is now elegantly handled by\n  org-element itself. See `org-ml-timestamp-get/set/map-deadline` instead.\n- `org-ml-headline-get/set/map-node-properties` now use a list of string pairs\n  like `(KEY VAL)` instead of raw node-property nodes.\n- `org-ml-timestamp-set-length` now takes a unit argument\n- `org-ml-clone-node` has been removed\n- `org-ml-unixtime-to-time-long/short` have been combined into\n  `org-ml-unixtime-to-timelist` which takes a flag to determine if the hours and\n  minutes should be included\n\nsummary of added features\n\n- added higher-level timestamp-diary functions for start and end time\n  manipulation (new in org 9.7)\n- `org-ml-timestamp-get/set/map-deadline` which manipulates what is commonly\n  called \"habits\" (new in org 9.7)\n- `org-ml-update-supercontents` and `org-ml-update-supersection` which are two\n  heavily-optimized functions which take advantage of the new lazy evalulation\n  in org 9.7; use these to update headline contents without touching the\n  headline itself\n- memoization for builder functions\n- ability to switch between pure and impure evaluation (the latter is faster but\n  less safe); see `org-ml-use-impure`\n\nbug fixes and refactorizations\n\n- added missing tests for purity\n- fixed many whitespace handling errors\n- use conda to pin exact emacs version for local development\n- use straight to pin exact versions of all dependencies\n- remove lispy dependency\n\n## 5.8.8\n\n- fix list-like syntax in secondary string parsing\n\n## 5.8.7\n\n- use strings for `org-ml-build-property-drawer!` arguments\n\n## 5.8.6\n\n- bugfixes\n\n## 5.8.5\n\n- make docstring clearer\n\n## 5.8.4\n\n- don't parse bold text as a headline\n\n## 5.8.3\n\n- fix typo in README.md\n\n## 5.8.2\n\n- fix blank table cell bug\n\n## 5.8.1\n\n- fix typo\n\n## 5.8.0\n\n- make myers diff algorithm use linear space\n- fix a bunch of compiler warnings for emacs 28+\n\n## 5.7.3\n\n- fix leaky abtraction bug\n\n## 5.7.2\n\n- fix incompatibility with org v9.5 (note: 9.5 not fully tested yet)\n\n## 5.7.1\n\n- add `org-ml-remove-parent(s)`\n\n## 5.7.0\n\n- add functions/checks for `org-data` nodes\n\n## 5.6.2\n\n- add explicit test path for emacs 27.2/org-mode 9.4\n\n## 5.6.1\n\n- make `org-ml-from-string` work correctly with all types and inputs\n\n## 5.6.0\n\n- add get/set/map functions for timestamp repeaters and warnings\n- add optional switch for habit parsing\n\n## 5.5.4\n\n- fix `org-ml-headline-set-node-property` nil property bug\n\n## 5.5.3\n\n- fix missing zero-length ending timestamps\n\n## 5.5.2\n\n- fix compile warnings for `org-ml-macs.el`\n\n## 5.5.1\n\n- fix potential merge sort stack overflow\n\n## 5.5.0\n\n- reorganized headline/subtree batch functions (and depreciated old names)\n\n## 5.4.3\n\n- fixed nested headline parsing for `org-ml-get-headlines` et al\n\n## 5.4.2\n\n- make indent/outdent/promote/demote functions more accurate/faster\n- further optimizations and additional benchmarks\n\n## 5.4.1\n\n- improve performance of string insertion and headline batch updating\n\n## 5.4.0\n\n- add pattern memoization to match function family\n- numerous performance enhancements including:\n  - remove majority of closures in anaphoric forms (eg `make-byte-code`)\n  - implement faster macros for plists\n  - improve timestamp processing\n  - remove equality checking from `org-ml~update`\n  - streamline headline batch functions (eg `org-ml-do-headlines` et al)\n\n## 5.3.0\n\n- add `org-ml-get-properties`\n- add `org-ml-item-get/set/map-paragraph`\n- make `org-ml-get-all-properties` public\n- numerous bug fixes and performance enhancments\n\n## 5.2.0\n\n- add benchmark framework\n- add intermediate functions to control Myers diff algorithm application\n- fix potential infinite loop using native `equal` function for node comparison\n\n## 5.1.0\n\n- fixed array overflow error in myers diff code\n- add affiliated keywords to polymorphic property interface and builder\n  functions\n\n## 5.0.2\n\n- fixed active timestamp bug for closed planning nodes\n- use Myers diff algorithm for update functions\n\n## 5.0.1\n\n- rearrange reference files\n- use buttercup for testing\n\n## 5.0.0\n\n- add robust headline logbook and contents function\n- rename indent/unindent functions to better reflect native org function naming\n  conventions\n- add `org-ml-from-string`\n- improve subtree parsing performance\n- fix whitespace errors for\n  `org-ml-headline-set-planning/node-properties/supercontents` functions\n\n## 4.0.1\n\n- fix `org-ml-parse-this-table-row` and `org-ml-parse-table-row-at`\n  beyond first row of table\n\n## 4.0.0\n\n- add `org-ml-get-parents`\n- add `org-ml-headline-get-logbook-loose` and `org-ml-headline-get-contents`\n- removed old `org-ml-headline-X-logbook` functions and replaced them with\n  `org-ml-headline-X-logbook-drawer` which can be made aware of other drawers\n  other than \"LOGBOOK\" or nothing\n- add `org-ml-clone-n`\n\n## 3.0.2\n\n- Update dependencies\n- Fix bugs\n\n## 3.0.1\n\n- Fix bugs\n  - Don't use `nreverse` unless needed\n  - Don't crash when `org-ml-headline-get-node-property` should return nil\n\n## 3.0.0\n\n- Update for org-mode 9.3\n\n## 2.0.1\n\n- Fixed byte compile\n- Clean up docstrings\n\n## 2.0.0 \n\n- Renamed from `om.el` to `org-ml` (org-metalanguage)\n- Renamed functions to be more consistent\n  - `org-ml-get-headlines` and friends to `org-ml-parse-headlines`\n  - `org-ml-do-headlines` and friends to `org-ml-update-headlines`\n- Add POSIX ERE-like regexp syntax to `org-ml-match` and friends\n- Add affiliated keyword support\n- Numerous bug fixes\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://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 <http://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<http://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<http://www.gnu.org/philosophy/why-not-lgpl.html>.\n"
  },
  {
    "path": "Makefile",
    "content": "EMACS ?= emacs -Q --batch --load init.el -L .\nRM ?= rm -f\n\nall: test\n\n# run all tests with both interpreted and compiled org-ml functions\ntest:\n\t${MAKE} unit\n\t${MAKE} compile\n\n# build docs for org-ml\ndocs:\n\t${EMACS} -L dev \\\n\t\t-l dev/org-ml-docs.el \\\n\t\t-f create-docs-files\n\n# run internal (stateless) tests with interpreted org-ml functions\ninternal:\n\t${EMACS} -L dev \\\n\t\t-l dev/org-ml-test-internal.el \\\n        -f buttercup-run-discover\n\n# run external (stateful) tests with interpreted org-ml functions\nexternal:\n\t${EMACS} -L dev \\\n\t\t-l dev/org-ml-test-external.el \\\n        -f buttercup-run-discover\n\n# run tests with interpreted org-ml functions\nunit: \n\t${EMACS} -L dev \\\n\t\t-l dev/org-ml-test-external.el \\\n\t\t-l dev/org-ml-test-internal.el \\\n        -f buttercup-run-discover\n\n\n# run tests with compiled org-ml functions\ncompile:\n\t${MAKE} build\n\t${MAKE} unit\n\t${MAKE} clean-elc\n\n# run and print benchmark results using compile org-ml functions\nbenchmark:\n\t${EMACS} build\n\t${EMACS} -L bench \\\n\t\t-l bench/org-ml-benchmarks.el \\\n\t\t-f org-ml-bench-run\n\t${MAKE} clean-elc\n\n# remove compiled lisp files\nclean-elc:\n\t${RM} *.elc\n\n# byte-compile all org-ml lisp files\nbuild:\n\t${EMACS} -f compile-target\n\n# install all development packages for the current version\ninstall:\n\t${EMACS} --eval '(print \"Install finished\")'\n\n# write lockfile for current emacs version given each repo dependency\nfreeze:\n\t${EMACS} -f straight-freeze-versions\n\nthaw:\n\t${EMACS} -f straight-thaw-versions\n\n.PHONY:\tall test docs unit\n"
  },
  {
    "path": "README.md",
    "content": "# org-ml ![CI](https://github.com/ndwarshuis/org-ml/actions/workflows/test.yml/badge.svg) ![MELPA VERSION](https://melpa.org/packages/org-ml-badge.svg)\n\nA functional API for org-mode inspired by\n[@magnars](https://github.com/magnars)'s\n[dash.el](https://github.com/magnars/dash.el) and\n[s.el](https://github.com/magnars/s.el) libraries.\n\n# Installation\n\nInstall from MELPA:\n\n```\nM-x package-install RET org-ml RET\n```\n\nAlternatively, clone this repository to somewhere in your load path:\n\n```\ngit clone https://github.com/ndwarshuis/org-ml ~/somewhere/in/load/path\n```\n\nThen require in your emacs config:\n\n```\n(require 'org-ml)\n```\n\n## Dependencies\n\n- emacs (28.2, 29.3, 30.1)\n- org-mode (9.7.9)\n- dash (2.17.0)\n- s (1.13.0)\n\nExplicit versions noted above have been tested. Other versions may work but are\nnot currently supported.\n\nNotably, *only* org 9.7.x and above will work (9.6 and below will absolutely\nbreak).\n\n# Motivation\n\nOrg-mode comes with a powerful, built-in parse-tree generator specified in\n`org-element.el`. The generated parse-tree is simply a heavily-nested list which\ncan be easily manipulated using (mostly pure) functional code. This contrasts\nthe majority of functions normally used to interface with org-mode files, which\nare imperative in nature (`org-insert-headine`, `outline-next-heading`, etc) as\nthey depend on the mutable state of Emacs buffers. In general, functional code\nis\n([arguably](https://en.wikipedia.org/wiki/Functional_programming#Comparison_to_imperative_programming))\nmore robust, readable, and testable, especially in use-cases such as this where\na stateless abstract data structure is being transformed and queried.\n\nThe `org-element.el` provides a minimal API for handling this parse-tree in a\nfunctional manner, but does not provide higher-level functions necessary for\nintuitive, large-scale use. The `org-ml` package is designed to provide this\nAPI. Furthermore, it is highly compatible with the `dash.el` package, which is a\ngeneralized functional library for emacs-lisp.\n\n# Org-Element Overview\n\nParsing a buffer with the function `org-element-parse-buffer` will yield a parse\ntree composed of nodes. Nodes have types and properties associated with them.\nSee [the org-element API\ndocumentation](https://orgmode.org/worg/dev/org-element-api.html#attributes) for\na list of all node types and their properties (also see the [terminology\nconventions](#terminology) and [property omissions](#properties) used in this\npackage).\n\nEach node is represented by a list where the first member is the type and the\nsecond member is a plist describing the node's properties:\n\n``` emacs-lisp\n(type (:prop1 value1 :prop2 value2 ...))\n```\n\nNode types may be either leaves or branches, where branches may have zero or\nmore child nodes and leaves may not have child nodes at all. Leaves will always\nhave lists of the form shown above. Branches, on the other hand, have their\nchildren appended to the end:\n\n``` emacs-lisp\n(type (:prop1 value1 :prop2 value2) child1 child2 ...)\n```\n\nIn addition to leaves and branches, node types can belong to one of\ntwo classes:\n- Objects: roughly correspond to raw, possibly-formatted text\n- Elements: more complex structures which may be built from objects\n\nWithin the branch node types, there are restrictions of which class is allowed\nto be a child depending on the type. There are three of these restrictions:\n- Branch element with child elements (aka 'greater elements'): these are element\n  types that are generally nestable inside one another (eg headlines,\n  plain-lists, items)\n- Branch elements with child objects (aka 'object containers'): these are\n  element types that hold textual information (eg paragraph)\n- Branch objects with child objects (aka 'recursive objects'): these are object\n  types used primarily for text formating (bold, italic, underline, etc)\n\nNote: it is never allowed for an element type to be a child of a branch object\ntype.\n\n# Conventions\n\n## Terminology\n\n- 'node' is a vertex in the parse tree, where 'element' and 'object' are two\n  classes used to describe said vertex\n- 'child' and 'children' are used here instead of 'content' and 'contents'\n- 'branch' is a node that has or can have other nodes in it (`org-element`\n  mostly uses 'container' to describe these)\n- 'leaf' is a node without other nodes in it (opposite of branch)\n\n## Properties\n\nAll properties specified by `org-element.el` are readable by this API (eg one\ncan query them with functions like `org-ml-get-property`).\n\nThe properties `:begin`, `:end`, `:contents-begin`, `:contents-end`, `:parent`,\nand `post-affiliated` are not settable by this API as they are not necessary for\nmanipulating the textual representation of the parse tree. In addition to these,\nsome properties unique to certain types are not settable for the same reason.\nEach type's build function (`org-ml-build-X`) describes the properties that are\nsettable.\n\nSee `org-ml-remove-parent` and `org-ml-remove-parents` for specific information\nand functions regarding the `:parent` property, why it can be annoying, and when\nyou would want to remove it.\n\n## Threading\n\nEach function that operates on an element/object will take the element/object as\nits right-most argument. This allows convenient function chaining using\n`dash.el`'s right-threading operators (`->>` and `-some->>`). The examples in\nthe [API reference](docs/api-reference.md) almost exclusively demonstrate this\npattern. Additionally, the right-argument convention also allows convenient\npartial application using `-partial` from `dash.el`.\n\n## Higher-order functions\n\nHigher-order functions (functions that take other functions as arguments) have\ntwo forms. The first takes a (usually unary) function and applies it:\n\n``` emacs-lisp\n(org-ml-map-property :value (lambda (s) (concat \"foo\" s)) node)\n(org-ml-map-property :value (-partial concat \"foo\") node)\n```\n\nThis can equivalently be written using an anaphoric form where the original\nfunction name is appended with `*`. The symbol `it` carries the value of the\nunary argument (unless otherwise specified):\n\n``` emacs-lisp\n(org-ml-map-property* :value (concat \"foo\" it) node)\n```\n\n## Side effect functions\n\nAll functions that read and write from buffers are named like\n`org-ml-OPERATION-THING-at` where `OPERATION` is some operation to be performed on\n`THING` in the current buffer. All these functions take `point` as one of their\narguments to denote where in the buffer to perform `OPERATION`.\n\nAll of these functions have current-point convenience analogues that are named\nas `org-ml-OPERATION-this-THING` where `OPERATION` and `THING` carry the same\nmeaning, but `OPERATION` is done at the current point and `point` is not an\nargument to the function.\n\nFor the sake of brevity, only the former form of these functions are given in\nthe [API reference](docs/api-reference.md).\n\n# Usage\n\nFor comprehensive documentation of all available functions see the [API\nreference](docs/api-reference.md).\n\n## Habits\n\nSince org 9.7, habits are stored in `:repeater-deadline-unit` and\n`:repeater-deadline-value` of `timestamp` nodes. \"Deadline\" refers to the last\nbit in the repeater of a timestamp (ie the \"3d\" in \"[2019-01-01 Tue 12:00\n+1d/3d]\").\n\nSee `org-ml-timestamp-get/set/map-deadline` to access and manipulate these.\n\n# Performance\n\nBenchmarking this library is still in the early stages.\n\nIntuitively, the most costly operations are going to be those that go\nback-and-forth between raw buffer text (here called \"buffer space\") and its node\nrepresentations (here called \"node space\") since those involve complicated\nstring formating, regular expressions, buffer searching, etc (examples:\n`org-ml-parse-this-THING`, `org-ml-update-this-THING` and friends). Once the\ndata is in node space, execution should be very fast since nodes are just lists.\nThus if you have performance-intensive code that requires many small edits to\norg-mode files, it might be better to use org-mode's built-in functions. On the\nother hand, if most of the complicated processing can be done in node space\nwhile limiting the number of conversions to/from buffer space, `org-ml` will be\nmuch faster.\n\nTo be more scientific, the current tests in the suite (see\n[here](bench/org-ml-benchmarks.el)) seem to support the following conclusions\nwhen comparing `org-ml` to equivalent code written using built-in org-mode\nfunctions (in line with the intuitions above):\n* reading data (a one way conversion from buffer to node space) is up to an\n  order of magnitude slower, specifically when the data to be obtained isn't\n  very large (eg, reading the TODO state from a headline)\n* text manipulations can be update to 10x slower *or faster* depending on what\n  they are:\n  * large edits like headline level changing are slower in `org-ml`\n  * updating headline todo and tags are faster in `org-ml`\n  * complex operations that involve lots of different functions tend to be\n    faster in `org-ml` (since there are more list operations vs buffer edits)\n  * changing the contents of headlines can be as fast or faster in `org-ml`,\n    especially when using memoization and `org-ml-update-supercontents` (see\n    below).\n\nTo run the benchmark suite:\n\n``` sh\nmake benchmark\n```\n\n## Deferred Properties\n\n### Overview\n\nStarting with org 9.7, `org-element`'s abstract syntax tree uses lazy evaluation\nfor several text-heavy operations. Thus the tree that `org-ml` consumes may have\nunevaluated (aka \"deferred\") properties in it. For the most part, this will not\naffect user experience, but understanding this will help in optimizing\nperformance, as preventing lazy properties from being unnecessarily resolved\nwill lead to significant performance gains.\n\nAs of version 9.7.9, the properties which are deferred are:\n* most properties in headlines (all except for :pre-blank and the properties in\n  `org-element--standard-properties`)\n* the :value property for code and verbatim nodes\n   \nSince most of the deferred properties are in headlines, and because headlines\nare so prevalent, the remainder of this discussion will focus on headlines.\n\nAccessing any deferred property in a headline will trigger that property to be\nresolved, which is slow (as of 9.7.9 this often results in multiple properties\nbeing resolved at once due to the interconnected nature of how a headilne is\nparsed). In `org-ml` this means using `org-ml-get-property` or similar, as well\nas `org-ml-to-string` which necessarily needs to read all properties to create a\nstring. Setting a property will resolve all properties, since (as noted above)\nmany deferred headline properties depend on others.\n\n### Optimizations in org-ml\n\nWith regard to buffer editing (ie `org-ml-update-X` functions) this also means\nthat any operation that does *not* edit the headline itself can be much faster\nunder this new lazy paradigm. Examples of this include updating CLOSED or\nSCHEDULED timestamps, editing the logbook, adding properties like Effort, or\nadding other contents between these and the next headline. Unlike previous\nversions of `org-ml` and `org` manipulating these would have involved parsing\nthe headline, parsing the stuff inside the headline, editing the stuff inside\nthe headline, then writing out a new headline. In 9.7, we can bypass most of the\nheadline parsing in this situation.\n\nThe functions to do this are `org-ml-update-supercontents` and\n`org-ml-update-supersection`. Both are only meant to edit the section underneath\nthe headlines in the buffer, and will not touch the headline itself. This takes\nadvantage of the new lazy evaluation system. These functions create an\nabstraction over the contents of the headline that can be manipulated in a sane\nway (see their docstrings for details).\n\nThere is one important caveat; if one changes the whitespace immediately after\nthe headline, this likely will change the :pre-blank property of the headline\nwhich will require the headline to be rewritten (and resolved) which negates\nthis performance benefit. However, these functions are smart enough to figure\nout when :pre-blank is changed.\n\n### Other Considerations\n\nBecause lazy evaluation defers parsing the buffer, this assumes that the buffer\nwill not be edited in between the time the org-element syntax tree is created\nand accessing any deferred properties. By extension it assumes the buffer is not\nentirely destroyed (which is probably when dealing with temp buffers).\n\nIf one expects that the buffer will not retain state prior to accessing deferred\nproperties, use `org-element-properties-resolve` (which will resolve deferred\nproperties in place) or either `org-element-copy` or `org-ml-copy` which will\nresolve deferred properties and copy the entire node (see more below).\n\n## Node Copying\n\nTo maintain functional purity, all public-facing functions in `org-ml` that\nmodify nodes should return a copy. This way, modifications to the returned node\nwill not \"go backward\" to the original input node.\n\nHowever, making copies can be slow. It also might be unnecessary within a\npipeline (usually with the threading macros `->` and `->>` from `dash.el`)\nsince the intermediate values are not bound to any variable, which leaves no\nopportunity for accidental side-effect leakage.\n\nTo solve this use-case, `org-ml` has the following specialized threading macros:\n\n- `org-ml->`\n- `org-ml->>`\n- `org-ml-->`\n- `org-ml-some->`\n- `org-ml-some->>`\n- `org-ml-some-->`\n\nThese correspond to the sans-`org-ml` macros from `dash.el`\n\nThe `org-ml` versions will set `org-ml-use-impure` to t, which will turn off all\ncopying within the pipeline. (see `org-ml-copy` which is a thin wrapper around\n`org-element-copy` with this switch built-in).\n\nNote that the performance benefits of this are significant but modest (5-10%\ndepending on the complexity of the operation), and this comes with a significant\ncost of reduced safety since it breaks the functional paradigm. Weight this\naccordingly.\n\n## Memoization\n\n### Build Functions\n\nNode building (functions like `org-ml-build-*`) is a pure operation (ie the\nresult only depends on the inputs). Furthermore, it is used in many places,\nincluding internally to `org-ml` itself.\n\nTherefore, memoizing these functions can produce significant performance gains.\n\nTo turn this on globally, set `org-ml-memoize-builders` to `t`. This will\nmemoize all leaf node builders by default (as it is assumed that any branch\nnodes will be sufficiently complicated that most will be unique and therefore\nmiss the cache). For more fine-grained control over which nodes are memoized,\nsee `org-ml-memoize-builder-types`.\n\n#### Shorthand Builders\n\nThere is an analogous optimization for 'shorthand' builders (functions like\n`org-ml-build-*`) which use simplified inputs. These are controled by\n`org-ml-memoize-shorthand-builders` and\n`org-ml-memoize-shorthand-builder-types`. These will by default memoize all\nshorthand builders except those for item and headline, for similar reasons to\nabove.\n\n### Match Patterns\n\nFor all pattern-matching functions (eg `org-ml-match` and `org-ml-match-X`), the\n`PATTERN` parameter is processed into a lambda function which computationally\ncarries out the pattern matching. If there are many calls using the same or a\nfew unique patterns, this lambda-generation overhead may be memoized by setting\n`org-ml-memoize-match-patterns`. See this varible's documentation for details.\n\n## Other potential optimizations\n\nThese are some ideas that may be implemented later depending on how much they\nmatter.\n\n### Tree-based Diff Algorithm\n\nIt makes sense to only update the parts of a buffer that actually change.\nHowever, this is complicated to do in practice.\n\nCurrent versions of `org-ml` can use the Myers Diff Algorithm (the thing that\npowers the `diff` program) to only edit the buffer contents that change (see\n`org~ml-update`). This can have some speedup since buffer editing is somewhat\nexpensive. The obvious tradeoff is the algorithm itself needs to be performed\nprior to the edit, and its complexity is quadratic.\n\nThe problem with this algorithm is that it only works on strings, thus the\norg-element tree needs to be interpreted for this to be used. Not only is this\ninherently expensive, it also negates any of the defferred property enhancements\nthat come with 9.7.\n\nThe (potential) solution is to implement a tree-based version of the Myers Diff\nalgorithm that works directly on the org-element tree. The result would be a\nlist of nodes to be inserted/deleted at a given position.\n\nThis would potentially have a huge benefit for deeply-nested edits, which often\nhappen in property drawers, logbooks, clocking entries, lists, etc.\n\n### Lazy Evaluation for Supercontents Functions\n\nThe functions `org-ml-get/set/map-supercontents` (and related) all operate on a\ncomplicated abstraction over a headline's section nodes. While this makes many\noperations easy and convenient, it has the drawback of converting the entire\nsection even if only a small part needs to be changed. Making some parts of this\ndata structure lazy could make this faster.\n\nThis most obviously matters for cases where one wants to edit the planning or\nproperty nodes of a headline which also has a massive logbook or a lot of\nclocks. Currently the entire logbook, clocks, etc, will be processed, despite\na tiny unrelated node actually being updated.\n\n# Development\n\nFor most stable results, make sure you have a working conda or mamba\ninstallation. Conda is not strictly needed, but reproducible testing results are\nnot guaranteed.\n\nBegin by creating a testing environment using the provided env* files (with the\ndesired version):\n\n```\nmamba env create -f env-XX.Y.yml\nconda activate org-ml-XX.Y\n```\n\nInstall all dependencies:\n\n```\nmake install\n```\n\nRun all tests:\n\n```\nmake unit\n```\n\nRun all tests with compilation:\n\n```\nmake compile\n```\n\nTo update a dependency, navidate to the `.emacs/XX.Y/straight/repos/<dep>`\ndirectory (after installation) and run `git reset --hard <ref>` (after fetch if\nneeded) to pull the desired git state. Then run:\n\n```\nmake freeze\n```\n\nWhich will update `.emacs/XX.Y/straight/versions/default.el`\n\nIf any of the above make commands fail with: `undefined symbol:\nmalloc_set_state` or similar, try the following:\n\n```\nexport LD_PRELOAD=/usr/lib/libc_malloc_debug.so\n```\n\n# Version History\n\nSee [changelog](CHANGELOG.md).\n\n# Acknowledgments\n\n- Ihor Radchenko: author or `org-element-ast.el`\n- Nicolas Goaziou: author of `org-element.el`\n- [@magnars](https://github.com/magnars):\n[dash.el](https://github.com/magnars/dash.el) and\n[s.el](https://github.com/magnars/s.el)\n"
  },
  {
    "path": "bench/org-ml-bench-fw.el",
    "content": ";;; org-ml-bench-fw.el --- Benchmark framework for org-ml -*- lexical-binding: t; -*-\n\n;; Copyright (C) 2020 Free Software Foundation, Inc.\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 <http://www.gnu.org/licenses/>.\n\n;;; Commentary:\n\n;; This framework provides a macro `org-ml-defbench' which is used to define\n;; the default benchmarks as part of this library, and can also be used by end\n;; users to define their own benchmarks.\n\n;;; Code:\n\n(require 's)\n(require 'dash)\n(require 'org-ml)\n\n(defvar org-ml-benchmarks '()\n  \"All defined benchmarks in the org-ml benchmark suite.\")\n\n(defun org-ml-bench-get-sys-info ()\n  (let ((cpumodel (if (eq system-type 'gnu/linux)\n                    (->> (shell-command-to-string \"lscpu | grep 'Model name:'\")\n                         (s-chop-prefix \"Model name:\")\n                         (s-trim))\n                    \"unknown\"))\n        (memtotal (if (eq system-type 'gnu/linux)\n                    (->> (shell-command-to-string \"grep MemTotal /proc/meminfo\")\n                         (s-chop-prefix \"MemTotal:\")\n                         (s-trim))\n                    \"unknown\"))\n        (org-ver (org-version))\n        (emacs-ver (s-replace \"\\n\" \"\" (emacs-version))))\n    (s-join \"\\n\"\n            (list\n             (format \"CPU:           %s\" cpumodel)\n             (format \"Total Memory:  %s\" memtotal)\n             (format \"Org Version:   %s\" org-ver)\n             (format \"Emacs Version: %s\" emacs-ver)))))\n\n(defmacro org-ml-bench-time-call (&rest body)\n  `(let ((start-time (float-time)))\n     ,@body\n     (- (float-time) start-time)))\n\n(defmacro org-ml-bench-with-org-file (repeated-pattern n &rest body)\n  (declare (indent 2))\n  `(let ((inhibit-message t))\n     (with-temp-buffer\n       (org-mode)\n       (insert (s-repeat ,n ,repeated-pattern))\n       (goto-char (point-min))\n       (garbage-collect)\n       (let ((time (org-ml-bench-time-call ,@body))\n             (res (buffer-string)))\n         (list res time)))))\n\n(defun org-ml-bench-compare (repeated-pattern n form1 form2)\n  (declare (indent 2))\n  `(-let (((res1 time1) (org-ml-bench-with-org-file ,repeated-pattern ,n ,form1))\n          ((res2 time2) (org-ml-bench-with-org-file ,repeated-pattern ,n ,form2)))\n     (unless (equal res1 res2)\n       (print \"WARNING: forms produced different buffer strings\")\n       (print (cadr (s-lines res1)))\n       (print (cadr (s-lines res2))))\n     (list time1 time2)))\n\n(defun org-ml-bench-format-result-row (title n time1 time2)\n  (format \"| %-40s | %6s | %10.5f | %10.5f | %10.2f |\" title n time1 time2\n          (/ time2 time1)))\n\n(defmacro org-ml-defbench (title n pattern form1 form2)\n  \"Define a benchmark.\n\nTITLE is a short string that will be used to identify the\nbenchmark (uniqueness isn't enforced but makes sense). FORM1 and\nFORM2 are the two forms to be compared; by convention the first\nis a function composed of built-in org commands and the second is\none composed of org-ml commands. FORM1 and FORM2 will be applied\nto a buffer with PATTERN repeated N times. Note the both forms\nwill only be called once and thus must contain the code for\niterating across PATTERN as desired.\n\nCalling `org-ml-bench-run' will execute all benchmarks in the\norder they are defined with this macro.\"\n  (declare (indent 2))\n  (let ((p (format \"%s\\n\" (if (listp pattern)\n                              (s-join \"\\n\" (eval pattern))\n                            pattern))))\n    `(add-to-list 'org-ml-benchmarks\n                  (lambda ()\n                    (print (format \"Starting benchmark: %s\" ,title))\n                    (-let (((time1 time2)\n                            ,(org-ml-bench-compare p n form1 form2)))\n                      (org-ml-bench-format-result-row ,title ,n time1 time2))))))\n\n(defun org-ml-bench-run ()\n  \"Run and print all defined benchmarks.\"\n  (let ((test-rows (--map (funcall it) (reverse org-ml-benchmarks))))\n    (print\n     (s-join \"\\n\"\n             (append\n              (list\n               \"\"\n               (org-ml-bench-get-sys-info)\n               \"\"\n               (format \"| %-40s | %6s | %10s | %10s | %10s |\"\n                       \"Test name\" \"N\" \"Native\" \"org-ml\" \"X Increase\"))\n              test-rows\n              (list \"\"))))))\n\n(provide 'org-ml-bench-fw)\n;;; org-ml-bench-fw.el ends here\n"
  },
  {
    "path": "bench/org-ml-benchmarks.el",
    "content": ";;; org-ml-benchmarks.el --- Benchmarks for org-ml -*- lexical-binding: t; -*-\n\n;; Copyright (C) 2020 Free Software Foundation, Inc.\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 <http://www.gnu.org/licenses/>.\n\n;;; Commentary:\n\n;;; Code:\n\n(require 'org-ml-bench-fw)\n\n(org-ml-defbench \"read TODO\" 10000\n  \"* TODO headline\"\n  (let ((next t))\n    (while next\n      (org-get-todo-state)\n      (setq next (outline-next-heading))))\n  \n  (->> (org-ml-parse-headlines 'all)\n       (--map (org-ml-get-property :todo-keyword it))))\n\n(org-ml-defbench \"read SCHEDULED epoch time\" 2500\n  (list \"* TODO headline\"\n        \"SCHEDULED: <2020-01-01 Tue>\")\n  (let ((next t))\n    (while next\n      (org-2ft (org-entry-get (point) \"SCHEDULED\"))\n      (setq next (outline-next-heading))))\n  \n  (--each (org-ml-parse-headlines 'all)\n    (->> (plist-get (org-ml-headline-get-planning it) :scheduled)\n         (org-ml-timelist-to-unixtime))))\n\n(org-ml-defbench \"TODO -> DONE\" 1000\n  \"* TODO headline\"\n  (let ((org-log-done 'time)\n        (org-todo-keywords '((sequence \"TODO\" \"|\" \"DONE\")))\n        (org-adapt-indentation nil)\n        (next t))\n    (while next\n      (org-todo 'done)\n      (setq next (outline-next-heading))))\n\n  (let ((planning `(:closed ,(org-ml-unixtime-to-timelist t (float-time)))))\n    (org-ml-wrap-impure\n     (org-ml-update-headlines* 'all\n       (->> (org-ml-set-property :todo-keyword \"DONE\" it)\n            (org-ml-headline-set-planning planning))))))\n\n(org-ml-defbench \"demote headlines\" 2500\n  \"* headline\"\n  (let ((org-adapt-indentation nil)\n        (next t))\n    (while next\n      (org-do-demote)\n      (setq next (outline-next-heading))))\n\n  (org-ml-wrap-impure\n   (org-ml-update-headlines* 'all\n     (org-ml-shift-property :level 1 it))))\n\n(org-ml-defbench \"demote subtrees\" 2500\n  (list \"* headline\"\n        \"** subheadline\")\n  (let ((org-adapt-indentation nil)\n        (next t))\n    (while next\n      (org-demote)\n      (setq next (outline-next-heading))))\n\n  (org-ml-wrap-impure\n   (org-ml-update-subtrees* 'all\n     (org-ml--headline-subtree-shift-level 1 it))))\n\n(org-ml-defbench \"tag headline\" 1000\n  \"* headline\"\n  (let ((next t))\n    (while next\n      (org-set-tags '(\"A\" \"B\" \"C\"))\n      (setq next (outline-next-heading))))\n\n  (org-ml-wrap-impure\n   (org-ml-update-headlines* 'all\n     (org-ml-set-property :tags '(\"A\" \"B\" \"C\") it))))\n\n;; TODO a better test for this would be to put a logbook underneath the\n;; planning ts since in that case we need to also parse the logbook\n(org-ml-defbench \"schedule headline\" 1000\n  (list \"* headline\")\n        ;; \":LOGGING:\"\n        ;; \"- Note taken on [2024-08-07 Wed 20:07] \\\\\"\n        ;; \"thingy\"\n        ;; \":END:\")\n  (let ((org-adapt-indentation nil)\n        (next t))\n    (while next\n      (org-schedule nil \"2000-01-01\")\n      (setq next (outline-next-heading))))\n\n  (let ((pl '(:scheduled (2000 1 1))))\n    (org-ml-wrap-impure\n     (org-ml-update-supercontents* nil 'all\n       (org-ml-supercontents-set-planning pl it)))))\n\n(org-ml-defbench \"schedule headline (memoized)\" 1000\n  (list \"* headline\")\n        ;; \":LOGGING:\"\n        ;; \"- Note taken on [2024-08-07 Wed 20:07] \\\\\"\n        ;; \"thingy\"\n        ;; \":END:\")\n  (let ((org-adapt-indentation nil)\n        (next t))\n    (while next\n      (org-schedule nil \"2000-01-01\")\n      (setq next (outline-next-heading))))\n\n  (let ((pl '(:scheduled (2000 1 1))))\n    (let ((org-ml-memoize-shorthand-builders t))\n      (org-ml-wrap-impure\n       (org-ml-update-supercontents* nil 'all\n         (org-ml-supercontents-set-planning pl it))))))\n\n(org-ml-defbench \"reschedule headline\" 1000\n  (list \"* headline\"\n        \"SCHEDULED: <2020-01-01 Wed>\")\n  (let ((org-adapt-indentation nil)\n        (next t))\n    (while next\n      (->> (org-get-scheduled-time (point))\n           (float-time)\n           ;; shift up one day\n           (+ (* 24 60 60))\n           (format-time-string \"%Y-%m-%d\")\n           (org-schedule nil))\n      (setq next (outline-next-heading))))\n\n  (org-ml-wrap-impure\n   (org-ml-update-supercontents* nil 'all\n     (org-ml-supercontents-set-planning\n      (list :scheduled\n            (org-ml-timelist-shift\n             1 'day\n             (plist-get (org-ml-supercontents-get-planning it) :scheduled)))\n      it))))\n\n(org-ml-defbench \"reschedule headline (memoized)\" 1000\n  (list \"* headline\"\n        \"SCHEDULED: <2020-01-01 Wed>\")\n  (let ((org-adapt-indentation nil)\n        (next t))\n    (while next\n      (->> (org-get-scheduled-time (point))\n           (float-time)\n           ;; shift up one day\n           (+ (* 24 60 60))\n           (format-time-string \"%Y-%m-%d\")\n           (org-schedule nil))\n      (setq next (outline-next-heading))))\n\n  (org-ml-wrap-impure\n   (let ((org-ml-memoize-shorthand-builders t))\n     (org-ml-update-supercontents* nil 'all\n       (org-ml-supercontents-set-planning\n        (list :scheduled\n              (org-ml-timelist-shift\n               1 'day\n               (plist-get (org-ml-supercontents-get-planning it) :scheduled)))\n        it)))))\n\n(org-ml-defbench \"set headline effort\" 1000\n  \"* headline\"\n  (let ((org-adapt-indentation nil)\n        (next t))\n    (while next\n      (org-set-property \"Effort\" \"0:05\")\n      (setq next (outline-next-heading))))\n\n  (org-ml-wrap-impure\n   (org-ml-update-supercontents* nil 'all\n     (org-ml-supercontents-set-node-properties '((\"Effort\" \"0:05\")) it))))\n\n(org-ml-defbench \"set headline effort (memoized)\" 1000\n  \"* headline\"\n  (let ((org-adapt-indentation nil)\n        (next t))\n    (while next\n      (org-set-property \"Effort\" \"0:05\")\n      (setq next (outline-next-heading))))\n\n  (org-ml-wrap-impure\n   (let ((org-ml-memoize-shorthand-builders t))\n     (org-ml-update-supercontents* nil 'all\n       (org-ml-supercontents-set-node-properties '((\"Effort\" \"0:05\")) it)))))\n\n(org-ml-defbench \"insert headline text\" 2500\n  \"* headline\"\n  (let ((org-adapt-indentation nil)\n        (next t))\n    (while next\n      (save-excursion\n        (org-end-of-subtree)\n        (insert \"\\n~some text~\"))\n      (setq next (outline-next-heading))))\n\n  (org-ml-wrap-impure\n   (org-ml-update-supercontents* nil 'all\n     (-> (org-ml-build-paragraph! \"~some text~\")\n         (list)\n         (org-ml-supercontents-set-contents it)))))\n\n(org-ml-defbench \"insert headline text (memoized)\" 2500\n  \"* headline\"\n  (let ((org-adapt-indentation nil)\n        (next t))\n    (while next\n      (save-excursion\n        (org-end-of-subtree)\n        (insert \"\\n~some text~\"))\n      (setq next (outline-next-heading))))\n\n  (org-ml-wrap-impure\n   (let ((org-ml-memoize-shorthand-builders t))\n     (org-ml-update-supercontents* nil 'all\n       (-> (org-ml-build-paragraph! \"~some text~\")\n           (list)\n           (org-ml-supercontents-set-contents it))))))\n\n(org-ml-defbench \"set checkboxes\" 1000\n  (list \"* headline [0/0]\"\n        \"- [ ] one\"\n        \"- [ ] two\")\n  (let ((org-adapt-indentation nil)\n        (next t))\n    (while next\n      (org-toggle-checkbox)\n      (setq next (outline-next-heading))))\n\n  (let ((org-ml-memoize-match-patterns 'compiled))\n    (org-ml-update-headlines* 'all\n      (org-ml->> (org-ml-match-map '(section plain-list item) #'org-ml-item-toggle-checkbox it)\n        (org-ml-headline-update-item-statistics)))))\n\n(org-ml-defbench \"set headline effort/TODO/scheduled\" 1000\n  \"* headline\"\n  (let ((org-log-done 'time)\n        (org-todo-keywords '((sequence \"TODO\" \"|\" \"DONE\")))\n        (org-adapt-indentation nil)\n        (next t))\n    (while next\n      (org-schedule nil \"2000-01-01\")\n      (org-set-property \"Effort\" \"0:05\")\n      (org-todo 'todo)\n      (setq next (outline-next-heading))))\n\n  (let ((pl '(:scheduled (2000 1 1))))\n    (let ((org-ml-memoize-shorthand-builders t))\n      (org-ml-update-headlines* 'all\n        (org-ml->> (org-ml-set-property :todo-keyword \"TODO\" it)\n          (org-ml-headline-set-node-property \"Effort\" \"0:05\")\n          (org-ml-headline-set-planning pl))))))\n\n(provide 'org-ml-benchmarks)\n;;; org-ml-benchmarks.el ends here\n"
  },
  {
    "path": "dev/org-ml-cookbook.el",
    "content": ";;; org-ml-cookbook.el --- Common patterns for org.el's  -*- lexical-binding: t -*-\n\n;; Copyright (C) 2015 Free Software Foundation, Inc.\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 <http://www.gnu.org/licenses/>.\n\n;;; Commentary:\n\n;;; Code:\n\n(require 's)\n(require 'dash)\n(require 'org-ml)\n\n(defrecipe \"Adding created time\"\n  \"This will add a property called CREATED with a timestamp (which could be modified to hold the current time)..\"\n  (\"* headine\")\n  (let ((ts (org-ml-to-string (org-ml-build-timestamp! '(2020 1 1 0 0)))))\n    (->> (org-ml-parse-this-headline)\n         (org-ml-headline-set-node-property \"CREATED\" ts)\n         (org-ml-to-string)))\n  => (:result \"* headline\"\n              \":PROPERTIES:\"\n              \":CREATED:  [2020-01-01 Wed 00:00]\"\n              \":END:\"))\n\n(provide 'org-ml-cookbook)\n;;; org-ml-cookbook.el ends here\n"
  },
  {
    "path": "dev/org-ml-docs.el",
    "content": ";;; org-ml-docs.el --- Extract org-ml's docs -*- lexical-binding: t; -*-\n\n;; Copyright (C) 2015 Free Software Foundation, Inc.\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 <http://www.gnu.org/licenses/>.\n\n;;; Commentary:\n\n;;; Code:\n\n(require 's)\n(require 'dash)\n(require 'help-fns)\n(require 'package)\n\n(setq text-quoting-style 'grave)\n\n(defvar org-ml-dev-examples-list '())\n\n(defvar org-ml-dev-recipe-list '())\n\n(defconst org-ml-elem--fill-column 80)\n\n(defun org-ml-get-package-version ()\n  \"Get version of om package.\"\n  (with-current-buffer (find-file-noselect \"org-ml.el\")\n    (mapconcat 'number-to-string (package-desc-version (package-buffer-info)) version-separator)))\n\n(defun format-multiline (s &optional ident)\n  (let ((ident (or ident 2)))\n    (cl-labels\n        ((go\n           (x i)\n           (let ((test (format \"%S\" x)))\n             (if (< (+ (length test) i) 80) test\n               (if (not (consp x)) test\n                 (-let* (((fun . rest) x)\n                         (sfun (format \"%S\" fun))\n                         (ws (concat \"\\n\" (s-repeat i \" \")))\n                         (srest (--map (go it (+ i ident)) rest)))\n                   (if (null srest) test\n                     (-let (((r . rs) srest))\n                       (if (< (+ (length sfun) (length r) 2) 80)\n                           (format \"(%S %s)\" fun (s-join ws srest))\n                         (format \"(%s)\" (s-join ws `(,sfun ,r ,@rs))))))))))))\n      (go s ident))))\n\n(defun format-actual (actual)\n  (format-multiline actual))\n\n(defun format-expected (sym expected)\n  (let* ((s (s-lines (format \"%S\" expected)))\n         (header (format \" ;; %S %s\" sym (car s)))\n         (rest (--map (if (stringp expected) (s-prepend \" ;      \" it)\n                        (s-prepend \" ;     \" it))\n                      (-drop 1 s))))\n    (s-join \"\\n\" (cons header rest))))\n\n(defun example-to-string (example)\n  (-let* (((actual sym expected) example)\n          (expected\n           (if (eq (and (listp expected) (car expected)) :result)\n               (s-join \"\\n\" (cdr expected))\n             expected))\n          (actual (format-actual actual))\n          (comment\n           (cond\n            ((eq sym '=>) (format-expected sym expected))\n            ;; ((eq sym '~>) (format-expected sym expected))\n            ((eq sym '$>) (concat \" ;; Output these buffer contents\\n\"\n                                  (format-expected sym expected)))\n            ((eq sym '!!>) (format \"Error\"))\n            (t (error \"Invalid test case: %s\" `(,actual ,sym ,expected))))))\n    (--> comment\n         (format \"%s\\n%s\\n\" actual it)\n         (replace-regexp-in-string \"\\\\\\\\\\\\?\" \"?\" it)\n         ;; (replace-regexp-in-string \"\\n\" \"\\\\n\" it t t)\n         ;; (replace-regexp-in-string \"\\t\" \"\\\\t\" it t t)\n         (replace-regexp-in-string \"\\r\" \"\\\\r\" it t t))))\n         ;; (format \"```el\\n%s\\n```\\n\" it))))\n\n(defun docs--signature (function)\n  \"Given FUNCTION (a symbol), return its argument list.\nFUNCTION may reference an elisp function, alias, macro or a subr.\"\n  (let* ((function-value (indirect-function function))\n         (is-alias (not (eq function-value (symbol-function function))))\n         ;; if FUNCTION isn't an alias, function-symbol is simply FUNCTION\n         (function-symbol function))\n\n    (when is-alias\n      ;; find the last symbol in the alias chain\n      (while (symbolp (symbol-function function-symbol))\n        (setq function-symbol (symbol-function function-symbol))))\n\n    (or\n     (-some->> (help-split-fundoc (documentation function-value)\n                                  function-symbol)\n               (car)\n               (downcase)\n               (read)\n               (cdr))\n     (help-function-arglist function-symbol))))\n;; (if (subrp function-value)\n;;     ;; read the docstring to find the signature for subrs\n;;     (let* ((docstring-args (car (help-split-fundoc\n;;                                  (documentation function-value)\n;;                                  function-symbol)))\n;;            (fun-with-args (read (downcase docstring-args))))\n;;       (cdr fun-with-args))\n;;   ;; otherwise get the signature directly\n;;   (help-function-arglist function-symbol))))\n\n(defun format-doc (cmd)\n  ;; remove extra signature for cl-defun org-ml-dev-examples-list\n  ;; TODO this is hacky but it works\n  (let ((doc (documentation cmd)))\n    (unless doc (error \"No docstring set for %s\" cmd))\n    (if (not (s-matches? \"(fn .*)\" doc)) doc\n      (->> (s-lines doc) (-drop-last 2) (s-join \"\\n\")))))\n\n(defun filter-hidden (args)\n  (->> (--split-when (eq it :end-hidden) args)\n       (--mapcat (--take-while (not (eq it :begin-hidden)) it))))\n\n(defmacro defexamples (cmd &rest examples)\n  `(add-to-list 'org-ml-dev-examples-list\n                (list\n                 ',cmd\n                 (docs--signature ',cmd)\n                 (format-doc ',cmd)\n                 (->> ',examples\n                      (filter-hidden)\n                      (-partition 3)\n                      (-map 'example-to-string)))))\n\n(defun format-buffer-contents (list)\n  (->> (--map (format \"; %s\" it) list)\n              (s-join \"\\n\")\n              (format \";; Given the following contents:\\n%s\\n\")))\n\n(defmacro defexamples-content (cmd docstring &rest args)\n  `(cl-flet\n       ((formatted-string?\n         (list)\n         (memq (and (listp list) (car list)) '(:buffer :comment)))\n        (format-content\n         (list)\n         (->> (car list)\n              (-drop 1)\n              (format-buffer-contents)))\n              ;; (--map (format \"; %s\" it))\n              ;; (s-join \"\\n\")\n              ;; (format \";; Given the following contents:\\n%s\\n\")))\n        (format-comment\n         (list)\n         (let ((comment (->> (car list)\n                             (-drop 1)\n                             (s-join \" \")\n                             (format \";; %s\"))))\n           (with-temp-buffer\n             (emacs-lisp-mode)\n             (insert comment)\n             (let ((fill-column org-ml-elem--fill-column))\n               (fill-paragraph))\n             (buffer-string)))))\n     (let* ((doc (or ,docstring (format-doc ',cmd)))\n            (example\n             (->> (filter-hidden ',args)\n                  (-partition-by #'formatted-string?)\n                  (--map (cond\n                          ((eq :comment (car (car it)))\n                           (format-comment it))\n                          ((eq :buffer (car (car it)))\n                           (format-content it))\n                          (t (-some->>\n                              (-partition 3 it)\n                              (-map #'example-to-string)\n                              (s-join \"\\n\"))))))))\n       (add-to-list 'org-ml-dev-examples-list (list ',cmd\n                                     (docs--signature ',cmd)\n                                     doc\n                                     (or example '(\"no examples :(\")))))))\n\n(defmacro defrecipe (header description contents form operator result)\n  `(let ((example (example-to-string (list ',form ',operator ',result)))\n         (contents* (format-buffer-contents ',contents)))\n     (add-to-list 'org-ml-dev-recipe-list\n                  (format \"## %s\\n\\n%s\\n\\n```el\\n%s\\n%s```\\n\"\n                          ,header ,description contents* example))))\n\n(defmacro def-example-subgroup (group desc &rest examples)\n  `(progn\n     ;; (add-to-list 'org-ml-dev-examples-list ,(concat \"### \" group))\n     (setq org-ml-dev-examples-list (cons ,(concat \"### \" group) org-ml-dev-examples-list))\n     (when ,desc\n       ;; (add-to-list 'org-ml-dev-examples-list ,desc))\n       (setq org-ml-dev-examples-list (cons ,desc org-ml-dev-examples-list)))\n     ,@examples))\n\n(defmacro def-example-group (group desc &rest examples)\n  `(progn\n     ;; (add-to-list 'org-ml-dev-examples-list ,(concat \"## \" group))\n     (setq org-ml-dev-examples-list (cons ,(concat \"## \" group) org-ml-dev-examples-list))\n     (when ,desc\n       ;; (add-to-list 'org-ml-dev-examples-list ,desc))\n       (setq org-ml-dev-examples-list (cons ,desc org-ml-dev-examples-list)))\n     ,@examples))\n\n\n(defun format-link (string-name)\n  (-let* ((name (intern string-name))\n          ((_ signature _ _) (assoc name org-ml-dev-examples-list)))\n    (if signature\n        (format \"[`%s`](#%s)\" name (github-id name signature))\n      (format \"`%s`\" name))))\n\n(defun format-docstring-forms (docstring)\n  (cl-labels\n      ((find-matching-right\n        (p)\n        ;; return point of matching \")\" or nil if not found\n        (ignore-errors\n          (save-excursion\n            (goto-char p)\n            (forward-sexp)\n            (1- (point)))))\n       (has-leading-function?\n        (string)\n        (->> (s-replace-regexp \"(+\" \"\" string)\n             (s-split \" \")\n             (car)\n             (intern)\n             (fboundp)))\n       (has-all-cap-syms?\n        (string)\n        (->> (s-replace-regexp \"[().,]\" \"\" string)\n             (s-replace \"[\" \"\")\n             (s-replace \"]\" \"\")\n             (s-split \" \")\n             (--remove (equal it \"\"))\n             (--all? (or (member it '(\"t\" \"nil\" \"|\" \"*\" \"?\"))\n                         (s-matches? \"^[A-Z0-9\\\\-]+$\" it)\n                         (s-matches? \"^\\\\(:\\\\|&\\\\)[a-z0-9-]+$\" it)))))\n       (is-form?\n        (string)\n        (or ;; (has-leading-function? string)\n         (has-all-cap-syms? string))))\n    (let (case-fold-search)\n      (with-temp-buffer\n        (insert docstring)\n        (goto-char (point-min))\n        (while (and (< (point) (point-max))\n                    (search-forward \"(\" nil t))\n          (-when-let (e (find-matching-right (1- (point))))\n            (when (is-form? (buffer-substring (point) e))\n              (downcase-region (point) e)\n              (goto-char (1- (point)))\n              (insert \"`\")\n              (goto-char (+ 2 e))\n              (insert \"`\")))\n          (unless (= (point) (point-max))\n            (forward-char)))\n        (buffer-string)))))\n\n(defun format-docstring-args (signature docstring)\n  (let ((sig-args (->> signature\n                       (--remove (memq it '(&optional &key &rest)))\n                       (--map (if (consp it) (car it) it))\n                       (-map #'symbol-name))))\n    (cl-flet\n        ((quote-and-downcase\n          (string)\n            ;; hack to work around % not being part of word boundaries\n          (let ((s (s-chop-suffix \"%\" (downcase string))))\n             (if (member s sig-args) (format \"**`%s`**\" s)\n               (format \"`%s`\" s)))))\n      (replace-regexp-in-string\n       \"\\\\b\\\\(?3:[A-Z][A-Z-]*[0-9*]*\\\\)\\\\(\\\\*\\\\|%\\\\|\\\\b\\\\)\"\n       ;; \"[^A-Z0-9-]\\\\([A-Z0-9-]+\\\\)[^A-Z0-9-]\"\n       #'quote-and-downcase docstring t nil 3))))\n\n(defun format-docstring-backquoted (docstring)\n  (cl-flet\n      ((unquote-and-link\n        (string)\n        (format-link (substring string 1 -1))))\n    (replace-regexp-in-string \"`\\\\([^ \\n]+\\\\)'\" #'unquote-and-link docstring t)))\n\n(defun format-docstring-indent (docstring)\n  (replace-regexp-in-string \"^  \" \"    \" docstring))\n\n(defun format-docstring-strings (docstring)\n  (cl-flet\n      ((quote-string\n        (string)\n        (format \"`%s`\" string)))\n  (s-replace-regexp \"\\\"[[:ascii:]]*?\\\"\" #'quote-string docstring)))\n\n(defun format-docstring (signature docstring)\n  (let (case-fold-search)\n    (->> docstring\n         (format-docstring-strings)\n         (format-docstring-forms)\n         (format-docstring-args signature)\n         (format-docstring-backquoted)\n         (format-docstring-indent))))\n\n(defun function-to-md (function)\n  (if (stringp function)\n      (concat \"\\n\" (s-replace \"### \" \"### \" function) \"\\n\")\n    (-let [(command-name signature docstring examples) function]\n      (unless docstring\n        (error \"No docstring supplied for %s\" command-name))\n      (format \"#### %s `%S`\\n\\n%s\\n\\n```el\\n%s\\n```\\n\"\n      ;; (format \"### %s `%s`\\n\\n%s\\n\\n%s\"\n              command-name\n              signature\n              (format-docstring signature docstring)\n              ;; (mapconcat 'identity (-take 3 examples) \"\\n\")))))\n              (mapconcat 'identity examples \"\\n\")))))\n\n;; (defun docs--chop-prefix (prefix s)\n;;   \"Remove PREFIX if it is at the start of S.\"\n;;   (let ((pos (length prefix)))\n;;     (if (and (>= (length s) (length prefix))\n;;              (string= prefix (substring s 0 pos)))\n;;         (substring s pos)\n;;       s)))\n\n;; (defun docs--chop-suffix (suffix s)\n;;   \"Remove SUFFIX if it is at end of S.\"\n;;   (let ((pos (- (length suffix))))\n;;     (if (and (>= (length s) (length suffix))\n;;              (string= suffix (substring s pos)))\n;;         (substring s 0 pos)\n;;       s)))\n\n(defun github-id (command-name signature)\n  (->> (format \"%S-%S\" command-name signature)\n       (s-downcase)\n       (s-replace-regexp \"[^a-zA-Z0-9- ]+\" \"\")\n       (s-replace \" \" \"-\")))\n\n(defun s-replace (old new s)\n  \"Replace OLD with NEW in S.\"\n  (replace-regexp-in-string (regexp-quote old) new s t t))\n\n(defun function-summary (function)\n  (if (stringp function)\n      (concat \"\\n\" function \"\\n\")\n    (let ((command-name (car function))\n          (signature (cadr function)))\n      (format \"* [%s](#%s) `%S`\" command-name (github-id command-name signature) signature))))\n\n(defun simplify-quotes ()\n  (goto-char (point-min))\n  (while (search-forward \"(quote nil)\" nil t)\n    (replace-match \"'()\"))\n  (goto-char (point-min))\n  (while (search-forward \"(quote \" nil t)\n    (forward-char -7)\n    (let ((p (point)))\n      (forward-sexp 1)\n      (delete-char -1)\n      (goto-char p)\n      (delete-char 7)\n      (insert \"'\"))))\n\n(defun goto-and-remove (s)\n  (goto-char (point-min))\n  (search-forward s)\n  (delete-char (- (length s))))\n\n(defun goto-and-replace-all (s replacement)\n  (while (progn (goto-char (point-min)) (search-forward s nil t))\n    (delete-char (- (length s)))\n    (insert replacement)))\n\n(defun create-cookbook ()\n  (with-temp-file \"./docs/cookbook.md\"\n    (insert \"# org-ml cookbook\\n\\n\")\n\n    (insert\n     (concat \"The following are a list of common use cases and formulations\"\n             \"for `org-ml`. If a function is not available straight from the\"\n             \"API it may be here.\\n\\n\"))\n\n    (insert (s-join \"\\n\" org-ml-dev-recipe-list))))\n\n(defun create-api-ref ()\n  (let ((org-ml-dev-examples-list (nreverse org-ml-dev-examples-list)))\n    (with-temp-file \"./docs/api-reference.md\"\n      (insert \"# API Reference\\n\")\n\n      (insert (mapconcat 'function-summary org-ml-dev-examples-list \"\\n\"))\n\n      (insert (mapconcat 'function-to-md org-ml-dev-examples-list \"\\n\"))\n\n      (insert (format \"Version: %s\" (org-ml-get-package-version)))\n\n      (simplify-quotes))))\n\n(defun create-docs-files ()\n  (create-cookbook)\n  (create-api-ref))\n\n;; require the examples\n\n(require 'org-ml-examples)\n(require 'org-ml-cookbook)\n\n;; tell user how many functions have no examples\n\n(defconst org-ml-dev-defined-names nil\n  \"Alist of all functions/macros defined in `org-ml.el'.\nThe two cells in the alist are 'private' and 'public'.\")\n\n(mapatoms\n (lambda (x)\n   (when (and (fboundp x) (s-starts-with-p \"org-ml-\" (symbol-name x)))\n     (push x org-ml-dev-defined-names))))\n\n(setq org-ml-dev-defined-names\n      (--group-by\n       (if (s-starts-with-p \"org-ml--\" (symbol-name it)) 'private 'public)\n       org-ml-dev-defined-names))\n\n(let ((public-syms (alist-get 'public org-ml-dev-defined-names))\n      (example-syms (->> (-remove #'stringp org-ml-dev-examples-list)\n                         (-map #'car))))\n  (-some->> (-difference public-syms example-syms)\n            (-map #'symbol-name)\n            (--remove (s-ends-with? \"*\" it))\n            (--remove (s-starts-with? \"org-ml-update-this-\" it))\n            (--remove (s-starts-with? \"org-ml-parse-this-\" it))\n            (--map (format \"  %s\" it))\n            (s-join \"\\n\")\n            (format \"The following functions don't have examples:\\n%s\")\n            (print)))\n\n(provide 'org-ml-docs)\n;;; org-ml-docs.el ends here\n"
  },
  {
    "path": "dev/org-ml-examples.el",
    "content": ";;; org-ml-examples.el --- Examples for org.el's API  -*- lexical-binding: t -*-\n\n;; Copyright (C) 2015 Free Software Foundation, Inc.\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 <http://www.gnu.org/licenses/>.\n\n;;; Commentary:\n\n;;; Code:\n\n(require 's)\n(require 'dash)\n(require 'org-ml)\n\n(def-example-group \"String Conversion\"\n  \"Convert nodes to strings.\"\n\n  ;; these are more thoroughly tested in `org-ml-test-internal.el'\n\n  (defexamples org-ml-to-string\n    (org-ml-to-string\n     '(bold\n       (:begin 1 :end 5 :parent nil :post-blank 0 :post-affiliated nil)\n       \"text\"))\n    => \"*text*\"\n    (org-ml-to-string\n     '(bold\n       (:begin 1 :end 5 :parent nil :post-blank 3 :post-affiliated nil)\n       \"text\"))\n    => \"*text*   \"\n    (org-ml-to-string nil) => \"\")\n\n  (defexamples org-ml-to-trimmed-string\n    (org-ml-to-trimmed-string\n     '(bold\n       (:begin 1 :end 5 :parent nil :post-blank 0 :post-affiliated nil)\n       \"text\"))\n    => \"*text*\"\n    (org-ml-to-trimmed-string\n     '(bold\n       (:begin 1 :end 5 :parent nil :post-blank 3 :post-affiliated nil)\n       \"text\"))\n    => \"*text*\"\n    (org-ml-to-trimmed-string nil) => \"\")\n\n  (defexamples org-ml-from-string\n    (->> (org-ml-from-string 'bold \"*text*\")\n         (org-ml-get-type))\n    => 'bold\n    (->> (org-ml-from-string 'bold \"*text*\")\n         (org-ml-get-property :begin))\n    => 1\n    (->> (org-ml-from-string 'bold \"*text*\")\n         (org-ml-get-property :end))\n    => 7\n    (->> (org-ml-from-string 'bold \"*text*\")\n         (org-ml-get-property :post-blank))\n    => 0\n    (->> (org-ml-from-string 'bold \"*text*\")\n         (org-ml-get-property :contents-begin))\n    => 2\n    (->> (org-ml-from-string 'bold \"*text*\")\n         (org-ml-get-property :contents-end))\n    => 6\n    (org-ml-from-string 'italic \"*text*\")\n    => nil))\n\n(def-example-group \"Buffer Parsing\"\n  \"Parse buffers to trees.\"\n\n  ;; these are more thoroughly tested in `org-ml-dev-test.el'\n\n  (defexamples-content org-ml-parse-this-buffer\n    nil\n    (:buffer \"text\")\n    (->> (org-ml-parse-this-buffer)\n         (org-ml-get-property :begin))\n    => 1\n    (->> (org-ml-parse-this-buffer)\n         (org-ml-get-property :end))\n    => 5)\n\n  (defexamples-content org-ml-parse-object-at\n    nil\n    (:buffer \"*text*\")\n    (->> (org-ml-parse-object-at 1)\n         (car))\n    => 'bold\n\n    (:buffer \"[2019-01-01 Tue]\")\n    (->> (org-ml-parse-object-at 1)\n         (car))\n    => 'timestamp\n\n    (:buffer \"- notme\")\n    (:comment \"Return nil when parsing an element\")\n    (org-ml-parse-object-at 1)\n    => nil)\n\n  (defexamples-content org-ml-parse-element-at\n    nil\n    (:buffer \"#+call: ktulu()\")\n    (->> (org-ml-parse-element-at 1)\n         (car))\n    => 'babel-call\n    \n    (:buffer \"- plain-list\")\n    (:comment \"Give the plain-list, not the item for this function\")\n    (->> (org-ml-parse-element-at 1)\n         (car))\n    => 'plain-list\n    \n    (:buffer \"| R | A |\"\n             \"| G | E |\")\n    (:comment \"Return a table, not the table-row for this function\")\n    (->> (org-ml-parse-element-at 1)\n         (car))\n    => 'table)\n\n  (defexamples-content org-ml-parse-table-row-at\n    nil\n    (:buffer \"| bow | stroke |\"\n             \"|-----+--------|\"\n             \"| wob | ekorts |\")\n    (:comment \"Return the row itself\")\n    (->> (org-ml-parse-table-row-at 1)\n         (car))\n    => 'table-row\n    (->> (org-ml-parse-table-row-at 20)\n         (car))\n    => 'table-row\n    (->> (org-ml-parse-table-row-at 40)\n         (car))\n    => 'table-row\n    (:comment \"Also return the row when not at beginning of line\")\n    (->> (org-ml-parse-table-row-at 5)\n         (car))\n    => 'table-row\n    (:buffer \"- bow and arrow choke\")\n    (:comment \"Return nil if not a table-row\")\n    (->> (org-ml-parse-table-row-at 1)\n         (car))\n    => nil)\n\n  (defexamples-content org-ml-parse-headline-at\n    nil\n    (:buffer \"* headline\")\n    (:comment \"Return the headline itself\")\n    (->> (org-ml-parse-headline-at 1)\n         (org-ml-to-trimmed-string))\n    => \"* headline\"\n    (:buffer \"* headline\"\n             \"section crap\")\n    (:comment \"Return headline and section\")\n    (->> (org-ml-parse-headline-at 1)\n         (org-ml-to-trimmed-string))\n    => (:result \"* headline\"\n                \"section crap\")\n    (:comment \"Return headline when point is in the section\")\n    (->> (org-ml-parse-headline-at 12)\n         (org-ml-to-trimmed-string))\n    => (:result \"* headline\"\n                \"section crap\")\n    (:buffer \"* headline\"\n             \"section crap\"\n             \"** not parsed\")\n    (:comment \"Don't parse any subheadlines\")\n    (->> (org-ml-parse-headline-at 1)\n         (org-ml-to-trimmed-string))\n    => (:result \"* headline\"\n                \"section crap\")\n    (:buffer \"nothing nowhere\")\n    (:comment \"Return nil if not under a headline\")\n    (->> (org-ml-parse-headline-at 1)\n         (org-ml-to-trimmed-string))\n    => \"\")\n\n  (defexamples-content org-ml-parse-subtree-at\n    nil\n    (:buffer \"* headline\")\n    (:comment \"Return the headline itself\")\n    (->> (org-ml-parse-subtree-at 1)\n         (org-ml-to-trimmed-string))\n    => \"* headline\"\n    (:buffer \"* headline\"\n             \"section crap\")\n    (:comment \"Return headline and section\")\n    (->> (org-ml-parse-subtree-at 1)\n         (org-ml-to-trimmed-string))\n    => (:result \"* headline\"\n                \"section crap\")\n    (:comment \"Return headline when point is in the section\")\n    (->> (org-ml-parse-subtree-at 12)\n         (org-ml-to-trimmed-string))\n    => (:result \"* headline\"\n                \"section crap\")\n    (:buffer \"* headline\"\n             \"section crap\"\n             \"** parsed\")\n    (:comment \"Return all the subheadlines\")\n    (->> (org-ml-parse-subtree-at 1)\n         (org-ml-to-trimmed-string))\n    => (:result \"* headline\"\n                \"section crap\"\n                \"** parsed\")\n    (:buffer \"nothing nowhere\")\n    (:comment \"Return nil if not under a headline\")\n    (->> (org-ml-parse-subtree-at 1)\n         (org-ml-to-trimmed-string))\n    => \"\")\n\n  (defexamples-content org-ml-parse-item-at\n    nil\n    (:buffer \"- item\")\n    (:comment \"Return the item itself\")\n    (->> (org-ml-parse-item-at 1)\n         (org-ml-to-trimmed-string))\n    => \"- item\"\n    (:comment \"Also return the item when not at beginning of line\")\n    (->> (org-ml-parse-item-at 5)\n         (org-ml-to-trimmed-string))\n    => \"- item\"\n    (:buffer \"- item\"\n             \"  - item 2\")\n    (:comment \"Return item and its subitems\")\n    (->> (org-ml-parse-item-at 1)\n         (org-ml-to-trimmed-string))\n    => (:result \"- item\"\n                \"  - item 2\")\n    (:buffer \"* not item\")\n    (:comment \"Return nil if not an item\")\n    (->> (org-ml-parse-item-at 1)\n         (org-ml-to-trimmed-string))\n    => \"\")\n  \n  (defexamples-content org-ml-parse-section-at\n    nil\n    (:buffer \"over headline\"\n             \"* headline\"\n             \"under headline\")\n    (:comment \"Return the section above the headline\")\n    (->> (org-ml-parse-section-at 1)\n         (org-ml-to-trimmed-string))\n    => \"over headline\"\n    (:comment \"Return the section under headline\")\n    (->> (org-ml-parse-section-at 25)\n         (org-ml-to-trimmed-string))\n    => \"under headline\"\n    (:buffer \"* headline\"\n             \"** subheadline\")\n    (:comment \"Return nil if no section under headline\")\n    (->> (org-ml-parse-section-at 1)\n         (org-ml-to-trimmed-string))\n    => \"\"\n    (:buffer \"\")\n    (:comment \"Return nil if no section at all\")\n    (->> (org-ml-parse-section-at 1)\n         (org-ml-to-trimmed-string))\n    => \"\")\n\n  (defexamples-content org-ml-parse-this-toplevel-section\n    nil\n    (:buffer \"over headline\"\n             \"* headline\"\n             \"under headline\")\n    (->> (org-ml-parse-this-toplevel-section)\n         (org-ml-to-trimmed-string))\n    => \"over headline\"\n    (:buffer \"* headline\"\n             \"under headline\")\n    (->> (org-ml-parse-this-toplevel-section)\n         (org-ml-to-trimmed-string))\n    => \"\")\n\n  (defexamples-content org-ml-this-buffer-has-headlines\n    nil\n    (:buffer \"not headline\"\n             \"* headline\")\n    (org-ml-this-buffer-has-headlines)\n    => t\n    (:buffer \"not headline\")\n    (org-ml-this-buffer-has-headlines)\n    => nil)\n\n  (defexamples-content org-ml-parse-headlines\n    nil\n    (:buffer \"not headline\"\n             \"* one\"\n             \"* two\"\n             \"* three\")\n    (->> (org-ml-parse-headlines 'all)\n         (-map #'org-ml-to-string)\n         (s-join \"\"))\n    => (:result \"* one\"\n                \"* two\"\n                \"* three\"\n                \"\")\n    (:buffer \"not headline\")\n    (->> (org-ml-parse-headlines 'all)\n         (-map #'org-ml-to-string)\n         (s-join \"\"))\n    => \"\"\n    (:buffer \"not headline\"\n             \"* one\"\n             \"** two\"\n             \"*** three\")\n    (->> (org-ml-parse-headlines 'all)\n         (-map #'org-ml-to-trimmed-string))\n    => '(\"* one\\n** two\\n*** three\" \"** two\\n*** three\" \"*** three\")\n\n    (:buffer \"not headline\"\n             \"*ignore this*\"\n             \"* one\"\n             \"* two\"\n             \"* three\")\n    (->> (org-ml-parse-headlines 0)\n         (-map #'org-ml-to-string)\n         (s-join \"\"))\n    => \"* one\\n\"\n    (->> (org-ml-parse-headlines '(0 1))\n         (-map #'org-ml-to-string)\n         (s-join \"\"))\n    => (:result \"* one\"\n                \"* two\\n\")\n    (->> (org-ml-parse-headlines [23 38])\n         (-map #'org-ml-to-string)\n         (s-join \"\"))\n    => (:result \"* one\"\n                \"* two\\n\"))\n\n  (defexamples-content org-ml-parse-subtrees\n    nil\n    (:buffer \"not headline\"\n             \"* one\"\n             \"** _one\"\n             \"* two\"\n             \"** _two\"\n             \"* three\"\n             \"** _three\")\n    (->> (org-ml-parse-subtrees 'all)\n         (-map #'org-ml-to-string)\n         (s-join \"\"))\n    => (:result \"* one\"\n                \"** _one\"\n                \"* two\"\n                \"** _two\"\n                \"* three\"\n                \"** _three\\n\")\n    (:buffer \"not headline\")\n    (->> (org-ml-parse-subtrees 'all)\n         (-map #'org-ml-to-string)\n         (s-join \"\"))\n    => \"\"\n\n    (:buffer \"not headline\"\n             \"* one\"\n             \"** _one\"\n             \"* two\"\n             \"** _two\"\n             \"* three\"\n             \"** _three\")\n    (->> (org-ml-parse-subtrees 0)\n         (-map #'org-ml-to-string)\n         (s-join \"\"))\n    => (:result \"* one\"\n                \"** _one\\n\")\n    (->> (org-ml-parse-subtrees '(0 1))\n         (-map #'org-ml-to-string)\n         (s-join \"\"))\n    => (:result \"* one\"\n                \"** _one\"\n                \"* two\"\n                \"** _two\\n\")\n    (->> (org-ml-parse-subtrees [10 30])\n         (-map #'org-ml-to-string)\n         (s-join \"\"))\n    => (:result \"* one\"\n                \"** _one\"\n                \"* two\"\n                \"** _two\\n\")))\n\n(def-example-group \"Building\"\n  \"Build new nodes.\"\n\n  (def-example-subgroup \"Leaf Object Nodes\"\n    nil\n\n    (defexamples org-ml-build-code\n      (->> (org-ml-build-code \"text\")\n           (org-ml-to-string))\n      => \"~text~\")\n\n    (defexamples org-ml-build-entity\n      (->> (org-ml-build-entity \"gamma\")\n           (org-ml-to-string))\n      => \"\\\\gamma\")\n\n    (defexamples org-ml-build-export-snippet\n      (->> (org-ml-build-export-snippet \"back\" \"value\")\n           (org-ml-to-string))\n      => \"@@back:value@@\")\n\n    (defexamples org-ml-build-inline-babel-call\n      (->> (org-ml-build-inline-babel-call \"name\")\n           (org-ml-to-string))\n      => \"call_name()\"\n      (->> (org-ml-build-inline-babel-call \"name\" :arguments '(\"n=4\"))\n           (org-ml-to-string))\n      => \"call_name(n=4)\"\n      (->> (org-ml-build-inline-babel-call \"name\" :inside-header '(:key val))\n           (org-ml-to-string))\n      => \"call_name[:key val]()\"\n      (->> (org-ml-build-inline-babel-call \"name\" :end-header '(:key val))\n           (org-ml-to-string))\n      => \"call_name()[:key val]\")\n\n    (defexamples org-ml-build-inline-src-block\n      (->> (org-ml-build-inline-src-block \"lang\")\n           (org-ml-to-string))\n      \n      => \"src_lang{}\"\n      (->> (org-ml-build-inline-src-block \"lang\" :value \"value\")\n           (org-ml-to-string))\n      \n      => \"src_lang{value}\"\n      (->> (org-ml-build-inline-src-block \"lang\" :value \"value\" :parameters '(:key val))\n           (org-ml-to-string))\n      => \"src_lang[:key val]{value}\")\n\n    (defexamples org-ml-build-line-break\n      (->> (org-ml-build-line-break)\n           (org-ml-to-string))\n      => \"\\\\\\\\\\n\")\n\n    (defexamples org-ml-build-latex-fragment\n      (->> (org-ml-build-latex-fragment \"$2+2=5$\")\n           (org-ml-to-string))\n      => \"$2+2=5$\")\n\n    (defexamples org-ml-build-macro\n      (->> (org-ml-build-macro \"economics\")\n           (org-ml-to-string))\n      => \"{{{economics}}}\"\n      (->> (org-ml-build-macro \"economics\" :args '(\"s=d\"))\n           (org-ml-to-string))\n      => \"{{{economics(s=d)}}}\")\n\n    (defexamples org-ml-build-statistics-cookie\n      (->> (org-ml-build-statistics-cookie '(nil))\n           (org-ml-to-string))\n      => \"[%]\"\n      (->> (org-ml-build-statistics-cookie '(nil nil))\n           (org-ml-to-string))\n      => \"[/]\"\n      (->> (org-ml-build-statistics-cookie '(50))\n           (org-ml-to-string))\n      => \"[50%]\"\n      (->> (org-ml-build-statistics-cookie '(1 3))\n           (org-ml-to-string))\n      => \"[1/3]\")\n\n    (defexamples org-ml-build-target\n      (->> (org-ml-build-target \"text\")\n           (org-ml-to-string))\n      => \"<<text>>\")\n\n    (defexamples org-ml-build-timestamp\n      (->> (org-ml-build-timestamp 'inactive 2019 1 15 2019 1 15)\n           (org-ml-to-string))\n      => \"[2019-01-15 Tue]\"\n      (->> (org-ml-build-timestamp 'active-range 2019 1 15 2019 1 16)\n           (org-ml-to-string))\n      => \"<2019-01-15 Tue>--<2019-01-16 Wed>\"\n      (->> (org-ml-build-timestamp\n            'inactive 2019 1 15 2019 1 15 :warning-type 'all\n            :warning-unit 'day :warning-value 1)\n           (org-ml-to-string))\n      => \"[2019-01-15 Tue -1d]\")\n\n    (defexamples org-ml-build-verbatim\n      (->> (org-ml-build-verbatim \"text\")\n           (org-ml-to-string))\n      => \"=text=\"))\n\n  (def-example-subgroup \"Branch Object Nodes\"\n    nil\n\n    (defexamples org-ml-build-bold\n      (->> (org-ml-build-bold \"text\")\n           (org-ml-to-string))\n      => \"*text*\")\n\n    (defexamples org-ml-build-footnote-reference\n      (->> (org-ml-build-footnote-reference)\n           (org-ml-to-string))\n      => \"[fn:]\"\n      (->> (org-ml-build-footnote-reference :label \"label\")\n           (org-ml-to-string))\n      => \"[fn:label]\"\n      (->> (org-ml-build-footnote-reference :label \"label\" \"content\")\n           (org-ml-to-string))\n      => \"[fn:label:content]\")\n\n    (defexamples org-ml-build-italic\n      (->> (org-ml-build-italic \"text\")\n           (org-ml-to-string))\n      => \"/text/\")\n\n    (defexamples org-ml-build-link\n      (->> (org-ml-build-link \"target\")\n           (org-ml-to-string))\n      => \"[[target]]\"\n      (->> (org-ml-build-link \"target\" :type \"file\")\n           (org-ml-to-string))\n      => \"[[file:target]]\"\n      (->> (org-ml-build-link \"target\" \"desc\")\n           (org-ml-to-string))\n      => \"[[target][desc]]\")\n\n    (defexamples org-ml-build-radio-target\n      (->> (org-ml-build-radio-target \"text\")\n           (org-ml-to-string))\n      => \"<<<text>>>\")\n\n    (defexamples org-ml-build-strike-through\n      (->> (org-ml-build-strike-through \"text\")\n           (org-ml-to-string))\n      => \"+text+\")\n\n    (defexamples org-ml-build-superscript\n      (->> (org-ml-build-superscript \"text\")\n           (org-ml-to-string))\n      => \"^text\")\n\n    (defexamples org-ml-build-subscript\n      (->> (org-ml-build-subscript \"text\")\n           (org-ml-to-string))\n      => \"_text\")\n\n    (defexamples org-ml-build-table-cell\n      (->> (org-ml-build-table-cell \"text\")\n           (org-ml-to-string))\n      => \" text |\")\n\n    (defexamples org-ml-build-underline\n      (->> (org-ml-build-underline \"text\")\n           (org-ml-to-string))\n      => \"_text_\"))\n\n  (def-example-subgroup \"Leaf Element Nodes\"\n    nil\n\n    (defexamples org-ml-build-babel-call\n      (->> (org-ml-build-babel-call \"name\")\n           (org-ml-to-trimmed-string))\n      => \"#+call: name()\"\n      (->> (org-ml-build-babel-call \"name\" :arguments '(\"arg=x\"))\n           (org-ml-to-trimmed-string))\n      => \"#+call: name(arg=x)\"\n      (->> (org-ml-build-babel-call \"name\" :inside-header '(:key val))\n           (org-ml-to-trimmed-string))\n      => \"#+call: name[:key val]()\"\n      (->> (org-ml-build-babel-call \"name\" :end-header '(:key val))\n           (org-ml-to-trimmed-string))\n      => \"#+call: name() :key val\")\n\n    (defexamples org-ml-build-clock\n      (->> (org-ml-build-clock (org-ml-build-timestamp! '(2019 1 1 0 0)))\n           (org-ml-to-trimmed-string))\n      => \"CLOCK: [2019-01-01 Tue 00:00]\"\n      (->> (org-ml-build-timestamp! '(2019 1 1 0 0) :end '(2019 1 1 1 0))\n           ;; TODO this is sloppy but also kinda a bad example anyways since\n           ;; the shortcut function exists\n           (org-ml-set-property :type 'inactive-range)\n           (org-ml-build-clock)\n           (org-ml-to-trimmed-string))\n      => \"CLOCK: [2019-01-01 Tue 00:00]--[2019-01-01 Tue 01:00] =>  1:00\")\n\n    (defexamples org-ml-build-comment\n      ;; TODO there is a bug that makes a blank string return a\n      ;; blank string (it should return a \"# \")\n      (->> (org-ml-build-comment \"text\")\n           (org-ml-to-trimmed-string))\n      => \"# text\"\n      (->> (org-ml-build-comment \"text\\nless\")\n           (org-ml-to-trimmed-string))\n      => \"# text\\n# less\")\n\n    (defexamples org-ml-build-comment-block\n      (->> (org-ml-build-comment-block)\n           (org-ml-to-trimmed-string))\n      => (:result \"#+begin_comment\"\n                  \"#+end_comment\")\n      (->> (org-ml-build-comment-block :value \"text\")\n           (org-ml-to-trimmed-string))\n      => (:result \"#+begin_comment\"\n                  \"text\"\n                  \"#+end_comment\"))\n\n    (defexamples org-ml-build-diary-sexp\n      (->> (org-ml-build-diary-sexp)\n           (org-ml-to-trimmed-string))\n      => \"%%()\"\n      (->> (org-ml-build-diary-sexp :value '(text))\n           (org-ml-to-trimmed-string))\n      => \"%%(text)\")\n\n    (defexamples org-ml-build-example-block\n      (->> (org-ml-build-example-block)\n           (org-ml-to-trimmed-string))\n      => (:result \"#+begin_example\"\n                  \"#+end_example\")\n      (->> (org-ml-build-example-block :value \"text\")\n           (org-ml-to-trimmed-string))\n      => (:result \"#+begin_example\"\n                  \"  text\"\n                  \"#+end_example\")\n      (->> (org-ml-build-example-block :value \"text\" :switches '(\"switches\"))\n           (org-ml-to-trimmed-string))\n      => (:result \"#+begin_example switches\"\n                  \"  text\"\n                  \"#+end_example\"))\n\n    (defexamples org-ml-build-export-block\n      (->> (org-ml-build-export-block \"type\" \"value\\n\")\n           (org-ml-to-trimmed-string))\n      => (:result \"#+begin_export type\"\n                  \"value\"\n                  \"#+end_export\"))\n\n    (defexamples org-ml-build-fixed-width\n      (->> (org-ml-build-fixed-width \"text\")\n           (org-ml-to-trimmed-string))\n      => \": text\")\n\n    (defexamples org-ml-build-horizontal-rule\n      (->> (org-ml-build-horizontal-rule)\n           (org-ml-to-trimmed-string))\n      => \"-----\")\n\n    (defexamples org-ml-build-keyword\n      (->> (org-ml-build-keyword \"FILETAGS\" \"tmsu\")\n           (org-ml-to-trimmed-string))\n      => \"#+filetags: tmsu\")\n\n    (defexamples org-ml-build-latex-environment\n      (->> (org-ml-build-latex-environment '(\"env\" \"text\"))\n           (org-ml-to-trimmed-string))\n      => (:result \"\\\\begin{env}\"\n                  \"text\"\n                  \"\\\\end{env}\"))\n\n    (defexamples org-ml-build-node-property\n      (->> (org-ml-build-node-property \"key\" \"val\")\n           (org-ml-to-trimmed-string))\n      => \":key:      val\")\n\n    (defexamples org-ml-build-planning\n      (->> (org-ml-build-planning :closed (org-ml-build-timestamp! '(2019 1 1) :active nil))\n           (org-ml-to-trimmed-string))\n      => \"CLOSED: [2019-01-01 Tue]\"\n      (->> (org-ml-build-planning :scheduled (org-ml-build-timestamp! '(2019 1 1) :active t))\n           (org-ml-to-trimmed-string))\n      => \"SCHEDULED: <2019-01-01 Tue>\"\n      (->> (org-ml-build-planning :deadline (org-ml-build-timestamp! '(2019 1 1) :active t))\n           (org-ml-to-trimmed-string))\n      => \"DEADLINE: <2019-01-01 Tue>\")\n\n    (defexamples org-ml-build-src-block\n      (->> (org-ml-build-src-block)\n           (org-ml-to-trimmed-string))\n      => (:result \"#+begin_src\"\n                  \"#+end_src\")\n      (->> (org-ml-build-src-block :value \"body\")\n           (org-ml-to-trimmed-string))\n      => (:result \"#+begin_src\"\n                  \"  body\"\n                  \"#+end_src\")\n      (->> (org-ml-build-src-block :value \"body\" :language \"emacs-lisp\")\n           (org-ml-to-trimmed-string))\n      => (:result \"#+begin_src emacs-lisp\"\n                  \"  body\"\n                  \"#+end_src\")\n      ;; TODO pretty sure this makes no sense...\n      (->> (org-ml-build-src-block :value \"body\" :switches '(\"-n 20\" \"-r\"))\n           (org-ml-to-trimmed-string))\n      => (:result \"#+begin_src -n 20 -r\"\n                  \"  body\"\n                  \"#+end_src\")\n      ;; TODO and this...\n      (->> (org-ml-build-src-block :value \"body\" :parameters '(:key val))\n           (org-ml-to-trimmed-string))\n      => (:result \"#+begin_src :key val\"\n                  \"  body\"\n                  \"#+end_src\")))\n\n  (def-example-subgroup \"Branch Element Nodes with Child Object Nodes\"\n    nil\n\n    (defexamples org-ml-build-paragraph\n      (->> (org-ml-build-paragraph \"text\")\n           (org-ml-to-trimmed-string))\n      => \"text\")\n\n    (defexamples org-ml-build-table-row\n      (->> (org-ml-build-table-cell \"a\")\n           (org-ml-build-table-row)\n           (org-ml-to-trimmed-string))\n      => \"| a |\")\n\n    ;; TODO should add a comment here to explain that newlines are necessary\n    (defexamples org-ml-build-verse-block\n      (->> (org-ml-build-verse-block \"text\\n\")\n           (org-ml-to-trimmed-string))\n      => (:result \"#+begin_verse\"\n                  \"text\"\n                  \"#+end_verse\")))\n\n  (def-example-subgroup \"Branch Element Nodes with Child Element Nodes\"\n    nil\n\n    (defexamples org-ml-build-org-data\n      (->> (org-ml-build-headline :title '(\"dummy\"))\n           (org-ml-build-org-data)\n           (org-ml-to-trimmed-string))\n      => \"* dummy\")\n\n    (defexamples org-ml-build-center-block\n      (->> (org-ml-build-center-block)\n           (org-ml-to-trimmed-string))\n      => (:result \"#+begin_center\"\n                  \"#+end_center\")\n      (->> (org-ml-build-paragraph \"text\")\n           (org-ml-build-center-block)\n           (org-ml-to-trimmed-string))\n      => (:result \"#+begin_center\"\n                  \"text\"\n                  \"#+end_center\"))\n\n    (defexamples org-ml-build-drawer\n      (->> (org-ml-build-drawer \"NAME\")\n           (org-ml-to-trimmed-string))\n      => (:result \":NAME:\"\n                  \":END:\")\n      (->> (org-ml-build-paragraph \"text\")\n           (org-ml-build-drawer \"NAME\")\n           (org-ml-to-trimmed-string))\n      => (:result \":NAME:\"\n                  \"text\"\n                  \":END:\"))\n\n    (defexamples org-ml-build-dynamic-block\n      (->> (org-ml-build-dynamic-block \"empty\")\n           (org-ml-to-trimmed-string))\n      => (:result \"#+begin: empty\"\n                  \"#+end:\")\n      (->> (org-ml-build-comment \"I'm in here\")\n           (org-ml-build-dynamic-block \"notempty\")\n           (org-ml-to-trimmed-string))\n      => (:result \"#+begin: notempty\"\n                  \"# I'm in here\"\n                  \"#+end:\"))\n\n    (defexamples org-ml-build-footnote-definition\n      (->> (org-ml-build-paragraph \"footnote contents\")\n           (org-ml-build-footnote-definition \"label\")\n           (org-ml-to-trimmed-string))\n      => \"[fn:label] footnote contents\")\n\n    (defexamples org-ml-build-headline\n      (->> (org-ml-build-headline)\n           (org-ml-to-trimmed-string))\n      => \"*\"\n      (->> (org-ml-build-headline :level 2 :title '(\"dummy\") :tags '(\"tmsu\"))\n           (org-ml-to-trimmed-string))\n      => \"** dummy            :tmsu:\"\n      (->> (org-ml-build-headline :todo-keyword \"TODO\" :archivedp t\n                                  :commentedp t :priority ?A)\n           (org-ml-to-trimmed-string))\n      => \"* TODO COMMENT [#A]  :ARCHIVE:\"\n      :begin-hidden\n      (->> (org-ml-build-headline :level 2)\n           (org-ml-to-trimmed-string))\n      => \"**\"\n      (->> (org-ml-build-headline :title '(\"dummy\"))\n           (org-ml-to-trimmed-string))\n      => \"* dummy\"\n      (->> (org-ml-build-headline :tags '(\"tmsu\"))\n           (org-ml-to-trimmed-string))\n      => \"*                   :tmsu:\"\n      (->> (org-ml-build-headline :todo-keyword \"DONE\")\n           (org-ml-to-trimmed-string))\n      => \"* DONE\"\n      (->> (org-ml-build-headline :priority ?A)\n           (org-ml-to-trimmed-string))\n      => \"* [#A]\"\n      (->> (org-ml-build-headline :footnote-section-p t)\n           (org-ml-to-trimmed-string))\n      => \"* Footnotes\"\n      (->> (org-ml-build-headline :commentedp t)\n           (org-ml-to-trimmed-string))\n      => \"* COMMENT\"\n      (->> (org-ml-build-headline :archivedp t)\n           (org-ml-to-trimmed-string))\n      => \"*                   :ARCHIVE:\"\n      :end-hidden)\n\n    (defexamples org-ml-build-item\n      (->> (org-ml-build-paragraph \"item contents\")\n           (org-ml-build-item)\n           (org-ml-to-trimmed-string))\n      => \"- item contents\"\n      (->> (org-ml-build-paragraph \"item contents\")\n           (org-ml-build-item :bullet 1)\n           (org-ml-to-trimmed-string))\n      => \"1. item contents\"\n      (->> (org-ml-build-paragraph \"item contents\")\n           (org-ml-build-item :checkbox 'on)\n           (org-ml-to-trimmed-string))\n      => \"- [X] item contents\"\n      (->> (org-ml-build-paragraph \"item contents\")\n           (org-ml-build-item :tag '(\"tmsu\"))\n           (org-ml-to-trimmed-string))\n      => \"- tmsu :: item contents\"\n      (->> (org-ml-build-paragraph \"item contents\")\n           (org-ml-build-item :counter 10)\n           (org-ml-to-trimmed-string))\n      => \"- [@10] item contents\")\n\n    (defexamples org-ml-build-plain-list\n      (->> (org-ml-build-paragraph \"item contents\")\n           (org-ml-build-item)\n           (org-ml-build-plain-list)\n           (org-ml-to-trimmed-string))\n      => \"- item contents\")\n\n    (defexamples org-ml-build-property-drawer\n      (->> (org-ml-build-property-drawer)\n           (org-ml-to-trimmed-string))\n      => (:result \":PROPERTIES:\"\n                  \":END:\")\n      (->> (org-ml-build-node-property \"key\" \"val\")\n           (org-ml-build-property-drawer)\n           (org-ml-to-trimmed-string))\n      => (:result \":PROPERTIES:\"\n                  \":key:      val\"\n                  \":END:\"))\n\n    (defexamples org-ml-build-quote-block\n      (->> (org-ml-build-quote-block)\n           (org-ml-to-trimmed-string))\n      => (:result \"#+begin_quote\"\n                  \"#+end_quote\")\n      (->> (org-ml-build-paragraph \"quoted stuff\")\n           (org-ml-build-quote-block)\n           (org-ml-to-trimmed-string))\n      => (:result \"#+begin_quote\"\n                  \"quoted stuff\"\n                  \"#+end_quote\"))\n\n    (defexamples org-ml-build-section\n      (->> (org-ml-build-paragraph \"text\")\n           (org-ml-build-section)\n           (org-ml-to-trimmed-string))\n      => \"text\")\n\n    ;; TODO add :parameters to test\n    (defexamples org-ml-build-special-block\n      (->> (org-ml-build-special-block \"monad\")\n           (org-ml-to-trimmed-string))\n      => (:result \"#+begin_monad\"\n                  \"#+end_monad\")\n      (->> (org-ml-build-comment \"Launch missiles\")\n           (org-ml-build-special-block \"monad\")\n           (org-ml-to-trimmed-string))\n      => (:result \"#+begin_monad\"\n                  \"# Launch missiles\"\n                  \"#+end_monad\"))\n\n    (defexamples org-ml-build-table\n      (->> (org-ml-build-table-cell \"cell\")\n           (org-ml-build-table-row)\n           (org-ml-build-table)\n           (org-ml-to-trimmed-string))\n      => \"| cell |\"))\n\n  (def-example-subgroup \"Miscellaneous Builders\"\n    nil\n\n    ;; (defexamples-content org-ml-clone-node\n    ;;   nil\n    ;;   (:buffer \"dolly\")\n    ;;   (let* ((node1 (org-ml-parse-this-element))\n    ;;          (node2 (org-ml-clone-node node1)))\n    ;;     (equal node1 node2))\n    ;;   => t\n    ;;   (let* ((node1 (org-ml-parse-this-element))\n    ;;          (node2 (org-ml-clone-node node1)))\n    ;;     (eq node1 node2))\n    ;;   => nil)\n\n    ;; (defexamples-content org-ml-clone-node-n\n    ;;   nil\n    ;;   (:buffer \"dolly\")\n    ;;   (-let* ((node1 (org-ml-parse-this-element))\n    ;;           ((node2 node3) (org-ml-clone-node-n 2 node1)))\n    ;;     (or (equal node1 node2)\n    ;;         (equal node1 node3)\n    ;;         (equal node2 node3)))\n    ;;   => t\n    ;;   (-let* ((node1 (org-ml-parse-this-element))\n    ;;           ((node2 node3) (org-ml-clone-node-n 2 node1)))\n    ;;     (or (eq node1 node2)\n    ;;         (eq node1 node3)\n    ;;         (eq node2 node3)))\n    ;;   => nil)\n\n    (defexamples org-ml-build-secondary-string!\n      (->> (org-ml-build-secondary-string! \"I'm plain\")\n           (-map #'org-ml-get-type))\n      => '(plain-text)\n      (->> (org-ml-build-secondary-string! \"I'm *not* plain\")\n           (-map #'org-ml-get-type))\n      => '(plain-text bold plain-text)\n      (->> (org-ml-build-secondary-string! \"1. I'm *not* a plain list\")\n           (-map #'org-ml-get-type))\n      => '(plain-text bold plain-text)\n      (->> (org-ml-build-secondary-string! \"* I'm not an object\")\n           (-map #'org-ml-get-type))\n      => '(plain-text))\n\n    (defexamples org-ml-build-table-row-hline\n      (->>  (org-ml-build-table\n             (org-ml-build-table-row\n              (org-ml-build-table-cell \"text\"))\n             (org-ml-build-table-row-hline))\n            (org-ml-to-trimmed-string))\n      => (:result \"| text |\"\n                  \"|------|\"))\n\n    (defexamples org-ml-build-timestamp-diary\n      (->> (org-ml-build-timestamp-diary '(diary-float t 4 2))\n           (org-ml-to-string))\n      => \"<%%(diary-float t 4 2)>\"\n      (->> (org-ml-build-timestamp-diary '(diary-float t 4 2) :start '(0 0))\n           (org-ml-to-string))\n      => \"<%%(diary-float t 4 2) 00:00>\"\n      (->> (org-ml-build-timestamp-diary '(diary-float t 4 2) :start '(0 0) :end '(1 0))\n           (org-ml-to-string))\n      => \"<%%(diary-float t 4 2) 00:00-01:00>\"\n      :begin-hidden\n      (->> (org-ml-build-timestamp-diary '(diary-float t 4 2) :end '(1 0))\n           (org-ml-to-string))\n      !!> arg-type-error\n      :end-hidden))\n\n  (def-example-subgroup \"Shorthand Builders\"\n    \"Build nodes with more convenient/shorter syntax.\"\n\n    (defexamples org-ml-build-timestamp!\n      (->> (org-ml-build-timestamp! '(2019 1 1))\n           (org-ml-to-string))\n      => \"[2019-01-01 Tue]\"\n      (->> (org-ml-build-timestamp! '(2019 1 1 12 0)\n                                    :active t\n                                    :warning '(all 1 day)\n                                    :repeater '(cumulate 1 month))\n           (org-ml-to-string))\n      => \"<2019-01-01 Tue 12:00 +1m -1d>\"\n      (->> (org-ml-build-timestamp! '(2019 1 1) :end '(2019 1 2))\n           (org-ml-to-string))\n      => \"[2019-01-01 Tue]--[2019-01-02 Wed]\")\n\n    (defexamples org-ml-build-clock!\n      (->> (org-ml-build-clock! '(2019 1 1))\n           (org-ml-to-trimmed-string))\n      => \"CLOCK: [2019-01-01 Tue]\"\n      (->> (org-ml-build-clock! '(2019 1 1 12 0))\n           (org-ml-to-trimmed-string))\n      => \"CLOCK: [2019-01-01 Tue 12:00]\"\n      (->> (org-ml-build-clock! '(2019 1 1 12 0) :end '(2019 1 1 13 0))\n           (org-ml-to-trimmed-string))\n      => \"CLOCK: [2019-01-01 Tue 12:00]--[2019-01-01 Tue 13:00] =>  1:00\")\n\n    (defexamples org-ml-build-planning!\n      (->> (org-ml-build-planning! :closed '(2019 1 1))\n           (org-ml-to-trimmed-string))\n      => \"CLOSED: [2019-01-01 Tue]\"\n      (->> (org-ml-build-planning! :closed '(2019 1 1)\n                                   :scheduled '(2018 1 1))\n           (org-ml-to-trimmed-string))\n      => \"SCHEDULED: <2018-01-01 Mon> CLOSED: [2019-01-01 Tue]\"\n      (->> (org-ml-build-planning! :scheduled '(2019 1 1 &warning all 1 day &repeater cumulate 1 month))\n           (org-ml-to-trimmed-string))\n      => \"SCHEDULED: <2019-01-01 Tue +1m -1d>\")\n\n    (defexamples org-ml-build-property-drawer!\n      (->> (org-ml-build-property-drawer! '(\"key\" \"val\"))\n           (org-ml-to-trimmed-string))\n      => (:result \":PROPERTIES:\"\n                  \":key:      val\"\n                  \":END:\"))\n\n    (defexamples org-ml-build-headline!\n      (->> (org-ml-build-headline! :title-text \"really impressive title\")\n           (org-ml-to-trimmed-string))\n      => \"* really impressive title\"\n      (->> (org-ml-build-headline! :title-text \"really impressive title\"\n                                   :statistics-cookie '(0 9000))\n           (org-ml-to-trimmed-string))\n      => \"* really impressive title [0/9000]\"\n      (->> (org-ml-build-headline!\n            :title-text \"really impressive title\"\n            :section-children\n            (list (org-ml-build-property-drawer! '(\"key\" \"val\"))\n                  (org-ml-build-paragraph! \"section text\"))\n            (org-ml-build-headline! :title-text \"subhead\"))\n           (org-ml-to-trimmed-string))\n      => (:result \"* really impressive title\"\n                  \":PROPERTIES:\"\n                  \":key:      val\"\n                  \":END:\"\n                  \"section text\"\n                  \"** subhead\"))\n\n    (defexamples org-ml-build-item!\n      (->> (org-ml-build-item!\n            :bullet 1\n            :tag \"complicated *tag*\"\n            :paragraph \"petulant /frenzy/\"\n            (org-ml-build-plain-list\n             (org-ml-build-item! :bullet '- :paragraph \"below\")))\n           (org-ml-to-trimmed-string))\n      => (:result \"1. complicated *tag* :: petulant /frenzy/\"\n                  \"     - below\"))\n\n    (defexamples org-ml-build-paragraph!\n      (->> (org-ml-build-paragraph! \"stuff /with/ *formatting*\" :post-blank 2)\n           (org-ml-to-string))\n      => (:result \"stuff /with/ *formatting*\"\n                  \"\"\n                  \"\"\n                  \"\")\n      (->> (org-ml-build-paragraph! \"* stuff /with/ *formatting*\")\n           (org-ml-to-string))\n      => (:result \"* stuff /with/ *formatting*\"\n                  \"\"))\n\n    (defexamples org-ml-build-table-cell!\n      (->> (org-ml-build-table-cell! \"rage\")\n           (org-ml-to-trimmed-string))\n      => \"rage |\"\n      (->> (org-ml-build-table-cell! \"*rage*\")\n           (org-ml-to-trimmed-string))\n      => \"*rage* |\")\n\n    (defexamples org-ml-build-table-row!\n      (->> (org-ml-build-table-row! '(\"R\" \"A\" \"G\" \"E\"))\n           (org-ml-to-trimmed-string))\n      => \"| R | A | G | E |\"\n      (->> (org-ml-build-table-row! '(\"S\" \"\" \"X\"))\n           (org-ml-to-trimmed-string))\n      => \"| S |  | X |\"\n      (->> (org-ml-build-table-row! 'hline)\n           (org-ml-to-trimmed-string))\n      => \"|-\")\n\n    (defexamples org-ml-build-table!\n      (->> (org-ml-build-table! '(\"R\" \"A\") '(\"G\" \"E\"))\n           (org-ml-to-trimmed-string))\n      => (:result \"| R | A |\"\n                  \"| G | E |\")\n      (->> (org-ml-build-table! '(\"S\" \"\") '(\"\" \"X\"))\n           (org-ml-to-trimmed-string))\n      => (:result \"| S |   |\"\n                  \"|   | X |\")\n      (->> (org-ml-build-table! '(\"L\" \"O\") 'hline '(\"V\" \"E\"))\n           (org-ml-to-trimmed-string))\n      => (:result \"| L | O |\"\n                  \"|---+---|\"\n                  \"| V | E |\")))\n\n  (def-example-subgroup \"Logbook Item Builders\"\n    \"Build item nodes for inclusion in headline logbooks\"\n\n    (defexamples org-ml-build-log-note\n      (-> (- 1546300800 (car (current-time-zone)))\n          (org-ml-build-log-note \"noteworthy\")\n          (org-ml-to-trimmed-string))\n      => (:result \"- Note taken on [2019-01-01 Tue 00:00] \\\\\\\\\"\n                  \"  noteworthy\"))\n    \n    (defexamples org-ml-build-log-done\n      (-> (- 1546300800 (car (current-time-zone)))\n          (org-ml-build-log-done)\n          (org-ml-to-trimmed-string))\n      => (:result \"- CLOSING NOTE [2019-01-01 Tue 00:00]\")\n      (-> (- 1546300800 (car (current-time-zone)))\n          (org-ml-build-log-done \"noteworthy\")\n          (org-ml-to-trimmed-string))\n      => (:result \"- CLOSING NOTE [2019-01-01 Tue 00:00] \\\\\\\\\"\n                  \"  noteworthy\"))\n\n    (defexamples org-ml-build-log-refile\n      (-> (- 1546300800 (car (current-time-zone)))\n          (org-ml-build-log-refile)\n          (org-ml-to-trimmed-string))\n      => (:result \"- Refiled on [2019-01-01 Tue 00:00]\")\n      (-> (- 1546300800 (car (current-time-zone)))\n          (org-ml-build-log-refile \"noteworthy\")\n          (org-ml-to-trimmed-string))\n      => (:result \"- Refiled on [2019-01-01 Tue 00:00] \\\\\\\\\"\n                  \"  noteworthy\"))\n\n    (defexamples org-ml-build-log-state\n      (-> (- 1546300800 (car (current-time-zone)))\n          (org-ml-build-log-state \"HOLD\" \"TODO\")\n          (org-ml-to-trimmed-string))\n      => (:result \"- State \\\"HOLD\\\"       from \\\"TODO\\\"       [2019-01-01 Tue 00:00]\")\n      (-> (- 1546300800 (car (current-time-zone)))\n          (org-ml-build-log-state \"HOLD\" \"TODO\" \"noteworthy\")\n          (org-ml-to-trimmed-string))\n      => (:result \"- State \\\"HOLD\\\"       from \\\"TODO\\\"       [2019-01-01 Tue 00:00] \\\\\\\\\"\n                  \"  noteworthy\"))\n\n    (defexamples org-ml-build-log-deldeadline\n      (-> (- 1546300800 (car (current-time-zone)))\n          (org-ml-build-log-deldeadline (org-ml-build-timestamp! '(2019 1 2)))\n          (org-ml-to-trimmed-string))\n      => (:result \"- Removed deadline, was \\\"[2019-01-02 Wed]\\\" on [2019-01-01 Tue 00:00]\")\n      (-> (- 1546300800 (car (current-time-zone)))\n          (org-ml-build-log-deldeadline (org-ml-build-timestamp! '(2019 1 2)) \"noteworthy\")\n          (org-ml-to-trimmed-string))\n      => (:result \"- Removed deadline, was \\\"[2019-01-02 Wed]\\\" on [2019-01-01 Tue 00:00] \\\\\\\\\"\n                  \"  noteworthy\"))\n\n    (defexamples org-ml-build-log-delschedule\n      (-> (- 1546300800 (car (current-time-zone)))\n          (org-ml-build-log-delschedule (org-ml-build-timestamp! '(2019 1 2)))\n          (org-ml-to-trimmed-string))\n      => (:result \"- Not scheduled, was \\\"[2019-01-02 Wed]\\\" on [2019-01-01 Tue 00:00]\")\n      (-> (- 1546300800 (car (current-time-zone)))\n          (org-ml-build-log-delschedule (org-ml-build-timestamp! '(2019 1 2)) \"noteworthy\")\n          (org-ml-to-trimmed-string))\n      => (:result \"- Not scheduled, was \\\"[2019-01-02 Wed]\\\" on [2019-01-01 Tue 00:00] \\\\\\\\\"\n                  \"  noteworthy\"))\n\n    (defexamples org-ml-build-log-redeadline\n      (-> (- 1546300800 (car (current-time-zone)))\n          (org-ml-build-log-redeadline (org-ml-build-timestamp! '(2019 1 2)))\n          (org-ml-to-trimmed-string))\n      => (:result \"- New deadline from \\\"[2019-01-02 Wed]\\\" on [2019-01-01 Tue 00:00]\")\n      (-> (- 1546300800 (car (current-time-zone)))\n          (org-ml-build-log-redeadline (org-ml-build-timestamp! '(2019 1 2)) \"noteworthy\")\n          (org-ml-to-trimmed-string))\n      => (:result \"- New deadline from \\\"[2019-01-02 Wed]\\\" on [2019-01-01 Tue 00:00] \\\\\\\\\"\n                  \"  noteworthy\"))\n\n    (defexamples org-ml-build-log-reschedule\n      (-> (- 1546300800 (car (current-time-zone)))\n          (org-ml-build-log-reschedule (org-ml-build-timestamp! '(2019 1 2)))\n          (org-ml-to-trimmed-string))\n      => (:result \"- Rescheduled from \\\"[2019-01-02 Wed]\\\" on [2019-01-01 Tue 00:00]\")\n      (-> (- 1546300800 (car (current-time-zone)))\n          (org-ml-build-log-reschedule (org-ml-build-timestamp! '(2019 1 2)) \"noteworthy\")\n          (org-ml-to-trimmed-string))\n      => (:result \"- Rescheduled from \\\"[2019-01-02 Wed]\\\" on [2019-01-01 Tue 00:00] \\\\\\\\\"\n                  \"  noteworthy\"))\n\n    (defexamples org-ml-build-log-type\n      (let ((org-log-note-headings '((test . \"Changed %s from %S on %t by %u\")))\n            (ut (- 1546300800 (car (current-time-zone)))))\n        (->> (org-ml-build-log-type 'test :unixtime ut :old \"TODO\" :new\n                                    \"DONE\" :username \"shadowbrokers\"\n                                    :note \"We're coming for you\")\n             (org-ml-to-trimmed-string)))\n      => (:result\n          \"- Changed \\\"DONE\\\" from \\\"TODO\\\" on [2019-01-01 Tue 00:00] by shadowbrokers \\\\\\\\\"\n          \"  We're coming for you\")\n      :begin-hidden\n      (let ((org-log-note-headings '((test . \"My note is %t\"))))\n        (->> (- 1546300800 (car (current-time-zone)))\n             (org-ml-build-log-type 'test :unixtime)\n             (org-ml-to-trimmed-string)))\n      => \"- My note is [2019-01-01 Tue 00:00]\"\n      (let ((org-log-note-headings '((test . \"My note is %T\"))))\n        (->> (- 1546300800 (car (current-time-zone)))\n             (org-ml-build-log-type 'test :unixtime)\n             (org-ml-to-trimmed-string)))\n      => \"- My note is <2019-01-01 Tue 00:00>\"\n      (let ((org-log-note-headings '((test . \"My note is %d\"))))\n        (->> (- 1546300800 (car (current-time-zone)))\n             (org-ml-build-log-type 'test :unixtime)\n             (org-ml-to-trimmed-string)))\n      => \"- My note is [2019-01-01 Tue]\"\n      (let ((org-log-note-headings '((test . \"My note is %D\"))))\n        (->> (- 1546300800 (car (current-time-zone)))\n             (org-ml-build-log-type 'test :unixtime)\n             (org-ml-to-trimmed-string)))\n      => \"- My note is <2019-01-01 Tue>\"\n      (let ((org-log-note-headings '((test . \"My name is %u\"))))\n        (->> (org-ml-build-log-type 'test :username \"slim\")\n             (org-ml-to-trimmed-string)))\n      => \"- My name is slim\"\n      (let ((org-log-note-headings '((test . \"My name is %U\"))))\n        (->> (org-ml-build-log-type 'test :full-username \"slimshady\")\n             (org-ml-to-trimmed-string)))\n      => \"- My name is slimshady\"\n      (let ((org-log-note-headings '((test . \"My note is %S\"))))\n        (->> (org-ml-build-log-type 'test :old \"DONE\")\n             (org-ml-to-trimmed-string)))\n      => \"- My note is \\\"DONE\\\"\"\n      (let ((org-log-note-headings '((test . \"My note is %S\"))))\n        (->> (org-ml-build-timestamp! '(2019 1 1 0 0))\n             (org-ml-build-log-type 'test :old)\n             (org-ml-to-trimmed-string)))\n      => \"- My note is \\\"[2019-01-01 Tue 00:00]\\\"\"\n      (let ((org-log-note-headings '((test . \"My note is %s\"))))\n        (->> (org-ml-build-log-type 'test :new \"DONE\")\n             (org-ml-to-trimmed-string)))\n      => \"- My note is \\\"DONE\\\"\"\n      (let ((org-log-note-headings '((test . \"My note is %s\"))))\n        (->> (org-ml-build-timestamp! '(2019 1 1 0 0))\n             (org-ml-build-log-type 'test :new)\n             (org-ml-to-trimmed-string)))\n      => \"- My note is \\\"[2019-01-01 Tue 00:00]\\\"\"\n      :end-hidden\n      )\n    ))\n\n(def-example-group \"Type Predicates\"\n  \"Test node types.\"\n\n  (defexamples-content org-ml-get-type\n    nil\n    (:buffer \"*I'm emboldened*\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-get-type))\n    => 'bold\n    (:buffer \"* I'm the headliner\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-get-type))\n    => 'headline\n    (:buffer \"[2112-12-21 Wed]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-get-type))\n    => 'timestamp)\n\n  (defexamples-content org-ml-is-type\n    nil\n    (:buffer \"*ziltoid*\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-is-type 'bold))\n    => t\n    (->> (org-ml-parse-this-object)\n         (org-ml-is-type 'italic))\n    => nil)\n\n  (defexamples-content org-ml-is-any-type\n    nil\n    (:buffer \"*ziltoid*\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-is-any-type '(bold)))\n    => t\n    (->> (org-ml-parse-this-object)\n         (org-ml-is-any-type '(bold italic)))\n    => t\n    (->> (org-ml-parse-this-object)\n         (org-ml-is-any-type '(italic)))\n    => nil)\n\n  (defexamples-content org-ml-is-element\n    nil\n    (:buffer \"*ziltoid*\")\n    (:comment \"Parsing this text as an element node gives a paragraph node\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-is-element))\n    => t\n    (:comment \"Parsing the same text as an object node gives a bold node\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-is-element))\n    => nil)\n\n  (defexamples-content org-ml-is-branch-node\n    nil\n    (:buffer \"*ziltoid*\")\n    (:comment \"Parsing this as an element node gives a paragraph node\"\n              \"(a branch node)\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-is-branch-node))\n    => t\n    (:comment \"Parsing this as an object node gives a bold node\"\n              \"(also a branch node)\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-is-branch-node))\n    => t\n    (:buffer \"~ziltoid~\")\n    (:comment \"Parsing this as an object node gives a code node\"\n              \"(not a branch node)\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-is-branch-node))\n    => nil\n    (:buffer \"# ziltoid\")\n    (:comment \"Parsing this as an element node gives a comment node\"\n              \"(also not a branch node)\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-is-branch-node))\n    => nil\n    (:buffer \"* I'm so great\")\n    (:comment \"Parsing this as an element node gives a headline node\"\n              \"(a branch node)\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-is-branch-node))\n    => t)\n\n  (defexamples-content org-ml-node-may-have-child-objects\n    nil\n    (:buffer \"*ziltoid*\")\n    (:comment \"Parsing this as an element node gives a paragraph node\"\n              \"(can have child object nodes)\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-node-may-have-child-objects))\n    => t\n    (:comment \"Parsing this as an object node gives a bold node\"\n              \"(also can have child object nodes)\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-node-may-have-child-objects))\n    => t\n    (:buffer \"~ziltoid~\")\n    (:comment \"Parsing this as an object node gives a code node\"\n              \"(not a branch node)\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-node-may-have-child-objects))\n    => nil\n    (:buffer \"# ziltoid\")\n    (:comment \"Parsing this as an element node gives a comment node\"\n              \"(not a branch node)\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-node-may-have-child-objects))\n    => nil\n    (:buffer \"* I'm so great\")\n    (:comment \"Parsing this as an element node gives a headline node\"\n              \"(can only have child element nodes)\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-node-may-have-child-objects))\n    => nil)\n\n  (defexamples-content org-ml-node-may-have-child-elements\n    nil\n    (:buffer \"* I'm so great\")\n    (:comment \"Parsing this as an element node gives a headline node\"\n              \"(can have child element nodes)\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-node-may-have-child-elements))\n    => t\n    (:buffer \"*ziltoid*\")\n    (:comment \"Parsing this as an element node gives a paragraph node\"\n              \"(can only have child object nodes)\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-node-may-have-child-elements))\n    => nil\n    (:buffer \"# ziltoid\")\n    (:comment \"Parsing this as an element node gives a comment node\"\n              \"(not a branch node)\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-node-may-have-child-elements))\n    => nil))\n\n(def-example-group \"Property Manipulation\"\n  \"Set, get, and map properties of nodes.\"\n\n  (def-example-subgroup \"Generic\"\n    nil\n\n    (defexamples-content org-ml-contains-point-p\n      nil\n      (:buffer \"*findme*\")\n      (->> (org-ml-parse-this-object)\n           (org-ml-contains-point-p 2))\n      => t\n      (->> (org-ml-parse-this-object)\n           (org-ml-contains-point-p 10))\n      => nil)\n\n    (defexamples-content org-ml-set-property\n      nil\n\n      (:buffer \"#+call: ktulu()\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :call \"cthulhu\")\n        (org-ml-set-property :inside-header '(:cache no))\n        (org-ml-set-property :arguments '(\"x=4\"))\n        (org-ml-set-property :end-header '(:exports results))\n        (org-ml-to-trimmed-string))\n      => \"#+call: cthulhu[:cache no](x=4) :exports results\"\n\n      :begin-hidden\n      (:buffer \"CLOCK: [2019-01-01 Tue]\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property\n         :value (org-ml-build-timestamp! '(2019 1 1) :end '(2019 1 2)))\n        (org-ml-to-trimmed-string))\n      => \"CLOCK: [2019-01-01 Tue]--[2019-01-02 Wed] => 24:00\"\n\n      (:buffer \"~learn to~\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-set-property :value \"why?\")\n        (org-ml-to-trimmed-string))\n      => \"~why?~\"\n\n      (:buffer \"# not here\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :value \"still not here\")\n        (org-ml-to-trimmed-string))\n      => \"# still not here\"\n\n      (:buffer \"#+begin_comment\"\n               \"not here\"\n               \"#+end_comment\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :value \"still not here\")\n        (org-ml-to-trimmed-string))\n      => (:result \"#+begin_comment\"\n                  \"still not here\"\n                  \"#+end_comment\")\n\n      (:buffer \"%%(print :valueble)\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :value '(print :invaluble))\n        (org-ml-to-trimmed-string))\n      => \"%%(print :invaluble)\"\n\n      (:buffer \":LOGBOOK:\"\n               \":END:\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :drawer-name \"BOOKOFSOULS\")\n        (org-ml-to-trimmed-string))\n      => (:result \":BOOKOFSOULS:\"\n                  \":END:\")\n\n      (:buffer \"#+begin: blockhead\"\n               \"#+end:\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :block-name \"blockfoot\")\n        (org-ml-set-property :arguments '(:cache no))\n        (org-ml-to-trimmed-string))\n      => (:result \"#+begin: blockfoot :cache no\"\n                  \"#+end:\")\n\n      (:buffer \"\\\\pi\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-set-property :name \"gamma\")\n        (org-ml-set-property :use-brackets-p t)\n        (org-ml-to-trimmed-string))\n      => \"\\\\gamma{}\"\n\n      ;; TODO test org-src-preserve-indentation\n      (:buffer \"#+begin_example\"\n               \"#+end_example\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :switches '(\"-n\"))\n        (org-ml-set-property :value \"example.com\")\n        (org-ml-to-trimmed-string))\n      => (:buffer \"#+begin_example -n\"\n                  \"  example.com\"\n                  \"#+end_example\")\n\n      (:buffer \"#+begin_export latex\"\n               \"#+end_export\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :type \"domestic\")\n        (org-ml-set-property :value \"bullets, bombs, and bigotry\")\n        (org-ml-to-trimmed-string))\n      => (:buffer \"#+begin_export domestic\"\n                  \"bullets, bombs, and bigotry\"\n                  \"#+end_export\")\n\n      (:buffer \"@@back-end:value@@\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-set-property :back-end \"latex\")\n        (org-ml-set-property :value \"new-value\")\n        (org-ml-to-trimmed-string))\n      => \"@@latex:new-value@@\"\n\n      (:buffer \": fixed\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :value \"unfixed\")\n        (org-ml-to-trimmed-string))\n      => \": unfixed\"\n\n      (:buffer \"[fn:whitelabel] society\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :label \"blacklabel\")\n        (org-ml-to-trimmed-string))\n      => \"[fn:blacklabel] society\"\n\n      (:buffer \"* dummy\"\n               \"stuff\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :archivedp t)\n        (org-ml-set-property :commentedp t)\n        (org-ml-set-property :level 2)\n        (org-ml-set-property :pre-blank 1)\n        (org-ml-set-property :priority ?A)\n        (org-ml-set-property :tags '(\"tmsu\"))\n        (org-ml-set-property :title '(\"smartie\"))\n        (org-ml-set-property :todo-keyword \"TODO\")\n        (org-ml-to-trimmed-string))\n      => (:result \"** TODO COMMENT [#A] smartie :tmsu:ARCHIVE:\"\n                  \"\"\n                  \"stuff\")\n      :begin-hidden\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :footnote-section-p t)\n        (org-ml-to-trimmed-string))\n      => (:result \"* Footnotes\"\n                  \"stuff\")\n      :end-hidden\n\n      (:buffer \"call_kthulu()\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-set-property :call \"cthulhu\")\n        (org-ml-set-property :inside-header '(:cache no))\n        (org-ml-set-property :arguments '(\"x=4\"))\n        (org-ml-set-property :end-header '(:exports results))\n        (org-ml-to-trimmed-string))\n      => \"call_cthulhu[:cache no](x=4)[:exports results]\"\n\n      (:buffer \"src_emacs{(print 'yeah-boi)}\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-set-property :language \"python\")\n        (org-ml-set-property :parameters '(:cache no))\n        (org-ml-set-property :value \"print \\\"yeah boi\\\"\")\n        (org-ml-to-trimmed-string))\n      => \"src_python[:cache no]{print \\\"yeah boi\\\"}\"\n      :end-hidden\n\n      (:buffer \"- thing\")\n      (org-ml->> (org-ml-parse-this-item)\n        (org-ml-set-property :bullet 1)\n        (org-ml-set-property :checkbox 'on)\n        (org-ml-set-property :counter 2)\n        (org-ml-set-property :tag '(\"tmsu\"))\n        (org-ml-to-trimmed-string))\n      => \"1. [@2] [X] tmsu :: thing\"\n\n      :begin-hidden\n      (:buffer \"#+KEY: VAL\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :key \"kee\")\n        (org-ml-set-property :value \"vahl\")\n        (org-ml-to-trimmed-string))\n      => \"#+kee: vahl\"\n\n      (:buffer \"\\begin{env}\"\n               \"body\"\n               \"\\end{env}\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :value \"\\begin{vne}\\nbody\\end{vne}\")\n        (org-ml-to-trimmed-string))\n      => (:buffer \"\\begin{vne}\"\n                  \"body\"\n                  \"\\end{vne}\")\n\n      (:buffer \"$2+2=4$\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-set-property :value \"$2+2=5$\")\n        (org-ml-to-trimmed-string))\n      => \"$2+2=5$\"\n\n      (:buffer \"https://example.com\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-set-property :path \"/dev/null\")\n        (org-ml-set-property :type \"file\")\n        (org-ml-set-property :format 'bracket)\n        (org-ml-to-trimmed-string))\n      => \"[[file:/dev/null]]\"\n\n      (:buffer \"{{{economics}}}\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-set-property :key \"freakonomics\")\n        (org-ml-set-property :args '(\"x=4\" \"y=2\"))\n        (org-ml-to-trimmed-string))\n      => \"{{{freakonomics(x=4,y=2)}}}\"\n\n      (:buffer \"* dummy\"\n               \":PROPERTIES:\"\n               \":KEY: VAL\"\n               \":END:\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-get-section)\n        (-first-item)\n        (org-ml-get-children)\n        (-first-item)\n        (org-ml-set-property :key \"kee\")\n        (org-ml-set-property :value \"vahl\")\n        (org-ml-to-trimmed-string))\n      => \":kee:      vahl\"\n\n      (:buffer \"* dummy\"\n               \"CLOSED: <2019-01-01 Tue>\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-get-section)\n        (-first-item)\n        (org-ml-set-property\n         :closed (org-ml-build-timestamp! '(2019 1 2) :active nil))\n        (org-ml-to-trimmed-string))\n      => \"CLOSED: [2019-01-02 Wed]\"\n\n      (:buffer \"#+begin_special\"\n               \"#+end_special\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :type \"talent\")\n        (org-ml-to-trimmed-string))\n      => (:result \"#+begin_talent\"\n                  \"#+end_talent\")\n\n      (:buffer \"#+begin_src\"\n               \"something amorphous\"\n               \"#+end_src\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :language \"emacs\")\n        (org-ml-set-property :value \"(print 'hi)\")\n        (org-ml-set-property :parameters '(:cache no))\n        (org-ml-set-property :switches '(\"-n\"))\n        ;; TODO test org-src-preserve-indentation\n        (org-ml-to-trimmed-string))\n      => (:result \"#+begin_src emacs -n :cache no\"\n                  \"  (print 'hi)\"\n                  \"#+end_src\")\n\n      (:buffer \"* dummy [50%]\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-get-statistics-cookie)\n        (org-ml-set-property :value '(0 5))\n        (org-ml-to-trimmed-string))\n      => \"[0/5]\"\n\n      (:buffer \"sub_woofer\")\n      (org-ml->> (org-ml-parse-object-at 5)\n        (org-ml-set-property :use-brackets-p t)\n        (org-ml-to-trimmed-string))\n      => \"_{woofer}\"\n\n      (:buffer \"super^woofer\")\n      (org-ml->> (org-ml-parse-object-at 7)\n        (org-ml-set-property :use-brackets-p t)\n        (org-ml-to-trimmed-string))\n      => \"^{woofer}\"\n\n      (:buffer \"| a |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :tblfm '(\"x=$2\"))\n        (org-ml-to-trimmed-string))\n      => (:result \"| a |\"\n                  \"#+TBLFM: x=$2\")\n\n      (:buffer \"<<found>>\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-set-property :value \"lost\")\n        (org-ml-to-trimmed-string))\n      => \"<<lost>>\"\n\n      (:buffer \"[2019-01-01 Tue]\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-set-property :year-start 2020)\n        (org-ml-set-property :month-start 2)\n        (org-ml-set-property :day-start 2)\n        (org-ml-set-property :hour-start 12)\n        (org-ml-set-property :minute-start 0)\n        (org-ml-set-property :year-end 2020)\n        (org-ml-set-property :month-end 2)\n        (org-ml-set-property :day-end 3)\n        (org-ml-set-property :hour-end 12)\n        (org-ml-set-property :minute-end 0)\n        (org-ml-set-property :type 'active-range)\n        (org-ml-set-property :warning-type 'all)\n        (org-ml-set-property :warning-unit 'day)\n        (org-ml-set-property :warning-value 1)\n        (org-ml-set-property :repeater-type 'cumulate)\n        (org-ml-set-property :repeater-unit 'day)\n        (org-ml-set-property :repeater-value 1)\n        (org-ml-to-trimmed-string))\n      => \"<2020-02-02 Sun 12:00 +1d -1d>--<2020-02-03 Mon 12:00 +1d -1d>\"\n\n      (:buffer \"=I am not a crook=\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-set-property :value \"You totally are\")\n        (org-ml-to-trimmed-string))\n      => \"=You totally are=\"\n\n      (:buffer \"plain\")\n      (org-ml->> (org-ml-set-property :post-blank 1 \"plain\")\n        (org-ml-to-string))\n      => \"plain \"\n\n      (:buffer \"*not plain*\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-set-property :post-blank 1)\n        (org-ml-to-string))\n      => \"*not plain* \"\n\n      ;; affiliated keywords\n\n      (:buffer \"short paragraph\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :name \"foo\")\n        (org-ml-to-trimmed-string))\n      => (:result \"#+name: foo\"\n                  \"short paragraph\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :attr_bar '(\"foo\"))\n        (org-ml-to-trimmed-string))\n      => (:result \"#+attr_bar: foo\"\n                  \"short paragraph\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :header '((:k1 \"h1\") (:k2 \"h2\")))\n        (org-ml-to-trimmed-string))\n      => (:result \"#+header: :k1 h1\"\n                  \"#+header: :k2 h2\"\n                  \"short paragraph\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :results '(\"bar\" \"foo\"))\n        (org-ml-to-trimmed-string))\n      => (:result \"#+results[bar]: foo\"\n                  \"short paragraph\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :caption '(\"cap\"))\n        (org-ml-to-trimmed-string))\n      => (:result \"#+caption: cap\"\n                  \"short paragraph\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :caption '((\"foo\" \"cap\")))\n        (org-ml-to-trimmed-string))\n      => (:result \"#+caption[foo]: cap\"\n                  \"short paragraph\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :caption '((\"FOO\" \"CAP\") (\"foo\" \"cap\")))\n        (org-ml-to-trimmed-string))\n      => (:result \"#+caption[FOO]: CAP\"\n                  \"#+caption[foo]: cap\"\n                  \"short paragraph\")\n      \n      (:buffer \"#+caption: cap\"\n               \"short paragraph\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :caption nil)\n        (org-ml-to-trimmed-string))\n      => \"short paragraph\"\n      (:buffer \"#+name: deleteme\"\n               \"short paragraph\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-property :name nil)\n        (org-ml-to-trimmed-string))\n      => \"short paragraph\"\n\n      :end-hidden\n\n      (:buffer \"* not valuable\")\n      (:comment \"Throw error when setting a property that doesn't exist\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-set-property :value \"wtf\")\n        (org-ml-to-trimmed-string))\n      !!> arg-type-error\n\n      (:comment \"Throw error when setting to an improper type\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-set-property :title 666)\n        (org-ml-to-trimmed-string))\n      !!> arg-type-error)\n\n    (defexamples-content org-ml-get-property\n      nil\n\n      (:buffer \"#+call: ktulu(x=4) :exports results\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :call))\n      => \"ktulu\"\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :inside-header))\n      => nil\n\n      :begin-hidden\n\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :arguments))\n      => '(\"x=4\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :end-header))\n      => '(:exports results)\n\n      (:buffer \"CLOCK: [2019-01-01 Tue]\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :value)\n           (org-ml-to-string))\n      => \"[2019-01-01 Tue]\"\n\n      (:buffer \"~learn to~\")\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :value))\n      => \"learn to\"\n\n      (:buffer \"# not here\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :value))\n      => \"not here\"\n\n      (:buffer \"#+begin_comment\"\n               \"not here\"\n               \"#+end_comment\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :value))\n      => \"not here\"\n\n      (:buffer \"%%(print :hi)\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :value))\n      => '(print :hi)\n\n      (:buffer \":LOGBOOK:\"\n               \":END:\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :drawer-name))\n      => \"LOGBOOK\"\n\n      (:buffer \"#+begin: blockhead :cache no\"\n               \"#+end:\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :block-name))\n      => \"blockhead\"\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :arguments))\n      => '(:cache no)\n\n      (:buffer \"\\\\pi{}\")\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :name))\n      => \"pi\"\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :use-brackets-p))\n      => t\n\n      ;; TODO test org-src-preserve-indentation\n      => (:buffer \"#+begin_example -n\"\n                  \"example.com\"\n                  \"#+end_example\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :switches))\n      => '(\"-n\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :value))\n      => \"example.com\"\n\n      (:buffer \"#+begin_export domestic\"\n               \"bullets, bombs, and bigotry\"\n               \"#+end_export\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :type))\n      ;; TODO why capitalized?\n      => \"DOMESTIC\"\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :value))\n      => \"bullets, bombs, and bigotry\\n\"\n\n      (:buffer \"@@back-end:value@@\")\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :back-end))\n      => \"back-end\"\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :value))\n      => \"value\"\n\n      (:buffer \": fixed\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :value))\n      => \"fixed\"\n\n      (:buffer \"[fn:blacklabel] society\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :label))\n      => \"blacklabel\"\n\n      ;; TODO the priority should be parsable after \"COMMENT\"\n      (:buffer \"** TODO [#A] COMMENT dummy     :tmsu:ARCHIVE:\"\n               \"\"\n               \"stuff\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :archivedp))\n      => t\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :commentedp))\n      => t\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :level))\n      => 2\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :pre-blank))\n      => 1\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :priority))\n      => ?A\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :tags))\n      => '(\"tmsu\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :title))\n      => '(\"dummy\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :todo-keyword))\n      => \"TODO\"\n\n      (:buffer \"* Footnotes\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :footnote-section-p))\n      => t\n\n      (:buffer \"call_ktulu[:cache no](x=4)[:exports results]\")\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :call))\n      => \"ktulu\"\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :inside-header))\n      =>  '(:cache no)\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :arguments))\n      => '(\"x=4\")\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :end-header))\n      => '(:exports results)\n\n      (:buffer \"src_python[:cache no]{print \\\"yeah boi\\\"}\")\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :language))\n      => \"python\"\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :parameters))\n      => '(:cache no)\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :value))\n      => \"print \\\"yeah boi\\\"\"\n\n      (:buffer \"- [@2] [X] tmsu :: thing\")\n      (->> (org-ml-parse-this-item)\n           (org-ml-get-property :bullet))\n      => '-\n      (->> (org-ml-parse-this-item)\n           (org-ml-get-property :checkbox))\n      => 'on\n      (->> (org-ml-parse-this-item)\n           (org-ml-get-property :counter))\n      => 2\n      (->> (org-ml-parse-this-item)\n           (org-ml-get-property :tag))\n      => '(\"tmsu\")\n\n      (:buffer \"#+KEY: VAL\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :key))\n      => \"KEY\"\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :value))\n      => \"VAL\"\n\n      (:buffer \"\\begin{env}\"\n               \"body\"\n               \"\\end{env}\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :value))\n      => (:buffer \"\\begin{env}\"\n                  \"body\"\n                  \"\\end{env}\")\n\n      (:buffer \"$2+2=4$\")\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :value))\n      => \"$2+2=4$\"\n\n      (:buffer \"[[file:/dev/null]]\")\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :path))\n      => \"/dev/null\"\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :type))\n      => \"file\"\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :format))\n      => 'bracket\n      \n      (:buffer \"{{{economics(x=4,y=2)}}}\")\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :key))\n      => \"economics\"\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :args))\n      => '(\"x=4\" \"y=2\")\n\n      (:buffer \"* dummy\"\n               \":PROPERTIES:\"\n               \":KEY: VAL\"\n               \":END:\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-section)\n           (-first-item)\n           (org-ml-get-children)\n           (-first-item)\n           (org-ml-get-property :key))\n      => \"KEY\"\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-section)\n           (-first-item)\n           (org-ml-get-children)\n           (-first-item)\n           (org-ml-get-property :value))\n      => \"VAL\"\n\n      (:buffer \"* dummy\"\n               \"CLOSED: [2019-01-01 Tue]\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-section)\n           (-first-item)\n           (org-ml-get-property :closed)\n           (org-ml-to-string))\n      => \"[2019-01-01 Tue]\"\n\n      (:buffer \"#+BEGIN_special\"\n               \"#+END_special\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :type))\n      => \"special\"\n\n      (:buffer \"#+begin_src emacs -n :cache no\"\n               \"  (print 'hi)\"\n               \"#+end_src\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :language))\n      => \"emacs\"\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :value))\n      ;; TODO why indented?\n      => \"  (print 'hi)\"\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :parameters))\n      => '(:cache no)\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :switches))\n      => '(\"-n\")\n\n      (:buffer \"* dummy [50%]\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-statistics-cookie)\n           (org-ml-get-property :value))\n      => '(50)\n\n      (:buffer \"sub_{woofer}\")\n      (->> (org-ml-parse-object-at 6)\n           (org-ml-get-property :use-brackets-p))\n      => t\n\n      (:buffer \"super_{woofer}\")\n      (->> (org-ml-parse-object-at 8)\n           (org-ml-get-property :use-brackets-p))\n      => t\n\n      (:buffer \"| a |\"\n               \"#+TBLFM: x=$2\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :tblfm))\n      => '(\"x=$2\")\n\n      (:buffer \"<<found>>\")\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :value))\n      => \"found\"\n\n      (:buffer \"<2020-02-02 Sun 12:00 +1d -1d>--<2020-02-03 Mon 12:00 +1d -1d>\")\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :year-start))\n      => 2020\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :month-start))\n      => 2\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :day-start))\n      => 2\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :hour-start))\n      => 12\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :minute-start))\n      => 0\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :year-end))\n      => 2020\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :month-end))\n      => 2\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :day-end))\n      => 3\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :hour-end))\n      => 12\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :minute-end))\n      => 0\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :type))\n      => 'active-range\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :warning-type))\n      => 'all\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :warning-unit))\n      => 'day\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :warning-value))\n      => 1\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :repeater-type))\n      => 'cumulate\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :repeater-unit))\n      => 'day\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :repeater-value))\n      => 1\n\n      (:buffer \"=I am not a crook=\")\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :value))\n      => \"I am not a crook\"\n\n      (:buffer \"*postable* \")\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :post-blank))\n      => 1\n\n      (:buffer \"/*child*/\")\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-children)\n           (car)\n           (org-ml-get-property :parent)\n           (org-ml-to-trimmed-string))\n      => \"/*child*/\"\n\n      (:buffer \"/16-chars-long/\")\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :begin))\n      => 1\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :contents-begin))\n      => 2\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :contents-end))\n      => 15\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-property :end))\n      => 16\n\n      ;; affiliated keywords\n\n      (:buffer \"#+name: name\"\n               \"#+attr_foo: bar\"\n               \"#+attr_foo: BAR\"\n               \"#+plot: poo\"\n               \"#+caption: koo\"\n               \"#+caption[COO]: KOO\"\n               \"#+results[hash]: res\"\n               \"#+header: :k1 h1\"\n               \"#+header: :k2 h2\"\n               \"#+begin_src\"\n               \"echo test for echo\"\n               \"#+end_src\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :name))\n      => \"name\"\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :plot))\n      => \"poo\"\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :attr_foo))\n      => '(\"bar\" \"BAR\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :header))\n      => '((:k1 \"h1\") (:k2 \"h2\"))\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :results))\n      => '(\"hash\" \"res\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-property :caption))\n      => '(\"koo\" (\"COO\" \"KOO\"))\n      \n      :end-hidden\n\n      (:buffer \"* not arguable\")\n      (:comment \"Throw error when requesting a property that doesn't exist\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-get-property :value))\n      !!> arg-type-error)\n\n    (defexamples-content org-ml-map-property\n      nil\n\n      :begin-hidden\n\n      (:buffer \"#+call: ktulu()\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-map-property :call #'s-upcase)\n        (org-ml-to-trimmed-string))\n      => \"#+call: KTULU()\"\n\n      (:buffer \"CLOCK: [2019-01-01 Tue 12:00]\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-map-property* :value (org-ml-timestamp-shift-end 1 'hour it))\n        (org-ml-to-trimmed-string))\n      => \"CLOCK: [2019-01-01 Tue 12:00]--[2019-01-01 Tue 13:00] =>  1:00\"\n\n      :end-hidden\n      \n      (:buffer \"~learn to~\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-map-property :value #'s-upcase)\n        (org-ml-to-trimmed-string))\n      => \"~LEARN TO~\"\n      (:comment \"Throw error if property doesn't exist\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-map-property :title #'s-upcase)\n        (org-ml-to-trimmed-string))\n      !!> arg-type-error\n      (:comment \"Throw error if function doesn't return proper type\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-map-property* :value (if it 1 0))\n        (org-ml-to-trimmed-string))\n      !!> arg-type-error\n\n      :begin-hidden\n\n      (:buffer \"# not here\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-map-property :value #'s-upcase)\n        (org-ml-to-trimmed-string))\n      => \"# NOT HERE\"\n\n      (:buffer \"#+begin_comment\"\n               \"not here\"\n               \"#+end_comment\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-map-property :value #'s-upcase)\n        (org-ml-to-trimmed-string))\n      => (:result \"#+begin_comment\"\n                  \"NOT HERE\"\n                  \"#+end_comment\")\n\n      (:buffer \"%%(diary-float t 1 -1)\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-map-property :value (org-ml--map-last* (+ 2 it) it))\n        (org-ml-to-trimmed-string))\n      => (:buffer \"%%(diary-float t 1 1)\")\n\n      (:buffer \":LOGBOOK:\"\n               \":END:\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-map-property :drawer-name #'s-capitalize)\n        (org-ml-to-trimmed-string))\n      => (:result \":Logbook:\"\n                  \":END:\")\n\n      (:buffer \"#+begin: blockhead\"\n               \"#+end:\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-map-property :block-name #'s-upcase)\n        (org-ml-to-trimmed-string))\n      => (:result \"#+begin: BLOCKHEAD\"\n                  \"#+end:\")\n\n      ;; TODO add entity\n\n      (:buffer \"#+begin_example\"\n               \"example.com\"\n               \"#+end_example\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-map-property* :value (concat \"https://\" it))\n        (org-ml-to-trimmed-string))\n      => (:result \"#+begin_example\"\n                  \"  https://example.com\"\n                  \"#+end_example\")\n\n      (:buffer \"#+begin_export domestic\"\n               \"bullets, bombs, and bigotry\"\n               \"#+end_export\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-map-property :type #'s-upcase)\n        (org-ml-map-property :value #'s-upcase)\n        (org-ml-to-trimmed-string))\n      => (:result \"#+begin_export DOMESTIC\"\n                  \"BULLETS, BOMBS, AND BIGOTRY\"\n                  \"#+end_export\")\n\n      (:buffer \"@@back-end:value@@\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-map-property :back-end #'s-upcase)\n        (org-ml-map-property :value #'s-upcase)\n        (org-ml-to-trimmed-string))\n      => \"@@BACK-END:VALUE@@\"\n\n      (:buffer \": fixed\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-map-property :value #'s-upcase)\n        (org-ml-to-trimmed-string))\n      => \": FIXED\"\n\n      (:buffer \"[fn:blacklabel] society\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-map-property :label #'s-upcase)\n        (org-ml-to-trimmed-string))\n      => \"[fn:BLACKLABEL] society\"\n\n      (:buffer \"* headline\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-map-property* :title (-map #'s-upcase it))\n        (org-ml-to-trimmed-string))\n      => \"* HEADLINE\"\n\n      (:buffer \"call_ktulu()\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-map-property :call #'s-upcase)\n        (org-ml-to-trimmed-string))\n      => \"call_KTULU()\"\n\n      (:buffer \"src_python{print \\\"hi\\\"}\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-map-property* :value (s-replace-regexp \"\\\".*\\\"\" #'s-upcase it))\n        (org-ml-to-trimmed-string))\n      => \"src_python{print \\\"HI\\\"}\"\n\n      (:buffer \"- tag :: thing\")\n      (org-ml->> (org-ml-parse-this-item)\n        (org-ml-map-property :tag (lambda (it) (-map #'s-upcase it)))\n        (org-ml-to-trimmed-string))\n      => \"- TAG :: thing\"\n\n      (:buffer \"#+key: VAL\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-map-property :key (-partial #'s-prepend \"OM_\"))\n        (org-ml-map-property :value (-partial #'s-prepend \"OM_\"))\n        (org-ml-to-trimmed-string))\n      => \"#+om_key: OM_VAL\"\n\n      ;; TODO add examples for latex frag/env\n\n      (:buffer \"[[https://downloadmoreram.org][legit]]\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-map-property* :path (s-replace \".org\" \".com\" it))\n        (org-ml-to-trimmed-string))\n      => \"[[https://downloadmoreram.com][legit]]\"\n\n      (:buffer \"{{{economics}}}\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-map-property :key #'s-upcase)\n        (org-ml-to-trimmed-string))\n      => \"{{{ECONOMICS}}}\"\n\n      (:buffer \"* dummy\"\n               \":PROPERTIES:\"\n               \":KEY: VAL\"\n               \":END:\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-get-section)\n        (-first-item)\n        (org-ml-get-children)\n        (-first-item)\n        (org-ml-map-property :key (-partial #'s-prepend \"OM_\"))\n        (org-ml-map-property :value (-partial #'s-prepend \"OM_\"))\n        (org-ml-to-trimmed-string))\n      => \":OM_KEY:   OM_VAL\"\n\n      ;; TODO add example for planning\n\n      (:buffer \"#+begin_special\"\n               \"#+end_special\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-map-property :type #'s-upcase)\n        (org-ml-to-trimmed-string))\n      => (:result \"#+begin_SPECIAL\"\n                  \"#+end_SPECIAL\")\n\n      ;; TODO add example for src block\n\n      ;; TODO add example for statistics cookie\n\n      (:buffer \"<<found>>\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-map-property :value #'s-upcase)\n        (org-ml-to-trimmed-string))\n      => \"<<FOUND>>\"\n\n      (:buffer \"=I am not a crook=\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-map-property :value #'s-upcase)\n        (org-ml-to-trimmed-string))\n      => \"=I AM NOT A CROOK=\"\n      :end-hidden)\n\n    (defexamples-content org-ml-toggle-property\n      nil\n\n      (:buffer \"\\\\pi\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-toggle-property :use-brackets-p)\n        (org-ml-to-trimmed-string))\n      => \"\\\\pi{}\"\n\n      ;; TODO test src/example block preserve indent\n\n      :begin-hidden\n      \n      (:buffer \"* headline\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-toggle-property :archivedp)\n        (org-ml-to-trimmed-string))\n      => \"* headline          :ARCHIVE:\"\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-toggle-property :commentedp)\n        (org-ml-to-trimmed-string))\n      => \"* COMMENT headline\"\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-toggle-property :footnote-section-p)\n        (org-ml-to-trimmed-string))\n      => \"* Footnotes\"\n\n\n      (:buffer \"sub_woofer\")\n      (org-ml->> (org-ml-parse-object-at 5)\n        (org-ml-toggle-property :use-brackets-p)\n        (org-ml-to-trimmed-string))\n      => \"_{woofer}\"\n\n      (:buffer \"super^woofer\")\n      (org-ml->> (org-ml-parse-object-at 7)\n        (org-ml-toggle-property :use-brackets-p)\n        (org-ml-to-trimmed-string))\n      => \"^{woofer}\"\n\n      :end-hidden\n\n      (:buffer \"- [ ] nope\")\n      (:comment \"Throw an error when trying to toggle a non-boolean property\")\n      (org-ml->> (org-ml-parse-this-item)\n        (org-ml-toggle-property :checkbox)\n        (org-ml-to-trimmed-string))\n      !!> arg-type-error)\n\n    (defexamples-content org-ml-shift-property\n      nil\n\n      (:buffer \"* no priorities\")\n      (:comment \"Do nothing if there is nothing to shift.\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-shift-property :priority 1)\n        (org-ml-to-trimmed-string))\n      => \"* no priorities\"\n\n      (:buffer \"* [#A] priorities\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-shift-property :priority -1)\n        (org-ml-to-trimmed-string))\n      => \"* [#B] priorities\"\n      (:comment \"Wrap priority around when crossing the min or max\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-shift-property :priority 1)\n        (org-ml-to-trimmed-string))\n      => \"* [#C] priorities\"\n\n      :begin-hidden\n\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-shift-property :priority -2)\n        (org-ml-to-trimmed-string))\n      => \"* [#C] priorities\"\n\n      :end-hidden\n\n      (:buffer \"* TODO or not todo\")\n      (:comment \"Throw error when shifting an unshiftable property\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-shift-property :todo-keyword 1)\n        (org-ml-to-string))\n      !!> arg-type-error\n\n      :begin-hidden\n\n      (:buffer \"*bold*\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-shift-property :post-blank 1)\n        (org-ml-to-string))\n      => \"*bold* \"\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-shift-property :post-blank -1)\n        (org-ml-to-string))\n      => \"*bold*\"\n\n      (:buffer \"1. thing\")\n      (org-ml->> (org-ml-parse-this-item)\n        (org-ml-shift-property :counter 1)\n        (org-ml-to-trimmed-string))\n      => \"1. thing\"\n\n      (:buffer \"1. [@1] thing\")\n      (org-ml->> (org-ml-parse-this-item)\n        (org-ml-shift-property :counter 1)\n        (org-ml-to-trimmed-string))\n      => \"1. [@2] thing\"\n      (org-ml->> (org-ml-parse-this-item)\n        (org-ml-shift-property :counter -1)\n        (org-ml-to-trimmed-string))\n      => \"1. [@1] thing\"\n\n      (:buffer \"* noob level\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-shift-property :level 1)\n        (org-ml-to-trimmed-string))\n      => \"** noob level\"\n\n      (:comment \"Do nothing when final value is less than one.\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-shift-property :level -1)\n        (org-ml-to-trimmed-string))\n      => \"* noob level\"\n\n      (:buffer \"* headline\"\n               \"stuff\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-shift-property :pre-blank 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \"\"\n                  \"stuff\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-shift-property :pre-blank -1)\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \"stuff\")\n      :end-hidden)\n\n    (defexamples-content org-ml-insert-into-property\n      nil\n\n      (:buffer \"#+call: ktulu(y=1)\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-insert-into-property :arguments 0 \"x=4\")\n        (org-ml-to-trimmed-string))\n      => \"#+call: ktulu(x=4,y=1)\"\n\n      (:comment \"Do nothing if the string is already in the list\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-insert-into-property :arguments 0 \"y=1\")\n        (org-ml-to-trimmed-string))\n      => \"#+call: ktulu(y=1)\"\n\n      (:comment \"Throw error when inserting into a property that is not a list of strings\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-insert-into-property :end-header 0 \"html\")\n        (org-ml-to-trimmed-string))\n      !!> arg-type-error\n\n      :begin-hidden\n\n      (:buffer \"* headline          :tag1:\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-insert-into-property :tags 0 \"tag0\")\n        (org-ml-to-trimmed-string))\n      => \"* headline          :tag0:tag1:\"\n\n      (:buffer \"#+begin_example -n\"\n               \"#+end_example\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-insert-into-property :switches -1 \"-r\")\n        (org-ml-to-trimmed-string))\n      => (:result \"#+begin_example -n -r\"\n                  \"#+end_example\")\n\n      (:buffer \"call_ktulu(y=1)\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-insert-into-property :arguments 0 \"x=4\")\n        (org-ml-to-trimmed-string))\n      => \"call_ktulu(x=4,y=1)\"\n\n      (:buffer \"{{{economics(x=4)}}}\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-insert-into-property :args 0 \"z=2\")\n        (org-ml-to-trimmed-string))\n      => \"{{{economics(z=2,x=4)}}}\"\n      \n      (:buffer \"#+begin_src emacs-lisp -n\"\n               \"#+end_src\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-insert-into-property :switches -1 \"-r\")\n        (org-ml-to-trimmed-string))\n      => (:result \"#+begin_src emacs-lisp -n -r\"\n                  \"#+end_src\")\n\n      (:buffer \"| a |\"\n               \"#+TBLFM: x=$2\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-insert-into-property :tblfm -1 \"y=$3\")\n        (org-ml-to-trimmed-string))\n      => (:result \"| a |\"\n                  \"#+TBLFM: y=$3\"\n                  \"#+TBLFM: x=$2\")\n      :end-hidden)\n\n    (defexamples-content org-ml-remove-from-property\n      nil\n\n      (:buffer \"#+call: ktulu(y=1)\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-remove-from-property :arguments \"y=1\")\n        (org-ml-to-trimmed-string))\n      => \"#+call: ktulu()\"\n\n      (:comment \"Do nothing if the string does not exist\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-remove-from-property :arguments \"d=666\")\n        (org-ml-to-trimmed-string))\n      => \"#+call: ktulu(y=1)\"\n\n      (:comment \"Throw error when removing from property that is not a string list\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-remove-from-property :end-header \":results\")\n        (org-ml-to-trimmed-string))\n      !!> arg-type-error\n\n      :begin-hidden\n\n      (:buffer \"* headline       :tag1:\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-remove-from-property :tags \"tag1\")\n        (org-ml-to-trimmed-string))\n      => \"* headline\"\n\n      (:buffer \"#+begin_example -n\"\n               \"#+end_example\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-remove-from-property :switches \"-n\")\n        (org-ml-to-trimmed-string))\n      => (:result \"#+begin_example\"\n                  \"#+end_example\")\n\n      (:buffer \"call_ktulu(y=1)\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-remove-from-property :arguments \"y=1\")\n        (org-ml-to-trimmed-string))\n      => \"call_ktulu()\"\n\n      (:buffer \"{{{economics(x=4)}}}\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-remove-from-property :args \"x=4\")\n        (org-ml-to-trimmed-string))\n      => \"{{{economics}}}\"\n      \n      (:buffer \"#+begin_src emacs-lisp -n\"\n               \"#+end_src\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-remove-from-property :switches \"-n\")\n        (org-ml-to-trimmed-string))\n      => (:result \"#+begin_src emacs-lisp\"\n                  \"#+end_src\")\n\n      (:buffer \"| a |\"\n               \"#+TBLFM: x=$2\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-remove-from-property :tblfm \"x=$2\")\n        (org-ml-to-trimmed-string))\n      => \"| a |\"\n      :end-header)\n\n    (defexamples-content org-ml-plist-put-property\n      nil\n\n      (:buffer \"#+call: ktulu[:cache no]()\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-plist-put-property :end-header :results 'html)\n        (org-ml-to-trimmed-string))\n      => \"#+call: ktulu[:cache no]() :results html\"\n      (:comment \"Change the value of key if it already is present\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-plist-put-property :inside-header :cache 'yes)\n        (org-ml-to-trimmed-string))\n      => \"#+call: ktulu[:cache yes]()\"\n      (:comment \"Do nothing if the key and value already exist\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-plist-put-property :inside-header :cache 'no)\n        (org-ml-to-trimmed-string))\n      => \"#+call: ktulu[:cache no]()\"\n      (:comment \"Throw error if setting property that isn't a plist\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-plist-put-property :arguments :cache 'no)\n        (org-ml-to-trimmed-string))\n      !!> arg-type-error\n\n      :begin-hidden\n\n      (:buffer \"#+begin: blockhead :format \\\"[%s]\\\"\"\n               \"#+end:\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-plist-put-property :arguments :format \"<%s>\")\n        (org-ml-to-trimmed-string))\n      => (:result \"#+begin: blockhead :format \\\"<%s>\\\"\"\n                  \"#+end:\")\n\n      (:buffer \"call_ktulu[:cache no]()\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-plist-put-property :inside-header :cache 'yes)\n        (org-ml-plist-put-property :end-header :results 'html)\n        (org-ml-to-trimmed-string))\n      => \"call_ktulu[:cache yes]()[:results html]\"\n\n      (:buffer \"src_emacs-lisp[:exports results]{}\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-plist-put-property :parameters :exports 'both)\n        (org-ml-to-trimmed-string))\n      => \"src_emacs-lisp[:exports both]{}\"\n\n      (:buffer \"#+begin_src emacs-lisp -n :exports results\"\n               \"#+end_src\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-plist-put-property :parameters :exports 'both)\n        (org-ml-to-trimmed-string))\n      => (:result \"#+begin_src emacs-lisp -n :exports both\"\n                  \"#+end_src\")\n      :end-hidden)\n\n    (defexamples-content org-ml-plist-remove-property\n      nil\n\n      (:buffer \"#+call: ktulu() :results html\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-plist-remove-property :end-header :results)\n        (org-ml-to-trimmed-string))\n      => \"#+call: ktulu()\"\n      (:comment \"Do nothing if the key is not present\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-plist-remove-property :inside-header :cache)\n        (org-ml-to-trimmed-string))\n      => \"#+call: ktulu() :results html\"\n      (:comment \"Throw error if trying to remove key from non-plist property\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-plist-remove-property :arguments :cache)\n        (org-ml-to-trimmed-string))\n      !!> arg-type-error\n\n      :begin-hidden\n\n      (:buffer \"#+begin: blockhead :format \\\"[%s]\\\"\"\n               \"#+end:\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-plist-remove-property :arguments :format)\n        (org-ml-to-trimmed-string))\n      => (:result \"#+begin: blockhead\"\n                  \"#+end:\")\n\n      (:buffer \"call_ktulu[:cache no]()[:results html]\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-plist-remove-property :inside-header :cache)\n        (org-ml-plist-remove-property :end-header :results)\n        (org-ml-to-trimmed-string))\n      => \"call_ktulu()\"\n\n      (:buffer \"src_emacs-lisp[:exports results]{}\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-plist-remove-property :parameters :exports)\n        (org-ml-to-trimmed-string))\n      => \"src_emacs-lisp{}\"\n\n      (:buffer \"#+begin_src emacs-lisp -n :exports results\"\n               \"#+end_src\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-plist-remove-property :parameters :exports)\n        (org-ml-to-trimmed-string))\n      => (:result \"#+begin_src emacs-lisp -n\"\n                  \"#+end_src\")\n      :end-hidden)\n\n    ;; (defexamples-content org-ml-property-is-nil-p\n    ;;   nil\n    ;;   (:buffer \"* TODO dummy\")\n    ;;   (->> (org-ml-parse-this-headline)\n    ;;        (org-ml-property-is-nil-p :todo-keyword))\n    ;;   => nil\n    ;;   (->> (org-ml-parse-this-headline)\n    ;;        (org-ml-property-is-nil-p :commentedp))\n    ;;   => t)\n\n    ;; (defexamples-content org-ml-property-is-non-nil-p\n    ;;   nil\n    ;;   (:buffer \"* TODO dummy\")\n    ;;   (->> (org-ml-parse-this-headline)\n    ;;        (org-ml-property-is-non-nil-p :todo-keyword))\n    ;;   => t\n    ;;   (->> (org-ml-parse-this-headline)\n    ;;        (org-ml-property-is-non-nil-p :commentedp))\n    ;;   => nil)\n\n    ;; (defexamples-content org-ml-property-is-eq-p\n    ;;   nil\n    ;;   (:buffer \"* [#A] dummy\")\n    ;;   (->> (org-ml-parse-this-headline)\n    ;;        (org-ml-property-is-eq-p :priority ?A))\n    ;;   => t\n    ;;   (->> (org-ml-parse-this-headline)\n    ;;        (org-ml-property-is-eq-p :priority ?B))\n    ;;   => nil)\n\n    ;; (defexamples-content org-ml-property-is-equal-p\n    ;;   nil\n    ;;   (:buffer \"* TODO dummy\")\n    ;;   (->> (org-ml-parse-this-headline)\n    ;;        (org-ml-property-is-equal-p :todo-keyword \"TODO\"))\n    ;;   => t\n    ;;   (->> (org-ml-parse-this-headline)\n    ;;        (org-ml-property-is-equal-p :todo-keyword \"DONE\"))\n    ;;   => nil)\n\n    ;; (defexamples-content org-ml-property-is-predicate-p\n    ;;   nil\n    ;;   (:buffer \"* this is a dummy\")\n    ;;   (->> (org-ml-parse-this-headline)\n    ;;        (org-ml-property-is-predicate-p*\n    ;;         :title (s-contains? \"dummy\" (car it))))\n    ;;   => t)\n\n    (defexamples-content org-ml-get-properties\n      nil\n\n      (:buffer \"call_ktulu[:cache no](x=4)[:exports results]\")\n      (->> (org-ml-parse-this-object)\n           (org-ml-get-properties '(:call :inside-header :arguments :end-header)))\n      => '(\"ktulu\" (:cache no) (\"x=4\") (:exports results)))\n\n    (defexamples-content org-ml-get-all-properties\n      nil\n\n      (:buffer \"*bold*\")\n      (--> (org-ml-parse-this-object)\n           (org-ml-get-all-properties it)\n           (plist-put it :buffer nil)\n           (plist-put it :parent nil))\n      => (list :begin 1\n               :post-affiliated nil\n               :contents-begin 2\n               :contents-end 6\n               :end 7\n               :post-blank 0\n               :secondary nil\n               :mode nil\n               :granularity nil\n               :cached nil\n               :org-element--cache-sync-key nil\n               :robust-begin nil\n               :robust-end nil\n               :true-level nil\n               :buffer nil\n               :deferred nil\n               :structure nil\n               :parent nil))\n\n    (defexamples-content org-ml-set-properties\n      nil\n      \n      (:buffer \"- thing\")\n      (org-ml->> (org-ml-parse-this-item)\n        (org-ml-set-properties (list :bullet 1\n                                     :checkbox 'on\n                                     :counter 2\n                                     :tag '(\"tmsu\")))\n        (org-ml-to-trimmed-string))\n      => \"1. [@2] [X] tmsu :: thing\"\n\n      (:buffer \"- plain\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-properties (list :name \"plain name\"\n                                     :attr_XXX '(\"tmsu\")))\n        (org-ml-to-trimmed-string))\n      => (:result \"#+name: plain name\"\n                  \"#+attr_xxx: tmsu\"\n                  \"- plain\"))\n\n    (defexamples-content org-ml-map-properties\n      nil\n\n      (:buffer \"#+KEY: VAL\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-map-properties\n         (list :key (-partial #'s-prepend \"OM_\")\n               :value (-partial #'s-prepend \"OM_\")))\n        (org-ml-to-trimmed-string))\n      => \"#+om_key: OM_VAL\"\n      (:comment \"Throw error if any of the properties are invalid\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-map-properties*\n         (:title (s-prepend \"OM_\" it) :value (s-prepend \"OM_\" it)))\n        (org-ml-to-trimmed-string))\n      !!> error\n      )\n\n    (defexamples-content org-ml-get-parents\n      nil\n      (:buffer \"* one\"\n               \"** two\"\n               \"*** three\")\n      (->> (org-ml-parse-this-subtree)\n           (org-ml-get-parents)\n           (--map (org-ml-get-property :begin it)))\n      => '(1)\n      (->> (org-ml-parse-this-subtree)\n           (org-ml-headline-get-subheadlines)\n           (car)\n           (org-ml-headline-get-subheadlines)\n           (car)\n           (org-ml-get-parents)\n           (--map (org-ml-get-property :begin it)))\n      => '(1 7 14))\n\n    (defexamples-content org-ml-remove-parent\n      nil\n      (:buffer \"one\")\n      (:comment \"This is actually a paragraph node, but parsing the object\"\n                \"will directly return a plain-text node with the :parent\"\n                \"pointing to the paragraph\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-remove-parent))\n      => \"one\"\n\n      (defexamples-content org-ml-remove-parents\n        nil\n        (:buffer \"one\")\n        (org-ml->> (org-ml-parse-this-element)\n          (org-ml-remove-parents))\n        => '(paragraph\n             (:begin 1 :end 4 :contents-begin 1 :contents-end 4 :post-blank 0 :post-affiliated 1 :parent nil)\n             \"one\"))\n\n      (:buffer \"* headline\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-remove-parents)\n        (org-ml-get-property :parent))\n      => nil\n\n      (:buffer \"- tag :: thingy\")\n      (org-ml->> (org-ml-parse-this-item)\n        (org-ml-remove-parents)\n        (org-ml-get-children)\n        (car)\n        (org-ml-get-property :parent))\n      nil\n\n      :begin-hidden\n\n      ;; for some reason the timestamps in planning don't have parents\n      (:buffer \"* headline\"\n               \"SCHEDULED: <2021-08-27 Fri>\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-headline-get-planning)\n           (org-ml-get-property :scheduled)\n           ;; (org-ml-remove-parents)\n           (org-ml-get-property :parent)\n           => nil)\n\n      ;; same thing with clocks...\n      (:buffer \"* headline\"\n               \"CLOCK: [2021-08-27 Fri 23:31]--[2021-08-28 Sat 00:45] =>  1:14\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-headline-get-logbook-clocks nil)\n           (car)\n           (org-ml-get-property :value)\n           (org-ml-get-property :parent)\n           => nil)\n\n      :end-hidden\n      ))\n\n  (def-example-subgroup \"Clock\"\n    nil\n\n    (defexamples-content org-ml-clock-is-running\n      nil\n      (:buffer \"CLOCK: [2019-01-01 Tue 00:00]\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-clock-is-running))\n      => t\n      (:buffer \"CLOCK: [2019-01-01 Tue 00:00]--[2019-01-02 Wed 00:00] => 24:00\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-clock-is-running))\n      => nil))\n\n  (def-example-subgroup \"Entity\"\n    nil\n\n    (defexamples-content org-ml-entity-get-replacement\n      nil\n      (:buffer \"\\\\pi{}\")\n      (->> (org-ml-parse-this-object)\n           (org-ml-entity-get-replacement :latex))\n      => \"\\\\pi\"\n      (->> (org-ml-parse-this-object)\n           (org-ml-entity-get-replacement :latex-math-p))\n      => t\n      (->> (org-ml-parse-this-object)\n           (org-ml-entity-get-replacement :html))\n      => \"&pi;\"\n      (->> (org-ml-parse-this-object)\n           (org-ml-entity-get-replacement :ascii))\n      => \"pi\"\n      (->> (org-ml-parse-this-object)\n           (org-ml-entity-get-replacement :latin1))\n      => \"pi\"\n      (->> (org-ml-parse-this-object)\n           (org-ml-entity-get-replacement :utf-8))\n      => \"π\"))\n\n  (def-example-subgroup \"Headline\"\n    nil\n\n    (defexamples-content org-ml-headline-set-title!\n      nil\n      (:buffer \"* really impressive title\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-title! \"really *impressive* title\" '(2 3))\n        (org-ml-to-trimmed-string))\n      => \"* really *impressive* title [2/3]\")\n\n    (defexamples-content org-ml-headline-is-done\n      nil\n      (:buffer \"* TODO darn\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-is-done))\n      => nil\n      (:buffer \"* DONE yay\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-is-done))\n      => t)\n\n    (defexamples-content org-ml-headline-has-tag\n      nil\n      (:buffer \"* dummy\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-has-tag \"tmsu\"))\n      => nil\n      (:buffer \"* dummy                  :tmsu:\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-has-tag \"tmsu\"))\n      => t)\n\n    (defexamples-content org-ml-headline-get-statistics-cookie\n      nil\n      (:buffer \"* statistically significant [10/10]\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-statistics-cookie)\n           (org-ml-to-string))\n      => \"[10/10]\"\n      (:buffer \"* not statistically significant\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-statistics-cookie))\n      => nil)\n\n    ;; TODO add the shortcut version title setter\n\n    )\n\n\n  ;; TODO add inlinetask\n\n  (def-example-subgroup \"Item\"\n    nil\n\n    ;; TODO add shortcut tag setter\n\n    (defexamples-content org-ml-item-toggle-checkbox\n      nil\n      (:buffer \"- [ ] one\")\n      (org-ml->> (org-ml-parse-this-item)\n        (org-ml-item-toggle-checkbox)\n        (org-ml-to-trimmed-string))\n      => \"- [X] one\"\n      (:buffer \"- [-] one\")\n      (:comment \"Ignore trans state checkboxes\")\n      (org-ml->> (org-ml-parse-this-item)\n        (org-ml-item-toggle-checkbox)\n        (org-ml-to-trimmed-string))\n      => \"- [-] one\"\n      (:buffer \"- one\")\n      (:comment \"Do nothing if there is no checkbox\")\n      (org-ml->> (org-ml-parse-this-item)\n        (org-ml-item-toggle-checkbox)\n        (org-ml-to-trimmed-string))\n      => \"- one\"))\n\n  (def-example-subgroup \"Statistics Cookie\"\n    nil\n    (defexamples-content org-ml-statistics-cookie-is-complete\n      nil\n      (:buffer \"* statistically significant [10/10]\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-statistics-cookie)\n           (org-ml-statistics-cookie-is-complete))\n      => t\n      (:buffer \"* statistically significant [1/10]\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-statistics-cookie)\n           (org-ml-statistics-cookie-is-complete))\n      => nil\n      (:buffer \"* statistically significant [100%]\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-statistics-cookie)\n           (org-ml-statistics-cookie-is-complete))\n      => t\n      (:buffer \"* statistically significant [33%]\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-statistics-cookie)\n           (org-ml-statistics-cookie-is-complete))\n      => nil))\n\n  ;; ;; TODO add these\n  ;; (def-example-subgroup \"Timestamp (Auxiliary)\"\n  ;;   \"Functions to work with timestamp data\"\n  \n  ;;   (defexamples-content org-ml-time-is-long\n  ;;     nil)\n\n  ;;   (defexamples-content org-ml-time-to-unixtime\n  ;;     nil)\n\n  ;;   (defexamples-content org-ml-unixtime-to-time-long\n  ;;     nil)\n\n  ;;   (defexamples-content org-ml-unixtime-to-time-short\n  ;;     nil))\n\n  ;; (def-example-subgroup \"Timestamp (Standard)\"\n  ;;   nil\n\n  (defexamples-content org-ml-timestamp-get-start-time\n    nil\n    (:buffer \"[2019-01-01 Tue]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-get-start-time))\n    => '(2019 1 1 nil nil)\n    (:buffer \"[2019-01-01 Tue]--[2019-01-02 Wed]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-get-start-time))\n    => '(2019 1 1 nil nil)\n    (:buffer \"[2019-01-01 Tue 00:00-12:00]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-get-start-time))\n    => '(2019 1 1 0 0))\n\n  (defexamples-content org-ml-timestamp-get-end-time\n    nil\n    (:buffer \"[2019-01-01 Tue]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-get-end-time))\n    => nil\n    (:buffer \"[2019-01-01 Tue]--[2019-01-02 Wed]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-get-end-time))\n    => '(2019 1 2 nil nil)\n    (:buffer \"[2019-01-01 Tue]--[2019-01-01 Tue]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-get-end-time))\n    => '(2019 1 1 nil nil)\n    (:buffer \"[2019-01-01 Tue 00:00-12:00]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-get-end-time))\n    => '(2019 1 1 12 0))\n\n  (defexamples-content org-ml-timestamp-get-range\n    nil\n    (:buffer \"[2019-01-01 Tue]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-get-range))\n    => 0\n    (:buffer \"[2019-01-01 Tue]--[2019-01-02 Wed]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-get-range))\n    => 86400\n    (:buffer \"[2019-01-01 Tue 00:00-12:00]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-get-range))\n    => 43200)\n\n  (defexamples-content org-ml-timestamp-is-active\n    nil\n    (:buffer \"<2019-01-01 Tue>\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-is-active))\n    => t\n    (:buffer \"[2019-01-01 Tue]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-is-active))\n    => nil)\n\n  (defexamples-content org-ml-timestamp-is-ranged\n    nil\n    (:buffer \"[2019-01-01 Tue]--[2019-01-02 Wed]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-is-ranged))\n    => t\n    (:buffer \"[2019-01-01 Tue 00:00-12:00]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-is-ranged))\n    => t\n    (:buffer \"[2019-01-01 Tue]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-is-ranged))\n    => nil)\n\n  (defexamples-content org-ml-timestamp-range-contains-p\n    nil\n    (:buffer \"[2019-01-01 Tue 00:00]\")\n    (let ((ut (org-ml-timelist-to-unixtime '(2019 1 1 0 0))))\n      (->> (org-ml-parse-this-object)\n           (org-ml-timestamp-range-contains-p ut)))\n    => t\n    (let ((ut (org-ml-timelist-to-unixtime '(2019 1 1 0 30))))\n      (->> (org-ml-parse-this-object)\n           (org-ml-timestamp-range-contains-p ut)))\n    => nil\n    (:buffer \"[2019-01-01 Tue 00:00-01:00]\")\n    (let ((ut (org-ml-timelist-to-unixtime '(2019 1 1 0 30))))\n      (->> (org-ml-parse-this-object)\n           (org-ml-timestamp-range-contains-p ut)))\n    => t)\n\n  (defexamples-content org-ml-timestamp-set-collapsed\n    nil\n    (:buffer \"[2019-01-01 Tue 12:00-13:00]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-collapsed nil)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue 12:00]--[2019-01-01 Tue 13:00]\"\n    (:buffer \"[2019-01-01 Tue 12:00-13:00]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-collapsed nil)\n      (org-ml-timestamp-set-collapsed t)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue 12:00-13:00]\"\n    (:buffer \"[2019-01-01 Tue 12:00]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-collapsed nil)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue 12:00]\"\n    (:buffer \"[2019-01-01 Tue]--[2019-01-02 Wed]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-collapsed nil)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue]--[2019-01-02 Wed]\")\n\n  (defexamples-content org-ml-timestamp-get-warning\n    nil\n    (:buffer \"[2019-01-01 Tue 12:00]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-get-warning))\n    => nil\n    (:buffer \"[2019-01-01 Tue 12:00 -1d]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-get-warning))\n    => '(all 1 day))\n\n  (defexamples-content org-ml-timestamp-set-warning\n    nil\n    (:buffer \"[2019-01-01 Tue 12:00]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-warning nil)\n      (org-ml-to-string))\n    => \"[2019-01-01 Tue 12:00]\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-warning '(all 1 day))\n      (org-ml-to-string))\n    => \"[2019-01-01 Tue 12:00 -1d]\"\n    (:buffer \"[2019-01-01 Tue 12:00]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-warning nil)\n      (org-ml-to-string))\n    => \"[2019-01-01 Tue 12:00]\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-warning '(all 1 year))\n      (org-ml-to-string))\n    => \"[2019-01-01 Tue 12:00 -1y]\")\n\n  (defexamples-content org-ml-timestamp-map-warning\n    nil\n    (:buffer \"[2019-01-01 Tue 12:00 -1d]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-map-warning* (-let (((y v u) it)) `(,y ,(1+ v) ,u)))\n      (org-ml-to-string))\n    => \"[2019-01-01 Tue 12:00 -2d]\")\n\n  (defexamples-content org-ml-timestamp-get-repeater\n    nil\n    (:buffer \"[2019-01-01 Tue 12:00]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-get-repeater))\n    => nil\n    (:buffer \"[2019-01-01 Tue 12:00 +1d]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-get-repeater))\n    => '(cumulate 1 day)\n    (:buffer \"[2019-01-01 Tue 12:00 +1d/3d]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-get-repeater))\n    => '(cumulate 1 day))\n\n  (defexamples-content org-ml-timestamp-get-deadline\n    nil\n    (:buffer \"[2019-01-01 Tue 12:00]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-get-deadline))\n    => nil\n    (:buffer \"[2019-01-01 Tue 12:00 +1d]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-get-deadline))\n    => nil\n    (:buffer \"[2019-01-01 Tue 12:00 +1d/3d]\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-get-deadline))\n    => '(3 day))\n\n  (defexamples-content org-ml-timestamp-set-repeater\n    nil\n    (:buffer \"[2019-01-01 Tue 12:00]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-repeater nil)\n      (org-ml-to-string))\n    => \"[2019-01-01 Tue 12:00]\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-repeater '(restart 1 day))\n      (org-ml-to-string))\n    => \"[2019-01-01 Tue 12:00 .+1d]\"\n    :begin-hidden\n    (:buffer \"[2019-01-01 Tue 12:00 .+1d]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-repeater nil)\n      (org-ml-to-string))\n    => \"[2019-01-01 Tue 12:00]\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-repeater '(cumulate 1 day))\n      (org-ml-to-string))\n    => \"[2019-01-01 Tue 12:00 +1d]\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-repeater '(cumulate 1 day))\n      (org-ml-to-string))\n    => \"[2019-01-01 Tue 12:00 +1d]\"\n    (:buffer \"[2019-01-01 Tue 12:00 .+1d/3d]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-repeater nil)\n      (org-ml-to-string))\n    => \"[2019-01-01 Tue 12:00]\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-repeater '(cumulate 1 day))\n      (org-ml-to-string))\n    => \"[2019-01-01 Tue 12:00 +1d/3d]\"\n    :end-hidden)\n\n  (defexamples-content org-ml-timestamp-set-deadline\n    nil\n    (:buffer \"[2019-01-01 Tue 12:00]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-deadline nil)\n      (org-ml-to-string))\n    => \"[2019-01-01 Tue 12:00]\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-deadline '(3 day))\n      (org-ml-to-string))\n    => \"[2019-01-01 Tue 12:00]\"\n    (:buffer \"[2019-01-01 Tue 12:00 .+1d]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-deadline nil)\n      (org-ml-to-string))\n    => \"[2019-01-01 Tue 12:00 .+1d]\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-deadline '(3 day))\n      (org-ml-to-string))\n    => \"[2019-01-01 Tue 12:00 .+1d/3d]\"\n    :begin-hidden\n    (:buffer \"[2019-01-01 Tue 12:00 .+1d/3d]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-deadline nil)\n      (org-ml-to-string))\n    => \"[2019-01-01 Tue 12:00 .+1d]\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-deadline '(5 day))\n      (org-ml-to-string))\n    => \"[2019-01-01 Tue 12:00 .+1d/5d]\"\n    :end-hidden)\n\n  (defexamples-content org-ml-timestamp-map-repeater\n    nil\n    (:buffer \"[2019-01-01 Tue 12:00 +1d]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-map-repeater* (-let (((y v u) it)) `(,y ,(1+ v) ,u)))\n      (org-ml-to-string))\n    => \"[2019-01-01 Tue 12:00 +2d]\")\n\n  (defexamples-content org-ml-timestamp-set-start-time\n    nil\n    (:buffer \"[2019-01-02 Wed]\")\n    (:comment \"If not a range this will turn into a range by moving only the start time.\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-start-time '(2019 1 1))\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue]--[2019-01-02 Wed]\"\n    (:comment \"Set a different time with different precision.\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-start-time '(2019 1 1 10 0))\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue 10:00]--[2019-01-02 Wed]\"\n    (:buffer \"[2019-01-02 Wed 12:00]\")\n    (:comment \"If not a range and set within a day, use short format\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-start-time '(2019 1 2 0 0))\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-02 Wed 00:00-12:00]\"\n    :begin-hidden\n    (:buffer \"[2019-01-02 Wed 12:00 +1d]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-start-time '(2019 1 2 0 0))\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-02 Wed 00:00-12:00 +1d]\"\n    (:buffer \"[2019-01-02 Wed 12:00 +1d/3d]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-start-time '(2019 1 2 0 0))\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-02 Wed 00:00-12:00 +1d/3d]\"\n    :end-hidden)\n\n  (defexamples-content org-ml-timestamp-set-end-time\n    nil\n    (:buffer \"[2019-01-01 Tue]\")\n    (:comment \"Add the end time\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-end-time '(2019 1 2))\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue]--[2019-01-02 Wed]\"\n    (:buffer \"[2019-01-01 Tue]--[2019-01-02 Wed]\")\n    (:comment \"Remove the end time\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-end-time nil)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue]\"\n    (:buffer \"[2019-01-01 Tue 12:00]\")\n    (:comment \"Use short range format\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-end-time '(2019 1 1 13 0))\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue 12:00-13:00]\")\n\n  (defexamples-content org-ml-timestamp-set-single-time\n    nil\n    (:buffer \"[2019-01-01 Tue]\")\n    (:comment \"Don't make a range\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-single-time '(2019 1 2))\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-02 Wed]\"\n    (:buffer \"[2019-01-01 Tue]--[2019-01-02 Wed]\")\n    (:comment \"Output is not a range despite input being ranged\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-single-time '(2019 1 3))\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-03 Thu]\")\n\n  (defexamples-content org-ml-timestamp-set-double-time\n    nil\n    (:buffer \"[2019-01-01 Tue]\")\n    (:comment \"Make a range\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-double-time '(2019 1 2) '(2019 1 3))\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-02 Wed]--[2019-01-03 Thu]\"\n    (:buffer \"[2019-01-01 Tue]--[2019-01-03 Wed]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-double-time '(2019 1 4) '(2019 1 5))\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-04 Fri]--[2019-01-05 Sat]\"\n    (:buffer \"[2019-01-01 Tue]--[2019-01-03 Wed]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-double-time '(2019 1 1 0 0) '(2019 1 1 1 0))\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue 00:00]--[2019-01-01 Tue 01:00]\")\n\n  (defexamples-content org-ml-timestamp-set-length\n    nil\n    (:buffer \"[2019-01-01 Tue]\")\n    (:comment \"Use days as the unit for short format\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-length 1 'day)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue]--[2019-01-02 Wed]\"\n    (:buffer \"[2019-01-01 Tue 00:00]\")\n    (:comment \"Use minutes as the unit for long format\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-length 3 'minute)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue 00:00-00:03]\"\n    (:buffer \"[2019-01-01 Tue]--[2019-01-03 Wed]\")\n    (:comment \"Set range to 0 to remove end time\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-length 0 'day)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue]\")\n\n  (defexamples-content org-ml-timestamp-set-active\n    nil\n    (:buffer \"[2019-01-01 Tue]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-active t)\n      (org-ml-to-trimmed-string))\n    => \"<2019-01-01 Tue>\"\n    (:buffer \"<2019-01-01 Tue>\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-set-active nil)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue]\")\n\n  (defexamples-content org-ml-timestamp-shift\n    nil\n    (:buffer \"[2019-01-01 Tue 12:00]\")\n    (:comment \"Change each unit, and wrap around to the next unit as needed.\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-shift 30 'minute)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue 12:30]\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-shift 13 'month)\n      (org-ml-to-trimmed-string))\n    => \"[2020-02-01 Sat 12:00]\"\n    :begin-hidden\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-shift 60 'minute)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue 13:00]\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-shift 1 'hour)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue 13:00]\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-shift 1 'day)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-02 Wed 12:00]\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-shift 31 'day)\n      (org-ml-to-trimmed-string))\n    => \"[2019-02-01 Fri 12:00]\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-shift 1 'month)\n      (org-ml-to-trimmed-string))\n    => \"[2019-02-01 Fri 12:00]\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-shift 1 'year)\n      (org-ml-to-trimmed-string))\n    => \"[2020-01-01 Wed 12:00]\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-shift 0 'year)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue 12:00]\"\n    :end-hidden\n    (:buffer \"[2019-01-01 Tue]\")\n    (:comment \"Error when shifting hour/minute in short format\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-shift 30 'minute)\n      (org-ml-to-trimmed-string))\n    !!> arg-type-error\n    :begin-hidden\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-shift 30 'hour)\n      (org-ml-to-trimmed-string))\n    !!> arg-type-error\n    :end-hidden)\n\n  (defexamples-content org-ml-timestamp-shift-start\n    nil\n    (:buffer \"[2019-01-01 Tue 12:00]\")\n    (:comment \"If not a range, change start time and leave implicit end time.\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-shift-start -1 'year)\n      (org-ml-to-trimmed-string))\n    => \"[2018-01-01 Mon 12:00]--[2019-01-01 Tue 12:00]\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-shift-start -1 'hour)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue 11:00-12:00]\"\n    (:buffer \"[2019-01-01 Tue]--[2019-01-03 Thu]\")\n    (:comment \"Change only start time if a range\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-shift-start 1 'day)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-02 Wed]--[2019-01-03 Thu]\")\n\n  (defexamples-content org-ml-timestamp-shift-end\n    nil\n    (:buffer \"[2019-01-01 Tue]\")\n    (:comment \"Shift implicit end time if not a range.\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-shift-end 1 'day)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue]--[2019-01-02 Wed]\"\n    (:buffer \"[2019-01-01 Tue]--[2019-01-02 Wed]\")\n    (:comment \"Move only the second time if a range.\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-shift-end 1 'day)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue]--[2019-01-03 Thu]\")\n\n  (defexamples-content org-ml-timestamp-toggle-active\n    nil\n    (:buffer \"[2019-01-01 Tue]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-toggle-active)\n      (org-ml-to-trimmed-string))\n    => \"<2019-01-01 Tue>\"\n    :begin-hidden\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-toggle-active)\n      (org-ml-timestamp-toggle-active)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue]\"\n    :end-hidden\n    (:buffer \"<2019-01-01 Tue>--<2019-01-02 Wed>\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-toggle-active)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue]--[2019-01-02 Wed]\"\n    :begin-hidden\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-toggle-active)\n      (org-ml-timestamp-toggle-active)\n      (org-ml-to-trimmed-string))\n    => \"<2019-01-01 Tue>--<2019-01-02 Wed>\"\n    :end-hidden)\n\n  (defexamples-content org-ml-timestamp-truncate\n    nil\n    (:buffer \"[2019-01-01 Tue]--[2019-01-02 Wed]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-truncate)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue]--[2019-01-02 Wed]\"\n    (:buffer \"[2019-01-01 Tue 12:00]--[2019-01-02 Wed 13:00]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-truncate)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue]--[2019-01-02 Wed]\")\n\n  (defexamples-content org-ml-timestamp-truncate-start\n    nil\n    (:buffer \"[2019-01-01 Tue 12:00]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-truncate-start)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue]\"\n    (:buffer \"[2019-01-01 Tue 12:00]--[2019-01-02 Wed 12:00]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-truncate-start)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue]--[2019-01-02 Wed 12:00]\"\n    (:buffer \"[2019-01-01 Tue]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-truncate-start)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue]\")\n\n  (defexamples-content org-ml-timestamp-truncate-end\n    nil\n    (:buffer \"[2019-01-01 Tue]--[2019-01-02 Wed]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-truncate-end)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue]--[2019-01-02 Wed]\"\n    (:buffer \"[2019-01-01 Tue 12:00]--[2019-01-02 Wed 13:00]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-truncate-end)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue 12:00]--[2019-01-02 Wed]\"\n    (:buffer \"[2019-01-01 Tue 12:00]\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-truncate-end)\n      (org-ml-to-trimmed-string))\n    => \"[2019-01-01 Tue 12:00]\"))\n\n(def-example-subgroup \"Timestamp (diary)\"\n  nil\n\n  (defexamples-content org-ml-timestamp-diary-set-value\n    nil\n    (:buffer \"<%%(diary-float t 4 2)>\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-set-value '(diary-float 1 3 2))\n      (org-ml-to-string))\n    => \"<%%(diary-float 1 3 2)>\")\n\n  (defexamples-content org-ml-timestamp-diary-set-single-time\n    nil\n    (:buffer \"<%%(diary-float t 4 2)>\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-set-single-time '(0 0))\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2) 00:00>\"\n    (:buffer \"<%%(diary-float t 4 2) 00:01>\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-set-single-time nil)\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2)>\")\n\n  (defexamples-content org-ml-timestamp-diary-set-double-time\n    nil\n    (:buffer \"<%%(diary-float t 4 2)>\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-set-double-time '(0 0) '(0 1))\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2) 00:00-00:01>\"\n    (:buffer \"<%%(diary-float t 4 2) 00:00-00:01>\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-set-double-time '(1 0) '(2 0))\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2) 01:00-02:00>\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-set-double-time '(1 0) nil)\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2) 01:00>\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-set-double-time nil nil)\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2)>\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-set-double-time nil '(2 0))\n      (org-ml-to-string))\n    !!> arg-type-error)\n\n  (defexamples-content org-ml-timestamp-diary-get-start-time\n    nil\n    (:buffer \"<%%(diary-float t 4 2)>\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-diary-get-start-time))\n    => nil\n    (:buffer \"<%%(diary-float t 4 2) 12:00-13:00>\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-diary-get-start-time))\n    => '(12 0))\n\n  (defexamples-content org-ml-timestamp-diary-set-start-time\n    nil\n    (:buffer \"<%%(diary-float t 4 2)>\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-set-start-time '(0 0))\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2) 00:00>\"\n    (:buffer \"<%%(diary-float t 4 2) 12:00>\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-set-start-time '(1 0))\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2) 01:00-12:00>\"\n    :begin-hidden\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-set-start-time '(0 0))\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2) 00:00-12:00>\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-set-start-time '(12 0))\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2) 12:00>\"\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-set-start-time nil)\n      (org-ml-to-string))\n    !!> arg-type-error\n    :end-hidden)\n\n  (defexamples-content org-ml-timestamp-diary-get-end-time\n    nil\n    (:buffer \"<%%(diary-float t 4 2)>\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-diary-get-end-time))\n    => nil\n    (:buffer \"<%%(diary-float t 4 2) 12:00-13:00>\")\n    (->> (org-ml-parse-this-object)\n         (org-ml-timestamp-diary-get-end-time))\n    => '(13 0))\n\n  (defexamples-content org-ml-timestamp-diary-set-end-time\n    nil\n    (:buffer \"<%%(diary-float t 4 2)>\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-set-end-time '(0 0))\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2)>\"\n    (:buffer \"<%%(diary-float t 4 2) 12:00>\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-set-end-time '(13 0))\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2) 12:00-13:00>\"\n    :begin-hidden\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-set-end-time nil)\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2) 12:00>\"\n    :end-hidden)\n\n  (defexamples-content org-ml-timestamp-diary-set-length\n    nil\n    (:buffer \"<%%(diary-float t 4 2)>\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-set-length 1 'hour)\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2)>\"\n    (:buffer \"<%%(diary-float t 4 2) 12:00>\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-set-length 1 'hour)\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2) 12:00-13:00>\"\n    (:buffer \"<%%(diary-float t 4 2) 12:00-13:00>\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-set-length 0 'hour)\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2) 12:00>\"\n    :begin-hidden\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-set-length 24 'hour)\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2) 12:00>\"\n    :end-hidden)\n\n  (defexamples-content org-ml-timestamp-diary-shift\n    nil\n    (:buffer \"<%%(diary-float t 4 2)>\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-shift 1 'hour)\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2)>\"\n    (:buffer \"<%%(diary-float t 4 2) 12:00>\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-shift 1 'hour)\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2) 13:00>\"\n    :begin-hidden\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-shift 24 'hour)\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2) 12:00>\"\n    :end-hidden)\n\n  (defexamples-content org-ml-timestamp-diary-shift-start\n    nil\n    (:buffer \"<%%(diary-float t 4 2)>\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-shift-start 1 'hour)\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2)>\"\n    (:buffer \"<%%(diary-float t 4 2) 12:00>\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-shift-start -1 'hour)\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2) 11:00-12:00>\"\n    :begin-hidden\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-shift-start 24 'hour)\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2) 12:00>\"\n    :end-hidden)\n\n  (defexamples-content org-ml-timestamp-diary-shift-end\n    nil\n    (:buffer \"<%%(diary-float t 4 2)>\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-shift-end 1 'hour)\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2)>\"\n    (:buffer \"<%%(diary-float t 4 2) 12:00>\")\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-shift-end 1 'hour)\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2) 12:00-13:00>\"\n    :begin-hidden\n    (org-ml->> (org-ml-parse-this-object)\n      (org-ml-timestamp-diary-shift-end 24 'hour)\n      (org-ml-to-string))\n    => \"<%%(diary-float t 4 2) 12:00>\"\n    :end-hidden\n    ))\n\n(def-example-group \"Branch/Child Manipulation\"\n  \"Set, get, and map the children of branch nodes.\"\n\n  (def-example-subgroup \"Polymorphic\"\n    nil\n\n    (defexamples-content org-ml-children-contain-point\n      nil\n      (:buffer \"* headline\"\n               \"findme\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-children-contain-point 2))\n      => nil\n      (->> (org-ml-parse-this-headline)\n           (org-ml-children-contain-point 15))\n      => t)\n\n    (defexamples-content org-ml-get-children\n      nil\n\n      (:buffer \"/this/ is a *paragraph*\")\n      (:comment \"Return child nodes for branch nodes\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-children)\n           (-map #'org-ml-get-type))\n      => '(italic plain-text bold)\n\n      (:buffer \"* headline\")\n      (:comment \"Return nil if no children\")\n      (->> (org-ml-parse-this-subtree)\n           (org-ml-get-children)\n           (-map #'org-ml-get-type))\n      => nil\n\n      ;; (:buffer \"#+call: ktulu()\")\n      ;; (:comment \"Throw error when attempting to get contents of a non-branch node\")\n      ;; (->> (org-ml-parse-this-element)\n      ;;      (org-ml-get-children)\n      ;;      (-map #'org-ml-get-type))\n      ;; !!> arg-type-error\n\n      :begin-hidden\n\n      (:buffer \"* headline\"\n               \"stuff\"\n               \"** subheadline\")\n      (:comment \"Return child element nodes\")\n      (->> (org-ml-parse-this-subtree)\n           (org-ml-get-children)\n           (-map #'org-ml-get-type))\n      => '(section headline)\n\n      (:buffer \"| a | b |\")\n      (->> (org-ml-parse-this-table-row)\n           (org-ml-get-children)\n           (-map #'org-ml-get-type))\n      => '(table-cell table-cell)\n\n      (:buffer \"#+begin_verse\"\n               \"verse /666/\"\n               \"#+end_verse\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-children)\n           (-map #'org-ml-get-type))\n      ;; plain-text for the newline at the end...I think\n      => '(plain-text italic plain-text)\n\n      (:buffer \"#+begin_center\"\n               \"paragraph thing\"\n               \"#+end_center\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-children)\n           (-map #'org-ml-get-type))\n      => '(paragraph)\n\n      (:buffer \":LOGBOOK:\"\n               \"- log entry\"\n               \"CLOCK: [2019-01-01 Tue]\"\n               \":END:\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-children)\n           (-map #'org-ml-get-type))\n      => '(plain-list clock)\n\n      (:buffer \"[fn:1] bigfoot\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-children)\n           (-map #'org-ml-get-type))\n      => '(paragraph)\n\n      (:buffer \"- item\"\n               \"  - subitem\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-children)\n           (-map #'org-ml-get-type))\n      => '(item)\n      (->> (org-ml-parse-this-item)\n           (org-ml-get-children)\n           (-map #'org-ml-get-type))\n      => '(paragraph plain-list)\n\n      (:buffer \"* dummy\"\n               \":PROPERTIES:\"\n               \":ONE: one\"\n               \":TWO: two\"\n               \":END:\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-section)\n           (-first-item)\n           (org-ml-get-children)\n           (-map #'org-ml-get-type))\n      => '(node-property node-property)\n\n      (:buffer \"#+begin_quote\"\n               \"no pity for the majority\"\n               \"#+end_quote\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-children)\n           (-map #'org-ml-get-type))\n      => '(paragraph)\n\n      ;; (:buffer \"* dummy\"\n      ;;           \"stuff\")\n      ;; (->> (org-ml-parse-this-headline)\n      ;;      (org-ml-headline-get-section)\n      ;;      (org-ml-get-children)\n      ;;      (-map #'org-ml-get-type))\n      ;; => '(paragraph)\n\n      (:buffer \"| a |\"\n               \"| b |\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-get-children)\n           (-map #'org-ml-get-type))\n      => '(table-row table-row)\n\n      :end-hidden)\n\n    (defexamples-content org-ml-set-children\n      nil\n\n      (:buffer \"/this/ is a *paragraph*\")\n      (:comment \"Set children for branch object\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-set-children (list \"this is lame\"))\n        (org-ml-to-trimmed-string))\n      => \"this is lame\"\n\n      (:buffer \"* headline\")\n      (:comment \"Set children for branch element nodes\")\n      (org-ml->> (org-ml-parse-this-subtree)\n        (org-ml-set-children (list (org-ml-build-headline! :title-text \"only me\" :level 2)))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \"** only me\")\n\n      ;; (:buffer \"#+call: ktulu()\")\n      ;; (:comment \"Throw error when attempting to set children of a non-branch nodes\")\n      ;; (->> (org-ml-parse-this-element)\n      ;;      (org-ml-set-children \"nil by mouth\")\n      ;;      (org-ml-to-trimmed-string))\n      ;; !!> arg-type-error\n\n      :begin-hidden\n\n      ;; TODO add hidden tests\n\n      :end-hidden)\n\n    (defexamples-content org-ml-map-children\n      nil\n\n      (:buffer \"/this/ is a *paragraph*\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-map-children\n          (lambda (objs) (append objs (list \" ...yeah\"))))\n        (org-ml-to-trimmed-string))\n      => \"/this/ is a *paragraph* ...yeah\"\n\n      (:buffer \"* headline\"\n               \"** subheadline\")\n      (org-ml->> (org-ml-parse-this-subtree)\n        (org-ml-map-children* (--map (org-ml-shift-property :level 1 it) it))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \"*** subheadline\")\n\n      ;; (:buffer \"#+call: ktulu()\")\n      ;; (:comment \"Throw error when attempting to map children of a non-branch node\")\n      ;; (->> (org-ml-parse-this-element)\n      ;;      (org-ml-map-children #'ignore)\n      ;;      (org-ml-to-trimmed-string))\n      ;; !!> arg-type-error\n\n      :begin-hidden\n\n      ;; TODO add hidden tests\n\n      :end-hidden)\n\n    \n    (defexamples-content org-ml-is-childless\n      nil\n      (:buffer \"* dummy\"\n               \"filled with useless knowledge\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-is-childless))\n      => nil\n      (:buffer \"* dummy\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-is-childless))\n      => t\n      ;; (:buffer \"#+call: ktulu()\")\n      ;; (:comment \"Throw error when attempting to determine if non-branch node is empty\")\n      ;; (->> (org-ml-parse-this-element)\n      ;;      (org-ml-is-childless))\n      ;; !!> arg-type-error\n      ))\n\n  (def-example-subgroup \"Object Nodes\"\n    nil\n\n    (defexamples-content org-ml-unwrap\n      nil\n      (:buffer \"_1 *2* 3 */4/* 5 /6/_\")\n      (:comment \"Remove the outer underline formatting\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-unwrap)\n        (apply #'org-ml-build-paragraph)\n        (org-ml-to-trimmed-string))\n      => \"1 *2* 3 */4/* 5 /6/\")\n    \n    (defexamples-content org-ml-unwrap-types-deep\n      nil\n      (:buffer \"_1 *2* 3 */4/* 5 /6/_\")\n      (:comment \"Remove bold formatting at any level\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-unwrap-types-deep '(bold))\n        (apply #'org-ml-build-paragraph)\n        (org-ml-to-trimmed-string))\n      => \"_1 2 3 /4/ 5 /6/_\")\n\n    (defexamples-content org-ml-unwrap-deep\n      nil\n      (:buffer \"_1 *2* 3 */4/* 5 /6/_\")\n      (:comment \"Remove all formatting\")\n      (org-ml->> (org-ml-parse-this-object)\n        (org-ml-unwrap-deep)\n        (apply #'org-ml-build-paragraph)\n        (org-ml-to-trimmed-string))\n      => \"1 2 3 4 5 6\"))\n\n  (def-example-subgroup \"Secondary Strings\"\n    nil\n\n    (defexamples-content org-ml-flatten\n      nil\n      (:buffer \"This (1 *2* 3 */4/* 5 /6/) is randomly formatted\")\n      (:comment \"Remove first level of formatting\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-map-children #'org-ml-flatten)\n        (org-ml-to-trimmed-string))\n      => \"This (1 2 3 /4/ 5 6) is randomly formatted\")\n\n    (defexamples-content org-ml-flatten-types-deep\n      nil\n      (:buffer \"This (1 *2* 3 */4/* 5 /6/) is randomly formatted\")\n      (:comment \"Remove italic formatting at any level\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-map-children* (org-ml-flatten-types-deep '(italic) it))\n        (org-ml-to-trimmed-string))\n      => \"This (1 *2* 3 *4* 5 6) is randomly formatted\")\n\n    (defexamples-content org-ml-flatten-deep\n      nil\n      (:buffer \"This (1 *2* 3 */4/* 5 /6/) is randomly formatted\")\n      (:comment \"Remove italic formatting at any level\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-map-children #'org-ml-flatten-deep)\n        (org-ml-to-trimmed-string))\n      => \"This (1 2 3 4 5 6) is randomly formatted\"))\n\n  (def-example-subgroup \"Item\"\n    nil\n\n    (defexamples-content org-ml-item-get-paragraph\n      nil\n      (:buffer \"- one\")\n      (->> (org-ml-parse-this-item)\n           (org-ml-item-get-paragraph))\n      => '(\"one\")\n      (:buffer \"- \")\n      (->> (org-ml-parse-this-item)\n           (org-ml-item-get-paragraph))\n      => nil)\n\n    (defexamples-content org-ml-item-set-paragraph\n      nil\n      (:buffer \"- one\")\n      (org-ml->> (org-ml-parse-this-item)\n        (org-ml-item-set-paragraph '(\"two\"))\n        (org-ml-to-string))\n      => \"- two\\n\"\n      (:buffer \"- one\")\n      (org-ml->> (org-ml-parse-this-item)\n        (org-ml-item-set-paragraph nil)\n        (org-ml-to-string))\n      => \"- \\n\")\n\n    (defexamples-content org-ml-item-map-paragraph\n      nil\n      (:buffer \"- one\")\n      (org-ml->> (org-ml-parse-this-item)\n        (org-ml-item-map-paragraph* (-map #'upcase it))\n        (org-ml-to-string))\n      => \"- ONE\\n\"))\n\n  (def-example-subgroup \"Headline\"\n    nil\n\n    (defexamples-content org-ml-headline-get-section\n      nil\n      (:buffer \"* headline 1\"\n               \"sectional stuff\"\n               \"** headline 2\"\n               \"** headline 3\")\n      (->> (org-ml-parse-this-subtree)\n           (org-ml-headline-get-section)\n           (-map #'org-ml-to-trimmed-string))\n      => '(\"sectional stuff\")\n      (:buffer \"* headline 1\"\n               \"** headline 2\"\n               \"** headline 3\")\n      (->> (org-ml-parse-this-subtree)\n           (org-ml-headline-get-section)\n           (org-ml-to-trimmed-string))\n      => \"\")\n\n    (defexamples-content org-ml-headline-set-section\n      nil\n      (:buffer \"* headline\")\n      (org-ml->> (org-ml-parse-this-subtree)\n        (org-ml-headline-set-section (list (org-ml-build-paragraph! \"x-section\")))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \"x-section\")\n      (:buffer \"* headline\"\n               \"x-section\")\n      (org-ml->> (org-ml-parse-this-subtree)\n        (org-ml-headline-set-section (list (org-ml-build-paragraph! \"x-guard\")))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \"x-guard\")\n      (org-ml->> (org-ml-parse-this-subtree)\n        (org-ml-headline-set-section nil)\n        (org-ml-to-trimmed-string))\n      => \"* headline\")\n\n    (defexamples-content org-ml-headline-map-section\n      nil\n      (:buffer \"* headline\"\n               \"x-section\")\n      (org-ml->> (org-ml-parse-this-subtree)\n        (org-ml-headline-map-section* (cons (org-ml-build-planning! :closed '(2019 1 1)) it))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \"CLOSED: [2019-01-01 Tue]\"\n                  \"x-section\"))\n\n    (defexamples-content org-ml-headline-get-subheadlines\n      nil\n      (:buffer \"* headline 1\"\n               \"sectional stuff\"\n               \"** headline 2\"\n               \"** headline 3\")\n      (org-ml->> (org-ml-parse-this-subtree)\n        (org-ml-headline-get-subheadlines)\n        (-map #'org-ml-to-trimmed-string))\n      => '(\"** headline 2\" \"** headline 3\")\n      (:buffer \"* headline 1\"\n               \"sectional stuff\")\n      (org-ml->> (org-ml-parse-this-subtree)\n        (org-ml-headline-get-subheadlines)\n        (-map #'org-ml-to-trimmed-string))\n      => nil)\n\n    (defexamples-content org-ml-headline-set-subheadlines\n      nil\n      (:buffer \"* headline 1\"\n               \"sectional stuff\"\n               \"** headline 2\"\n               \"** headline 3\")\n      (org-ml->> (org-ml-parse-this-subtree)\n        (org-ml-headline-set-subheadlines (list (org-ml-build-headline! :level 2 :title-text \"headline x\")))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline 1\"\n                  \"sectional stuff\"\n                  \"** headline x\")\n      (org-ml->> (org-ml-parse-this-subtree)\n        (org-ml-headline-set-subheadlines nil)\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline 1\"\n                  \"sectional stuff\"))\n\n    (defexamples-content org-ml-headline-map-subheadlines\n      nil\n      (:buffer \"* headline 1\"\n               \"** headline 2\"\n               \"** headline 3\")\n      (org-ml->> (org-ml-parse-this-subtree)\n        (org-ml-headline-map-subheadlines* (--map (org-ml-set-property :todo-keyword \"TODO\" it) it))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline 1\"\n                  \"** TODO headline 2\"\n                  \"** TODO headline 3\")))\n\n  (def-example-subgroup \"Headline (metadata)\"\n    nil\n\n    (defexamples-content org-ml-headline-get-planning\n      nil\n      (:buffer \"* headline\"\n               \"CLOSED: [2019-01-01 Tue]\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-planning))\n      => '(:closed (2019 1 1 nil nil) :scheduled nil :deadline nil)\n      (:buffer \"* headline\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-planning))\n      => nil)\n\n    (defexamples-content org-ml-headline-set-planning\n      nil\n      (:buffer \"* headline\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-planning '(:closed (2019 1 1)))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \"CLOSED: [2019-01-01 Tue]\")\n      (:buffer \"* headline\"\n               \"CLOSED: [2019-01-01 Tue]\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-planning '(:scheduled (2019 1 1)))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \"SCHEDULED: <2019-01-01 Tue>\")\n      (:buffer \"* headline\"\n               \"CLOSED: [2019-01-01 Tue]\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-planning nil)\n        (org-ml-to-trimmed-string))\n      => \"* headline\"\n      :begin-hidden\n      (:buffer \"* headline\"\n               \"\"\n               \"rest\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-planning '(:scheduled (2019 1 1)))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \"SCHEDULED: <2019-01-01 Tue>\"\n                  \"\"\n                  \"rest\")\n      (:buffer \"* headline\"\n               \"SCHEDULED: <2019-01-01 Tue>\"\n               \"\"\n               \"rest\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-planning nil)\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \"\"\n                  \"rest\")\n      :end-hidden\n      )\n\n    (defexamples-content org-ml-headline-map-planning\n      nil\n      (:buffer \"* headline\"\n               \"CLOSED: [2019-01-01 Tue]\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-map-planning*\n          (list :closed (org-ml-timelist-shift 1 'day (plist-get it :closed))))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \"CLOSED: [2019-01-02 Wed]\"))\n\n    (defexamples-content org-ml-headline-get-node-properties\n      nil\n      (:buffer \"* headline\"\n               \":PROPERTIES:\"\n               \":Effort:   1:00\"\n               \":ID:       minesfake\"\n               \":END:\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-node-properties))\n      => '((\"Effort\" \"1:00\") (\"ID\" \"minesfake\"))\n      (:buffer \"* headline\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-node-properties)\n           (-map #'org-ml-to-trimmed-string))\n      => nil\n      :begin-hidden\n      (:buffer \"* headline\"\n               \"CLOSED: [2019-01-01 Tue]\"\n               \":PROPERTIES:\"\n               \":Effort:   1:00\"\n               \":ID:       minesfake\"\n               \":END:\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-node-properties))\n      => '((\"Effort\" \"1:00\") (\"ID\" \"minesfake\"))\n      :end-hidden)\n\n    (defexamples-content org-ml-headline-set-node-properties\n      nil\n      (:buffer \"* headline\"\n               \":PROPERTIES:\"\n               \":Effort:   1:00\"\n               \":ID:       minesfake\"\n               \":END:\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-node-properties '((\"Effort\" \"0:01\") (\"ID\" \"easy\")))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \":PROPERTIES:\"\n                  \":Effort:   0:01\"\n                  \":ID:       easy\"\n                  \":END:\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-node-properties nil)\n        (org-ml-to-trimmed-string))\n      => \"* headline\"\n      :begin-hidden\n      (:buffer \"* headline\"\n               \"CLOSED: [2019-01-01 Tue]\"\n               \":PROPERTIES:\"\n               \":Effort:   1:00\"\n               \":ID:       minesfake\"\n               \":END:\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-node-properties '((\"Effort\" \"0:01\") (\"ID\" \"easy\")))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \"CLOSED: [2019-01-01 Tue]\"\n                  \":PROPERTIES:\"\n                  \":Effort:   0:01\"\n                  \":ID:       easy\"\n                  \":END:\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-node-properties nil)\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \"CLOSED: [2019-01-01 Tue]\")\n      (:buffer \"* headline\"\n               \"\"\n               \"section\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-node-properties '((\"New\" \"world man\")))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \":PROPERTIES:\"\n                  \":New:      world man\"\n                  \":END:\"\n                  \"\"\n                  \"section\")\n      (:buffer \"* headline\"\n               \"CLOSED: [2019-01-01 Tue]\"\n               \"\"\n               \"section\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-node-properties '((\"New\" \"world man\")))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \"CLOSED: [2019-01-01 Tue]\"\n                  \":PROPERTIES:\"\n                  \":New:      world man\"\n                  \":END:\"\n                  \"\"\n                  \"section\")\n      (:buffer \"* headline\"\n               \"CLOSED: [2019-01-01 Tue]\"\n               \":PROPERTIES:\"\n               \":Effort:   0:01\"\n               \":ID:       easy\"\n               \":END:\"\n               \"\"\n               \"section\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-node-properties nil)\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \"CLOSED: [2019-01-01 Tue]\"\n                  \"\"\n                  \"section\"))\n\n    (defexamples-content org-ml-headline-map-node-properties\n      nil\n      (:buffer \"* headline\"\n               \":PROPERTIES:\"\n               \":Effort:   1:00\"\n               \":ID:       minesfake\"\n               \":END:\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-map-node-properties*\n          (cons (list \"New\" \"world man\") it))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \":PROPERTIES:\"\n                  \":New:      world man\"\n                  \":Effort:   1:00\"\n                  \":ID:       minesfake\"\n                  \":END:\")\n      ;; assume this will work with planning in front because\n      ;; we already tested the get/set base functions\n      )\n\n    ;; assume these will work when planning is in front since\n    ;; they are all based on the -map-properties (plural) functions\n    ;; and these have already been tested\n    (defexamples-content org-ml-headline-get-node-property\n      nil\n      (:buffer \"* headline\"\n               \":PROPERTIES:\"\n               \":ID:       fake\"\n               \":END:\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-node-property \"ID\"))\n      => \"fake\"\n\n      (:buffer \"* headline\"\n               \":PROPERTIES:\"\n               \":ID:       fake\"\n               \":END:\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-node-property \"READ_ID\"))\n      => nil)\n\n    (defexamples-content org-ml-headline-set-node-property\n      nil\n      (:buffer \"* headline\"\n               \":PROPERTIES:\"\n               \":ID:       fake\"\n               \":END:\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-node-property \"ID\" \"real\")\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \":PROPERTIES:\"\n                  \":ID:       real\"\n                  \":END:\")\n      (:buffer \"* headline\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-node-property \"ID\" \"real\")\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \":PROPERTIES:\"\n                  \":ID:       real\"\n                  \":END:\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-node-property \"ID\" nil)\n        (org-ml-to-trimmed-string))\n      => \"* headline\"\n      (:buffer \"* headline\"\n               \":PROPERTIES:\"\n               \":ID:       real\"\n               \":END:\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-node-property \"ID\" nil)\n        (org-ml-to-trimmed-string))\n      => \"* headline\")\n\n    (defexamples-content org-ml-headline-map-node-property\n      nil\n      (:buffer \"* headline\"\n               \":PROPERTIES:\"\n               \":ID:       fake\"\n               \":END:\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-map-node-property \"ID\" #'s-upcase)\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \":PROPERTIES:\"\n                  \":ID:       FAKE\"\n                  \":END:\")))\n\n  (def-example-subgroup \"Headline (logbook and contents)\"\n    nil\n\n\n    (defexamples-content org-ml-headline-get-supercontents\n      nil\n      \n      (:buffer \"* headline\"\n               \"CLOSED: [2019-01-01 Tue 00:00]\"\n               \":PROPERTIES:\"\n               \":Effort: 0:30\"\n               \":END:\"\n               \":LOGGING:\"\n               \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n               \"  log note\"\n               \":END:\"\n               \":CLOCKING:\"\n               \"CLOCK: [2019-01-01 Tue 00:00]\"\n               \":END:\"\n               \"contents\")\n\n      (let ((config (list :log-into-drawer \"LOGGING\"\n                          :clock-into-drawer \"CLOCKING\")))\n        (->> (org-ml-parse-this-headline)\n             (org-ml-headline-get-supercontents config)\n             (org-ml-supercontents-get-logbook)\n             (org-ml-logbook-get-items)\n             (-map #'org-ml-to-trimmed-string)))\n      => '(\"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\\n  log note\")\n\n      (let ((config (list :log-into-drawer \"LOGGING\"\n                          :clock-into-drawer \"CLOCKING\")))\n        (->> (org-ml-parse-this-headline)\n             (org-ml-headline-get-supercontents config)\n             (org-ml-supercontents-get-logbook)\n             (org-ml-logbook-get-clocks)\n             (-map #'org-ml-to-trimmed-string)))\n      => '(\"CLOCK: [2019-01-01 Tue 00:00]\")\n\n      (let ((config (list :log-into-drawer \"LOGGING\"\n                          :clock-into-drawer \"CLOCKING\")))\n        (->> (org-ml-parse-this-headline)\n             (org-ml-headline-get-supercontents config)\n             (org-ml-supercontents-get-logbook)\n             (alist-get :unknown)\n             (-map #'org-ml-to-trimmed-string)))\n      => nil\n\n      (let ((config (list :log-into-drawer \"LOGGING\"\n                          :clock-into-drawer \"CLOCKING\")))\n        (->> (org-ml-parse-this-headline)\n             (org-ml-headline-get-supercontents config)\n             (org-ml-supercontents-get-contents)\n             (-map #'org-ml-to-trimmed-string)))\n      => '(\"contents\"))\n\n    (defexamples-content org-ml-headline-set-supercontents\n      nil\n      \n      (:buffer \"* headline\"\n               \"CLOSED: [2019-01-01 Tue 00:00]\"\n               \":PROPERTIES:\"\n               \":Effort:    0:30\"\n               \":END:\"\n               \":LOGGING:\"\n               \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n               \"  log note\"\n               \":END:\"\n               \":CLOCKING:\"\n               \"CLOCK: [2019-01-01 Tue 00:00]\"\n               \":END:\"\n               \"contents\")\n\n      (let ((config (list :log-into-drawer \"LOGGING\"\n                          :clock-into-drawer \"CLOCKING\")))\n        (org-ml->> (org-ml-parse-this-headline)\n          (org-ml-headline-set-supercontents\n           config `(:blank 0 :contents ,(list (org-ml-build-paragraph! \"new contents\"))))\n          (org-ml-to-trimmed-string)))\n      => (:result \"* headline\"\n                  \"new contents\"))\n\n    (defexamples-content org-ml-headline-map-supercontents\n      nil\n      \n      (:buffer \"* headline\"\n               \"CLOSED: [2019-01-01 Tue 00:00]\"\n               \":PROPERTIES:\"\n               \":Effort:    0:30\"\n               \":END:\"\n               \":LOGGING:\"\n               \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n               \"  log note\"\n               \":END:\"\n               \":CLOCKING:\"\n               \"CLOCK: [2019-01-01 Tue 00:00]\"\n               \":END:\"\n               \"contents\")\n\n      (let ((config (list :log-into-drawer \"LOGGING\"\n                          :clock-into-drawer \"CLOCKING\")))\n        (org-ml->> (org-ml-parse-this-headline)\n          (org-ml-headline-map-supercontents*\n              config (org-ml-supercontents-map-contents*\n                       (cons (org-ml-build-paragraph! \"new contents\") it) it))\n          (org-ml-to-trimmed-string)))\n      => (:result \"* headline\"\n                  \"CLOSED: [2019-01-01 Tue 00:00]\"\n                  \":PROPERTIES:\"\n                  \":Effort:   0:30\"\n                  \":END:\"\n                  \":LOGGING:\"\n                  \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n                  \"  log note\"\n                  \":END:\"\n                  \":CLOCKING:\"\n                  \"CLOCK: [2019-01-01 Tue 00:00]\"\n                  \":END:\"\n                  \"new contents\"\n                  \"contents\"))\n\n    (defexamples-content org-ml-headline-get-logbook-items\n      nil\n      (:buffer \"* headline\"\n               \"CLOSED: [2019-01-01 Tue 00:00]\"\n               \":PROPERTIES:\"\n               \":Effort: 0:30\"\n               \":END:\"\n               \":LOGGING:\"\n               \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n               \"  log note\"\n               \":END:\"\n               \":CLOCKING:\"\n               \"CLOCK: [2019-01-01 Tue 00:00]\"\n               \":END:\"\n               \"contents\")\n      (let ((config (list :log-into-drawer \"LOGGING\"\n                          :clock-into-drawer \"CLOCKING\")))\n        (->> (org-ml-parse-this-headline)\n             (org-ml-headline-get-logbook-items config)\n             (-map #'org-ml-to-trimmed-string)))\n      => '(\"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\\n  log note\"))\n\n    (defexamples-content org-ml-headline-set-logbook-items\n      nil\n      (:buffer \"* headline\"\n               \"CLOSED: [2019-01-01 Tue 00:00]\"\n               \":PROPERTIES:\"\n               \":Effort: 0:30\"\n               \":END:\"\n               \":LOGGING:\"\n               \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n               \"  log note\"\n               \":END:\"\n               \":CLOCKING:\"\n               \"CLOCK: [2019-01-01 Tue 00:00]\"\n               \":END:\"\n               \"contents\")\n      (let ((config (list :log-into-drawer \"LOGGING\"\n                          :clock-into-drawer \"CLOCKING\")))\n        (org-ml->> (org-ml-parse-this-headline)\n          (org-ml-headline-set-logbook-items config nil)\n          (org-ml-to-trimmed-string)))\n      =>  (:result \"* headline\"\n                   \"CLOSED: [2019-01-01 Tue 00:00]\"\n                   \":PROPERTIES:\"\n                   \":Effort:   0:30\"\n                   \":END:\"\n                   \":CLOCKING:\"\n                   \"CLOCK: [2019-01-01 Tue 00:00]\"\n                   \":END:\"\n                   \"contents\")\n      :begin-hidden\n      (:buffer \"* headline\"\n               \"CLOSED: [2019-01-01 Tue 00:00]\"\n               \":PROPERTIES:\"\n               \":Effort: 0:30\"\n               \":END:\"\n               \":LOGGING:\"\n               \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n               \"  log note\"\n               \":END:\"\n               \"\"\n               \"contents\")\n      (let ((config (list :log-into-drawer \"LOGGING\"\n                          :clock-into-drawer \"CLOCKING\")))\n        (org-ml->> (org-ml-parse-this-headline)\n          (org-ml-headline-set-logbook-items config nil)\n          (org-ml-to-trimmed-string)))\n      =>  (:result \"* headline\"\n                   \"CLOSED: [2019-01-01 Tue 00:00]\"\n                   \":PROPERTIES:\"\n                   \":Effort:   0:30\"\n                   \":END:\"\n                   \"\"\n                   \"contents\")\n      :end-hidden)\n\n    (defexamples-content org-ml-headline-map-logbook-items\n      nil\n      (:buffer \"* headline\"\n               \"CLOSED: [2019-01-01 Tue 00:00]\"\n               \":PROPERTIES:\"\n               \":Effort: 0:30\"\n               \":END:\"\n               \":LOGGING:\"\n               \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n               \"  log note\"\n               \":END:\"\n               \":CLOCKING:\"\n               \"CLOCK: [2019-01-01 Tue 00:00]\"\n               \":END:\"\n               \"contents\")\n      (let ((config (list :log-into-drawer \"LOGGING\"\n                          :clock-into-drawer \"CLOCKING\")))\n        (org-ml->> (org-ml-parse-this-headline)\n          (org-ml-headline-map-logbook-items* config\n            (--map (org-ml-map-children*\n                     (--map (org-ml-map-children*\n                              (--map-when (org-ml-is-type 'plain-text it)\n                                          (upcase it)\n                                          it)\n                              it)\n                            it)\n                     it)\n                   it))\n          (org-ml-to-trimmed-string)))\n      =>  (:result \"* headline\"\n                   \"CLOSED: [2019-01-01 Tue 00:00]\"\n                   \":PROPERTIES:\"\n                   \":Effort:   0:30\"\n                   \":END:\"\n                   \":LOGGING:\"\n                   \"- NOTE TAKEN ON [2018-12-31 Mon 00:00] \\\\\\\\\"\n                   \"  LOG NOTE\"\n                   \":END:\"\n                   \":CLOCKING:\"\n                   \"CLOCK: [2019-01-01 Tue 00:00]\"\n                   \":END:\"\n                   \"contents\"))\n\n    (defexamples-content org-ml-headline-get-logbook-clocks\n      nil\n      (:buffer \"* headline\"\n               \"CLOSED: [2019-01-01 Tue 00:00]\"\n               \":PROPERTIES:\"\n               \":Effort: 0:30\"\n               \":END:\"\n               \":LOGGING:\"\n               \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n               \"  log note\"\n               \":END:\"\n               \":CLOCKING:\"\n               \"CLOCK: [2019-01-01 Tue 00:00]\"\n               \":END:\"\n               \"contents\")\n      (let ((config (list :log-into-drawer \"LOGGING\"\n                          :clock-into-drawer \"CLOCKING\")))\n        (->> (org-ml-parse-this-headline)\n             (org-ml-headline-get-logbook-clocks config)\n             (-map #'org-ml-to-trimmed-string)))\n      => '(\"CLOCK: [2019-01-01 Tue 00:00]\"))\n\n    (defexamples-content org-ml-headline-set-logbook-clocks\n      nil\n      (:buffer \"* headline\"\n               \"CLOSED: [2019-01-01 Tue 00:00]\"\n               \":PROPERTIES:\"\n               \":Effort: 0:30\"\n               \":END:\"\n               \":LOGGING:\"\n               \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n               \"  log note\"\n               \":END:\"\n               \":CLOCKING:\"\n               \"CLOCK: [2019-01-01 Tue 00:00]\"\n               \":END:\"\n               \"contents\")\n      (let ((config (list :log-into-drawer \"LOGGING\"\n                          :clock-into-drawer \"CLOCKING\")))\n        (org-ml->> (org-ml-parse-this-headline)\n          (org-ml-headline-set-logbook-clocks config nil)\n          (org-ml-to-trimmed-string)))\n      =>  (:result \"* headline\"\n                   \"CLOSED: [2019-01-01 Tue 00:00]\"\n                   \":PROPERTIES:\"\n                   \":Effort:   0:30\"\n                   \":END:\"\n                   \":LOGGING:\"\n                   \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n                   \"  log note\"\n                   \":END:\"\n                   \"contents\"))\n\n    (defexamples-content org-ml-headline-map-logbook-clocks\n      nil\n      (:buffer \"* headline\"\n               \"CLOSED: [2019-01-01 Tue 00:00]\"\n               \":PROPERTIES:\"\n               \":Effort: 0:30\"\n               \":END:\"\n               \":LOGGING:\"\n               \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n               \"  log note\"\n               \":END:\"\n               \":CLOCKING:\"\n               \"CLOCK: [2019-01-01 Tue 00:00]\"\n               \":END:\"\n               \"contents\")\n      (let ((config (list :log-into-drawer \"LOGGING\"\n                          :clock-into-drawer \"CLOCKING\")))\n        (org-ml->> (org-ml-parse-this-headline)\n          (org-ml-headline-map-logbook-clocks* config\n            (--map (org-ml-map-property* :value\n                     (org-ml-timestamp-shift 1 'day it)\n                     it)\n                   it))\n          (org-ml-to-trimmed-string)))\n      =>  (:result \"* headline\"\n                   \"CLOSED: [2019-01-01 Tue 00:00]\"\n                   \":PROPERTIES:\"\n                   \":Effort:   0:30\"\n                   \":END:\"\n                   \":LOGGING:\"\n                   \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n                   \"  log note\"\n                   \":END:\"\n                   \":CLOCKING:\"\n                   \"CLOCK: [2019-01-02 Wed 00:00]\"\n                   \":END:\"\n                   \"contents\"))\n\n    (defexamples-content org-ml-headline-get-contents\n      nil\n      (:buffer \"* headline\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-contents\n            (list :log-into-drawer t\n                  :clock-into-drawer t\n                  :clock-out-notes t))\n           (-map #'org-ml-to-trimmed-string))\n      => nil\n\n      (:buffer \"* headline\"\n               \"something\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-contents\n            (list :log-into-drawer t\n                  :clock-into-drawer t\n                  :clock-out-notes t))\n           (-map #'org-ml-to-trimmed-string))\n      => '(\"something\")\n\n      (:buffer \"* headline\"\n               \"CLOSED: [2019-01-01 Tue 00:00]\"\n               \":LOGBOOK:\"\n               \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n               \"  log note\"\n               \"CLOCK: [2019-01-01 Tue 00:00]\"\n               \":END:\"\n               \"\"\n               \"- not log\")\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-contents\n            (list :log-into-drawer t\n                  :clock-into-drawer t\n                  :clock-out-notes t))\n           (-map #'org-ml-to-trimmed-string))\n      => '(\"- not log\")\n\n      (:buffer \"* headline\"\n               \"CLOSED: [2019-01-01 Tue 00:00]\"\n               \":LOGGING:\"\n               \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n               \"  log note\"\n               \":END:\"\n               \":CLOCKING:\"\n               \"CLOCK: [2019-01-01 Tue 00:00]\"\n               \":END:\"\n               \"\"\n               \"- not log\")\n\n      (->> (org-ml-parse-this-headline)\n           (org-ml-headline-get-contents\n            (list :log-into-drawer \"LOGGING\"\n                  :clock-into-drawer \"CLOCKING\"))\n           (-map #'org-ml-to-trimmed-string))\n      => '(\"- not log\"))\n\n    (defexamples-content org-ml-headline-set-contents\n      nil\n\n      (:buffer \"* headline\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-contents\n         (list :log-into-drawer t\n               :clock-into-drawer t\n               :clock-out-notes t)\n         (list (org-ml-build-paragraph! \"I'm new\")))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \"I'm new\")\n\n      (:buffer \"* headline\"\n               \"something\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-contents\n         (list :log-into-drawer t\n               :clock-into-drawer t\n               :clock-out-notes t)\n         (list (org-ml-build-paragraph! \"I'm new\")))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \"I'm new\")\n\n      (:buffer \"* headline\"\n               \":LOGBOOK:\"\n               \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n               \"  log1\"\n               \":END:\"\n               \"something\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-contents\n         (list :log-into-drawer t\n               :clock-into-drawer t\n               :clock-out-notes t)\n         (list (org-ml-build-paragraph! \"I'm new\")))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \":LOGBOOK:\"\n                  \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n                  \"  log1\"\n                  \":END:\"\n                  \"I'm new\")\n\n      (:buffer \"* headline\"\n               \":LOGBOOK:\"\n               \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n               \"  log1\"\n               \":END:\"\n               \"something\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-set-contents\n         (list :log-into-drawer t\n               :clock-into-drawer t\n               :clock-out-notes t)\n         nil)\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \":LOGBOOK:\"\n                  \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n                  \"  log1\"\n                  \":END:\"))\n\n    (defexamples-content org-ml-headline-map-contents\n      nil\n\n      (:buffer \"* headline\"\n               \"something\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-map-contents*\n            (list :log-into-drawer t\n                  :clock-into-drawer t\n                  :clock-out-notes t)\n          (cons (org-ml-build-paragraph! \"I'm new\") it))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \"I'm new\"\n                  \"something\"))\n\n    (defexamples-content org-ml-headline-logbook-append-item\n      nil\n      (:buffer \"* headline\")\n      (let ((ut (- 1546300800 (car (current-time-zone)))))\n        (org-ml->> (org-ml-parse-this-headline)\n          (org-ml-headline-logbook-append-item\n           (list :log-into-drawer t\n                 :clock-into-drawer t\n                 :clock-out-notes t)\n           (org-ml-build-log-note ut \"new note\"))\n          (org-ml-to-trimmed-string)))\n      => (:result \"* headline\"\n                  \":LOGBOOK:\"\n                  \"- Note taken on [2019-01-01 Tue 00:00] \\\\\\\\\"\n                  \"  new note\"\n                  \":END:\")\n\n      (:buffer \"* headline\"\n               \":LOGBOOK:\"\n               \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n               \"  old note\"\n               \":END:\")\n      (let ((ut (- 1546300800 (car (current-time-zone)))))\n        (org-ml->> (org-ml-parse-this-headline)\n          (org-ml-headline-logbook-append-item\n           \n           (list :log-into-drawer t\n                 :clock-into-drawer t\n                 :clock-out-notes t)\n           (org-ml-build-log-note ut \"new note\"))\n          (org-ml-to-trimmed-string)))\n      => (:result \"* headline\"\n                  \":LOGBOOK:\"\n                  \"- Note taken on [2019-01-01 Tue 00:00] \\\\\\\\\"\n                  \"  new note\"\n                  \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n                  \"  old note\"\n                  \":END:\")\n\n      (:buffer \"* headline\"\n               \":LOGGING:\"\n               \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n               \"  old note\"\n               \":END:\"\n               \":CLOCKING:\"\n               \"CLOCK: [2112-01-01 Fri]\"\n               \":END:\")\n      (let ((ut (- 1546300800 (car (current-time-zone)))))\n        (org-ml->> (org-ml-parse-this-headline)\n          (org-ml-headline-logbook-append-item\n           (list :log-into-drawer \"LOGGING\"\n                 :clock-into-drawer \"CLOCKING\")\n           (org-ml-build-log-note ut \"new note\"))\n          (org-ml-to-trimmed-string)))\n      => (:result \"* headline\"\n                  \":LOGGING:\"\n                  \"- Note taken on [2019-01-01 Tue 00:00] \\\\\\\\\"\n                  \"  new note\"\n                  \"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\"\n                  \"  old note\"\n                  \":END:\"\n                  \":CLOCKING:\"\n                  \"CLOCK: [2112-01-01 Fri]\"\n                  \":END:\")\n\n      ;; :begin-hidden\n      ;; (:buffer \"* headline\"\n      ;;          \"\"\n      ;;          \"something\")\n      ;; (let ((ut (- 1546300800 (car (current-time-zone)))))\n      ;;   (org-ml->> (org-ml-parse-this-headline)\n      ;;     (org-ml-headline-logbook-append-item\n      ;;      (list :log-into-drawer t\n      ;;            :clock-into-drawer t\n      ;;            :clock-out-notes t)\n      ;;      (org-ml-build-log-note ut \"new note\"))\n      ;;     (org-ml-to-trimmed-string)))\n      ;; => (:result \"* headline\"\n      ;;             \":LOGBOOK:\"\n      ;;             \"- Note taken on [2019-01-01 Tue 00:00] \\\\\\\\\"\n      ;;             \"  new note\"\n      ;;             \":END:\"\n      ;;             \"\"\n      ;;             \"something\")\n      ;; :end-hidden\n      )\n\n    (defexamples-content org-ml-headline-logbook-append-open-clock\n      nil\n\n      (:buffer \"* headline\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-logbook-append-open-clock\n         (list :log-into-drawer t\n               :clock-into-drawer t\n               :clock-out-notes t)\n         (- 1546300800 (car (current-time-zone))))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \":LOGBOOK:\"\n                  \"CLOCK: [2019-01-01 Tue 00:00]\"\n                  \":END:\")\n\n      (:buffer \"* headline\"\n               \":LOGBOOK:\"\n               \"- note taken on [2018-12-30 Sun 00:00]\"\n               \":END:\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-logbook-append-open-clock\n         (list :log-into-drawer t\n               :clock-into-drawer t\n               :clock-out-notes t)\n         (- 1546300800 (car (current-time-zone))))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \":LOGBOOK:\"\n                  \"CLOCK: [2019-01-01 Tue 00:00]\"\n                  \"- note taken on [2018-12-30 Sun 00:00]\"\n                  \":END:\")\n\n      (:buffer \"* headline\"\n               \":LOGGING:\"\n               \"- note taken on [2018-12-30 Sun 00:00]\"\n               \":END:\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-logbook-append-open-clock\n         (list :log-into-drawer \"LOGGING\"\n               :clock-into-drawer \"CLOCKING\")\n         (- 1546300800 (car (current-time-zone))))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \":LOGGING:\"\n                  \"- note taken on [2018-12-30 Sun 00:00]\"\n                  \":END:\"\n                  \":CLOCKING:\"\n                  \"CLOCK: [2019-01-01 Tue 00:00]\"\n                  \":END:\"))\n\n    (defexamples-content org-ml-headline-logbook-close-open-clock\n      nil\n      (:buffer \"* headline\"\n               \":LOGBOOK:\"\n               \"- note taken on [2018-12-30 Sun 00:00]\"\n               \":END:\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-logbook-close-open-clock\n         (list :log-into-drawer t\n               :clock-into-drawer t\n               :clock-out-notes t)\n         (- 1546300800 (car (current-time-zone))) nil)\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \":LOGBOOK:\"\n                  \"- note taken on [2018-12-30 Sun 00:00]\"\n                  \":END:\")\n\n      (:buffer \"* headline\"\n               \":LOGBOOK:\"\n               \"CLOCK: [2018-12-31 Mon 00:00]\"\n               \"- note taken on [2018-12-30 Sun 00:00]\"\n               \":END:\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-logbook-close-open-clock\n         (list :log-into-drawer t\n               :clock-into-drawer t\n               :clock-out-notes t)\n         (- 1546300800 (car (current-time-zone))) nil)\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \":LOGBOOK:\"\n                  \"CLOCK: [2018-12-31 Mon 00:00]--[2019-01-01 Tue 00:00] => 24:00\"\n                  \"- note taken on [2018-12-30 Sun 00:00]\"\n                  \":END:\")\n\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-logbook-close-open-clock\n         (list :log-into-drawer t\n               :clock-into-drawer t\n               :clock-out-notes t)\n         (- 1546300800 (car (current-time-zone))) \"new note\")\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \":LOGBOOK:\"\n                  \"CLOCK: [2018-12-31 Mon 00:00]--[2019-01-01 Tue 00:00] => 24:00\"\n                  \"- new note\"\n                  \"- note taken on [2018-12-30 Sun 00:00]\"\n                  \":END:\")\n\n      (:buffer \"* headline\"\n               \":LOGGING:\"\n               \"- note taken on [2018-12-30 Sun 00:00]\"\n               \":END:\"\n               \":CLOCKING:\"\n               \"CLOCK: [2018-12-31 Mon 00:00]\"\n               \":END:\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-logbook-close-open-clock\n         (list :log-into-drawer \"LOGGING\"\n               :clock-into-drawer \"CLOCKING\"\n               :clock-out-notes t)\n         (- 1546300800 (car (current-time-zone))) nil)\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \":LOGGING:\"\n                  \"- note taken on [2018-12-30 Sun 00:00]\"\n                  \":END:\"\n                  \":CLOCKING:\"\n                  \"CLOCK: [2018-12-31 Mon 00:00]--[2019-01-01 Tue 00:00] => 24:00\"\n                  \":END:\"))\n\n    (defexamples-content org-ml-headline-logbook-convert-config\n      nil\n      (:buffer \"* headline\"\n               \"CLOCK: [2018-12-31 Mon 00:00]--[2019-01-01 Tue 00:00] => 24:00\"\n               \"- note taken on [2018-12-30 Sun 00:00]\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-logbook-convert-config\n         nil\n         (list :log-into-drawer t :clock-into-drawer t))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \":LOGBOOK:\"\n                  \"CLOCK: [2018-12-31 Mon 00:00]--[2019-01-01 Tue 00:00] => 24:00\"\n                  \"- note taken on [2018-12-30 Sun 00:00]\"\n                  \":END:\")\n\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-logbook-convert-config\n         nil\n         (list :log-into-drawer \"LOGGING\" :clock-into-drawer \"CLOCKING\"))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \":LOGGING:\"\n                  \"- note taken on [2018-12-30 Sun 00:00]\"\n                  \":END:\"\n                  \":CLOCKING:\"\n                  \"CLOCK: [2018-12-31 Mon 00:00]--[2019-01-01 Tue 00:00] => 24:00\"\n                  \":END:\")\n\n      (:buffer \"* headline\"\n               \":LOGBOOK:\"\n               \"CLOCK: [2018-12-31 Mon 00:00]--[2019-01-01 Tue 00:00] => 24:00\"\n               \"- note taken on [2018-12-30 Sun 00:00]\"\n               \":END:\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-logbook-convert-config\n         (list :log-into-drawer t\n               :clock-into-drawer t)\n         (list :log-into-drawer \"LOGGING\"\n               :clock-into-drawer \"CLOCKING\"))\n        (org-ml-to-trimmed-string))\n      => (:result \"* headline\"\n                  \":LOGGING:\"\n                  \"- note taken on [2018-12-30 Sun 00:00]\"\n                  \":END:\"\n                  \":CLOCKING:\"\n                  \"CLOCK: [2018-12-31 Mon 00:00]--[2019-01-01 Tue 00:00] => 24:00\"\n                  \":END:\")))\n\n  (def-example-subgroup \"Headline (misc)\"\n    nil\n\n    (defexamples-content org-ml-headline-get-path\n      nil\n      (:buffer \"* one\"\n               \"** two\"\n               \"*** three\")\n      (->> (org-ml-parse-this-subtree)\n           (org-ml-headline-get-path))\n      => '(\"one\")\n      (->> (org-ml-parse-this-subtree)\n           (org-ml-headline-get-subheadlines)\n           (car)\n           (org-ml-headline-get-subheadlines)\n           (car)\n           (org-ml-headline-get-path))\n      => '(\"one\" \"two\" \"three\"))\n\n    (defexamples-content org-ml-headline-update-item-statistics\n      nil\n      (:buffer \"* statistically significant [/]\"\n               \"- irrelevant data\"\n               \"- [ ] good data\"\n               \"- [X] bad data\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-update-item-statistics)\n        (org-ml-to-trimmed-string))\n      => (:result \"* statistically significant [1/2]\"\n                  \"- irrelevant data\"\n                  \"- [ ] good data\"\n                  \"- [X] bad data\")\n\n      :begin-hidden\n      (:buffer \"* statistically significant [%]\"\n               \"- irrelevant data\"\n               \"- [ ] good data\"\n               \"- [X] bad data\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-update-item-statistics)\n        (org-ml-to-trimmed-string))\n      => (:result \"* statistically significant [50%]\"\n                  \"- irrelevant data\"\n                  \"- [ ] good data\"\n                  \"- [X] bad data\")\n      :end-hidden\n\n      (:buffer \"* statistically significant\"\n               \"- irrelevant data\"\n               \"- [ ] good data\"\n               \"- [X] bad data\")\n      (:comment \"Do nothing if nothing to update\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-headline-update-item-statistics)\n        (org-ml-to-trimmed-string))\n      => (:result \"* statistically significant\"\n                  \"- irrelevant data\"\n                  \"- [ ] good data\"\n                  \"- [X] bad data\"))\n\n    (defexamples-content org-ml-headline-update-todo-statistics\n      nil\n      (:buffer \"* statistically significant [/]\"\n               \"** irrelevant data\"\n               \"** TODO good data\"\n               \"** DONE bad data\")\n      (org-ml->> (org-ml-parse-this-subtree)\n        (org-ml-headline-update-todo-statistics)\n        (org-ml-to-trimmed-string))\n      => (:result \"* statistically significant [1/2]\"\n                  \"** irrelevant data\"\n                  \"** TODO good data\"\n                  \"** DONE bad data\")\n\n      :begin-hidden\n      (:buffer \"* statistically significant [%]\"\n               \"** irrelevant data\"\n               \"** TODO good data\"\n               \"** DONE bad data\")\n      (org-ml->> (org-ml-parse-this-subtree)\n        (org-ml-headline-update-todo-statistics)\n        (org-ml-to-trimmed-string))\n      => (:result \"* statistically significant [50%]\"\n                  \"** irrelevant data\"\n                  \"** TODO good data\"\n                  \"** DONE bad data\")\n      :end-hidden\n\n      (:buffer \"* statistically significant\"\n               \"** irrelevant data\"\n               \"** TODO good data\"\n               \"** DONE bad data\")\n      (:comment \"Do nothing if nothing to update\")\n      (org-ml->> (org-ml-parse-this-subtree)\n        (org-ml-headline-update-todo-statistics)\n        (org-ml-to-trimmed-string))\n      => (:result \"* statistically significant\"\n                  \"** irrelevant data\"\n                  \"** TODO good data\"\n                  \"** DONE bad data\"))\n\n    (defexamples-content org-ml-headline-demote-subheadline\n      nil\n      (:buffer \"* one\"\n               \"** two\"\n               \"** three\"\n               \"*** four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-headline-demote-subheadline 0)\n        (org-ml-to-trimmed-string))\n      !!> error\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-headline-demote-subheadline 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"* one\"\n                  \"** two\"\n                  \"*** three\"\n                  \"*** four\")\n\n      :begin-hidden\n\n      (:buffer \"* one\"\n               \"\"\n               \"** two\"\n               \"\"\n               \"** three\"\n               \"\"\n               \"*** four\")\n      (->> (org-ml-parse-element-at 1)\n           (org-ml-headline-demote-subheadline 1)\n           (org-ml-to-trimmed-string))\n      => (:result \"* one\"\n                  \"\"\n                  \"** two\"\n                  \"\"\n                  \"*** three\"\n                  \"\"\n                  \"*** four\")\n\n      (:buffer \"* one\"\n               \"\"\n               \"** two\"\n               \"\"\n               \"*** two-ish\"\n               \"\"\n               \"** three\"\n               \"\"\n               \"*** four\")\n      (->> (org-ml-parse-element-at 1)\n           (org-ml-headline-demote-subheadline 1)\n           (org-ml-to-trimmed-string))\n      => (:result \"* one\"\n                  \"\"\n                  \"** two\"\n                  \"\"\n                  \"*** two-ish\"\n                  \"\"\n                  \"*** three\"\n                  \"\"\n                  \"*** four\")\n\n      ;; TODO this is a bug in the interpreter that squishes whichspace from\n      ;; internal nodes underneath headlines and section\n      ;;\n      ;; (:buffer \"* one\"\n      ;;          \"something\"\n      ;;          \"\"\n      ;;          \"** two\"\n      ;;          \"something\"\n      ;;          \"\"\n      ;;          \"** three\"\n      ;;          \"something\"\n      ;;          \"\"\n      ;;          \"*** four\")\n      ;; (->> (org-ml-parse-element-at 1)\n      ;;      (org-ml-headline-demote-subheadline 1)\n      ;;      (org-ml-to-trimmed-string))\n      ;; => (:result \"* one\"\n      ;;             \"something\"\n      ;;             \"\"\n      ;;             \"** two\"\n      ;;             \"something\"\n      ;;             \"\"\n      ;;             \"*** three\"\n      ;;             \"something\"\n      ;;             \"\"\n      ;;             \"*** four\")\n\n      :end-hidden)\n\n    (defexamples-content org-ml-headline-demote-subtree\n      nil\n      (:buffer \"* one\"\n               \"** two\"\n               \"** three\"\n               \"*** four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-headline-demote-subtree 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"* one\"\n                  \"** two\"\n                  \"*** three\"\n                  \"**** four\")\n      :begin-hidden\n      (:buffer \"* one\"\n               \"\"\n               \"** two\"\n               \"\"\n               \"** three\"\n               \"\"\n               \"*** four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-headline-demote-subtree 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"* one\"\n                  \"\"\n                  \"** two\"\n                  \"\"\n                  \"*** three\"\n                  \"\"\n                  \"**** four\")\n      :end-hidden)\n\n    (defexamples-content org-ml-headline-promote-subheadline\n      nil\n      (:buffer \"* one\"\n               \"** two\"\n               \"** three\"\n               \"*** four\"\n               \"*** four\"\n               \"*** four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-headline-promote-subheadline 1 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"* one\"\n                  \"** two\"\n                  \"** three\"\n                  \"*** four\"\n                  \"** four\"\n                  \"*** four\")\n      :begin-hidden\n      (:buffer \"* one\"\n               \"** two\"\n               \"** three\"\n               \"*** four\"\n               \"*** four\"\n               \"**** five\"\n               \"*** four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-headline-promote-subheadline 1 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"* one\"\n                  \"** two\"\n                  \"** three\"\n                  \"*** four\"\n                  \"** four\"\n                  \"*** five\"\n                  \"*** four\")\n      ;; TODO this is a whitespace bug in 9.7\n      ;; (:buffer \"* one\"\n      ;;          \"\"\n      ;;          \"** two\"\n      ;;          \"\"\n      ;;          \"** three\"\n      ;;          \"\"\n      ;;          \"*** four\"\n      ;;          \"\"\n      ;;          \"*** four\"\n      ;;          \"\"\n      ;;          \"*** four\")\n      ;; (org-ml->> (org-ml-parse-element-at 1)\n      ;;   (org-ml-headline-promote-subheadline 1 1)\n      ;;   (org-ml-to-trimmed-string))\n      ;; => (:result \"* one\"\n      ;;             \"\"\n      ;;             \"** two\"\n      ;;             \"\"\n      ;;             \"** three\"\n      ;;             \"\"\n      ;;             \"*** four\"\n      ;;             \"\"\n      ;;             \"** four\"\n      ;;             \"\"\n      ;;             \"*** four\")\n      :end-hidden)\n\n    (defexamples-content org-ml-headline-promote-all-subheadlines\n      nil\n      (:buffer \"* one\"\n               \"** two\"\n               \"** three\"\n               \"*** four\"\n               \"*** four\"\n               \"*** four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-headline-promote-all-subheadlines 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"* one\"\n                  \"** two\"\n                  \"** three\"\n                  \"** four\"\n                  \"** four\"\n                  \"** four\")))\n\n  (def-example-subgroup \"Plain List\"\n    nil\n    \n    (defexamples-content org-ml-plain-list-set-type\n      nil\n      (:buffer \"- [ ] one\"\n               \"- [X] two\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-plain-list-set-type 'ordered)\n        (org-ml-to-trimmed-string))\n      => (:result \"1. [ ] one\"\n                  \"2. [X] two\")\n      (:buffer \"1. [ ] one\"\n               \"2. [X] two\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-plain-list-set-type 'unordered)\n        (org-ml-to-trimmed-string))\n      => (:result \"- [ ] one\"\n                  \"- [X] two\"))\n\n    (defexamples-content org-ml-plain-list-indent-item\n      nil\n      (:buffer \"- one\"\n               \"- two\"\n               \"  - three\"\n               \"- four\")\n      (:comment \"It makes no sense to indent the first item\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-indent-item 0)\n        (org-ml-to-trimmed-string))\n      !!> error\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-indent-item 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"- one\"\n                  \"  - two\"\n                  \"  - three\"\n                  \"- four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-indent-item 2)\n        (org-ml-to-trimmed-string))\n      => (:result \"- one\"\n                  \"- two\"\n                  \"  - three\"\n                  \"  - four\")\n      :begin-hidden\n      (:buffer \"- one\"\n               \"\"\n               \"- two\"\n               \"\"\n               \"- four\"\n               \"\"\n               \"  - five\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-indent-item 2)\n        (org-ml-to-trimmed-string))\n      => (:result \"- one\"\n                  \"\"\n                  \"- two\"\n                  \"\"\n                  \"  - four\"\n                  \"\"\n                  \"  - five\")\n      (:buffer \"- one\"\n               \"\"\n               \"- two\"\n               \"\"\n               \"  - three\"\n               \"\"\n               \"- four\"\n               \"\"\n               \"  - five\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-indent-item 2)\n        (org-ml-to-trimmed-string))\n      => (:result \"- one\"\n                  \"\"\n                  \"- two\"\n                  \"\"\n                  \"  - three\"\n                  \"\"\n                  \"  - four\"\n                  \"\"\n                  \"  - five\")\n      :end-hidden)\n\n    (defexamples-content org-ml-plain-list-indent-item-tree\n      nil\n      (:buffer \"- one\"\n               \"  - one-ish\"\n               \"- two\"\n               \"  - three\"\n               \"- four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-indent-item-tree 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"- one\"\n                  \"  - one-ish\"\n                  \"  - two\"\n                  \"    - three\"\n                  \"- four\")\n      :begin-hidden\n      (:buffer \"- one\"\n               \"\"\n               \"- two\"\n               \"\"\n               \"  - three\"\n               \"\"\n               \"- four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-indent-item-tree 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"- one\"\n                  \"\"\n                  \"  - two\"\n                  \"\"\n                  \"    - three\"\n                  \"\"\n                  \"- four\")\n      (:buffer \"- one\"\n               \"\"\n               \"  - one-ish\"\n               \"\"\n               \"- two\"\n               \"\"\n               \"  - three\"\n               \"\"\n               \"- four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-indent-item-tree 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"- one\"\n                  \"\"\n                  \"  - one-ish\"\n                  \"\"\n                  \"  - two\"\n                  \"\"\n                  \"    - three\"\n                  \"\"\n                  \"- four\")\n      :end-hidden)\n\n    (defexamples-content org-ml-plain-list-outdent-item\n      nil\n      (:buffer \"- one\"\n               \"- two\"\n               \"  - three\"\n               \"  - three\"\n               \"  - three\"\n               \"- four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-outdent-item 1 0)\n        (org-ml-to-trimmed-string))\n      => (:result \"- one\"\n                  \"- two\"\n                  \"- three\"\n                  \"  - three\"\n                  \"  - three\"\n                  \"- four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-outdent-item 1 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"- one\"\n                  \"- two\"\n                  \"  - three\"\n                  \"- three\"\n                  \"  - three\"\n                  \"- four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-outdent-item 2 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"- one\"\n                  \"- two\"\n                  \"  - three\"\n                  \"  - three\"\n                  \"  - three\"\n                  \"- four\")\n\n      :begin-hidden\n      (:buffer \"- one\"\n               \"\"\n               \"- two\"\n               \"\"\n               \"  - three\"\n               \"\"\n               \"  - three\"\n               \"\"\n               \"  - three\"\n               \"\"\n               \"- four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-outdent-item 1 0)\n        (org-ml-to-trimmed-string))\n      => (:result \"- one\"\n                  \"\"\n                  \"- two\"\n                  \"\"\n                  \"- three\"\n                  \"\"\n                  \"  - three\"\n                  \"\"\n                  \"  - three\"\n                  \"\"\n                  \"- four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-outdent-item 1 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"- one\"\n                  \"\"\n                  \"- two\"\n                  \"\"\n                  \"  - three\"\n                  \"\"\n                  \"- three\"\n                  \"\"\n                  \"  - three\"\n                  \"\"\n                  \"- four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-outdent-item 2 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"- one\"\n                  \"\"\n                  \"- two\"\n                  \"\"\n                  \"  - three\"\n                  \"\"\n                  \"  - three\"\n                  \"\"\n                  \"  - three\"\n                  \"\"\n                  \"- four\")\n      :end-hidden)\n    \n    (defexamples-content org-ml-plain-list-outdent-all-items\n      nil\n      (:buffer \"- one\"\n               \"- two\"\n               \"  - three\"\n               \"  - three\"\n               \"  - three\"\n               \"- four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-outdent-all-items 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"- one\"\n                  \"- two\"\n                  \"- three\"\n                  \"- three\"\n                  \"- three\"\n                  \"- four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-outdent-all-items 2)\n        (org-ml-to-trimmed-string))\n      => (:result \"- one\"\n                  \"- two\"\n                  \"  - three\"\n                  \"  - three\"\n                  \"  - three\"\n                  \"- four\")\n\n      (:buffer \"- one\"\n               \"- two\"\n               \"  - three\"\n               \"  - three\"\n               \"  - three\"\n               \"    - three-ish\"\n               \"- four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-outdent-all-items 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"- one\"\n                  \"- two\"\n                  \"- three\"\n                  \"- three\"\n                  \"- three\"\n                  \"  - three-ish\"\n                  \"- four\")\n\n      :begin-hidden\n      (:buffer \"- one\"\n               \"\"\n               \"- two\"\n               \"\"\n               \"  - three\"\n               \"\"\n               \"  - three\"\n               \"\"\n               \"  - three\"\n               \"\"\n               \"- four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-outdent-all-items 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"- one\"\n                  \"\"\n                  \"- two\"\n                  \"\"\n                  \"- three\"\n                  \"\"\n                  \"- three\"\n                  \"\"\n                  \"- three\"\n                  \"\"\n                  \"- four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-outdent-all-items 2)\n        (org-ml-to-trimmed-string))\n      => (:result \"- one\"\n                  \"\"\n                  \"- two\"\n                  \"\"\n                  \"  - three\"\n                  \"\"\n                  \"  - three\"\n                  \"\"\n                  \"  - three\"\n                  \"\"\n                  \"- four\")\n\n      (:buffer \"- one\"\n               \"\"\n               \"- two\"\n               \"\"\n               \"  - three\"\n               \"\"\n               \"  - three\"\n               \"\"\n               \"  - three\"\n               \"\"\n               \"    - three-ish\"\n               \"\"\n               \"    rest\"\n               \"\"\n               \"- four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-outdent-all-items 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"- one\"\n                  \"\"\n                  \"- two\"\n                  \"\"\n                  \"- three\"\n                  \"\"\n                  \"- three\"\n                  \"\"\n                  \"- three\"\n                  \"\"\n                  \"  - three-ish\"\n                  \"\"\n                  \"  rest\"\n                  \"\"\n                  \"- four\")\n\n      (:buffer \"- one\"\n               \"\"\n               \"- two\"\n               \"\"\n               \"  - three\"\n               \"\"\n               \"  - three\"\n               \"\"\n               \"  - three\"\n               \"\"\n               \"  rest\"\n               \"\"\n               \"- four\")\n      (org-ml->> (org-ml-parse-element-at 1)\n        (org-ml-plain-list-outdent-all-items 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"- one\"\n                  \"\"\n                  \"- two\"\n                  \"\"\n                  \"- three\"\n                  \"\"\n                  \"- three\"\n                  \"\"\n                  \"- three\"\n                  \"\"\n                  \"rest\"\n                  \"\"\n                  \"- four\")\n      :end-hidden))\n\n  (def-example-subgroup \"Table\"\n    nil\n\n    (defexamples-content org-ml-table-get-cell\n      nil\n      (:buffer \"| 1 | 2 | 3 |\"\n               \"|---+---+---|\"\n               \"| a | b | c |\")\n      (->> (org-ml-parse-this-element)\n           (org-ml-table-get-cell 0 0)\n           (org-ml-get-children)\n           (car))\n      => \"1\"\n      (->> (org-ml-parse-this-element)\n           (org-ml-table-get-cell 1 1)\n           (org-ml-get-children)\n           (car))\n      => \"b\"\n      (->> (org-ml-parse-this-element)\n           (org-ml-table-get-cell -1 -1)\n           (org-ml-get-children)\n           (car))\n      => \"c\"\n      :begin-hidden\n      (->> (org-ml-parse-this-element)\n           (org-ml-table-get-cell 0 3)\n           (org-ml-get-children)\n           (car))\n      !!> arg-type-error\n      :end-hidden)\n\n    (defexamples-content org-ml-table-delete-column\n      nil\n      (:buffer \"| a | b |\"\n               \"|---+---|\"\n               \"| c | d |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-table-delete-column 0)\n        (org-ml-to-trimmed-string))\n      => (:result \"| b |\"\n                  \"|---|\"\n                  \"| d |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-table-delete-column 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"| a |\"\n                  \"|---|\"\n                  \"| c |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-table-delete-column -1)\n        (org-ml-to-trimmed-string))\n      => (:result \"| a |\"\n                  \"|---|\"\n                  \"| c |\"))\n\n    (defexamples-content org-ml-table-delete-row\n      nil\n      (:buffer \"| a | b |\"\n               \"|---+---|\"\n               \"| c | d |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-table-delete-row 0)\n        (org-ml-to-trimmed-string))\n      => (:result \"|---+---|\"\n                  \"| c | d |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-table-delete-row 1)\n        (org-ml-to-trimmed-string))\n      => (:result \"| a | b |\"\n                  \"| c | d |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-table-delete-row -1)\n        (org-ml-to-trimmed-string))\n      => (:result \"| a | b |\"\n                  \"|---+---|\"))\n\n    (defexamples-content org-ml-table-insert-column!\n      nil\n      (:buffer \"| a | b |\"\n               \"|---+---|\"\n               \"| c | d |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-table-insert-column! 1 '(\"x\" \"y\"))\n        (org-ml-to-trimmed-string))\n      => (:result \"| a | x | b |\"\n                  \"|---+---+---|\"\n                  \"| c | y | d |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-table-insert-column! -1 '(\"x\" \"y\"))\n        (org-ml-to-trimmed-string))\n      => (:result \"| a | b | x |\"\n                  \"|---+---+---|\"\n                  \"| c | d | y |\"))\n\n    (defexamples-content org-ml-table-insert-row!\n      nil\n      (:buffer \"| a | b |\"\n               \"|---+---|\"\n               \"| c | d |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-table-insert-row! 1 '(\"x\" \"y\"))\n        (org-ml-to-trimmed-string))\n      => (:result \"| a | b |\"\n                  \"| x | y |\"\n                  \"|---+---|\"\n                  \"| c | d |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-table-insert-row! 2 '(\"x\" \"y\"))\n        (org-ml-to-trimmed-string))\n      => (:result \"| a | b |\"\n                  \"|---+---|\"\n                  \"| x | y |\"\n                  \"| c | d |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-table-insert-row! -1 '(\"x\" \"y\"))\n        (org-ml-to-trimmed-string))\n      => (:result \"| a | b |\"\n                  \"|---+---|\"\n                  \"| c | d |\"\n                  \"| x | y |\"))\n\n    (defexamples-content org-ml-table-replace-cell!\n      nil\n      (:buffer \"| 1 | 2 |\"\n               \"|---+---|\"\n               \"| a | b |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-table-replace-cell! 0 0 \"2\")\n        (org-ml-to-trimmed-string))\n      => (:result \"| 2 | 2 |\"\n                  \"|---+---|\"\n                  \"| a | b |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-table-replace-cell! 0 0 nil)\n        (org-ml-to-trimmed-string))\n      => (:result \"|   | 2 |\"\n                  \"|---+---|\"\n                  \"| a | b |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-table-replace-cell! -1 -1 \"B\")\n        (org-ml-to-trimmed-string))\n      => (:result \"| 1 | 2 |\"\n                  \"|---+---|\"\n                  \"| a | B |\"))\n\n    (defexamples-content org-ml-table-replace-column!\n      nil\n      (:buffer \"| a | b |\"\n               \"|---+---|\"\n               \"| c | d |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-table-replace-column! 0 '(\"A\" \"B\"))\n        (org-ml-to-trimmed-string))\n      => (:result \"| A | b |\"\n                  \"|---+---|\"\n                  \"| B | d |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-table-replace-column! 0 nil)\n        (org-ml-to-trimmed-string))\n      => (:result \"|   | b |\"\n                  \"|---+---|\"\n                  \"|   | d |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-table-replace-column! -1 '(\"A\" \"B\"))\n        (org-ml-to-trimmed-string))\n      => (:result \"| a | A |\"\n                  \"|---+---|\"\n                  \"| c | B |\"))\n\n    (defexamples-content org-ml-table-replace-row!\n      nil\n      (:buffer \"| a | b |\"\n               \"|---+---|\"\n               \"| c | d |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-table-replace-row! 0 '(\"A\" \"B\"))\n        (org-ml-to-trimmed-string))\n      => (:result \"| A | B |\"\n                  \"|---+---|\"\n                  \"| c | d |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-table-replace-row! 0 nil)\n        (org-ml-to-trimmed-string))\n      => (:result \"|   |   |\"\n                  \"|---+---|\"\n                  \"| c | d |\")\n      (org-ml->> (org-ml-parse-this-element)\n        (org-ml-table-replace-row! -1 '(\"A\" \"B\"))\n        (org-ml-to-trimmed-string))\n      => (:result \"| a | b |\"\n                  \"|---+---|\"\n                  \"| A | B |\"))))\n\n(def-example-group \"Node Matching\"\n  \"Use pattern-matching to selectively perform operations on nodes in trees.\"\n\n  (defexamples-content org-ml-match\n    nil\n\n    (:buffer \"* headline 1\"\n             \"** TODO headline 2\"\n             \"stuff\"\n             \"- item 1\"\n             \"- item 2\"\n             \"- item 3\"\n             \"** DONE headline 3\"\n             \"- item 4\"\n             \"- item 5\"\n             \"- item 6\"\n             \"** TODO COMMENT headline 4\"\n             \"- item 7\"\n             \"- item 8\"\n             \"- item 9\")\n    (:comment \"Match items (excluding the first) in headlines that\"\n              \"are marked \\\"TODO\\\" and not commented.\"\n              \"The :many keyword matches the section and plain-list\"\n              \"nodes holding the items.\")\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((:and (:todo-keyword \"TODO\") (:commentedp nil))\n                         :any *\n                         (:and item (> 0))))\n         (-map #'org-ml-to-trimmed-string))\n    => '(\"- item 2\" \"- item 3\")\n\n    (:buffer \"*one* *two* *three* *four* *five* *six*\")\n    (:comment \"Return all bold nodes\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-match '(bold))\n         (-map #'org-ml-to-trimmed-string))\n    => '(\"*one*\" \"*two*\" \"*three*\" \"*four*\" \"*five*\" \"*six*\")\n    (:comment \"Return first bold node\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-match '(:first bold))\n         (-map #'org-ml-to-trimmed-string))\n    => '(\"*one*\")\n    (:comment \"Return last bold node\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-match '(:last bold))\n         (-map #'org-ml-to-trimmed-string))\n    => '(\"*six*\")\n    (:comment \"Return a select bold node\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-match '(:nth 2 bold))\n         (-map #'org-ml-to-trimmed-string))\n    => '(\"*three*\")\n    (:comment \"Return a sublist of matched bold nodes\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-match '(:sub 1 3 bold))\n         (-map #'org-ml-to-trimmed-string))\n    => '(\"*two*\" \"*three*\" \"*four*\")\n\n    :begin-hidden\n\n    ;; Test all atomic and compound condition combinations here.\n    ;; These tests ensure that:\n    ;; - `org-ml--match-make-condition-form' is correct for all VALID\n    ;;   condition combinations (the error cases are tested in\n    ;;   `org-ml-test.el')\n    ;; - the single and multiple condition paths in\n    ;;   `org-ml--match-make-inner-pattern-form' are correct\n    \n    (:buffer \"* one\"\n             \"** TODO two\"\n             \"2\"\n             \"** COMMENT three\"\n             \"3\"\n             \"** four\"\n             \"4\"\n             \"** DONE five\"\n             \"5\")\n    \n    ;; type\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '(headline section))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"2\" \"3\" \"4\" \"5\")\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '(headline table))\n         (--map (org-ml-to-trimmed-string it)))\n    => nil\n\n    ;; index\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '(0 section))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"2\")\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '(-1 section))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"5\")\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '(4 section))\n         (--map (org-ml-to-trimmed-string it)))\n    => nil\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '(-5 section))\n         (--map (org-ml-to-trimmed-string it)))\n    => nil\n\n    ;; relative index\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((> 0) section))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"3\" \"4\" \"5\")\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((>= 1) section))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"3\" \"4\" \"5\")\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((<= -2) section))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"2\" \"3\" \"4\")\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((< -1) section))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"2\" \"3\" \"4\")\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((< 0) section))\n         (--map (org-ml-to-trimmed-string it)))\n    => nil\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((> 3) section))\n         (--map (org-ml-to-trimmed-string it)))\n    => nil\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((> -1) section))\n         (--map (org-ml-to-trimmed-string it)))\n    => nil\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((< -4) section))\n         (--map (org-ml-to-trimmed-string it)))\n    => nil\n\n    ;; properties\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((:todo-keyword \"TODO\") section))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"2\")\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((:todo-keyword nil) section))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"3\" \"4\")\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((:todo-keyword \"DONE\") section))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"5\")\n\n    ;; pred\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((:pred org-ml-headline-is-done) section))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"5\")\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((:pred stringp) section)) ; silly but proves my point\n         (--map (org-ml-to-trimmed-string it)))\n    => nil\n\n    ;; :not\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((:not (:todo-keyword nil)) section))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"2\" \"5\")\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((:not headline) section))\n         (--map (org-ml-to-trimmed-string it)))\n    => nil\n    \n    ;; :and\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((:and (< 2) (:todo-keyword nil)) section))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"3\")\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((:and (:archivedp t) (:todo-keyword nil)) section))\n         (--map (org-ml-to-trimmed-string it)))\n    => nil\n\n    ;; :or\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((:or (:todo-keyword \"DONE\") (:todo-keyword \"TODO\")) section))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"2\" \"5\")\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((:or (:archivedp t) (:todo-keyword \"NEXT\")) section))\n         (--map (org-ml-to-trimmed-string it)))\n    => nil\n    (->> (org-ml-parse-this-subtree)\n         (org-ml-match '((:or (:todo-keyword \"DONE\") (:todo-keyword \"TODO\")) section))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"2\" \"5\")\n\n    ;; Test the remaining paths of `org-ml--match-make-inner-pattern-form'\n    ;; These test cases ensure that:\n    ;; - the :any + condition path is correct\n    ;; - the condition + :any path is correct\n    ;; - the * path is correct\n    ;; - the + path is correct\n    ;; - the ordering of each above path is correct (assumed because the tests\n    ;;   contain nodes with multiple children that have a defined order to be\n    ;;   preserved)\n    ;;\n    ;; Note that all error cases are tested in `org-ml-test.el'\n    ;;\n    ;; Also note that we assume `org-ml--match-make-condition-form' is\n    ;; independent of `org-ml--match-make-inner-pattern-form' which\n    ;; liberates us from testing all predicate patterns again below.\n\n    ;; :any (first)\n    (:buffer \"*_1_* */2/* _*3*_ _/4/_ /*5*/ /_6_/\")\n\n    (->> (org-ml-parse-this-element)\n         (org-ml-match '(:any (:or bold italic)))\n         (--map (org-ml-to-trimmed-string it)))\n    \n    ;; :any (last)\n    => '(\"/2/\" \"*3*\" \"/4/\" \"*5*\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-match '((:or bold italic) :any))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"_1_\" \"/2/\" \"*5*\" \"_6_\")\n\n    ;; *\n    (:buffer \"* one\"\n             \"- 1\"\n             \"- 2\"\n             \"  - 3\"\n             \"** two\"\n             \"- 4\"\n             \"- 5\"\n             \"  - 6\"\n             \"** three\"\n             \"- 7\"\n             \"- 8\"\n             \"  - 9\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-match '(:any * item))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"- 1\" \"- 2\\n  - 3\" \"- 3\" \"- 4\" \"- 5\\n  - 6\" \"- 6\" \"- 7\"\n         \"- 8\\n  - 9\" \"- 9\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-match '(section plain-list :any * item))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"- 1\" \"- 2\\n  - 3\" \"- 3\")\n\n    ;; + and ?\n    (:buffer \"* one\"\n             \"** two\"\n             \"*** three\"\n             \"** four\"\n             \"*** five\"\n             \"**** six\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-match '(headline +))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"** two\\n*** three\" \"*** three\" \"** four\\n*** five\\n**** six\"\n         \"*** five\\n**** six\" \"**** six\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-match '(headline + headline))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"*** three\" \"*** five\\n**** six\" \"**** six\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-match '(headline headline \\?))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"** two\\n*** three\" \"** four\\n*** five\\n**** six\" \"*** three\"\n         \"*** five\\n**** six\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-match '(headline headline \\? headline))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"*** three\" \"*** five\\n**** six\" \"**** six\")\n\n    ;; alternation\n    (:buffer \"* one\"\n             \"** two\"\n             \"*a* /_b_/\"\n             \"*** three\"\n             \"*c* /_d_/\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-match '(headline section paragraph (bold | italic underline)))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"*a*\" \"_b_\")\n    (->> (org-ml-parse-this-element)\n         (org-ml-match '(headline (nil | headline) section paragraph (bold | italic underline)))\n         (--map (org-ml-to-trimmed-string it)))\n    => '(\"*a*\" \"_b_\" \"*c*\" \"_d_\")\n\n    ;; slicer tests are not here, see `org-ml-test.el'\n\n    :end-hidden)\n\n  (defexamples-content org-ml-match-delete\n    nil\n    (:buffer \"* headline one\"\n             \"** headline two\"\n             \"** headline three\"\n             \"** headline four\")\n    (:comment \"Selectively delete headlines\")\n    (org-ml->> (org-ml-parse-this-subtree)\n      (org-ml-match-delete '(headline))\n      (org-ml-to-trimmed-string))\n    => \"* headline one\"\n    (org-ml->> (org-ml-parse-this-subtree)\n      (org-ml-match-delete '(:first headline))\n      (org-ml-to-trimmed-string))\n    => (:result \"* headline one\"\n                \"** headline three\"\n                \"** headline four\")\n    (org-ml->> (org-ml-parse-this-subtree)\n      (org-ml-match-delete '(:last headline))\n      (org-ml-to-trimmed-string))\n    => (:result \"* headline one\"\n                \"** headline two\"\n                \"** headline three\"))\n\n  (defexamples-content org-ml-match-extract\n    nil\n    (:buffer \"pull me /under/\")\n    (--> (org-ml-parse-this-element)\n         (org-ml-match-extract '(:any * italic) it)\n         (cons (-map #'org-ml-to-trimmed-string (car it))\n               (org-ml-to-trimmed-string (cdr it))))\n    => '((\"/under/\") . \"pull me\"))\n\n  (defexamples-content org-ml-match-map\n    nil\n\n    (:buffer \"* headline one\"\n             \"** TODO headline two\"\n             \"** headline three\"\n             \"** headline four\")\n\n    (:comment \"Selectively mark headlines as DONE\")\n    (org-ml->> (org-ml-parse-this-subtree)\n      (org-ml-match-map '(headline)\n        (lambda (it) (org-ml-set-property :todo-keyword \"DONE\" it)))\n      (org-ml-to-trimmed-string))\n    => (:result \"* headline one\"\n                \"** DONE headline two\"\n                \"** DONE headline three\"\n                \"** DONE headline four\")\n    (org-ml->> (org-ml-parse-this-subtree)\n      (org-ml-match-map* '(:first headline)\n        (org-ml-set-property :todo-keyword \"DONE\" it))\n      (org-ml-to-trimmed-string))\n    => (:result \"* headline one\"\n                \"** DONE headline two\"\n                \"** headline three\"\n                \"** headline four\")\n    (org-ml->> (org-ml-parse-this-subtree)\n      (org-ml-match-map '(:last headline)\n        (-partial #'org-ml-set-property :todo-keyword \"DONE\"))\n      (org-ml-to-trimmed-string))\n    => (:result \"* headline one\"\n                \"** TODO headline two\"\n                \"** headline three\"\n                \"** DONE headline four\"))\n\n  ;; (:buffer \"* headline\"\n  ;;          \":PROPERTIES:\"\n  ;;          \":Effort:   0:30\"\n  ;;          \":END:\")\n  ;; (:comment \"Match the literal property-drawer node and map the\"\n  ;;           \"node-property inside if the property-drawer exists\")\n  ;; (let ((hl (org-ml-parse-this-headline)))\n  ;;   (-if-let (pd (org-ml-headline-get-property-drawer hl))\n  ;;       (->> hl\n  ;;            (org-ml-match-map* `(,pd node-property)\n  ;;                           (org-ml-set-property :value \"1:30\" it))\n  ;;            (org-ml-to-trimmed-string))\n  ;;     (print \"...or do something else if no drawer\")))\n  ;; => (:result \"* headline\"\n  ;;             \":PROPERTIES:\"\n  ;;             \":Effort:   1:30\"\n  ;;             \":END:\"))\n  \n  (defexamples-content org-ml-match-mapcat\n    nil\n\n    (:buffer \"* one\"\n             \"** two\")\n    (org-ml->> (org-ml-parse-this-subtree)\n      (org-ml-match-mapcat* '(:first headline)\n        (list (org-ml-build-headline! :title-text \"1.5\" :level 2) it))\n      (org-ml-to-trimmed-string))\n    => (:result \"* one\"\n                \"** 1.5\"\n                \"** two\"))\n\n  (defexamples-content org-ml-match-replace\n    nil\n    (:buffer \"*1* 2 *3* 4 *5* 6 *7* 8 *9* 10\")\n    (org-ml->> (org-ml-parse-this-element)\n      (org-ml-match-replace '(:any * bold)\n        (org-ml-build-bold :post-blank 1 \"0\"))\n      (org-ml-to-trimmed-string))\n    => \"*0* 2 *0* 4 *0* 6 *0* 8 *0* 10\")\n\n  (defexamples-content org-ml-match-insert-before\n    nil\n    (:buffer \"* one\"\n             \"** two\"\n             \"** three\")\n    (org-ml->> (org-ml-parse-this-subtree)\n      (org-ml-match-insert-before '(headline)\n        (org-ml-build-headline! :title-text \"new\" :level 2))\n      (org-ml-to-trimmed-string))\n    => (:result \"* one\"\n                \"** new\"\n                \"** two\"\n                \"** new\"\n                \"** three\"))\n\n  (defexamples-content org-ml-match-insert-after\n    nil\n    (:buffer \"* one\"\n             \"** two\"\n             \"** three\")\n    (org-ml->> (org-ml-parse-this-subtree)\n      (org-ml-match-insert-after '(headline)\n        (org-ml-build-headline! :title-text \"new\" :level 2))\n      (org-ml-to-trimmed-string))\n    => (:result \"* one\"\n                \"** two\"\n                \"** new\"\n                \"** three\"\n                \"** new\"))\n\n  (defexamples-content org-ml-match-insert-within\n    nil\n    (:buffer \"* one\"\n             \"** two\"\n             \"** three\")\n    (org-ml->> (org-ml-parse-this-subtree)\n      (org-ml-match-insert-within '(headline) 0\n        (org-ml-build-headline! :title-text \"new\" :level 3))\n      (org-ml-to-trimmed-string))\n    => (:result \"* one\"\n                \"** two\"\n                \"*** new\"\n                \"** three\"\n                \"*** new\")\n    (:comment \"The nil pattern denotes top-level element\")\n    (org-ml->> (org-ml-parse-this-subtree)\n      (org-ml-match-insert-within nil 1\n        (org-ml-build-headline! :title-text \"new\" :level 2))\n      (org-ml-to-trimmed-string))\n    => (:result \"* one\"\n                \"** two\"\n                \"** new\"\n                \"** three\"))\n\n  (defexamples-content org-ml-match-splice\n    nil\n    (:buffer \"* one\"\n             \"** two\"\n             \"** three\")\n    (let ((L (list\n              (org-ml-build-headline! :title-text \"new0\" :level 2)\n              (org-ml-build-headline! :title-text \"new1\" :level 2))))\n      (org-ml->> (org-ml-parse-this-subtree)\n        (org-ml-match-splice '(0) L)\n        (org-ml-to-trimmed-string)))\n    => (:result \"* one\"\n                \"** new0\"\n                \"** new1\"\n                \"** three\"))\n\n  (defexamples-content org-ml-match-splice-before\n    nil\n    (:buffer \"* one\"\n             \"** two\"\n             \"** three\")\n    (let ((L (list\n              (org-ml-build-headline! :title-text \"new0\" :level 2)\n              (org-ml-build-headline! :title-text \"new1\" :level 2))))\n      (org-ml->> (org-ml-parse-this-subtree)\n        (org-ml-match-splice-before '(0) L)\n        (org-ml-to-trimmed-string)))\n    => (:result \"* one\"\n                \"** new0\"\n                \"** new1\"\n                \"** two\"\n                \"** three\"))\n\n  (defexamples-content org-ml-match-splice-after\n    nil\n    (:buffer \"* one\"\n             \"** two\"\n             \"** three\")\n    (let ((L (list\n              (org-ml-build-headline! :title-text \"new0\" :level 2)\n              (org-ml-build-headline! :title-text \"new1\" :level 2))))\n      (org-ml->> (org-ml-parse-this-subtree)\n        (org-ml-match-splice-after '(0) L)\n        (org-ml-to-trimmed-string)))\n    => (:result \"* one\"\n                \"** two\"\n                \"** new0\"\n                \"** new1\"\n                \"** three\"))\n\n  (defexamples-content org-ml-match-splice-within\n    nil\n    (:buffer \"* one\"\n             \"** two\"\n             \"** three\"\n             \"*** four\")\n    (let ((L (list\n              (org-ml-build-headline! :title-text \"new0\" :level 3)\n              (org-ml-build-headline! :title-text \"new1\" :level 3))))\n      (org-ml->> (org-ml-parse-this-subtree)\n        (org-ml-match-splice-within '(headline) 0 L)\n        (org-ml-to-trimmed-string)))\n    => (:result \"* one\"\n                \"** two\"\n                \"*** new0\"\n                \"*** new1\"\n                \"** three\"\n                \"*** new0\"\n                \"*** new1\"\n                \"*** four\")\n    (let ((L (list\n              (org-ml-build-headline! :title-text \"new0\" :level 2)\n              (org-ml-build-headline! :title-text \"new1\" :level 2))))\n      (org-ml->> (org-ml-parse-this-subtree)\n        (org-ml-match-splice-within nil 1 L)\n        (org-ml-to-trimmed-string)))\n    => (:result \"* one\"\n                \"** two\"\n                \"** new0\"\n                \"** new1\"\n                \"** three\"\n                \"*** four\"))\n\n  (defexamples-content org-ml-match-do\n    nil))\n\n(def-example-group \"Buffer Side Effects\"\n  \"Map node manipulations into buffers.\"\n\n  (def-example-subgroup \"Insert\"\n    nil\n\n    (defexamples-content org-ml-insert\n      nil\n      (:buffer \"* one\"\n               \"\")\n      (:comment \"Insert single node\")\n      (->> (org-ml-build-headline! :title-text \"two\")\n           (org-ml-insert (point-max)))\n      $> (:result \"* one\"\n                  \"* two\")\n      (:comment \"Insert multiple nodes\")\n      (->> (org-ml-build-headline! :title-text \"two\")\n           (list (org-ml-build-headline! :title-text \"more\"))\n           (org-ml-insert (point-max)))\n      $> (:result \"* one\"\n                  \"* more\"\n                  \"* two\")\n\n      (:buffer \"a *game* or a /boy/\")\n      (->> (org-ml-build-paragraph! \"we don't care if you're\")\n           (org-ml-insert (point-min)))\n      $> (:result \"we don't care if you're\"\n                  \"a *game* or a /boy/\"))\n\n    (defexamples-content org-ml-insert-tail\n      nil\n      :begin-hidden\n      (:buffer \"* one\"\n               \"\")\n      (:comment \"Insert single node\")\n      (->> (org-ml-build-headline! :title-text \"two\")\n           (org-ml-insert-tail (point-max)))\n      $> (:result \"* one\"\n                  \"* two\")\n      (:comment \"Insert multiple nodes\")\n      (->> (org-ml-build-headline! :title-text \"two\")\n           (list (org-ml-build-headline! :title-text \"more\"))\n           (org-ml-insert (point-max)))\n      $> (:result \"* one\"\n                  \"* more\"\n                  \"* two\")\n\n      (:buffer \"a *game* or a /boy/\")\n      (->> (org-ml-build-paragraph! \"we don't care if you're\")\n           (org-ml-insert-tail (point-min)))\n      $> (:result \"we don't care if you're\"\n                  \"a *game* or a /boy/\")\n      :end-hidden))\n\n  (def-example-subgroup \"Update\"\n    nil\n\n    (defexamples-content org-ml-update\n      nil\n      \n      (:buffer \"* TODO win grammy\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-update\n          (lambda (hl) (org-ml-set-property :todo-keyword \"DONE\" hl))))\n      $> \"* DONE win grammy\"\n\n      (:buffer \"* win grammy [0/0]\"\n               \"- [ ] write punk song\"\n               \"- [ ] get new vocalist\"\n               \"- [ ] sell 2 singles\")\n      (org-ml->> (org-ml-parse-this-headline)\n        (org-ml-update*\n          (->> (org-ml-match-map '(:any * item) #'org-ml-item-toggle-checkbox it)\n               (org-ml-headline-update-item-statistics))))\n      $> (:result \"* win grammy [3/3]\"\n                  \"- [X] write punk song\"\n                  \"- [X] get new vocalist\"\n                  \"- [X] sell 2 singles\"))\n\n    (defexamples-content org-ml-update-object-at\n      nil\n      (:buffer \"[[http://example.com][desc]]\")\n      (org-ml-update-object-at* (point)\n        (org-ml-set-property :path \"//buymoreram.com\" it))\n      $> \"[[http://buymoreram.com][desc]]\")\n\n    (defexamples-content org-ml-update-element-at\n      nil\n      (:buffer \"#+call: ktulu()\")\n      (org-ml-update-element-at* (point)\n        (org-ml-set-properties\n         (list :call \"cthulhu\"\n               :inside-header '(:cache no)\n               :arguments '(\"x=4\")\n               :end-header '(:results html))\n         it))\n      $> \"#+call: cthulhu[:cache no](x=4) :results html\")\n\n    (defexamples-content org-ml-update-table-row-at\n      nil\n      (:buffer \"| a | b |\")\n      (org-ml-update-table-row-at* (point)\n        (org-ml-map-children* (cons (org-ml-build-table-cell! \"0\") it) it))\n      $> \"| 0 | a | b |\")\n\n    (defexamples-content org-ml-update-item-at\n      nil\n      (:buffer \"- [ ] thing\")\n      (org-ml-update-item-at* (point)\n        (org-ml-item-toggle-checkbox it))\n      $> \"- [X] thing\")\n\n    (defexamples-content org-ml-update-headline-at\n      nil\n      (:buffer \"* TODO might get done\"\n               \"* DONE no need to update\")\n      (org-ml-update-headline-at* (point)\n        (org-ml-set-property :todo-keyword \"DONE\" it))\n      $> (:result \"* DONE might get done\"\n                  \"* DONE no need to update\"))\n\n    (defexamples-content org-ml-update-subtree-at\n      nil\n      (:buffer \"* one\"\n               \"** two\"\n               \"** three\"\n               \"* not updated\")\n      (org-ml-update-subtree-at* (point)\n        (org-ml-headline-demote-subheadline 1 it))\n      $> (:result \"* one\"\n                  \"** two\"\n                  \"*** three\"\n                  \"* not updated\"))\n\n    (defexamples-content org-ml-update-section-at\n      nil\n      (:buffer \"#+key1: VAL1\"\n               \"#+key2: VAL2\"\n               \"* irrelevant headline\")\n      (:comment \"Update the top buffer section before the headlines start\")\n      (org-ml-update-section-at* (point)\n        (org-ml-map-children* (--map (org-ml-map-property :value #'s-downcase it) it) it))\n      $> (:result \"#+key1: val1\"\n                  \"#+key2: val2\"\n                  \"* irrelevant headline\"))\n\n    (defexamples-content org-ml-update-headlines\n      nil\n      (:buffer \"* one\"\n               \"* two\"\n               \"* three\")\n      (org-ml-update-headlines* 0\n        (org-ml-set-property :todo-keyword \"DONE\" it))\n      $> (:result \"* DONE one\"\n                  \"* two\"\n                  \"* three\")\n      (org-ml-update-headlines* '(0 1)\n        (org-ml-set-property :todo-keyword \"DONE\" it))\n      $> (:result \"* DONE one\"\n                  \"* DONE two\"\n                  \"* three\")\n      (org-ml-update-headlines* [2 nil]\n        (org-ml-set-property :todo-keyword \"DONE\" it))\n      $> (:result \"* one\"\n                  \"* DONE two\"\n                  \"* DONE three\")\n      (org-ml-update-headlines* [2 10]\n        (org-ml-set-property :todo-keyword \"DONE\" it))\n      $> (:result \"* one\"\n                  \"* DONE two\"\n                  \"* three\")\n\n      (:buffer \"* one\"\n               \"* two\"\n               \"* three\")\n      (org-ml-update-headlines* 'all\n        (org-ml-set-property :todo-keyword \"DONE\" it))\n      $> (:result \"* DONE one\"\n                  \"* DONE two\"\n                  \"* DONE three\"))\n\n    (defexamples-content org-ml-update-subtrees\n      nil\n      (:buffer \"* one [/]\"\n               \"** DONE _one\"\n               \"* two [/]\"\n               \"** DONE _one\"\n               \"* three [/]\"\n               \"** DONE _one\")\n      (org-ml-update-subtrees* 0\n        (org-ml-headline-update-todo-statistics it))\n      $> (:result \"* one [1/1]\"\n                  \"** DONE _one\"\n                  \"* two [/]\"\n                  \"** DONE _one\"\n                  \"* three [/]\"\n                  \"** DONE _one\")\n      (org-ml-update-subtrees* '(0 1)\n        (org-ml-headline-update-todo-statistics it))\n      $> (:result \"* one [1/1]\"\n                  \"** DONE _one\"\n                  \"* two [1/1]\"\n                  \"** DONE _one\"\n                  \"* three [/]\"\n                  \"** DONE _one\")\n      (org-ml-update-subtrees* [2 nil]\n        (org-ml-headline-update-todo-statistics it))\n      $> (:result \"* one [/]\"\n                  \"** DONE _one\"\n                  \"* two [1/1]\"\n                  \"** DONE _one\"\n                  \"* three [1/1]\"\n                  \"** DONE _one\")\n      (org-ml-update-subtrees* [nil 5]\n        (org-ml-headline-update-todo-statistics it))\n      $> (:result \"* one [1/1]\"\n                  \"** DONE _one\"\n                  \"* two [/]\"\n                  \"** DONE _one\"\n                  \"* three [/]\"\n                  \"** DONE _one\")\n      (:buffer \"* one [/]\"\n               \"** DONE _one\"\n               \"** DONE _two\"\n               \"* two [/]\"\n               \"** DONE _one\"\n               \"** DONE _two\"))\n\n    (defexamples-content org-ml-update-supercontents\n      nil\n      (:buffer \"* one\")\n      (let ((pl '(:scheduled (2000 1 1))))\n        (org-ml-wrap-impure\n         (org-ml-update-supercontents* nil 'all\n           (org-ml-supercontents-set-planning pl it))))\n      $> (:result \"* one\"\n                  \"SCHEDULED: <2000-01-01 Sat>\")\n      (:buffer \"* one\"\n               \"\"\n               \"something\")\n      (let ((pl '(:scheduled (2000 1 1))))\n        (org-ml-wrap-impure\n         (org-ml-update-supercontents* nil 'all\n           (org-ml-supercontents-set-planning pl it))))\n      $> (:result \"* one\"\n                  \"SCHEDULED: <2000-01-01 Sat>\"\n                  \"\"\n                  \"something\")\n      (:buffer \"* one\"\n               \"** two\")\n      (let ((pl '(:scheduled (2000 1 1))))\n        (org-ml-wrap-impure\n         (org-ml-update-supercontents* nil 'all\n           (org-ml-supercontents-set-planning pl it))))\n      $> (:result \"* one\"\n                  \"SCHEDULED: <2000-01-01 Sat>\"\n                  \"** two\"\n                  \"SCHEDULED: <2000-01-01 Sat>\")\n      (:buffer \"* one\"\n               \"** two\"\n               \"stuff\")\n      (let ((pl '(:scheduled (2000 1 1))))\n        (org-ml-wrap-impure\n         (org-ml-update-supercontents* nil 'all\n           (org-ml-supercontents-set-planning pl it))))\n      $> (:result \"* one\"\n                  \"SCHEDULED: <2000-01-01 Sat>\"\n                  \"** two\"\n                  \"SCHEDULED: <2000-01-01 Sat>\"\n                  \"stuff\")\n      (:buffer \"* one\"\n               \"stuff\")\n      (let ((pl '(:scheduled (2000 1 1))))\n        (org-ml-wrap-impure\n         (org-ml-update-supercontents* nil 'all\n           (org-ml-supercontents-set-planning pl it))))\n      $> (:result \"* one\"\n                  \"SCHEDULED: <2000-01-01 Sat>\"\n                  \"stuff\")))\n\n  (def-example-subgroup \"Misc\"\n    nil\n\n    (defexamples-content org-ml-fold\n      nil)\n\n    (defexamples-content org-ml-unfold\n      nil)))\n\n(provide 'org-ml-examples)\n;;; org-ml-examples.el ends here\n"
  },
  {
    "path": "dev/org-ml-test-common.el",
    "content": ";;; org-ml-test-common.el --- Common Test functions -*- lexical-binding: t; -*-\n\n;; Copyright (C) 2020 Nathan Dwarshuis\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 <http://www.gnu.org/licenses/>.\n\n;;; Commentary:\n\n;;; Code:\n\n(require 's)\n(require 'dash)\n(require 'buttercup)\n\n;; set up standard org environment\n\n(defmacro org-ml--with-org-env (&rest body)\n  \"Execute BODY in a standardized Org-mode buffer.\"\n  `(let ((org-tags-column 20)\n         (org-todo-keywords '((sequence \"TODO\" \"DONE\")))\n         (org-archive-tag \"ARCHIVE\")\n         (org-lowest-priority ?C)\n         (org-highest-priority ?A)\n         (org-list-allow-alphabetical nil)\n         (org-log-into-drawer \"LOGBOOK\"))\n     (with-temp-buffer\n       (org-mode)\n       ,@body)))\n\n(defun example-to-should (actual sym expected)\n  (let ((expected\n         (if (eq (and (listp expected) (car expected)) :result)\n             (s-join \"\\n\" (cdr expected))\n           expected)))\n    (cond ((eq sym '=>)\n           `(expect ,actual :to-equal ,expected))\n          ;; this will only work with defexamples-content\n          ((eq sym '$>)\n           `(expect (progn ,actual (s-trim (buffer-string))) :to-equal ,expected))\n          ;; TODO I never use this?\n          ;; ((eq sym '~>)\n          ;;  `(should (approx-equal ,actual ,expected)))\n          ((eq sym '!!>)\n           `(should-error (eval ',actual) :type ',expected))\n          (t\n           (error \"Invalid test case: %S\" `(,actual ,sym ,expected))))))\n\n(defmacro defexamples (cmd &rest examples)\n  (declare (indent 1))\n  (let ((tests (->> examples\n                    (remove :begin-hidden)\n                    (remove :end-hidden)\n                    (-partition 3)\n                    (--map (apply #'example-to-should it)))))\n    (when tests\n      `(it ,(format \"%S\" cmd) (org-ml--with-org-env ,@tests)))))\n\n(defmacro defexamples-content (cmd _docstring &rest args)\n  (declare (indent 1))\n  (cl-flet*\n      ((make-test-form\n        (test contents)\n        `(org-ml--with-org-env\n          (when ,contents (insert ,contents))\n          (goto-char (point-min))\n          ,test))\n       (make-tests\n        (list)\n        (let ((contents (->> (car list) (-drop 1) (s-join \"\\n\")))\n              (tests\n               (->> (-drop 1 list)\n                    (--remove (eq (and (listp it) (car it)) :comment))\n                    (-partition 3)\n                    (--map (apply #'example-to-should it)))))\n          (--map (make-test-form it contents) tests))))\n    (let ((body\n           (->> args\n                (remove :begin-hidden)\n                (remove :end-hidden)\n                (-partition-before-pred\n                 (lambda (it) (eq (and (listp it) (car it)) :buffer)))\n                (-mapcat #'make-tests))))\n      (when body\n        `(it ,(format \"%S\" cmd) ,@body)))))\n\n(defmacro def-example-subgroup (title _subtitle &rest specs)\n  (declare (indent 1))\n  (when specs\n    `(describe ,title ,@specs)))\n\n(defmacro def-example-group (title _subtitle &rest specs)\n  (declare (indent 1))\n  (when specs\n    `(describe ,title ,@specs)))\n\n(provide 'org-ml-test-common)\n;;; org-ml-test-common.el ends here\n"
  },
  {
    "path": "dev/org-ml-test-external.el",
    "content": ";;; org-ml-test-external.el --- External tests for org-ml -*- lexical-binding: t; -*-\n\n;;; Commentary:\n\n;;; Code:\n\n(require 'org-ml-test-common)\n(require 'org-ml-examples)\n\n(provide 'org-ml-test-external)\n;;; org-ml-test-external.el ends here\n"
  },
  {
    "path": "dev/org-ml-test-internal.el",
    "content": ";;; org-ml-test-internal.el --- Internal tests for org-ml -*- lexical-binding: t; -*-\n\n;;; Commentary:\n\n;;; Code:\n\n(require 's)\n(require 'dash)\n(require 'org-ml)\n(require 'org-ml-macs)\n(require 'org-ml-test-common)\n\n(defconst org-ml--inter-ignore-props\n  (list :begin :contents-begin :end :contents-end :parent :post-affiliated :name\n        :plot :header :results :caption :granularity :mode :robust-begin :robust-end))\n\n;;; LIST OPERATIONS\n\n(describe \"internal list functions\"\n  (describe \"org-ml--pad-or-truncate\"\n    (before-each\n      (setq finite-list '(1 2 3)))\n    (it \"zero length list with zero length\"\n      (expect (org-ml--pad-or-truncate 0 'x nil) :to-equal nil))\n    (it \"zero length list with positive length\"\n      (expect (org-ml--pad-or-truncate 1 'x nil) :to-equal '(x)))\n    (it \"positive length list; length is less\"\n      (expect (org-ml--pad-or-truncate 2 'x finite-list) :to-equal '(1 2)))\n    (it \"positive length list; length is equal\"\n      (expect (org-ml--pad-or-truncate 3 'x finite-list) :to-equal '(1 2 3)))\n    (it \"positive length list; length is greater\"\n      (expect (org-ml--pad-or-truncate 4 'x finite-list) :to-equal '(1 2 3 x)))\n    (it \"positive length list; length is zero\"\n      (expect (org-ml--pad-or-truncate 0 'x finite-list) :to-equal nil)))\n\n  ;; TODO add plist-get-keys?\n  ;; TODO add plist-get-vals?\n  ;; TODO add plist-map-values?\n\n  (describe \"org-ml--is-plist\"\n    (it \"finite plist\"\n      (expect (org-ml--is-plist '(:one one :two 2 :three \"3\")) :to-be-truthy))\n    (it \"zero-length plist\"\n      (expect (org-ml--is-plist nil) :to-be-truthy))\n    (it \"symbols instead of keywords\"\n      (expect (org-ml--is-plist '(one one two 2 three \"3\")) :not :to-be-truthy))\n    (it \"incomplete\"\n      (expect (org-ml--is-plist '(:one one :two 2 :three)) :not :to-be-truthy))\n    (it \"not list\"\n      (expect (org-ml--is-plist \":one one :two 2 :three\") :not :to-be-truthy))))\n\n;; TODO add plist-remove?\n\n;;; inter-list operations\n\n(defmacro org-ml--inter-list-ops-test (fun input output-single\n                                           output-upper output-lower)\n  \"Return form to test intra-index list operations using FUN.\nINPUT is an input list, OUTPUT-SINGLE is a list made as if FUN were\napplied to an empty list, OUTPUT-UPPER is the input list with FUN\napplied as if it was given the highest possible index, and OUTPUT-LOWER\nis the converse.\"\n  (declare (indent 1))\n  `(progn\n     (it \"zero length list at 0\"\n       (expect ,output-single :to-equal (funcall ,fun 0 nil))\n       (expect ,output-single :to-equal (funcall ,fun -1 nil)))\n     (it \"zero length list (overrange)\"\n       (should-error (funcall fun 100 nil))\n       (expect ,output-single :to-equal (funcall ,fun 100 nil t)))\n     (it \"zero length list (underrange)\"\n       (should-error (funcall fun -100 nil))\n       (expect ,output-single :to-equal (funcall ,fun -100 nil t)))\n     (it \"finite list (in range)\"\n       (expect ,output-lower :to-equal (funcall ,fun 0 ,input))\n       (expect ,output-upper :to-equal (funcall ,fun -1 ,input)))\n     (it \"finite list (overrange)\"\n       (should-error (funcall fun 100 '(1 2)))\n       (expect ,output-upper :to-equal (funcall ,fun 100 ,input t)))\n     (it \"finite list (underrange)\"\n       (should-error (funcall fun -100 '(1 2)))\n       (expect ,output-lower :to-equal (funcall ,fun -100 ,input t)))))\n\n(defmacro org-ml--intra-list-ops-test (fun input output-upper output-lower)\n  \"Return form to test intra-index list operations using FUN.\nINPUT is an input list, OUTPUT-UPPER is the input list with FUN\napplied as if it was given the highest possible index, and OUTPUT-LOWER\nis the converse.\"\n  (declare (indent 1))\n  `(progn\n     (it \"index 0 in an empty list\"\n       (should-error (funcall ,fun 0 nil))\n       (should-error (funcall ,fun 0 nil t))\n       (expect (funcall ,fun 0 nil 'permit-empty) :not :to-be-truthy))\n     (it \"overrange in empty list\"\n       (should-error (funcall ,fun 100 nil))\n       (should-error (funcall ,fun 100 nil t))\n       (expect (funcall ,fun 100 nil 'permit-empty) :not :to-be-truthy))\n     (it \"underrange in empty list\"\n       (should-error (funcall ,fun -100 nil))\n       (should-error (funcall ,fun -100 nil t))\n       (expect (funcall ,fun -100 nil 'permit-empty) :not :to-be-truthy))\n     (it \"positive in finite list\"\n       (expect ,output-lower :to-equal (funcall ,fun 0 ,input)))\n     (it \"negative in finite list\"\n       (expect ,output-upper :to-equal (funcall ,fun -1 ,input)))\n     (it \"positive overrange in finite list\"\n       (expect ,output-upper :to-equal (funcall ,fun 100 ,input t))\n       (should-error (funcall ,fun 100 input)))\n     (it \"negative underrange in finite list\"\n       (expect ,output-lower :to-equal (funcall ,fun -100 ,input t))\n       (should-error (funcall fun -100 ,input)))))\n\n(describe \"test consistency of internal list function index references\"\n  (describe \"inter-member references\"\n    ;; These functions operate using indices that refer to spaces between list\n    ;; members. As such there is no such thing as a nonsensical index. Since\n    ;; there will always be the option to add to the front or the back of the\n    ;; list, even an empty list has a logical index that points to these\n    ;; locations (they just happen to be the same). Therefore, the only errors\n    ;; we need to catch here are those that refer to out of range indices.\n\n    (describe \"org-ml--insert-at\"\n      (org-ml--inter-list-ops-test (lambda (n list &optional p)\n                                     (org-ml--insert-at n 'x list p))\n        '(1 2) '(x) '(1 2 x) '(x 1 2)))\n    (describe \"org-ml--split-at\"\n      (org-ml--inter-list-ops-test #'org-ml--split-at\n        '(1 2) nil '((1 2) nil) '(nil (1 2))))\n    (describe \"org-ml--splice-at\"\n      (org-ml--inter-list-ops-test (lambda (n list &optional p)\n                                     (org-ml--splice-at n '(x y) list p))\n        '(1 2) '(x y) '(1 2 x y) '(x y 1 2))))\n\n  (describe \"intra-member references\"\n    ;; These functions operate using indices that refer to explicit members of a\n    ;; list. As such there will be no possible integers that will be valid for\n    ;; an empty list. This provides one extra error case to test, which is the\n    ;; possibility that we cannot operate on the list and thus return nil. All\n    ;; else is the same relative to the inter-list operations tests above\n\n    (describe \"org-ml--remove-at/properties\"\n      (org-ml--intra-list-ops-test #'org-ml--remove-at '(1 2 3) '(1 2) '(2 3)))\n\n    (describe \"org-ml--replace-at/properties\"\n      (org-ml--intra-list-ops-test (lambda (n list &optional p)\n                                     (org-ml--replace-at n 'x list p))\n                                   '(1 2 3) '(1 2 x) '(x 2 3)))\n\n    (describe \"org-ml--nth/properties\"\n      (org-ml--intra-list-ops-test #'org-ml--nth '(1 2 3) 3 1))))\n\n(defmacro org-ml--test-list-functor (fun map-fun single-a single-b multi-a multi-b)\n  (declare (indent 2))\n  `(progn\n    (it \"mapping empty list should return empty list\"\n      (expect (,fun (,map-fun it) nil) :not :to-be-truthy))\n    (it \"mapping list with one member should return that member modified\"\n      (expect ,single-a :to-equal (,fun (,map-fun it) ,single-b)))\n    (it \"mapping list with multiple members should only modify one member\"\n      (expect ,multi-a :to-equal (,fun (,map-fun it) ,multi-b)))\n    (it \"identity should hold true for any length list (0, 1, and 1+)\"\n      (--each '(nil (1) (1 2))\n        (expect it :to-equal (,fun (identity it) it))))))\n\n(describe \"list functors\"\n  (describe \"org-ml--map-first\"\n    (org-ml--test-list-functor org-ml--map-first* upcase\n      '(\"X\") '(\"x\") '(\"A\" \"b\" \"c\") '(\"a\" \"b\" \"c\")))\n  (describe \"org-ml--map-last\"\n    (org-ml--test-list-functor org-ml--map-last* upcase\n      '(\"X\") '(\"x\") '(\"a\" \"b\" \"C\") '(\"a\" \"b\" \"c\"))))\n\n;;; FROM STRING CONVERSTION\n\n(defun org-ml--plist-nonequal-p (exclude-props plist1 plist2)\n  (cl-flet\n      ((partition-plist\n        (props plist)\n        (->> (-partition 2 plist)\n             (--remove (memq (car it) props)))))\n    (let* ((a (partition-plist exclude-props plist1))\n           (b (partition-plist exclude-props plist2))\n           (suba (-difference a b))\n           (subb (-difference b a)))\n      (when (or suba subb)\n        (list suba subb)))))\n\n(defun org-ml--equal~ (exclude-props node1 node2)\n  (if (and (stringp node1) (stringp node2))\n      `(expect ,node1 :to-equal ,node2)\n    (cl-flet\n        ((prop2\n           (key node1 node2)\n           (list (org-element-property key node1)\n                 (org-element-property key node2))))\n      (-let (((type1 . (props1 . children1)) node1)\n             ((type2 . (props2 . children2)) node2)\n             ((pb1 pb2) (prop2 :post-blank node1 node2))\n             ;; NOTE exclude post-blank since some elements won't have it in their\n             ;; regular plist, and we already query it above\n             (xs (append '(:post-blank :standard-properties) exclude-props)))\n        `(progn\n           (expect ',type1 :to-be ',type2)\n           (expect ,pb1 :to-be ,pb2)\n           (expect (org-ml--plist-nonequal-p ',xs ',props1 ',props2) :to-be nil)\n           (and (eq ',type1 ',type2)\n                (->> (-zip-fill nil ',children1 ',children2)\n                     (--all? (org-ml--equal~ ',xs (car it) (cdr it))))))))))\n\n(defun org-ml--test-from-string (omit-props &rest specs)\n  (declare (indent 1))\n  (let ((props (append omit-props org-ml--inter-ignore-props)))\n    (->> (-partition 2 specs)\n         (--map (-let* (((node string) it)\n                        (type (org-ml-get-type node)))\n                  `(it ,(format \"%s - %s\" type (s-replace \"\\n\" \"\\\\n\" string))\n                     ,(org-ml--equal~ props node (org-ml-from-string type string))))))))\n\n(defmacro describe-many (header &rest forms)\n  (declare (indent 1))\n  (let ((it-forms (-flatten-n 1 (-map #'eval forms))))\n    `(describe ,header ,@it-forms)))\n\n(describe \"converting from string\"\n  (describe-many \"object leaf nodes\"\n    (org-ml--test-from-string nil\n      (org-ml-build-code \"code\") \"~code~\")\n    (org-ml--test-from-string '(:latex :latex-math-p :ascii :html :latin1 :utf-8)\n      (org-ml-build-entity \"pi\") \"\\\\pi\")\n    (org-ml--test-from-string nil\n      (org-ml-build-export-snippet \"be\" \"val\") \"@@be:val@@\")\n    (org-ml--test-from-string '(:value)\n      (org-ml-build-inline-babel-call \"ktulu\") \"call_ktulu()\")\n    (org-ml--test-from-string '(:value)\n      (org-ml-build-inline-src-block \"python\") \"src_python{}\")\n    (org-ml--test-from-string nil\n      (org-ml-build-line-break) \"\\\\\\\\\\n\")\n    (org-ml--test-from-string nil\n      (org-ml-build-latex-fragment \"$1+1$\") \"$1+1$\")\n    (org-ml--test-from-string nil\n      (org-ml-build-macro \"macro\") \"{{{macro}}}\")\n    (org-ml--test-from-string '(:value)\n      (org-ml-build-radio-target \"radio\") \"<<<radio>>>\")\n    (org-ml--test-from-string nil\n      (org-ml-build-statistics-cookie '(1 2)) \"[1/2]\"\n      (org-ml-build-statistics-cookie '(nil nil)) \"[/]\"\n      (org-ml-build-statistics-cookie '(50)) \"[50%]\"\n      (org-ml-build-statistics-cookie '(nil)) \"[%]\")\n    (org-ml--test-from-string nil\n      (org-ml-build-target \"target\") \"<<target>>\")\n    ;; TODO this is a bug in org-element which should include :repeater-deadline-value/unit\n    (org-ml--test-from-string '(:raw-value :repeater-deadline-value :repeater-deadline-unit)\n      (org-ml-build-timestamp! '(2020 1 1 0 0)\n                               :end '(2020 1 1 0 10)\n                               :repeater '(cumulate 1 day)\n                               :warning '(all 1 day))\n      \"[2020-01-01 Tue 00:00-00:10 -1d +1d]\")\n    (org-ml--test-from-string nil (org-ml-build-verbatim \"b\") \"=b=\"))\n\n  (describe-many \"object branch nodes\"\n    (org-ml--test-from-string nil\n      (org-ml-build-bold \"bold\") \"*bold*\")\n    (org-ml--test-from-string '(:type)\n      (org-ml-build-footnote-reference \"ref\") \"[fn::ref]\")\n    (org-ml--test-from-string nil\n      (org-ml-build-italic \"italic\") \"/italic/\")\n    (org-ml--test-from-string '(:raw-link :format)\n      (org-ml-build-link \"//example.com\" :type \"https\") \"https://example.com\")\n    (org-ml--test-from-string nil\n      (org-ml-build-subscript \"ss\") \"_ss\")\n    (org-ml--test-from-string nil\n      (org-ml-build-superscript \"ss\") \"^ss\")\n    (org-ml--test-from-string nil\n      (org-ml-build-strike-through \"s\") \"+s+\")\n    (org-ml--test-from-string nil\n      (org-ml-build-table-cell \"cell\") \" cell |\")\n    (org-ml--test-from-string nil\n      (org-ml-build-underline \"u\") \"_u_\"))\n\n  (describe-many \"element leaf nodes\"\n    (org-ml--test-from-string '(:value)\n      (org-ml-build-babel-call \"name\") \"#+call: name()\")\n    (org-ml--test-from-string nil\n      (org-ml-build-center-block) \"#+begin_center\\n#+end_center\"\n      (org-ml-build-center-block (org-ml-build-paragraph! \"p\")) \"#+begin_center\\np\\n#+end_center\")\n    ;; NOTE special treatment for clock so we can compare values directly\n    (-let* ((s \"CLOCK: [2020-01-01 Tue 00:00]\")\n            (result (org-ml-from-string 'clock s))\n            (node (org-ml-build-clock! '(2020 1 1 0 0)))\n            (type (org-ml-get-type node)))\n      `((it ,(format \"clock - %s\" s)\n        (expect ',type :to-be 'clock)\n        (org-ml--equal~ (cons :value org-ml--inter-ignore-props) ',node ',result)\n        (org-ml--equal~ org-ml--inter-ignore-props\n                        (org-element-property :value ',node)\n                        (org-element-property :value ',result)))))\n    (org-ml--test-from-string nil\n      (org-ml-build-comment \"comment\") \"# comment\")\n    (org-ml--test-from-string nil\n      (org-ml-build-comment-block) \"#+begin_comment\\n#+end_comment\"\n      (org-ml-build-comment-block :value \"p\\n\") \"#+begin_comment\\np\\n#+end_comment\")\n    (org-ml--test-from-string nil\n      (org-ml-build-diary-sexp :value '(print 'hi)) \"%%(print 'hi)\")\n    (org-ml--test-from-string '(:value :retain-labels :use-labels)\n      (org-ml-build-example-block) \"#+begin_example\\n#+end_example\"\n      (org-ml-build-example-block :value \"v\\n\") \"#+begin_example\\nv\\n#+end_example\")\n    (org-ml--test-from-string nil\n      (org-ml-build-export-block \"TYPE\" \"value\\n\") \"#+begin_export TYPE\\nvalue\\n#+end_export\")\n    (org-ml--test-from-string nil\n      (org-ml-build-fixed-width \"val\") \": val\")\n    (org-ml--test-from-string nil\n      (org-ml-build-horizontal-rule) \"------\")\n    (org-ml--test-from-string nil\n      (org-ml-build-latex-environment '(\"env\" \"value\")) \"\\\\begin{env}\\nvalue\\n\\\\end{env}\")\n    (org-ml--test-from-string nil\n      (org-ml-build-keyword \"K\" \"v\") \"#+K: v\")\n    (org-ml--test-from-string nil\n      (org-ml-build-special-block \"type\") \"#+begin_type\\n#+end_type\")\n    (org-ml--test-from-string '(:number-lines :retain-labels :use-labels :label-fmt)\n      (org-ml-build-src-block :value \"(print 'hi)\\n\") \"#+begin_src\\n(print 'hi)\\n#+end_src\")\n    (org-ml--test-from-string nil\n      (org-ml-build-node-property \"KEY\" \"val\") \":KEY: val\")\n    (org-ml--test-from-string '(:scheduled :deadline :closed)\n      (org-ml-build-planning! :scheduled '(2020 1 1)) \"SCHEDULED: [2020-01-01 Tue]\"))\n\n  (describe-many \"element branch nodes\"\n    (org-ml--test-from-string nil\n      (org-ml-build-drawer \"DRAW\") \":DRAW:\\n:END:\"\n      (org-ml-build-drawer \"DRAW\" (org-ml-build-paragraph! \"p\")) \":DRAW:\\np\\n:END:\")\n    (org-ml--test-from-string nil\n      (org-ml-build-dynamic-block \"name\") \"#+begin: name\\n#+end\"\n      (org-ml-build-dynamic-block \"name\" (org-ml-build-paragraph! \"p\")) \"#+begin: name\\np\\n#+end\")\n    (org-ml--test-from-string nil\n      (org-ml-build-footnote-definition \"label\" (org-ml-build-paragraph! \"p\")) \"[fn:label] p\")\n    (org-ml--test-from-string '(:raw-value)\n      (org-ml-build-headline! :title-text \"headline\") \"* headline\")\n    (org-ml--test-from-string '(:structure)\n      (org-ml-build-item! :paragraph \"item\") \"- item\")\n    (org-ml--test-from-string nil\n      (org-ml-build-paragraph! \"para\") \"para\"\n      (org-ml-build-paragraph! \"*para\") \"*para\"\n      (org-ml-build-paragraph) \"\")\n    (org-ml--test-from-string '(:structure :type)\n      (org-ml-build-plain-list (org-ml-build-item! :paragraph \"item\")) \"- item\")\n    (org-ml--test-from-string nil\n      (org-ml-build-section (org-ml-build-paragraph! \"sec\")) \"sec\"\n      (org-ml-build-section (org-ml-build-paragraph! \"*sec\")) \"*sec\")\n    (org-ml--test-from-string nil\n      (org-ml-build-property-drawer) \":PROPERTIES:\\n:END:\"\n      (org-ml-build-property-drawer! '(\"KEY\" \"val\")) \":PROPERTIES:\\n:KEY: val\\n:END:\")\n    (org-ml--test-from-string nil\n      (org-ml-build-quote-block) \"#+begin_quote\\n#+end_quote\"\n      (org-ml-build-quote-block (org-ml-build-paragraph! \"p\")) \"#+begin_quote\\np\\n#+end_quote\")\n    (org-ml--test-from-string nil\n      (org-ml-build-table! '(\"a\")) \"| a |\"\n      (org-ml-build-table) \"|\")\n    (org-ml--test-from-string nil\n      (org-ml-build-table-row! '(\"a\")) \"| a |\"\n      (org-ml-build-table-row-hline) \"|---|\"\n      (org-ml-build-table-row) \"|\")\n    (org-ml--test-from-string nil\n      (org-ml-build-verse-block) \"#+begin_verse\\n#+end_verse\"\n      (org-ml-build-verse-block \"hi\\n\") \"#+begin_verse\\nhi\\n#+end_verse\")))\n\n;;; PARSING INVERTABILITY\n\n;; For all org buffer contents, parsing and printing should be\n;; perfect inverses.\n\n;; These tests test/use the following:\n;; - all the parse functions\n;; - `org-ml-to-string'\n;; - `org-ml-get-type'\n\n(defun org-ml--test-contents-parse-inversion (type parse-fun contents-list\n                                                   &optional prefix suffix)\n  \"Return form to test the parse/print inversion of CONTENTS-LIST.\nUse PARSE-FUN to get the node tree from the contents. All should\nbe parsed to TYPE.\"\n  (declare (indent 2))\n  (let* ((contents-list (--map (if (consp it) (s-join \"\\n\" it) it)\n                               contents-list))\n         (suffix-char (if (memq type org-ml-elements) \"\\n\" \" \"))\n         ;; Also test each string with a space after it. In some cases, this\n         ;; won't parse correctly, hence the filter.\n         (contents-list-space\n          (unless (memq type '(node-property plain-text line-break table-cell))\n            (--map (s-append suffix-char it) contents-list)))\n         (test-list (append contents-list contents-list-space)))\n    (--each test-list\n      (-let* ((at (if prefix (1+ (length prefix)) 1))\n              ((parsed parsed-type)\n               (org-ml--with-org-env\n                (when prefix (insert prefix))\n                (insert it)\n                (when suffix (insert suffix))\n                (let ((p (funcall parse-fun at)))\n                  (list (org-ml-to-string p) (org-ml-get-type p))))))\n        (if (equal type parsed-type)\n            (should t)\n          (print (format \"%s parsed as %s\" it parsed-type)))\n        (should (equal it parsed))))))\n\n(describe \"parse and print should be perfect inverses\"\n  (describe \"object nodes\"\n    (describe \"leaves\"\n      (it \"code\"\n        (org-ml--test-contents-parse-inversion 'code #'org-ml-parse-object-at\n          (list \"~code~\")))\n\n      (it \"entity\"\n        (org-ml--test-contents-parse-inversion 'entity #'org-ml-parse-object-at\n          (list \"\\\\pi\" \"\\\\pi{}\")))\n\n      (it \"export-snippet\"\n        (org-ml--test-contents-parse-inversion 'export-snippet #'org-ml-parse-object-at\n          (list \"@@x:y@@\")))\n\n      (it \"inline-babel-call\"\n        (org-ml--test-contents-parse-inversion 'inline-babel-call #'org-ml-parse-object-at\n          (list \"call_ktulu()\"\n                \"call_ktulu(n=1)\"\n                \"call_ktulu[:x y]()\"\n                \"call_ktulu[:x y](n=1)\"\n                \"call_ktulu()[:a b]\"\n                \"call_ktulu(n=1)[:a b]\"\n                \"call_ktulu[:x y]()[:a b]\"\n                \"call_ktulu[:x y](n=1)[:a b]\")))\n\n      (it \"inline-src-block\"\n        (org-ml--test-contents-parse-inversion 'inline-src-block #'org-ml-parse-object-at\n          (list \"src_python{}\"\n                \"src_python{print \\\"yo\\\"}\"\n                \"src_python[:x y]{}\"\n                \"src_python[:x y]{print \\\"yo\\\"}\")))\n\n      (it \"line-break\"\n        (org-ml--test-contents-parse-inversion 'line-break #'org-ml-parse-object-at\n          (list \"\\\\\\\\\\n\")))\n\n      (it \"latex-fragment\"\n        (org-ml--test-contents-parse-inversion 'latex-fragment #'org-ml-parse-object-at\n          (list \"$2+2=5$\")))\n\n      (it \"macro\"\n        (org-ml--test-contents-parse-inversion 'macro #'org-ml-parse-object-at\n          (list \"{{{key}}}\"\n                \"{{{key(x=4)}}}\")))\n\n      (it \"statistics-cookie\"\n        (org-ml--test-contents-parse-inversion 'statistics-cookie #'org-ml-parse-object-at\n          (list \"[/]\"\n                \"[0/0]\"\n                \"[%]\"\n                \"[0%]\")))\n\n      (it \"timestamp\"\n        (org-ml--test-contents-parse-inversion 'timestamp #'org-ml-parse-object-at\n          (list \"[2019-01-01 Tue]\"\n                \"[2019-01-01 Tue 12:00]\"\n                \"[2019-01-01 Tue 12:00-13:00]\"\n                \"[2019-01-01 Tue 12:00]--[2019-01-01 Tue 13:00]\"\n                \"[2019-01-01 Tue]--[2019-01-02 Wed]\"\n                \"<2019-01-01 Tue>\"\n                \"[2019-01-01 Tue +1d]\"\n                \"[2019-01-01 Tue -1y]\"\n                \"[2019-01-01 Tue +1d -1y]\"\n                \"<%%(diary-float 1 3 2) 00:00>\"\n                \"<%%(diary-float 1 3 2) 00:00-12:00>\"\n                \"<%%(diary-float 1 3 2)>\")))\n\n      (it \"verbatim\"\n        (org-ml--test-contents-parse-inversion 'verbatim #'org-ml-parse-object-at\n          (list \"=verbatim=\")))\n\n      (it \"plain-text\"\n        (org-ml--test-contents-parse-inversion 'plain-text #'org-ml-parse-object-at\n          (list \"plain-text\"\n                ;; all syntax chars by themselves should be plain-text\n                \"**\" \"~~\" \"@@:@@\" \"//\" \"[]\" \"[[]]\" \"{{{}}}\" \"<>\" \"<<>>\"\n                \"<<<>>>\" \"++\" \"^\" \"_\" \"__\" \"==\"))))\n\n    (describe \"branches\"\n      (it \"bold\"\n        (org-ml--test-contents-parse-inversion 'bold #'org-ml-parse-object-at\n          (list \"*bold*\")))\n\n      (it \"footnote-reference\"\n        (org-ml--test-contents-parse-inversion 'footnote-reference #'org-ml-parse-object-at\n          (list \"[fn:label]\" \"[fn:label:nodes]\")\n          \" \"))\n\n      (it \"italic\"\n        (org-ml--test-contents-parse-inversion 'italic #'org-ml-parse-object-at\n          (list \"/italic/\")))\n\n      (it \"link\"\n        ;; ignore the value of `org-link-abbrev-alist'\n        (let ((org-link-abbrev-alist '((\"test\" . \"fail\"))))\n          (org-ml--test-contents-parse-inversion 'link #'org-ml-parse-object-at\n            ;; this is not exhaustive but hopefully good enough\n            (list \"https://downloadmoreram.com\"\n                  \"mailto:vladimirputin@pwned.ru\"\n                  \"file:/home/kalilinux/pwneddata\"\n                  \"<https://downloadmoreram.com>\"\n                  \"[[test:foo]]\"\n                  \"[[https://downloadmoreram.com]]\"\n                  \"[[https://downloadmoreram.com][legit advice]]\"))))\n\n      (it \"radio-target\"\n        (org-ml--test-contents-parse-inversion 'radio-target #'org-ml-parse-object-at\n          (list \"<<<radio>>>\")))\n\n      (it \"strike-through\"\n        (org-ml--test-contents-parse-inversion 'strike-through #'org-ml-parse-object-at\n          (list \"+strike+\")))\n\n      (it \"subscript\"\n        (org-ml--test-contents-parse-inversion 'subscript #'org-ml-parse-object-at\n          (list \"_sub\" \"_{sub}\")\n          \"dummy\"))\n\n      (it \"superscript\"\n        (org-ml--test-contents-parse-inversion 'superscript #'org-ml-parse-object-at\n          (list \"^super\" \"^{super}\")\n          \"dummy\"))\n\n      (it \"table-cell\"\n        (org-ml--test-contents-parse-inversion 'table-cell #'org-ml-parse-object-at\n          (list \" cell |\")\n          \"|\"))))\n\n  (describe \"element nodes\"\n    (describe \"leaves\"\n      (it \"babel-call\"\n        (org-ml--test-contents-parse-inversion 'babel-call #'org-ml-parse-element-at\n          (list \"#+call: name()\\n\"\n                \"#+call: name(x=1)\\n\"\n                \"#+call: name[:x y](x=1)\\n\"\n                \"#+call: name[:x y]()\\n\"\n                \"#+call: name[:x y](x=1) :a b\\n\"\n                \"#+call: name[:x y]() :a b\\n\"\n                \"#+call: name[]() :a b\\n\")))\n\n      ;; TODO this doesn't work\n      (it \"clock\"\n        (org-ml--test-contents-parse-inversion 'clock #'org-ml-parse-element-at\n          (list \"CLOCK: [2019-01-01 Tue]\\n\"\n                \"CLOCK: [2019-01-01 Tue]--[2019-01-02 Wed] => 24:00\\n\"\n                \"CLOCK: [2019-01-01 Tue 00:00-01:00] =>  1:00\\n\"\n                )))\n\n      (it \"comment\"\n        (org-ml--test-contents-parse-inversion 'comment #'org-ml-parse-element-at\n          (list \"# one\\n\"\n                '(\"# one\"\n                  \"# two\\n\")\n                ;; TODO this doesn't work\n                ;; \"#\\n\"\n                )))\n\n      (it \"comment-block\"\n        (org-ml--test-contents-parse-inversion 'comment-block #'org-ml-parse-element-at\n          (list '(\"#+begin_comment\"\n                  \"battle of being\"\n                  \"#+end_comment\\n\")\n                '(\"#+begin_comment\"\n                  \"#+end_comment\\n\"))))\n\n      (it \"diary-sexp\"\n        (org-ml--test-contents-parse-inversion 'diary-sexp #'org-ml-parse-element-at\n          (list \"%%()\\n\" \"%%(whatever)\\n\")))\n\n      (it \"example-block\"\n        (org-ml--test-contents-parse-inversion 'example-block #'org-ml-parse-element-at\n          (list '(\"#+begin_example\"\n                  \"  example.com\"\n                  \"#+end_example\\n\")\n                '(\"#+begin_example\"\n                  \"#+end_example\\n\"))))\n\n      (it \"export-block\"\n        (org-ml--test-contents-parse-inversion 'export-block #'org-ml-parse-element-at\n          (list '(\"#+begin_export PLAIN\"\n                  \"bullet, bombs, bigotry\"\n                  \"#+end_export\\n\")\n                ;; TODO type needs to always be uppercase?\n                ;; '(\"#+BEGIN_EXPORT plain\"\n                ;;   \"#+END_EXPORT\\n\")\n                '(\"#+begin_export PLAIN\"\n                  \"#+end_export\\n\"))))\n\n      ;; ;; TODO this will randomly insert a blank after it is parsed\n      ;; (it \"fixed-width\"\n      ;;   (org-ml--test-contents-parse-inversion 'fixed-width #'org-ml-parse-element-at\n      ;;     (list \": crucifixed\"\n      ;;           ;; TODO this make a blank\n      ;;           ;; \":\\n\"\n      ;;           )))\n\n      (it \"horizontal-rule\"\n        (org-ml--test-contents-parse-inversion 'horizontal-rule #'org-ml-parse-element-at\n          (list \"-----\\n\")))\n\n      (it \"keyword\"\n        (org-ml--test-contents-parse-inversion 'keyword #'org-ml-parse-element-at\n          (list \"#+key: val\\n\"\n                ;; TODO must be lowercase and must have at least a space\n                ;; \"#+KEY: \\n\"\n                \"#+key: \\n\")))\n\n      (it \"latex-environment\"\n        (org-ml--test-contents-parse-inversion 'latex-environment #'org-ml-parse-element-at\n          (list '(\"\\\\begin{env}\"\n                  \"\\\\end{env}\\n\")\n                '(\"\\\\begin{env}\"\n                  \"latex >>> ms word\"\n                  \"\\\\end{env}\\n\"))))\n\n      (it \"node-property\"\n        (org-ml--test-contents-parse-inversion 'node-property #'org-ml-parse-element-at\n          (list \":node:     prop\\n\"\n                ;; TODO node props will always be returned with 5 spaces after\n                ;; \":node:\\n\"\n                \":node:     \\n\")\n          \"* dummy\\n:PROPERTIES:\\n\"\n          \":END:\\n\"))\n\n      (it \"planning\"\n        (org-ml--test-contents-parse-inversion 'planning #'org-ml-parse-element-at\n          (list \"CLOSED: <2019-01-01 Tue>\\n\"\n                \"CLOSED: <2019-01-01 Tue +1d>\\n\"\n                \"CLOSED: <2019-01-01 Tue -1y>\\n\"\n                \"CLOSED: <2019-01-01 Tue +1d -1y>\\n\")\n          \"* dummy\\n\"))\n\n      (it \"src-block\"\n        (org-ml--test-contents-parse-inversion 'src-block #'org-ml-parse-element-at\n          (list '(\"#+begin_src\"\n                  \"#+end_src\\n\")\n                ;; TODO this doesn't work if is isn't indented\n                '(\"#+begin_src python -n :x y\"\n                  \"  print \\\"yo\\\"\"\n                  \"#+end_src\\n\")))))\n\n    (describe \"branches (object node children)\"\n      (it \"paragraph\"\n        (org-ml--test-contents-parse-inversion 'paragraph #'org-ml-parse-element-at\n          ;; TODO there are probably other things I could put here\n          (list \"paragraph\\n\")))\n\n      (it \"table-row\"\n        (org-ml--test-contents-parse-inversion 'table-row #'org-ml-parse-table-row-at\n          (list \"| cell |\\n\"\n                ;; TODO this makes an empty string\n                ;; \"| |\\n\"\n                )))\n\n      (it \"verse-block\"\n        (org-ml--test-contents-parse-inversion 'verse-block #'org-ml-parse-element-at\n          (list '(\"#+begin_verse\"\n                  \"#+end_verse\\n\")\n                '(\"#+begin_verse\"\n                  \"Once upon a midnight dreary...\"\n                  \"#+end_verse\\n\")))))\n\n    (describe \"branches (element node children)\"\n      (it \"center-block\"\n        (org-ml--test-contents-parse-inversion 'center-block #'org-ml-parse-element-at\n          (list '(\"#+begin_center\"\n                  \"#+end_center\\n\")\n                '(\"#+begin_center\"\n                  \"Of the universe...\"\n                  \"#+end_center\\n\"))))\n\n      (it \"drawer\"\n        (org-ml--test-contents-parse-inversion 'drawer #'org-ml-parse-element-at\n          (list '(\":LOGBOOK:\"\n                  \":END:\\n\")\n                '(\":LOGBOOK:\"\n                  \"- logged thingy\"\n                  \":END:\\n\"))))\n\n      (it \"dynamic-block\"\n        (org-ml--test-contents-parse-inversion 'dynamic-block #'org-ml-parse-element-at\n          (list '(\"#+begin: name\"\n                  \"#+end:\\n\")\n                '(\"#+begin: name\"\n                  \"Random contents...\"\n                  \"#+end:\\n\"))))\n\n      (it \"footnote-definition\"\n        ;; TODO blanks are apparently not allowed and will error\n        (org-ml--test-contents-parse-inversion 'footnote-definition #'org-ml-parse-element-at\n          (list ;; \"[fn:label] \\n\"\n           ;; TODO needs a random space at the end\n           ;; \"[fn:label]\"\n           \"[fn:label] stuff after\\n\"\n           )))\n\n      (it \"headline\"\n        (org-ml--test-contents-parse-inversion 'headline #'org-ml-parse-element-at\n          ;; this is not exhaustive...\n          (list \"* dummy\\n\"\n                \"** dummy\\n\"\n                \"* COMMENT dummy\\n\"\n                \"* TODO COMMENT dummy\\n\"\n                \"* TODO dummy\\n\"\n                \"* TODO [#A] dummy\\n\"\n                \"* dummy\\n\\n\"\n\n                ;; BUG extra space added to the end of this (no section seems fine)\n                ;; \"* dummy\\nsomething\\n\"\n                ;; \"* dummy\\n\\n** dummy\\n\"\n\n                ;; BUG additionally, the space after \"something\" gets taken out\n                ;; \"* dummy\\nsomething\\n\\n** dummy\\n\"\n\n                ;; BUG order of comment and priority comes out reversed\n                ;; \"* TODO [#A] COMMENT dummy\\n\"\n                ;; \"* [#A] COMMENT dummy\\n\"\n                )))\n\n      (it \"item\"\n        (org-ml--test-contents-parse-inversion 'item #'org-ml-parse-item-at\n          ;; this is not exhaustive...\n          ;; TODO not sure why these have two newlines\n          (list \"- \\n\\n\"\n                \"1. \\n\\n\"\n                ;; TODO this becomes -\n                ;; \"+ \\n\\n\"\n                ;; TODO this becomes 1.\n                ;; \"1) \\n\\n\"\n                \"- thing\\n\"\n                \"- tagged :: thing\\n\"\n                \"1. [@20] thing\\n\"\n                )))\n\n      (it \"plain-list\"\n        (org-ml--test-contents-parse-inversion 'plain-list #'org-ml-parse-element-at\n          (list \"- thing\\n\"\n                \"1. thing\\n\"\n                '(\"- thing\"\n                  \"- more thing\\n\")\n                \"- one\\n- two\\n\"\n                \"- one\\n  - two\\n\"\n                \"- one\\n\\n  - two\\n\"\n                \"- one\\n  - two\\n\\n\"\n                )))\n\n\n      (it \"property-drawer\"\n        (org-ml--test-contents-parse-inversion 'property-drawer #'org-ml-parse-element-at\n          (list '(\":PROPERTIES:\"\n                  \":END:\\n\")\n                '(\":PROPERTIES:\"\n                  \":Effort:   0:30\"\n                  \":END:\\n\"))\n          \"* dummy\\n\"))\n\n      (it \"quote-block\"\n        (org-ml--test-contents-parse-inversion 'quote-block #'org-ml-parse-element-at\n          (list '(\"#+begin_quote\"\n                  \"#+end_quote\\n\")\n                '(\"#+begin_quote\"\n                  \"Fear is the mind killer...\"\n                  \"#+end_quote\\n\"))))\n\n      (it \"section\"\n        (org-ml--test-contents-parse-inversion 'section #'org-ml-parse-section-at\n          (list \"things that could be a paragraph\\n\"\n                \"#+key: val\\n\"\n                \"# nothing important...\\n\")))\n\n      (it \"special-block\"\n        (org-ml--test-contents-parse-inversion 'special-block #'org-ml-parse-element-at\n          (list '(\"#+begin_special\"\n                  \"#+end_special\\n\")\n                '(\"#+begin_special\"\n                  \"You don't belong here\"\n                  \"#+end_special\\n\"))))\n\n      (it \"table\"\n        (org-ml--test-contents-parse-inversion 'table #'org-ml-parse-element-at\n          (list \"| simple |\\n\"\n                \"| less | simple |\\n\"\n                '(\"| R | A |\"\n                  \"| G | E |\\n\")\n                ;; TODO this makes a blank string\n                ;; \"| |\\n\"\n                ))))))\n\n;;; FUNCTIONAL PURITY\n\n(defmacro org-ml--test-purity (header node &rest forms)\n  \"Test that FORMS will not modify NODE by side effect.\nHEADER is the it-header.\"\n  (declare (indent 1))\n  (let ((it-forms (-map\n                   (lambda (form)\n                     `(let* ((it ,node)\n                             (s0 (org-ml-to-string it))\n                             (sx (org-ml-to-string ,form))\n                             (s1 (org-ml-to-string it)))\n                        (if (equal s0 s1)\n                            (should t)\n                          (let ((x (format \"Form %S has a side effect on '%s', making '%s'\" ',form s0 s1)))\n                            (expect x :to-be nil)))\n                        (if (not (equal s0 sx))\n                            (should t)\n                          (let ((x (format \"Form %S has no effect on '%s'\" ',form s0)))\n                            (expect x :to-be nil)))))\n                   forms)))\n    `(it ,header ,@it-forms)))\n\n(describe \"all functions that modify nodes should be pure\"\n  (org-ml--test-purity \"polymorphic setters\"\n    (org-ml-build-headline! :title-text \"hi\" :tags '(\"stuff\"))\n    (org-ml-set-property :level 2 it)\n    (org-ml-set-properties '(:level 2 :archivedp t) it)\n    (org-ml-shift-property :level 1 it)\n    (org-ml-map-property :level (lambda (x) (1+ x)) it)\n    (org-ml-map-properties '(:level (lambda (_) 2) :archivedp (lambda (_) t)) it)\n    (org-ml-toggle-property :archivedp it)\n    (org-ml-insert-into-property :tags 0 \"stfu\" it)\n    (org-ml-remove-from-property :tags \"stuff\" it))\n\n  (org-ml--test-purity \"polymorphic setters (plist)\"\n    (org-ml-from-string 'inline-call \"#+call: ktulu[:cache no]()\")\n    (org-ml-plist-put-property :end-header :results 'html it)\n    (org-ml-plist-remove-property :inside-header :cache it))\n\n  (describe \"leaf nodes\"\n    (org-ml--test-purity \"timestamp setters\"\n      (org-ml-build-timestamp! '(2024 1 1 0 0) :end '(2024 1 1 0 1))\n      (org-ml-timestamp-set-start-time '(2024 2 1 0 0) it)\n      (org-ml-timestamp-set-end-time '(2024 2 1 0 0) it)\n      (org-ml-timestamp-set-single-time '(2024 1 2 0 0) it)\n      (org-ml-timestamp-set-double-time '(2024 1 2 0 0) '(2024 1 3 0 0) it)\n      (org-ml-timestamp-set-length 1 'day it)\n      (org-ml-timestamp-set-active t it)\n      (org-ml-timestamp-shift 1 'day it)\n      (org-ml-timestamp-shift-start 1 'day it)\n      (org-ml-timestamp-shift-end 1 'day it)\n      (org-ml-timestamp-toggle-active it)\n      (org-ml-timestamp-truncate it)\n      (org-ml-timestamp-truncate-start it)\n      (org-ml-timestamp-truncate-end it)\n      (org-ml-timestamp-set-collapsed nil it)\n      (org-ml-timestamp-set-warning '(all 1 day) it)\n      (org-ml-timestamp-map-warning (lambda (it) '(all 2 day)) it)\n      (org-ml-timestamp-set-repeater '(restart 1 day) it)\n      (org-ml-timestamp-map-repeater (lambda (it) '(restart 2 day)) it)\n      (org-ml-timestamp-set-repeater '(restart 1 day) it)\n      (org-ml-timestamp-map-repeater (lambda (it) '(restart 2 day)) it))\n\n    (org-ml--test-purity \"timestamp diary setters\"\n      (org-ml-build-timestamp-diary '(diary-float t 4 2) :start '(12 0) :end '(13 0))\n      (org-ml-timestamp-diary-set-value '(diary-float t 4 3) it)\n      (org-ml-timestamp-diary-set-start-time '(0 0) it)\n      (org-ml-timestamp-diary-set-end-time '(0 0) it)\n      (org-ml-timestamp-diary-set-single-time '(0 0) it)\n      (org-ml-timestamp-diary-set-double-time '(0 0) '(0 1) it)\n      (org-ml-timestamp-diary-set-length 2 'hour it)\n      (org-ml-timestamp-diary-shift 1 'hour it)\n      (org-ml-timestamp-diary-shift-start 1 'hour it)\n      (org-ml-timestamp-diary-shift-end 1 'hour it))\n\n    (org-ml--test-purity \"headline setters\"\n      (org-ml-build-headline! :title-text \"really impressive title\" :section-children (list (org-ml-build-paragraph! \"hi\")))\n      (org-ml-headline-set-title! \"really *impressive* title\" '(2 3) it))\n\n    (org-ml--test-purity \"item setters\"\n      (org-ml-build-item!\n       :checkbox 'off\n       :paragraph \"petulant /frenzy/\"\n       (org-ml-build-plain-list\n        (org-ml-build-item! :bullet '- :paragraph \"below\")))\n      (org-ml-item-toggle-checkbox it)))\n\n  (describe \"branch nodes\"\n\n  ;; TODO polymorphic branch setters\n\n    (org-ml--test-purity \"polymorphic\"\n      (org-ml-build-paragraph! \"/this/ is a *paragraph*\")\n      (org-ml-set-children (list \"this is lame\") it)\n      (org-ml-map-children (lambda (_) (list \"this is lame\")) it))\n\n    (org-ml--test-purity \"objects\"\n      (org-ml-from-string 'underline \"_1 *2* 3 */4/* 5 /6/_ \")\n      (apply #'org-ml-build-paragraph (org-ml-unwrap it))\n      (apply #'org-ml-build-paragraph (org-ml-unwrap-types-deep '(bold) it))\n      (apply #'org-ml-build-paragraph (org-ml-unwrap-deep it)))\n\n    (org-ml--test-purity \"secondary strings\"\n      (org-ml-build-paragraph! \"This (1 *2* 3 */4/* 5 /6/) is randomly formatted \")\n      (->> (org-ml-get-children it)\n           (org-ml-flatten)\n           (apply #'org-ml-build-paragraph))\n      (->> (org-ml-get-children it)\n           (org-ml-flatten-types-deep '(italic))\n           (apply #'org-ml-build-paragraph))\n      (->> (org-ml-get-children it)\n           (org-ml-flatten-deep)\n           (apply #'org-ml-build-paragraph))\n      )\n\n    (org-ml--test-purity \"item setters\"\n      (org-ml-build-item!\n       :checkbox 'off\n       :paragraph \"petulant frenzy\"\n       (org-ml-build-plain-list\n        (org-ml-build-item! :bullet '- :paragraph \"below\")))\n      (org-ml-item-set-paragraph '(\"calm\") it)\n      (org-ml-item-map-paragraph* (-map #'upcase it) it))\n\n    (org-ml--test-purity \"headline setters\"\n      (org-ml-build-headline!\n       :title-text \"really impressive title\"\n       :pre-blank 1\n       :section-children (list (org-ml-build-paragraph! \"something useful\")))\n      (org-ml-headline-set-section (list (org-ml-build-paragraph! \"x-section\")) it)\n      (org-ml-headline-map-section*\n        (cons (org-ml-build-planning! :closed '(2019 1 1)) it)\n        it)\n      (org-ml-headline-set-subheadlines\n       (list (org-ml-build-headline! :level 2 :title-text \"headline x\"))\n       it)\n      (org-ml-headline-map-subheadlines*\n        (cons (org-ml-build-headline! :level 2 :title-text \"headline x\") it)\n        it)\n      (org-ml-headline-set-planning '(:closed (2019 1 1)) it)\n      (org-ml-headline-map-planning (lambda (_) '(:closed (2019 1 1))) it)\n      (org-ml-headline-set-node-properties '((\"Effort\" \"0:01\") (\"ID\" \"easy\")) it)\n      (org-ml-headline-map-node-properties*\n        (cons (list \"New\" \"world man\") it)\n        it)\n      (org-ml-headline-set-node-property \"ID\" \"real\" it)\n      (org-ml-headline-map-node-property \"ID\" (lambda (_) \"real\") it))\n\n    (org-ml--test-purity \"headline setters (logbook)\"\n      (->> (org-ml-build-headline! :title-text \"really impressive title\")\n           (org-ml-headline-logbook-append-open-clock\n            '(:log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n            (- 1546300800 (car (current-time-zone))))\n           (org-ml-headline-logbook-append-item\n            '(:log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n            (org-ml-build-log-note (- 1546300800 (car (current-time-zone))) \"new note\")))\n      (org-ml-headline-set-logbook-items\n       '(:log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n       nil\n       it)\n      (org-ml-headline-map-logbook-items\n       '(:log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n       (lambda (_) nil)\n       it)\n      (org-ml-headline-set-logbook-clocks\n       '(:log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n       nil\n       it)\n      (org-ml-headline-map-logbook-clocks\n       '(:log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n       (lambda (_) nil)\n       it)\n      (org-ml-headline-set-supercontents\n       '(:log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n       `(:blank 0 :contents (,(org-ml-build-paragraph! \"new contents\")))\n       it)\n      (org-ml-headline-map-supercontents\n       '(:log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n        (lambda (_) `(:blank 0 :contents (,(org-ml-build-paragraph! \"new contents\"))))\n        it)\n      (org-ml-headline-logbook-append-item\n       '(:log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n       (org-ml-build-log-note (- 1546300800 (car (current-time-zone))) \"new note\")\n       it)\n      (org-ml-headline-logbook-append-open-clock\n       '(:log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n       (- 1546300800 (car (current-time-zone)))\n       it)\n      (org-ml-headline-logbook-close-open-clock\n       '(:log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n       (- 1546310800 (car (current-time-zone))) nil\n       it)\n      (org-ml-headline-set-contents\n       '(:log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n       (list (org-ml-build-paragraph! \"I'm new\"))\n       it)\n      (org-ml-headline-map-contents\n       '(:log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n        (lambda (_) (list (org-ml-build-paragraph! \"I'm new\")))\n        it)\n      (org-ml-headline-logbook-convert-config\n       '(:log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n       '(:log-into-drawer \"LLL\" :clock-into-drawer \"CCC\" :clock-out-notes t)\n       it))\n      \n    (org-ml--test-purity \"headline setters (stats cookie item)\"\n      (org-ml-build-headline!\n       :title-text \"really impressive title\"\n       :statistics-cookie '(0 0)\n       :section-children (list\n                          (org-ml-build-plain-list\n                           (org-ml-build-item! :checkbox 'on :paragraph \"the one\"))))\n      (org-ml-headline-update-item-statistics it))\n    \n    (org-ml--test-purity \"headline setters (stats cookie todo)\"\n      (org-ml-build-headline!\n       :title-text \"really impressive title\"\n       :statistics-cookie '(0 0)\n       (org-ml-build-headline! :title-text \"the one\" :todo-keyword \"DONE\"))\n      (org-ml-headline-update-todo-statistics it))\n\n    (org-ml--test-purity \"headline setters (indentation)\"\n      (org-ml-build-headline!\n       :title-text \"one\"\n       (org-ml-build-headline!\n        :level 2 :title-text \"two\")\n       (org-ml-build-headline!\n        :level 2 :title-text \"three\"\n        (org-ml-build-headline! :level 3 :title-text \"four\")\n        (org-ml-build-headline! :level 3 :title-text \"five\")\n        (org-ml-build-headline! :level 3 :title-text \"six\")))\n      (org-ml-headline-demote-subheadline 1 it)\n      (org-ml-headline-demote-subtree 1 it)\n      (org-ml-headline-promote-subheadline 1 1 it)\n      (org-ml-headline-promote-all-subheadlines 1 it))\n\n    (org-ml--test-purity \"plain list\"\n      (org-ml-build-plain-list\n       (org-ml-build-item! :checkbox 'off :paragraph \"one\")\n       (org-ml-build-item! :checkbox 'on :paragraph \"two\"))\n      (org-ml-plain-list-set-type 'ordered it))\n\n    (org-ml--test-purity \"plain list (indentation)\"\n      (org-ml-build-plain-list\n       (org-ml-build-item! :paragraph \"one\")\n       (org-ml-build-item!\n        :paragraph \"two\"\n        (org-ml-build-plain-list\n         (org-ml-build-item! :paragraph \"three\")\n         (org-ml-build-item! :paragraph \"four\")\n         (org-ml-build-item! :paragraph \"five\")))\n       (org-ml-build-item! :paragraph \"six\"))\n      (org-ml-plain-list-indent-item 1 it)\n      (org-ml-plain-list-indent-item-tree 1 it)\n      (org-ml-plain-list-outdent-item 1 0 it)\n      (org-ml-plain-list-outdent-all-items 1 it))\n\n    (org-ml--test-purity \"table\"\n      (org-ml-from-string 'table \"| a | b |\\n|---+---|\\n| c | d |\")\n      (org-ml-table-delete-column 0 it)\n      (org-ml-table-delete-row 0 it)\n      (org-ml-table-insert-column! 1 '(\"x\" \"y\") it)\n      (org-ml-table-insert-row! 1 '(\"x\" \"y\") it)\n      (org-ml-table-replace-cell! 0 0 \"2\" it)\n      (org-ml-table-replace-column! 0 '(\"A\" \"B\") it)\n      (org-ml-table-replace-row! 0 '(\"A\" \"B\") it))))\n\n;;; NODE PROPERTY COMPLETENESS\n\n(defun should-have-equal-properties (e1 e2)\n  (unless (eq (org-ml-get-type e1) (org-ml-get-type e2))\n    (error \"Type mismatch: %s\\n\\n%s\" e1 e2))\n  (cl-flet\n      ((plist-get-keys\n        (plist)\n        (let ((keys (-slice plist 0 nil 2)))\n          (if (org-ml-is-any-type org-ml--element-nodes-with-affiliated e1)\n              (-difference keys '(:name :plot :header :results :caption))\n            keys))))\n    (let ((p1 (plist-get-keys (nth 1 e1)))\n          (p2 (plist-get-keys (nth 1 e2))))\n      (expect (-difference p1 p2) :not :to-be-truthy)\n      (expect (-difference p2 p1) :not :to-be-truthy))))\n\n(defun org-ml--compare-object-props (elem string)\n  (should-have-equal-properties\n   elem\n   (->> (org-ml--from-string (concat \" \" string))\n        (org-ml--get-descendent '(0 1)))))\n\n(defun org-ml--compare-element-props (elem string)\n  (should-have-equal-properties\n   elem\n   (->> (org-ml--from-string string)\n        (org-ml--get-descendent '(0)))))\n\n(describe \"ensure builders include all properties\"\n  (describe \"object nodes\"\n    (describe \"leaves\"\n      (it \"org-ml--code\"\n        (org-ml--compare-object-props\n         (org-ml-build-code \"value\") \"~code~\"))\n\n      (it \"org-ml--entity\"\n        (org-ml--compare-object-props\n         (org-ml-build-entity \"pi\") \"\\\\pi\"))\n\n      (it \"org-ml--export-snippet\"\n        (org-ml--compare-object-props\n         (org-ml-build-export-snippet \"backend\" \"value\") \"@@im:padme@@\"))\n\n      (it \"org-ml--inline-babel-call\"\n        (org-ml--compare-object-props\n         (org-ml-build-inline-babel-call \"name\") \"call_name()\"))\n\n      (it \"org-ml--inline-src-block\"\n        (org-ml--compare-object-props\n         (org-ml-build-inline-src-block \"lang\") \"src_lang{value}\"))\n\n      ;; TODO add latex fragment\n\n      (it \"org-ml--line-break\"\n        (org-ml--compare-object-props\n         (org-ml-build-line-break) \"\\\\\\\\\\n\"))\n\n      (it \"org-ml--macro\"\n        (org-ml--compare-object-props\n         (org-ml-build-macro \"value\") \"{{{value}}}\"))\n\n      (it \"org-ml--statistics-cookie\"\n        (org-ml--compare-object-props\n         (org-ml-build-statistics-cookie '(1)) \"[/]\"))\n\n      (it \"org-ml--target\"\n        (org-ml--compare-object-props\n         (org-ml-build-target \"value\") \"<<value>>\"))\n\n      (it \"org-ml--timestamp\"\n        (org-ml--compare-object-props\n         (org-ml-build-timestamp! '(2019 1 1))\n         ;; TODO the timestamp parser does not add properties for warnings,\n         ;; deadlines, or repeaters if they are not given, this appears to be a\n         ;; bug\n         \"[2019-01-01 Tue +1d/3d -1d]\"))\n\n      (it \"org-ml--verbatim\"\n        (org-ml--compare-object-props\n         (org-ml-build-verbatim \"value\") \"=value=\")))\n\n    (describe \"branches\"\n      (it \"org-ml--bold\"\n        (org-ml--compare-object-props\n         (org-ml-build-bold) \"*bold*\"))\n\n      (it \"org-ml--footnote-reference\"\n        (org-ml--compare-object-props\n         (org-ml-build-footnote-reference) \"[fn:1]\"))\n\n      (it \"org-ml--italic\"\n        (org-ml--compare-object-props\n         (org-ml-build-italic) \"/italic/\"))\n\n      (it \"org-ml--link\"\n        (org-ml--compare-object-props\n         (org-ml-build-link \"path\") \"[[path]]\"))\n\n      (it \"org-ml--radio-target\"\n        (org-ml--compare-object-props\n         (org-ml-build-radio-target) \"<<<target>>>\"))\n\n      (it \"org-ml--strike-through\"\n        (org-ml--compare-object-props\n         (org-ml-build-strike-through) \"+bad+\"))\n\n      (it \"org-ml--superscript\"\n        (should-have-equal-properties\n         (org-ml-build-superscript)\n         (->> (org-ml--from-string \"thisis^super\")\n              (org-ml--get-descendent '(0 1)))))\n\n      (it \"org-ml--subscript\"\n        (should-have-equal-properties\n         (org-ml-build-subscript)\n         (->> (org-ml--from-string \"thisis_subpar\")\n              (org-ml--get-descendent '(0 1)))))\n\n      (it \"org-ml--table-cell\"\n        (should-have-equal-properties\n         (org-ml-build-table-cell \"cell\")\n         (->> (org-ml--from-string \"| cell |\")\n              (org-ml--get-descendent '(0 0 0)))))\n\n      (it \"org-ml--underline\"\n        (org-ml--compare-object-props\n         (org-ml-build-underline) \"_bad_\"))))\n\n  (describe \"element nodes\"\n    (describe \"leaves\"\n      (it \"org-ml--babel-call\"\n        (org-ml--compare-element-props\n         (org-ml-build-babel-call \"call\") \"#+call: name()\"))\n\n      (it \"org-ml--clock\"\n        (org-ml--compare-element-props\n         (org-ml-build-clock (org-ml-build-timestamp! '(2019 1 1)))\n         \"CLOCK: [2019-01-01 Tue]\"))\n\n      (it \"org-ml--comment\"\n        (org-ml--compare-element-props\n         (org-ml-build-comment \"useless\") \"# useless\"))\n\n      (it \"org-ml--comment-block\"\n        (org-ml--compare-element-props\n         (org-ml-build-comment-block)\n         \"#+begin_comment\\nuseless\\n#+end_comment\"))\n\n      (it \"org-ml--diary-sexp\"\n        (org-ml--compare-element-props\n         (org-ml-build-diary-sexp) \"%%()\"))\n\n      (it \"org-ml--example-block\"\n        (org-ml--compare-element-props\n         (org-ml-build-example-block)\n         \"#+begin_example\\nuseless\\n#+end_example\"))\n\n      (it \"org-ml--export-block\"\n        (org-ml--compare-element-props\n         (org-ml-build-export-block \"type\" \"value\")\n         \"#+begin_export type\\nuseless\\n#+end_export\"))\n\n      (it \"org-ml--fixed-width\"\n        (org-ml--compare-element-props\n         (org-ml-build-fixed-width \"value\") \": value\"))\n\n      (it \"org-ml--horizontal-rule\"\n        (org-ml--compare-element-props\n         (org-ml-build-horizontal-rule) \"-----\"))\n\n      (it \"org-ml--keyword\"\n        (org-ml--compare-element-props\n         (org-ml-build-keyword \"key\" \"val\") \"#+KEY: val\"))\n\n      (it \"org-ml--latex-environment\"\n        (org-ml--compare-element-props\n         (org-ml-build-latex-environment '(\"gloves\" \"text\"))\n         \"\\\\begin{env}\\nvalue\\n\\\\end{env}\"))\n\n      (it \"org-ml--node-property\"\n        (should-have-equal-properties\n         (org-ml-build-node-property \"key\" \"value\")\n         (->> (org-ml--from-string \"* dummy\\n:PROPERTIES:\\n:key: val\\n:END:\")\n              (org-ml--get-descendent '(0 0 0)))))\n\n      (it \"org-ml--planning\"\n        (should-have-equal-properties\n         (org-ml-build-planning :closed (org-ml-build-timestamp! '(2019 1 1) :active nil))\n         (->> (org-ml--from-string \"* dummy\\nCLOSED: <2019-01-01 Tue>\")\n              (org-ml--get-descendent '(0 0)))))\n\n      (it \"org-ml--src-block\"\n        (org-ml--compare-element-props\n         (org-ml-build-src-block)\n         \"#+begin_src\\nuseless\\n#+end_src\")))\n\n    (describe \"branches\"\n      (it \"org-ml--paragraph\"\n        (should-have-equal-properties\n         (org-ml-build-paragraph)\n         (->> (org-ml--from-string \"text\")\n              (org-ml--get-descendent '(0)))))\n\n      (it \"org-ml--table-row\"\n        (should-have-equal-properties\n         (org-ml-build-table-row)\n         (->> (org-ml--from-string \"| row |\")\n              (org-ml--get-descendent '(0 0)))))\n\n      (it \"org-ml--verse-block\"\n        (should-have-equal-properties\n         (org-ml-build-verse-block)\n         (->> (org-ml--from-string \"#+begin_verse\\nthing\\n#+end_verse\")\n              (org-ml--get-descendent '(0)))))\n\n      (it \"org-ml--center-block\"\n        (org-ml--compare-element-props\n         (org-ml-build-center-block)\n         \"#+begin_center\\nuseless\\n#+end_center\"))\n\n      (it \"org-ml--drawer\"\n        (org-ml--compare-element-props\n         (org-ml-build-drawer \"name\")\n         \":LOGBOOK:\\nuseless\\n:END:\"))\n\n      (it \"org-ml--dynamic-block\"\n        (org-ml--compare-element-props\n         (org-ml-build-dynamic-block \"name\" :arguments '(:key val))\n         \"#+begin: name args\\nuseless\\n#+end:\"))\n\n      (it \"org-ml--footnote-definition\"\n        (org-ml--compare-element-props\n         (org-ml-build-footnote-definition \"label\") \"[fn:label]\\n\"))\n\n      (it \"org-ml--headline\"\n        (should-have-equal-properties\n         (org-ml-build-headline)\n         (org-ml--from-string \"* head\")))\n\n      (it \"org-ml--item\"\n        (should-have-equal-properties\n         (org-ml-build-item)\n         (->> (org-ml--from-string \"- head\")\n              (org-ml--get-descendent '(0 0)))))\n\n      (it \"org-ml--plain-list\"\n        (org-ml--compare-element-props\n         (org-ml-build-plain-list) \"- item\"))\n\n      (it \"org-ml--property-drawer\"\n        (should-have-equal-properties\n         (org-ml-build-property-drawer)\n         (->> (org-ml--from-string \"* dummy\\n:PROPERTIES:\\n:END:\")\n              (org-ml--get-descendent '(0 0)))))\n\n      (it \"org-ml--quote-block\"\n        (org-ml--compare-element-props\n         (org-ml-build-quote-block)\n         \"#+begin_quote\\n#+end_quote\"))\n\n      (it \"org-ml--section\"\n        (org-ml--compare-element-props\n         (org-ml-build-section) \"* dummy\\nstuff\"))\n\n      (it \"org-ml--special-block\"\n        (org-ml--compare-element-props\n         (org-ml-build-special-block \"type\")\n         \"#+begin_type:\\n#+end_type:\"))\n\n      (it \"org-ml--table\"\n        (org-ml--compare-element-props\n         (org-ml-build-table) \"| table |\")))))\n\n;; ;; SPECIALIZED DEFUN MACRO TESTS\n\n(describe \"org-ml--defun-kw internal definition\"\n  (describe \"org-ml--make-header\"\n    (it \"make header\"\n      (expect (org-ml--make-header '(\"docstring\" (print 'hi)) nil)\n              :to-equal\n              \"docstring\\n\\n(fn)\")\n      (expect (org-ml--make-header '(\"docstring\" (print 'hi)) '(one))\n              :to-equal\n              \"docstring\\n\\n(fn ONE)\")\n      (expect (org-ml--make-header '(\"docstring\" (print 'hi)) '(one two))\n              :to-equal\n              \"docstring\\n\\n(fn ONE TWO)\")))\n\n  (describe \"org-ml--make-kwarg-let/error\"\n    (it \"list too long\"\n      (should-error (org-ml--make-kwarg-let '(one two three))))\n    (it \"keyword slot must be a real keyword\"\n      (should-error (org-ml--make-kwarg-let '((one two))))\n      (should-error (org-ml--make-kwarg-let '((one two) three))))\n    (it \"single arg must be a symbol but not a keyword\"\n      ;; TODO the keyword guard does not work yet\n      ;; (should-error (org-ml--make-kwarg-let :one))\n      (should-error (org-ml--make-kwarg-let 1))\n      (should-error (org-ml--make-kwarg-let \"one\"))\n      (should-error (org-ml--make-kwarg-let '(1)))))\n\n  (describe \"org-ml--make-rest-partition-form\"\n    (describe \"valid restargs\"\n      (it \"single arg\"\n        (expect '(nil . (one)) :to-equal\n                (org-ml--make-rest-partition-form '(one) nil t)))\n      (it \"multiple args\"\n        (expect '(nil . (one two)) :to-equal\n                (org-ml--make-rest-partition-form '(one two) nil t))))\n\n    (describe \"error\"\n      (it \"invalid keywords\"\n        (should-error (org-ml--make-rest-partition-form '(:one one) '(:two) nil)))\n      (it \"too many arguments\"\n        (should-error (org-ml--make-rest-partition-form '(:one one two) (:one) nil)))\n      (it \"multiple keywords\"\n        (should-error (org-ml--make-rest-partition-form '(:one one :one three two) (:one) nil))))))\n\n;;; SUPERCONTENTS FRAMEWORK TESTING\n\n(defun org-ml--test-merge-logbook-valid (config output items clocks)\n  `(expect (org-ml--merge-logbook ,config ,items ,clocks)\n           :to-equal ,output))\n\n(defun org-ml--test-merge-logbook-error (config items clocks)\n  `(should-error (org-ml--merge-logbook ,config ,items ,clocks)))\n\n(defmacro org-ml--test-merge-logbook-specs (config &rest specs)\n  (declare (indent 1))\n  (let ((forms\n         (->> (-partition 4 specs)\n              (--map\n               (-let (((title output items clocks) it))\n                 `(it ,title\n                    ,(if (eq output 'error)\n                         (org-ml--test-merge-logbook-error config items clocks)\n                       (org-ml--test-merge-logbook-valid config output items clocks))))))))\n    `(progn ,@forms)))\n\n(describe \"org-ml--merge-logbook\"\n  (before-all\n    (setq enconf (org-ml--scc-encode nil)\n          enconf-notes (org-ml--scc-encode '(:clock-out-notes t))\n          c1 (org-ml-build-clock! '(2020 1 1 0 0) :end '(2020 1 1 1 0))\n          i1 (org-ml-build-log-note (org-ml-timelist-to-unixtime '(2020 1 2 0 0)) \"1\")\n          c2 (org-ml-build-clock! '(2020 1 3 0 0) :end '(2020 1 3 1 0))\n          i2 (org-ml-build-log-note (org-ml-timelist-to-unixtime '(2020 1 4 0 0)) \"2\")\n          n1 (org-ml-build-item! :paragraph \"clock note\")\n          p1 (org-ml-build-plain-list i1)\n          p2 (org-ml-build-plain-list i2)\n          pn1 (org-ml-build-plain-list n1)\n          p1n1 (org-ml-build-plain-list n1 i1)\n          ;; there should never be a p12 analogue since that's not the right order\n          p21 (org-ml-build-plain-list i2 i1)\n          x1 (org-ml-build-code \"I should cause a fatal error\")))\n  (describe \"without clock notes\"\n    (org-ml--test-merge-logbook-specs enconf\n      \"nothing\" nil nil nil\n      \"just clocks\" `(,c2 ,c1) nil `(,c1 ,c2)\n      \"just items\" `(,p21) `(,i1 ,i2) nil\n      \"single clock and item\" `(,p1 ,c1) `(,i1) `(,c1)\n      \"clocks and items\" `(,p2 ,c2 ,p1 ,c1) `(,i1 ,i2) `(,c1 ,c2)\n      \"just clocks (note)\" error nil `(,c1 ,n1 ,c2)\n      \"single clock (note) and item\" error `(,i1) `(,c1 ,n1)\n      \"clocks (note) and items\" error `(,i1 ,i2) `(,c1 ,n1 ,c2)\n      \"just clocks (note in wrong place)\" error nil `(,n1 ,c1 ,c2)\n      \"just garbage (items)\" error `(,x1) nil\n      \"just garbage (clocks)\" error nil `(,x1)))\n  \n  (describe \"with clock notes\"\n    (org-ml--test-merge-logbook-specs enconf-notes\n      \"nothing\" nil nil nil\n      \"just clocks\" `(,c2 ,c1) nil `(,c1 ,c2)\n      \"just items\" `(,p21) `(,i1 ,i2) nil\n      \"single clock and item\" `(,p1 ,c1) `(,i1) `(,c1)\n      \"clocks and items\" `(,p2 ,c2 ,p1 ,c1) `(,i1 ,i2) `(,c1 ,c2)\n      \"just clocks (note)\" `(,c2 ,c1 ,pn1) nil `(,c1 ,n1 ,c2)\n      \"single clock (note) and item\" `(,p1 ,c1 ,pn1) `(,i1) `(,c1 ,n1)\n      \"clocks (note) and items\" `(,p2 ,c2 ,p1 ,c1 ,pn1) `(,i1 ,i2) `(,c1 ,n1 ,c2)\n      \"clocks (note) and items (different order)\" `(,p2 ,c2 ,p1n1 ,c1) `(,i1 ,i2) `(,c1 ,c2 ,n1)\n      \"just clocks (note in wrong place)\" error nil `(,n1 ,c1 ,c2)\n      \"just garbage (items)\" error `(,x1) nil\n      \"just garbage (clocks)\" error nil `(,x1))))\n\n(defmacro expect-separated (c m items clocks unknown in)\n  `(-let (((&alist 'items i 'clocks c 'unknown u)\n           (-group-by #'car (org-ml--separate-logbook ,c ,m ,in))))\n     (expect (list ,items ,clocks ,unknown)\n             :to-equal\n             (list (-map #'cdr i)\n                   (-map #'cdr c)\n                   (-map #'cdr u)))))\n\n(defmacro org-ml--test-separate-logbook-specs (config mode &rest specs)\n  (declare (indent 2))\n  (let ((forms\n         (->> (-partition 5 specs)\n              (--map\n               (-let (((title items clocks unknown input) it))\n                 `(it ,title (expect-separated ,config ,mode ,items ,clocks ,unknown ,input)))))))\n    `(progn ,@forms)))\n\n(describe \"org-ml--separate-logbook\"\n  (before-all\n    (setq enconf (org-ml--scc-encode nil)\n          enconf-notes (org-ml--scc-encode '(:clock-out-notes t))\n          c1 (org-ml-build-clock! '(2020 1 1 0 0) :end '(2020 1 1 1 0))\n          i1 (org-ml-build-log-note (org-ml-timelist-to-unixtime '(2020 1 2 0 0)) \"1\")\n          c2 (org-ml-build-clock! '(2020 1 3 0 0) :end '(2020 1 3 1 0))\n          i2 (org-ml-build-log-note (org-ml-timelist-to-unixtime '(2020 1 4 0 0)) \"2\")\n          n1 (org-ml-build-item! :paragraph \"clock note\")\n          p1 (org-ml-build-plain-list i1)\n          p2 (org-ml-build-plain-list i2)\n          pn1 (org-ml-build-plain-list n1)\n          pn11 (org-ml-build-plain-list n1 i1)\n          p1n1 (org-ml-build-plain-list i1 n1)\n          p12 (org-ml-build-plain-list i1 i2)\n          x1 (org-ml-build-code \"I should cause a fatal error\")))\n\n  (describe \"mixed mode\"\n    (describe \"without clock notes\"\n      (org-ml--test-separate-logbook-specs enconf :mixed\n        \"nothing\" nil nil nil nil\n        \"single item\" `(,i1) nil nil `(,p1)\n        \"single clock\" nil `(,c1) nil `(,c1)\n        \"single garbage entry\" nil nil `(,x1) `(,x1)\n        \"single item and clock\" `(,i1) `(,c1) nil `(,p1 ,c1)\n        \"single item and garbage\" `(,i1) nil `(,x1) `(,p1 ,x1)\n        \"single clock and garbage\" nil `(,c1) `(,x1) `(,c1 ,x1)\n        \"single item, clock, and garbage\" `(,i1) `(,c1) `(,x1) `(,p1 ,c1 ,x1)\n        \"multiple items and clocks\" `(,i2 ,i1) `(,c2 ,c1) nil `(,p12 ,c1 ,c2)\n        \"multiple items and clocks (interlaced)\" `(,i2 ,i1) `(,c2 ,c1) nil `(,p1 ,c1 ,p2 ,c2)\n        \"clock with note\" nil `(,c1) `(,n1) `(,c1 ,pn1)\n        \"clock with note in wrong place\" nil `(,c1) `(,n1) `(,pn1 ,c1)\n        \"clock with note and item\" `(,i1) `(,c1) `(,n1) `(,c1 ,pn11)\n        \"clock with item and note\" `(,i1) `(,c1) `(,n1) `(,c1 ,p1n1)))\n    (describe \"clock notes\"\n      (org-ml--test-separate-logbook-specs enconf-notes :mixed\n        \"nothing\" nil nil nil nil\n        \"single item\" `(,i1) nil nil `(,p1)\n        \"single clock\" nil `(,c1) nil `(,c1)\n        \"single garbage entry\" nil nil `(,x1) `(,x1)\n        \"single item and clock\" `(,i1) `(,c1) nil `(,p1 ,c1)\n        \"single item and garbage\" `(,i1) nil `(,x1) `(,p1 ,x1)\n        \"single clock and garbage\" nil `(,c1) `(,x1) `(,c1 ,x1)\n        \"single item, clock, and garbage\" `(,i1) `(,c1) `(,x1) `(,p1 ,c1 ,x1)\n        \"multiple items and clocks\" `(,i2 ,i1) `(,c2 ,c1) nil `(,p12 ,c1 ,c2)\n        \"multiple items and clocks (interlaced)\" `(,i2 ,i1) `(,c2 ,c1) nil `(,p1 ,c1 ,p2 ,c2)\n        \"clock with note\" nil `(,n1 ,c1) nil `(,c1 ,pn1)\n        \"clock with note in wrong place\" nil `(,c1) `(,n1) `(,pn1 ,c1)\n        \"clock with note and item\" `(,i1) `(,n1 ,c1) nil `(,c1 ,pn11)\n        \"clock with item and note\" `(,i1) `(,c1) `(,n1) `(,c1 ,p1n1))))\n\n  (describe \"items mode\"\n    (describe \"without clock notes\"\n      (org-ml--test-separate-logbook-specs enconf :items\n        \"nothing\" nil nil nil nil\n        \"single item\" `(,i1) nil nil `(,p1)\n        \"single clock\" nil nil `(,c1) `(,c1)\n        \"single garbage entry\" nil nil `(,x1) `(,x1)\n        \"single item and clock\" `(,i1) nil `(,c1) `(,p1 ,c1)\n        \"single item and garbage\" `(,i1) nil `(,x1) `(,p1 ,x1)\n        \"single clock and garbage\" nil nil `(,x1 ,c1) `(,c1 ,x1)\n        \"single item, clock, and garbage\" `(,i1) nil `(,x1 ,c1) `(,p1 ,c1 ,x1)\n        \"multiple items and clocks\" `(,i2 ,i1) nil `(,c2 ,c1) `(,p12 ,c1 ,c2)\n        \"multiple items and clocks (interlaced)\" `(,i2 ,i1) nil `(,c2 ,c1) `(,p1 ,c1 ,p2 ,c2)\n        \"clock with note\" nil nil `(,n1 ,c1) `(,c1 ,pn1)\n        \"clock with note in wrong place\" nil nil `(,c1 ,n1) `(,pn1 ,c1)\n        \"clock with note and item\" `(,i1) nil `(,n1 ,c1) `(,c1 ,pn11)\n        \"clock with item and note\" `(,i1) nil `(,n1 ,c1) `(,c1 ,p1n1)))\n    (describe \"with clock notes\"\n      (org-ml--test-separate-logbook-specs enconf-notes :items\n        \"nothing\" nil nil nil nil\n        \"single item\" `(,i1) nil nil `(,p1)\n        \"single clock\" nil nil `(,c1) `(,c1)\n        \"single garbage entry\" nil nil `(,x1) `(,x1)\n        \"single item and clock\" `(,i1) nil `(,c1) `(,p1 ,c1)\n        \"single item and garbage\" `(,i1) nil `(,x1) `(,p1 ,x1)\n        \"single clock and garbage\" nil nil `(,x1 ,c1) `(,c1 ,x1)\n        \"single item, clock, and garbage\" `(,i1) nil `(,x1 ,c1) `(,p1 ,c1 ,x1)\n        \"multiple items and clocks\" `(,i2 ,i1) nil `(,c2 ,c1) `(,p12 ,c1 ,c2)\n        \"multiple items and clocks (interlaced)\" `(,i2 ,i1) nil `(,c2 ,c1) `(,p1 ,c1 ,p2 ,c2)\n        \"clock with note\" nil nil `(,n1 ,c1) `(,c1 ,pn1)\n        \"clock with note in wrong place\" nil nil `(,c1 ,n1) `(,pn1 ,c1)\n        \"clock with note and item\" `(,i1) nil `(,n1 ,c1) `(,c1 ,pn11)\n        \"clock with item and note\" `(,i1) nil `(,n1 ,c1) `(,c1 ,p1n1))))\n\n  (describe \"clocks mode\"\n    (describe \"without clock notes\"\n      (org-ml--test-separate-logbook-specs enconf :clocks\n        \"nothing\" nil nil nil nil\n        \"single item\" nil nil `(,i1) `(,p1)\n        \"single clock\" nil `(,c1) nil `(,c1)\n        \"single garbage entry\" nil nil `(,x1) `(,x1)\n        \"single item and clock\" nil `(,c1) `(,i1) `(,p1 ,c1)\n        \"single item and garbage\" nil nil `(,x1 ,i1) `(,p1 ,x1)\n        \"single clock and garbage\" nil `(,c1) `(,x1) `(,c1 ,x1)\n        \"single item, clock, and garbage\" nil `(,c1) `(,x1 ,i1) `(,p1 ,c1 ,x1)\n        \"multiple items and clocks\" nil `(,c2 ,c1) `(,i2 ,i1) `(,p12 ,c1 ,c2)\n        \"multiple items and clocks (interlaced)\" nil `(,c2 ,c1) `(,i2 ,i1) `(,p1 ,c1 ,p2 ,c2)\n        \"clock with note\" nil `(,c1) `(,n1) `(,c1 ,pn1)\n        \"clock with note in wrong place\" nil `(,c1) `(,n1) `(,pn1 ,c1)\n        \"clock with note and item\" nil `(,c1) `(,i1 ,n1) `(,c1 ,pn11)\n        \"clock with item and note\" nil `(,c1) `(,n1 ,i1) `(,c1 ,p1n1)))\n    (describe \"with clock notes\"\n      (org-ml--test-separate-logbook-specs enconf-notes :clocks\n        \"nothing\" nil nil nil nil\n        \"single item\" nil nil `(,i1) `(,p1)\n        \"single clock\" nil `(,c1) nil `(,c1)\n        \"single garbage entry\" nil nil `(,x1) `(,x1)\n        \"single item and clock\" nil `(,c1) `(,i1) `(,p1 ,c1)\n        \"single item and garbage\" nil nil `(,x1 ,i1) `(,p1 ,x1)\n        \"single clock and garbage\" nil `(,c1) `(,x1) `(,c1 ,x1)\n        \"single item, clock, and garbage\" nil `(,c1) `(,x1 ,i1) `(,p1 ,c1 ,x1)\n        \"multiple items and clocks\" nil `(,c2 ,c1) `(,i2 ,i1) `(,p12 ,c1 ,c2)\n        \"multiple items and clocks (interlaced)\" nil `(,c2 ,c1) `(,i2 ,i1) `(,p1 ,c1 ,p2 ,c2)\n        \"clock with note\" nil `(,n1 ,c1) nil `(,c1 ,pn1)\n        \"clock with note in wrong place\" nil `(,c1) `(,n1) `(,pn1 ,c1)\n        \"clock with note and item\" nil `(,n1 ,c1) `(,i1) `(,c1 ,pn11)\n        \"clock with item and note\" nil `(,c1) `(,n1 ,i1) `(,c1 ,p1n1)))))\n\n(defmacro org-ml--test-logbook-to-nodes (c out items clocks)\n  `(->> (org-ml--logbook-init ,items ,clocks nil)\n        (org-ml--logbook-to-nodes ,c)\n        (equal ,out)\n        (should)))\n\n(defmacro org-ml--test-logbook-to-nodes-specs (&rest specs)\n  (let ((forms\n         (->> (-partition 5 specs)\n              (--map\n               (-let (((title config output items clocks) it))\n                 `(it ,title (org-ml--test-logbook-to-nodes ,config ,output ,items ,clocks)))))))\n    `(progn ,@forms)))\n\n(describe \"logbook to nodes\"\n  (before-all\n    (setq i-name \"LOGGING\"\n          c-name \"CLOCKING\"\n          m-name \"LOGBOOK\"\n          c1 (->> (org-ml-build-clock! '(2020 1 1 0 0) :end '(2020 1 1 1 0))\n                  (org-ml-remove-parents))\n          i1 (->> (org-ml-build-log-note (org-ml-timelist-to-unixtime '(2020 1 2 0 0)) \"1\")\n                  (org-ml-remove-parents))\n          c2 (->> (org-ml-build-clock! '(2020 1 3 0 0) :end '(2020 1 3 1 0))\n                  (org-ml-remove-parents))\n          i2 (->> (org-ml-build-log-note (org-ml-timelist-to-unixtime '(2020 1 4 0 0)) \"2\")\n                  (org-ml-remove-parents))\n          p1 (org-ml-build-plain-list i1)\n          p2 (org-ml-build-plain-list i2)\n          p21 (org-ml-build-plain-list i2 i1)\n          di (org-ml-build-drawer i-name p21)\n          di* (org-ml-build-drawer m-name p21)\n          dc (org-ml-build-drawer c-name c2 c1)\n          dc* (org-ml-build-drawer m-name c2 c1)\n          dm (org-ml-build-drawer m-name p2 c2 p1 c1)))\n  ;; ASSUME the sorting function takes care of clock notes, and since\n  ;; everything passes through that, don't test it here\n  (org-ml--test-logbook-to-nodes-specs\n   \"no config\" nil\n   `(,p2 ,c2 ,p1 ,c1) `(,i1 ,i2) `(,c1 ,c2)\n\n   \"item drawer\" `(:log-into-drawer ,i-name)\n   `(,di ,c2 ,c1) `(,i1 ,i2) `(,c1 ,c2)\n\n   \"clock drawer\" `(:clock-into-drawer ,c-name)\n   `(,dc ,p21) `(,i1 ,i2) `(,c1 ,c2)\n\n   \"item and clock drawer (different)\" `(:log-into-drawer ,i-name :clock-into-drawer ,c-name)\n   `(,di ,dc) `(,i1 ,i2) `(,c1 ,c2)\n\n   \"items and clock drawer (same)\" '(:log-into-drawer t :clock-into-drawer t)\n   `(,dm) `(,i1 ,i2) `(,c1 ,c2)\n   \"clock limit\" '(:log-into-drawer nil :clock-into-drawer 1)\n   `(,dc* ,p21) `(,i1 ,i2) `(,c1 ,c2)\n\n   \"clock limit (higher)\" '(:log-into-drawer nil :clock-into-drawer 2)\n   `(,p2 ,c2 ,p1 ,c1) `(,i1 ,i2) `(,c1 ,c2)\n   \n   \"clock limit and item drawer (same)\" '(:log-into-drawer t :clock-into-drawer 1)\n   `(,dm) `(,i1 ,i2) `(,c1 ,c2)\n   \n   \"clock limit (higher) and item drawer (same)\" '(:log-into-drawer t :clock-into-drawer 2)\n   `(,di* ,c2 ,c1) `(,i1 ,i2) `(,c1 ,c2)\n\n   \"clock limit and item drawer (different)\" `(:log-into-drawer ,i-name :clock-into-drawer 1)\n   `(,di ,dc*) `(,i1 ,i2) `(,c1 ,c2)\n\n   \"clock limit (higher and item drawer (different)\" `(:log-into-drawer ,i-name :clock-into-drawer 2)\n   `(,di ,c2 ,c1) `(,i1 ,i2) `(,c1 ,c2) nil))\n\n;; eight possible configurations for the logbook based on the values of\n;; `org-log-into-drawer' (L) and `org-clock-into-drawer' (C)\n;; - L = C = nil: 'mixed'\n;; - L = string, C = nil: 'single-items'\n;; - L = nil, C = string: 'single-clocks'\n;; - L = C = string: 'single-mixed'\n;; - L = string1, C = string2: 'dual'\n;; - L = nil, C = int: 'single-clocks-or-mixed'\n;; - L = string, C = int: 'single-items-or-dual'\n;; - L = \"LOGBOOK\", C = int: 'single-mixed-or-single-items'\n\n(defmacro expect-supercontents (config nodes items clocks unknown\n                                               blank rest)\n  (declare (indent 2))\n  `(expect (org-ml--supersection-to-supercontents ,config (list :pre-blank 0 :section ,nodes))\n           :to-equal\n           (org-ml--supercontents-init nil nil ,items ,clocks ,unknown ,blank ,rest)))\n\n(defmacro org-ml--test-supercontents-specs (config &rest specs)\n  (declare (indent 1))\n  (let ((forms\n         (->> (-partition 7 specs)\n              (--map\n               (-let (((title input items clocks unknown post-blank contents) it))\n                 `(it ,title (expect-supercontents ,config ,input\n                               ,items ,clocks ,unknown ,post-blank ,contents)))))))\n    `(progn ,@forms)))\n\n(describe \"org-ml--supercontents-mixed\"\n  (before-all\n    (setq config nil\n          config-notes '(:clock-out-notes t)\n          i1 (org-ml-build-log-note 1603767576 \"i1\")\n          i2 (org-ml-build-item! :paragraph \"clock note\")\n          p1 (org-ml-build-plain-list i1)\n          p2 (org-ml-build-plain-list i2)\n          p12 (org-ml-build-plain-list i1 i2)\n          p21 (org-ml-build-plain-list i2 i1)\n          c1 (org-ml-build-clock (org-ml-build-timestamp! '(2112 1 1 0 0) :end '(2112 1 2 0 0)))\n          r1 (org-ml-build-paragraph! \"foo\")))\n  (describe \"with clock notes\"\n    (org-ml--test-supercontents-specs config-notes\n      \"nothing\" nil nil nil nil 0 nil\n      \"no logbook\" (list r1) nil nil nil 0 `(,r1)\n      \"item clock note rest\" (list p1 c1 p2 r1) `(,i1) `(,c1 ,i2) nil 0 `(,r1)\n      \"item note clock rest\" (list p1 p2 c1 r1) `(,i1) nil nil 0 `(,p2 ,c1 ,r1)\n      \"clock item note rest\" (list c1 p2 p1 r1) `(,i1) `(,c1 ,i2) nil 0 `(,r1)\n      \"clock note item rest\" (list c1 p1 p2 r1) `(,i1) `(,c1) nil 0 `(,p2 ,r1)\n      \"note item clock rest\" (list p2 p1 c1 r1) nil nil nil 0 `(,p21 ,c1 ,r1)\n      \"note clock item rest\" (list p2 c1 p1 r1) nil nil nil 0 `(,p2 ,c1 ,p1 ,r1)\n      \"item clock note\" (list p1 c1 p2) `(,i1) `(,c1 ,i2) nil 0 nil\n      \"item note clock\" (list p1 p2 c1) `(,i1) nil nil 0 `(,p2 ,c1)\n      \"clock item note\" (list c1 p2 p1) `(,i1) `(,c1 ,i2) nil 0 nil\n      \"clock note item\" (list c1 p1 p2) `(,i1) `(,c1) nil 0 `(,p2)\n      \"note item clock\" (list p2 p1 c1) nil nil nil 0 `(,p21 ,c1)\n      \"note clock item\" (list p2 c1 p1) nil nil nil 0 `(,p2 ,c1 ,p1)\n      \"item clock\" (list p1 c1) `(,i1) `(,c1) nil 0 nil\n      \"item note\" (list p1 p2) `(,i1) nil nil 0 `(,p2)\n      \"clock note\" (list c1 p2) nil `(,c1 ,i2) nil 0 nil\n      \"clock item\" (list c1 p1) `(,i1) `(,c1) nil 0 nil\n      \"note item\" (list p2 p1) nil nil nil 0 `(,p21)\n      \"note clock\" (list p2 c1) nil nil nil 0 `(,p2 ,c1)\n      \"rest list\" (list r1 p1) nil nil nil 0 `(,r1 ,p1)))\n\n  (describe \"without clock notes\"\n    (org-ml--test-supercontents-specs config\n      \"nothing\" nil nil nil nil 0 nil\n      \"no logbook\" (list r1) nil nil nil 0 `(,r1)\n      \"item clock note rest\" (list p1 c1 p2 r1) `(,i1) `(,c1) nil 0 `(,p2 ,r1)\n      \"item note clock rest\" (list p1 p2 c1 r1) `(,i1) nil nil 0 `(,p2 ,c1 ,r1)\n      \"clock item note rest\" (list c1 p2 p1 r1) nil `(,c1) nil 0 `(,p21 ,r1)\n      \"clock note item rest\" (list c1 p1 p2 r1) `(,i1) `(,c1) nil 0 `(,p2 ,r1)\n      \"note item clock rest\" (list p2 p1 c1 r1) nil nil nil 0 `(,p21 ,c1 ,r1)\n      \"note clock item rest\" (list p2 c1 p1 r1) nil nil nil 0 `(,p2 ,c1 ,p1 ,r1)\n      \"item clock note\" (list p1 c1 p2) `(,i1) `(,c1) nil 0 `(,p2)\n      \"item note clock\" (list p1 p2 c1) `(,i1) nil nil 0 `(,p2 ,c1)\n      \"clock item note\" (list c1 p2 p1) nil `(,c1) nil 0 `(,p21)\n      \"clock note item\" (list c1 p1 p2) `(,i1) `(,c1) nil 0 `(,p2)\n      \"note item clock\" (list p2 p1 c1) nil nil nil 0 `(,p21 ,c1)\n      \"note clock item\" (list p2 c1 p1) nil nil nil 0 `(,p2 ,c1 ,p1)\n      \"item note\" (list p1 c1) `(,i1) `(,c1) nil 0 nil\n      \"clock note\" (list p1 p2) `(,i1) nil nil 0 `(,p2)\n      \"clock item\" (list c1 p2) nil `(,c1) nil 0 `(,p2)\n      \"note item\" (list c1 p1) `(,i1) `(,c1) nil 0 nil\n      \"note clock\" (list p2 p1) nil nil nil 0 `(,p21)\n      \"rest list\" (list p2 c1) nil nil nil 0 `(,p2 ,c1))))\n\n(describe \"org-ml--supercontents-single-items\"\n  (before-all\n    (setq id-name \"LOGGING\"\n          config `(:log-into-drawer ,id-name)\n          config-notes `(:log-into-drawer ,id-name :clock-out-notes t)\n          i1 (org-ml-build-item! :paragraph \"i1\")\n          i2 (org-ml-build-log-note 1603767576 \"log note\")\n          i3 (org-ml-build-log-note 1603767576 \"log note in drawer\")\n          p1 (org-ml-build-plain-list i1)\n          p2 (org-ml-build-plain-list i2)\n          p3 (org-ml-build-plain-list i3)\n          p4 (org-ml-build-plain-list i1 i2)\n          drwr (org-ml-build-drawer id-name p3)\n          ts1 (org-ml-build-timestamp! '(2112 1 1 0 0\n                                              :end '(2112 1 2 0 0)))\n          c1 (org-ml-build-clock ts1)\n          r1 (org-ml-build-paragraph! \"foo\")))\n  (describe \"without clock notes\"\n    (org-ml--test-supercontents-specs config\n      \"nothing\" nil nil nil nil 0 nil\n      \"no logbook\" (list r1) nil nil nil 0 `(,r1)\n      \"clock note (no notes)\" (list c1 p1 r1) nil `(,c1) nil 0 `(,p1 ,r1)\n      \"clock note item (no notes)\" (list c1 p4 r1) nil `(,c1) nil 0 `(,p4 ,r1)\n      ;; ASSUME the code that stops splitting after finding an invalid item is\n      ;; fully tested with this example and will therefore do the same in the\n      ;; permutations below\n      \"item clock (store none)\" (list p1 c1 r1) nil nil nil 0 `(,p1 ,c1 ,r1)\n      \"drawer clock (store both)\" (list drwr c1 r1) `(,i3) `(,c1) nil 0 `(,r1)\n      \"clock drawer (store both)\" (list c1 drwr r1) `(,i3) `(,c1) nil 0 `(,r1)\n      \"clock item drawer (don't store note)\" (list c1 p1 drwr r1) nil `(,c1) nil 0 `(,p1 ,drwr ,r1)))\n  (describe \"with clock notes\"\n    (org-ml--test-supercontents-specs config-notes\n      \"clock item drawer (store all)\" (list c1 p1 drwr r1) `(,i3) `(,c1 ,i1) nil 0 `(,r1)\n      \"clock note item\" (list c1 p4 r1) nil `(,c1 ,i1) nil 0 `(,p2 ,r1)\n      \"clock note\" (list c1 p1 r1) nil `(,c1 ,i1) nil 0 `(,r1)\n      \"clock item\" (list c1 p2 r1) nil `(,c1) nil 0 `(,p2 ,r1))))\n\n(describe \"org-ml--supercontents-single-clocks\"\n  (before-all\n    (setq cd-name \"CLOCKING\"\n          config `(:clock-into-drawer ,cd-name)\n          config-notes `(:clock-into-drawer ,cd-name :clock-out-notes t)\n          i1 (org-ml-build-log-note 1603767576 \"note 1\")\n          i2 (org-ml-build-log-note 1603767576 \"note 2\")\n          i3 (org-ml-build-item! :paragraph \"clock note\")\n          p1 (org-ml-build-plain-list i1)\n          p2 (org-ml-build-plain-list i2)\n          p3 (org-ml-build-plain-list i3)\n          ts1 (org-ml-build-timestamp! '(2112 1 1 0 0) :end '(2112 1 2 0 0))\n          c1 (org-ml-build-clock ts1)\n          drwr1 (org-ml-build-drawer cd-name c1)\n          drwr2 (org-ml-build-drawer cd-name c1 p3)\n          r1 (org-ml-build-paragraph! \"foo\")))\n  (describe \"without clock notes\"\n    (org-ml--test-supercontents-specs config\n      \"nothing\" nil nil nil nil 0 nil\n      \"no logbook\" (list r1) nil nil nil 0 `(,r1)\n      ;; this only has five valid combinations\n      ;;\n      \"item, drawer, item\" (list p1 drwr1 p2 r1) `(,i1 ,i2) `(,c1) nil 0 `(,r1)\n      \"item, drawer\" (list p1 drwr1 r1) `(,i1) `(,c1) nil 0 `(,r1)\n      \"item\" (list p1 r1) `(,i1) nil nil 0 `(,r1)\n      \"drawer, item\" (list drwr1 p1 r1) `(,i1) `(,c1) nil 0 `(,r1)\n      \"drawer\" (list drwr1 r1) nil `(,c1) nil 0 `(,r1)\n      ;; invalid\n      \"loose clock anywhere\" (list c1 p1 r1) nil nil nil 0 `(,c1 ,p1 ,r1)))\n  (describe \"with clock notes\"\n    (org-ml--test-supercontents-specs config-notes\n      \"drawer with clock notes\" (list drwr2 r1) nil `(,c1 ,i3) nil 0 `(,r1))))\n\n(describe \"org-ml--supercontents-dual\"\n  (before-all\n    (setq cd-name \"CLOCKING\"\n          id-name \"LOGGING\"\n          config `(:log-into-drawer ,id-name :clock-into-drawer ,cd-name)\n          i1 (org-ml-build-log-note 1603767576 \"note\")\n          p1 (org-ml-build-plain-list i1)\n          ts1 (org-ml-build-timestamp! '(2112 1 1 0 0) :end '(2112 1 2 0 0))\n          c1 (org-ml-build-clock ts1)\n          drwr1 (org-ml-build-drawer cd-name c1)\n          drwr2 (org-ml-build-drawer id-name p1)\n          r1 (org-ml-build-paragraph! \"foo\")))\n  (org-ml--test-supercontents-specs config\n    \"nothing\" nil nil nil nil 0 nil\n    \"no logbook\" (list r1) nil nil nil 0 `(,r1)\n    \"one drawer\" (list drwr1 r1) nil `(,c1) nil 0 `(,r1)\n    \"one drawer (other one)\" (list drwr2 r1) `(,i1) nil nil 0 `(,r1)\n    \"two drawers\" (list drwr1 drwr2 r1) `(,i1) `(,c1) nil 0 `(,r1)\n    \"two drawers (other order)\" (list drwr2 drwr1 r1) `(,i1) `(,c1) nil 0 `(,r1)\n    \"clock outside (invalid)\" (list c1 drwr2 r1) nil nil nil 0 `(,c1 ,drwr2 ,r1)\n    \"item outside (invalid)\" (list p1 drwr1 r1) nil nil nil 0 `(,p1 ,drwr1 ,r1)))\n\n(describe \"org-ml--supercontents-single-mixed\"\n  (before-all\n    (setq d-name \"LOGBOOK\"\n          config '(:log-into-drawer t :clock-into-drawer t)\n          config-notes (list :log-into-drawer t\n                             :clock-into-drawer t\n                             :clock-out-notes t)\n          i1 (org-ml-build-log-note 1603767576 \"note 2\")\n          i2 (org-ml-build-item! :paragraph \"clock note\")\n          p1 (org-ml-build-plain-list i1)\n          p2 (org-ml-build-plain-list i2)\n          ts1 (org-ml-build-timestamp! '(2112 1 1 0 0) :end '(2112 1 2 0 0))\n          c1 (org-ml-build-clock ts1)\n          drwr1 (org-ml-build-drawer d-name c1 p1)\n          drwr2 (org-ml-build-drawer d-name c1 p2 p1)\n          r1 (org-ml-build-paragraph! \"foo\")))\n  (describe \"without clock notes\"\n    (org-ml--test-supercontents-specs config\n      \"nothing\" nil nil nil nil 0 nil\n      \"no logging\" (list r1) nil nil nil 0 `(,r1)\n      \"single drawer\" (list drwr1 r1) `(,i1) `(,c1) nil 0 `(,r1)\n      \"clock outside (invalid)\" (list c1 drwr1 r1) nil nil nil 0 `(,c1 ,drwr1 ,r1)\n      \"item outside (invalid)\" (list p1 drwr1 r1) nil nil nil 0 `(,p1 ,drwr1 ,r1)))\n  (describe \"with clock notes\"\n    (org-ml--test-supercontents-specs config-notes\n      \"single drawer with notes\" (list drwr2 r1) `(,i1) `(,c1 ,i2) nil 0 `(,r1))))\n\n(describe \"org-ml--supercontents-single-clocks-or-mixed\"\n  ;; ASSUME clock notes are tested using the mixed and single-clocks tests\n  (before-all\n    (setq clock-limit 1\n          config `(:clock-into-drawer ,clock-limit)\n          i1 (org-ml-build-log-note 1603767576 \"note 1\")\n          i2 (org-ml-build-log-note 1603767576 \"note 2\")\n          p1 (org-ml-build-plain-list i1)\n          p2 (org-ml-build-plain-list i2)\n          ts1 (org-ml-build-timestamp! '(2112 1 1 0 0) :end '(2112 1 2 0 0))\n          c1 (org-ml-build-clock ts1)\n          ts2 (org-ml-build-timestamp! '(2112 1 2 0 0) :end '(2112 1 3 0 0))\n          c2 (org-ml-build-clock ts1)\n          drwr (org-ml-build-drawer \"LOGBOOK\" c1)\n          r1 (org-ml-build-paragraph! \"foo\")))\n  (org-ml--test-supercontents-specs config\n    \"nothing\" nil nil nil nil 0 nil\n    \"no logbook\" (list r1) nil nil nil 0 `(,r1)\n    ;; same tests as single-clocks when over clock limit\n    ;;\n    \"plain-list, drawer, plain-list\" (list p1 drwr p2 r1) `(,i1 ,i2) `(,c1) nil 0 `(,r1)\n    \"plain-list, drawer\" (list p1 drwr r1) `(,i1) `(,c1) nil 0 `(,r1)\n    \"plain-list\" (list p1 r1) `(,i1) nil nil 0 `(,r1)\n    \"drawer, plain-list\" (list drwr p1 r1) `(,i1) `(,c1) nil 0 `(,r1)\n    \"drawer\" (list drwr r1) nil `(,c1) nil 0 `(,r1)\n    ;; same as mixed\n    ;;\n    \"loose clock under clock limit\" (list c1 p1 r1) `(,i1) `(,c1) nil 0 `(,r1)\n    \"too many clocks\" (list c1 c2 p1 r1) nil `(,c1) nil 0 `(,c2 ,p1 ,r1)))\n\n(describe \"org-ml--supercontents-single-items-or-dual\"\n  (before-all\n    (setq id-name \"LOGGING\"\n          clock-limit 1\n          config `(:log-into-drawer ,id-name :clock-into-drawer ,clock-limit)\n          i1 (org-ml-build-log-note 1603767576 \"note 1\")\n          i2 (org-ml-build-log-note 1603767576 \"note 2\")\n          i3 (org-ml-build-log-note 1603767576 \"note 3\")\n          p1 (org-ml-build-plain-list i1)\n          p2 (org-ml-build-plain-list i1 i2)\n          p3 (org-ml-build-plain-list i3)\n          drwr1 (org-ml-build-drawer id-name p3)\n          ts1 (org-ml-build-timestamp! '(2112 1 1 0 0) :end '(2112 1 2 0 0))\n          c1 (org-ml-build-clock ts1)\n          ts2 (org-ml-build-timestamp! '(2112 1 2 0 0) :end '(2112 1 3 0 0))\n          c2 (org-ml-build-clock ts2)\n          drwr2 (org-ml-build-drawer \"LOGBOOK\" c1)\n          r1 (org-ml-build-paragraph! \"foo\")))\n  (org-ml--test-supercontents-specs config\n    \"nothing\" nil nil nil nil 0 nil\n    \"no logbook\" (list r1) nil nil nil 0 `(,r1)\n    ;; same as single-items\n    ;;\n    \"clock item (don't store note)\" (list c1 p1 r1) nil `(,c1) nil 0 `(,p1 ,r1)\n    \"clock items (store only clock)\" (list c1 p2 r1) nil `(,c1) nil 0 `(,p2 ,r1)\n    ;; ASSUME the code that stops splitting\n    ;; after finding an invalid item is fully tested with this example and will\n    ;; therefore do the same in the permutations below\n    \"item clock (store none)\" (list p1 c1 r1) nil nil nil 0 `(,p1 ,c1 ,r1)\n    \"drawer clock (store both)\" (list drwr1 c1 r1) `(,i3) `(,c1) nil 0 `(,r1)\n    \"clock drawer (store both)\" (list c1 drwr1 r1) `(,i3) `(,c1) nil 0 `(,r1)\n    \"drawer clock item (store only clock)\" (list drwr1 c1 p1 r1) `(,i3) `(,c1) nil 0 `(,p1 ,r1)\n    \"clock item drawer (don't store note)\" (list c1 p1 drwr1 r1) nil `(,c1) nil 0 `(,p1 ,drwr1 ,r1)\n    \"too many clocks\" (list c1 c2 p1 drwr1 r1) nil `(,c1) nil 0 `(,c2 ,p1 ,drwr1 ,r1)\n    \"dual drawer (clock only)\" (list drwr2 r1) nil `(,c1) nil 0 `(,r1)\n    \"dual drawer (item only)\" (list drwr1 r1) `(,i3) nil nil 0 `(,r1)\n    \"dual drawer (both)\" (list drwr1 drwr2 r1) `(,i3) `(,c1) nil 0 `(,r1)))\n\n(describe \"org-ml--supercontents-single-mixed-or-single-items\"\n  (before-all\n    (setq clock-limit 1\n          config `(:log-into-drawer t :clock-into-drawer ,clock-limit)\n          i1 (org-ml-build-log-note 1603767576 \"note 1\")\n          p1 (org-ml-build-plain-list i1)\n          ts1 (org-ml-build-timestamp! '(2112 1 1 0 0) :end '(2112 1 2 0 0))\n          c1 (org-ml-build-clock ts1)\n          ts2 (org-ml-build-timestamp! '(2112 1 2 0 0) :end '(2112 1 3 0 0))\n          c2 (org-ml-build-clock ts2)\n          drwr1 (org-ml-build-drawer \"LOGBOOK\" c1 p1)\n          drwr2 (org-ml-build-drawer \"LOGBOOK\" p1)\n          r1 (org-ml-build-paragraph! \"foo\")))\n  (org-ml--test-supercontents-specs config\n    \"nothing\" nil nil nil nil 0 nil\n    \"no logging\" (list r1) nil nil nil 0 `(,r1)\n    \"single drawer\" (list drwr1 r1) `(,i1) `(,c1) nil 0 `(,r1)\n    \"clock outside and inside\" (list c1 drwr1 r1) `(,i1) `(,c1) `(,c1) 0 `(,r1)\n    \"clocks outside and not inside\" (list c1 drwr2 r1) `(,i1) `(,c1) nil 0 `(,r1)\n    \"too many clocks outside\" (list c1 c2 drwr2 r1) nil `(,c1) nil 0 `(,c2 ,drwr2 ,r1)\n    \"item outside (invalid)\" (list p1 drwr1 r1) nil nil nil 0 `(,p1 ,drwr1 ,r1)))\n\n;; logbook blank line testing\n;;\n;; assume these tests cover all code paths\n;; - any logbook type followed by a blank (item, clock, drawer)\n;; - in the case of clocks with notes, clocks followed by plain lists where the\n;;   first item has a blank after it\n\n(describe \"org-ml--supercontents-mixed-blank-line\"\n  (before-all\n    (setq config nil\n          config-notes '(:clock-out-notes t)\n          config-drawer '(:log-into-drawer t)\n          i1 (->> (org-ml-build-log-note 1603767576 \"note 1\")\n                  (org-ml-set-property :post-blank 1))\n          i2 (org-ml-build-log-note 1603767576 \"note 2\")\n          i3 (org-ml-build-item! :post-blank 1 :paragraph \"clock note\")\n          p1 (org-ml-build-plain-list i1 i2)\n          p2 (org-ml-build-plain-list i1)\n          p3 (org-ml-build-plain-list i2)\n          p4 (org-ml-build-plain-list :post-blank 1 i1 i2)\n          p5 (org-ml-build-plain-list :post-blank 1 i2)\n          p6 (org-ml-build-plain-list i3 i2)\n          p66 (org-ml-build-plain-list i2)\n          ;; (p6 (org-ml-build-plain-list i1)\n          p7 (org-ml-build-plain-list :post-blank 1 i2)\n          ts1 (org-ml-build-timestamp! '(2112 1 1 0 0) :end '(2112 1 2 0 0))\n          c1 (org-ml-build-clock ts1)\n          c2 (org-ml-build-clock ts1 :post-blank 1)\n          drwr (org-ml-build-drawer \"LOGBOOK\" :post-blank 1 p2)\n          r1 (org-ml-build-paragraph! \"foo\")))\n  (describe \"with clock notes\"\n    (org-ml--test-supercontents-specs config-notes\n      \"item space item\" (list p1 c1 r1) `(,i1) nil nil 1 `(,p3 ,c1 ,r1)\n      \"clock item space item\" (list c1 p1 r1) `(,i1) `(,c1) nil 1 `(,p3 ,r1)\n      \"clock note space item\" (list c1 p6 r1) nil `(,c1 ,i3) nil 1 `(,p66 ,r1)\n      \"clock space item\" (list c2 p1 r1) nil `(,c2) nil 1 `(,p1 ,r1)\n      \"item space item space\" (list p4 r1) `(,i1) nil nil 1 `(,p7 ,r1)))\n  (describe \"without clock notes\"\n    (org-ml--test-supercontents-specs config\n      \"item space item\" (list p1 c1 r1) `(,i1) nil nil 1 `(,p3 ,c1 ,r1)\n      \"clock item space item\" (list c1 p1 r1) `(,i1) `(,c1) nil 1 `(,p3 ,r1)\n      \"clock note space item\" (list c1 p6 r1) nil `(,c1) nil 0 `(,p6 ,r1)\n      \"clock space item\" (list c2 p1 r1) nil `(,c2) nil 1 `(,p1 ,r1)\n      \"item space item space\" (list p4 r1) `(,i1) nil nil 1 `(,p7 ,r1)))\n  (describe \"with drawer\"\n    ;; TODO this has side effects :(\n    (org-ml--test-supercontents-specs config-drawer\n      \"single drawer\" (list drwr r1) `(,i1) nil nil 1 `(,r1))))\n\n;;; MATCH FRAMEWORK TESTING\n\n;; These are tests for `org-ml-match' and friends. Proceed with caution :)\n\n(defmacro should-error-arg (form)\n  \"Make an ert error form to test if FORM signals an `arg-type-error'.\"\n  `(should-error ,form :type 'arg-type-error))\n\n(describe \"org-ml--match-make-condition-form/error\"\n  ;; Ensure `org-ml--match-make-condition-form' will error when it\n  ;; supposed to do so. All errors (in theory) should be tested here\n  ;; so that we don't need to bother testing them anywhere else when\n  ;; we test functions higher in the framework\n  (unless (fboundp 'org-ml--match-make-condition-form)\n    (error \"Function not defined\"))\n  (before-all\n    (setq fun #'org-ml--match-make-condition-form))\n  (it \"quoted\"\n    (should-error-arg (funcall fun '(quote bold)))\n    (should-error-arg (funcall fun '(function bold))))\n  (it \"invalid type\"\n    (should-error-arg (funcall fun 'protoss)))\n  (it \"invalid operator\"\n    (should-error-arg (funcall fun '(= 1)))\n    (should-error-arg (funcall fun '(=/ 1))))\n  (it \"valid operator with non-integer\"\n    (should-error-arg (funcall fun '(< \"1\"))))\n  (it \"valid operator with too many arguments\"\n    (should-error-arg (funcall fun '(< 1 2))))\n  (it \"pred with no arguments\"\n    (should-error-arg (funcall fun '(:pred))))\n  (it \"pred with too many arguments\"\n    (should-error-arg (funcall fun '(:pred stringp integerp))))\n  (it \"not with no arguments\"\n    (should-error-arg (funcall fun '(:not))))\n  (it \"not with too many arguments\"\n    (should-error-arg (funcall fun '(:not 1 3))))\n  (it \"and with no arguments\"\n    (should-error-arg (funcall fun '(:and))))\n  (it \"and with nonsense\"\n    (should-error-arg (funcall fun '(:and bold :2))))\n  (it \"or with no arguments\"\n    (should-error-arg (funcall fun '(:or))))\n  (it \"or with nonsense\"\n    (should-error-arg (funcall fun '(:or bold :2))))\n  (it \"properties with symbols instead of keywords\"\n    (should-error-arg (funcall fun '(tags '(\"hi\")))))\n  (it \"multiple properties\"\n    (should-error-arg (funcall fun '(:tags '(\"hi\") :todo-keyword \"DONE\"))))\n  (it \"just wrong...\"\n    (should-error-arg (funcall fun nil))\n    (should-error-arg (funcall fun :1))))\n\n(describe \"org-ml--match-pattern-make-inner-form/error\"\n  ;; Ensure `org-ml--match-make-inner-form' will error when it supposed to\n  ;; do so. All errors (in theory) should be tested here so that\n  ;; we don't need to bother testing them anywhere else when we test\n  ;; functions higher in the framework\n  ;;\n  ;; Assume:\n  ;; - all invalid patterns at the condition level will be caught by\n  ;;   `org-ml--match-make-condition-form/error'.\n  ;; - these error paths are independent of `END?' and `LIMIT' so\n  ;;   set them both to nil\n  (unless (fboundp 'org-ml--match-pattern-make-inner-form)\n    (error \"Function not defined\"))\n  (before-all\n    (setq fun (-partial #'org-ml--match-pattern-make-inner-form nil nil)))\n  (it \"slicers present\"\n    (should-error-arg (funcall fun '(:first bold)))\n    (should-error-arg (funcall fun '(:last bold)))\n    (should-error-arg (funcall fun '(:nth bold)))\n    (should-error-arg (funcall fun '(:sub bold)))\n    (should-error-arg (funcall fun '(bold :first)))\n    (should-error-arg (funcall fun '(bold :last)))\n    (should-error-arg (funcall fun '(bold :nth)))\n    (should-error-arg (funcall fun '(bold :sub))))\n  (it \"just wrong...\"\n    (should-error-arg (funcall fun '(:swaggart)))))\n\n(defun should-expand-to-alts (pattern alt-patterns)\n  (should (equal (org-ml--match-pattern-expand-alternations pattern)\n                 alt-patterns)))\n\n(describe \"org-ml--match-pattern-expand-alternations\"\n  ;; ensure that alternations expand properly\n  (unless (fboundp 'org-ml--match-pattern-expand-alternations)\n    (error \"Function not defined\"))\n  (it \"no alternations\"\n    (should-expand-to-alts '(a) '((a)))\n    (should-expand-to-alts '(a b c) '((a b c))))\n  (it \"1-level alternations\"\n    (should-expand-to-alts '((x | y)) '((x) (y)))\n    (should-expand-to-alts '((x | y) a) '((x a) (y a)))\n    (should-expand-to-alts '(a (x | y)) '((a x) (a y)))\n    (should-expand-to-alts '(a (x | y) b) '((a x b) (a y b))))\n  (it \"1-level alternations with nil\"\n    (should-expand-to-alts '((nil | y)) '(nil (y)))\n    (should-expand-to-alts '((nil | y) a) '((a) (y a)))\n    (should-expand-to-alts '(a (nil | y)) '((a) (a y)))\n    (should-expand-to-alts '(a (nil | y) b) '((a b) (a y b))))\n  (it \"1-level serial alternations\"\n    (should-expand-to-alts '((m | n) (x | y)) '((m x) (m y) (n x) (n y)))\n    (should-expand-to-alts '(a (m | n) b (x | y) c) '((a m b x c) (a m b y c)\n                                                      (a n b x c) (a n b y c))))\n  (it \"1-level serial alternations with nil\"\n    (should-expand-to-alts '((nil | n) (x | y)) '((x) (y) (n x) (n y)))\n    (should-expand-to-alts '((m | n) (nil | y)) '((m) (m y) (n) (n y)))\n    (should-expand-to-alts '(a (nil | n) b (x | y) c) '((a b x c) (a b y c)\n                                                        (a n b x c) (a n b y c)))\n    (should-expand-to-alts '(a (m | n) b (nil | y) c) '((a m b c) (a m b y c)\n                                                        (a n b c) (a n b y c))))\n  (it \"2-level alternations\"\n    (should-expand-to-alts '((x | (m | n))) '((x) (m) (n)))\n    (should-expand-to-alts '(a (x | (m | n))) '((a x) (a m) (a n)))\n    (should-expand-to-alts '((x | y (m | n))) '((x) (y m) (y n)))\n    (should-expand-to-alts '(a (x | y (m | n))) '((a x) (a y m) (a y n))))\n  (it \"2-level alternations with nil\"\n    (should-expand-to-alts '((nil | (m | n))) '(nil (m) (n)))\n    (should-expand-to-alts '(a (nil | (m | n))) '((a) (a m) (a n)))\n    (should-expand-to-alts '((nil | y (m | n))) '(nil (y m) (y n)))\n    (should-expand-to-alts '(a (nil | y (m | n))) '((a) (a y m) (a y n)))\n    (should-expand-to-alts '((x | (nil | n))) '((x) nil (n)))\n    (should-expand-to-alts '(a (x | (nil | n))) '((a x) (a) (a n)))\n    (should-expand-to-alts '((x | y (nil | n))) '((x) (y) (y n)))\n    (should-expand-to-alts '(a (x | y (nil | n))) '((a x) (a y) (a y n)))))\n\n(defun should-expand-to (pattern expanded-pattern)\n  (should (equal (org-ml--match-pattern-simplify-wildcards pattern)\n                 expanded-pattern)))\n\n(describe \"org-ml--match-pattern-simplify-wildcards\"\n  ;; ensure that bracket and + wildcards expand properly\n  (unless (fboundp 'org-ml--match-pattern-simplify-wildcards)\n    (error \"Function not defined\"))\n  (it \"?\"\n    (should-expand-to '(x \\?) '((nil | x)))\n    (should-expand-to '(x \\? y) '((nil | x) y)))\n  (it \"+\"\n    (should-expand-to '(x +) '(x x *))\n    (should-expand-to '(x + y) '(x x * y)))\n  (it \"brackets\"\n    (should-expand-to '(x [1]) '(x))\n    (should-expand-to '(x [2]) '(x x))\n    (should-expand-to '(x [0 1]) '((nil | x)))\n    (should-expand-to '(x [2 2]) '(x x))\n    (should-expand-to '(x [1 2]) '((x | x x)))\n    (should-expand-to '(x [1 nil]) '(x x *))))\n\n(describe \"org-ml--match-pattern-simplify-wildcards/error\"\n  ;; test errors in wildcard expansion\n  ;; note, we assume that any malformed patterns are caught later\n  ;; so no need to test if we supply two +'s in a row and other garbage\n  (unless (fboundp 'org-ml--match-pattern-simplify-wildcards)\n    (error \"Function not defined\"))\n  (it \"zero not allowed\"\n    (should-error-arg (org-ml--match-pattern-simplify-wildcards '(x [0]))))\n  (it \"negative not allowed\"\n    (should-error-arg (org-ml--match-pattern-simplify-wildcards '(x [-1]))))\n  (it \"double zeros not allowed\"\n    (should-error-arg (org-ml--match-pattern-simplify-wildcards '(x [0 0]))))\n  (it \"negatives not allowed\"\n    (should-error-arg (org-ml--match-pattern-simplify-wildcards '(x [-1 1])))\n    (should-error-arg (org-ml--match-pattern-simplify-wildcards '(x [1 -1])))\n    (should-error-arg (org-ml--match-pattern-simplify-wildcards '(x [-1 -1]))))\n  (it \"must be ascending order\"\n    (should-error-arg (org-ml--match-pattern-simplify-wildcards '(x [2 1])))))\n\n(describe \"org-ml--match-make-slicer-form\"\n  ;; Ensure `org-ml--match-make-inner-form' will error when it supposed to\n  ;; do so. All errors (in theory) should be tested here so that\n  ;; we don't need to bother testing them anywhere else when we test\n  ;; functions higher in the framework\n  ;;\n  ;; Assume that all invalid patterns at the predicate and wildcard\n  ;; level will be caught by `org-ml--match-make-condition-form/error' and\n  ;; `org-ml--match-pattern-make-inner-form/error'\n  (unless (fboundp 'org-ml--match-make-slicer-form)\n    (error \"Function not defined\"))\n  (before-all\n    (setq fun #'org-ml--match-make-slicer-form))\n  (it \"nth with non-integer\"\n    (should-error-arg (funcall fun '(:nth \"1\" bold))))\n  (it \"sub with non-integers\"\n    (should-error-arg (funcall fun '(:sub \"1\" 2 bold)))\n    (should-error-arg (funcall fun '(:sub 1 \"2\" bold))))\n  (it \"sub with flipped integers\"\n    (should-error-arg (funcall fun '(:sub 2 1 bold)))\n    (should-error-arg (funcall fun '(:sub -1 -2 bold))))\n  (it \"sub with split integers\"\n    (should-error-arg (funcall fun '(:sub -1 2 bold)))))\n\n(defmacro match-should-equal (node result &rest patterns)\n  \"Return form to test if all PATTERNS applied NODE return RESULT.\"\n  (declare (indent 2))\n  (let ((tests (--map\n                `(expect ,result\n                         :to-equal\n                         (->> (org-ml-match ',it ,node)\n                              (-map #'org-ml-to-trimmed-string)))\n                patterns)))\n    `(progn ,@tests)))\n\n(defmacro match-slicer-should-equal (node expected pattern)\n  \"Return form to test if PATTERN applied to NODE works with all slicers.\nEXPECTED is a list of matches returned using PATTERN if no slicer is\napplied.\"\n  (declare (indent 1))\n  ;; The basic behavior of slicers can be put in terms of -drop(-last)\n  ;; and -take(-last). Additionally, some slicing operations have\n  ;; multiple syntactical representations. Ensure equality of all\n  ;; these specifications here\n  `(progn\n     ;; these slicers have multiple equivalent expressions\n     ;;\n     (it \"first match\"\n       (match-should-equal node (-take 1 ,expected)\n                           (:first ,@pattern) (:nth 0 ,@pattern)\n                           (:sub 0 0 ,@pattern)))\n     (it \"last match\"\n       (match-should-equal node (-take-last 1 ,expected)\n                           (:last ,@pattern) (:nth -1 ,@pattern)\n                           (:sub -1 -1 ,@pattern)))\n     (it \"nth match positive\"\n       (match-should-equal node (-drop 1 (-take 2 ,expected))\n                           (:nth 1 ,@pattern) (:sub 1 1 ,@pattern)))\n     (it \"nth match negative\"\n       (match-should-equal node (-drop-last 1 (-take-last 2 ,expected))\n                           (:nth -2 ,@pattern) (:sub -2 -2 ,@pattern)))\n     (it \"out of range positive\"\n       (match-should-equal node nil\n                           (:nth 100 ,@pattern) (:sub 100 100 ,@pattern)))\n     (it \"out of range negative\"\n       (match-should-equal node nil\n                           (:nth -100 ,@pattern) (:sub -100 -100 ,@pattern)))\n     (it \"bounded to out of range\"\n       (match-should-equal node ,expected\n                           (:sub 0 100 ,@pattern) (:sub -100 -1 ,@pattern)))\n     ;;\n     ;; these slicers can only be expressed one way\n     ;;\n     (it \"zero-bounded finite positive\"\n       (match-should-equal node (-take 2 ,expected)\n                           (:sub 0 1 ,@pattern)))\n     (it \"zero-bounded finite negative\"\n       (match-should-equal node (-take-last 2 ,expected)\n                           (:sub -2 -1 ,@pattern)))\n     (it \"floating finite positive\"\n       (match-should-equal node (-drop 1 (-take 3 ,expected))\n                           (:sub 1 2 ,@pattern)))\n     (it \"floating finite negative\"\n       (match-should-equal node (-drop-last 1 (-take-last 3 ,expected))\n                           (:sub -3 -2 ,@pattern)))\n     (it \"floating out of range positive\"\n       (match-should-equal node (-drop 1 ,expected)\n                           (:sub 1 100 ,@pattern)))\n     (it \"floating out of range negative\"\n       (match-should-equal node (-drop-last 1 ,expected)\n                           (:sub -100 -2 ,@pattern)))))\n\n;; Here we test the following pattern combinations\n;; - multi-level condition\n;; - :any + condition\n;; - condition + :any\n;; - *\n;;\n;; The reason for choosing these combinations is that all of them\n;; combined should hit each of the valid form-building switches in\n;; `org-ml--match-pattern-make-inner-form'. Since the behavior of these\n;; depends on the value of `LIMIT' and `END?' and these are set\n;; depending on the slicer, testing these combinations with all\n;; reasonable slicer combination should ensure that every path with\n;; every combination of `LIMIT' and `END?' is tested. Note this\n;; assumes that `org-ml--match-make-condition-form' is working correctly\n;; as the following test only use a few combinations in this function.\n;; However, `org-ml--match-make-condition-form' is independent of the\n;; chosen slicer so this should not matter\n\n(describe \"org-ml-match/slicer-predicate\"\n  ;; test the single/multiple condition path with all slicers\n  (before-all\n    (setq node (->> (s-join \"\\n\"\n                            '(\"* one\"\n                             \"** TODO two\"\n                             \"2\"\n                             \"** COMMENT three\"\n                             \"3\"\n                             \"** four\"\n                             \"4\"\n                             \"** DONE five\"\n                             \"5\"))\n                   (org-ml--from-string))))\n  (match-slicer-should-equal node '(\"2\" \"3\" \"4\" \"5\") (headline section)))\n\n(describe \"org-ml-match/slicer-any-first\"\n  ;; test the :any + condition path with all slicers\n  (before-all\n    (setq node (org-ml-build-paragraph! \"*_1_* */2/* _*3*_ _/4/_ /*5*/ /_6_/\")))\n  (match-slicer-should-equal node '(\"/2/\" \"*3*\" \"/4/\" \"*5*\") (:any (:or bold italic))))\n\n(describe \"org-ml-match/slicer-any-last\"\n  ;; test the condition + :any path with all slicers\n  (before-all\n    (setq node (org-ml-build-paragraph! \"*_1_* */2/* _*3*_ _/4/_ /*5*/ /_6_/\")))\n  (match-slicer-should-equal node '(\"_1_\" \"/2/\" \"*5*\" \"_6_\") ((:or bold italic) :any)))\n\n(describe \"org-ml-match/empty-patterns\"\n  (before-all\n    (setq node (->> (s-join \"\\n\"\n                            '(\"* one\"\n                              \"** two\"\n                              \"** three\"))\n                    (org-ml--from-string)))\n    (defun match-empty (p)\n      (expect (org-ml-match p node) :to-equal (list node))))\n  (it \"empty patterns\"\n    (match-empty '())\n    (match-empty '(:first))\n    (match-empty '(:last))\n    (match-empty '(:nth 0))\n    (match-empty '(:sub 0 0)))\n  (it \"wildcards with the empty pattern\"\n    (match-empty '(:first headline \\?))\n    (match-empty '(:first headline *))\n    (match-empty '(:first (nil | headline)))\n    (match-empty '(:last (headline | nil)))))\n\n(ert-deftest org-ml-match/slicer-many ()\n  ;; Test the * paths with all slicers. Here the node\n  ;; is chosen such that some values are nested and thus * will\n  ;; return them but *! will not\n  (let ((node (->> (s-join \"\\n\"\n                           '(\"* one\"\n                             \"- 1\"\n                             \"- 2\"\n                             \"  - 3\"\n                             \"** two\"\n                             \"- 4\"\n                             \"- 5\"\n                             \"  - 6\"\n                             \"** three\"\n                             \"- 7\"\n                             \"- 8\"\n                             \"  - 9\"))\n                   (org-ml--from-string)))\n        (expected '(\"- 1\" \"- 2\\n  - 3\" \"- 3\" \"- 4\" \"- 5\\n  - 6\"\n                    \"- 6\" \"- 7\" \"- 8\\n  - 9\" \"- 9\"))\n        (expected! '(\"- 1\" \"- 2\\n  - 3\" \"- 4\" \"- 5\\n  - 6\" \"- 7\"\n                     \"- 8\\n  - 9\")))\n    (match-slicer-should-equal node expected (:any * item))\n    (match-slicer-should-equal node expected! (:any *! item))))\n\n;;; DIFF ALGORITHM\n\n(defun org-ml--diff-apply (str-a str-b)\n  \"Turn STR-A into STR-B using the diff algorithm.\nObviously this should return a string identical to STR-B assuming\ndiff is working correctly, and if not, well...get it together,\ndummy.\"\n  (cl-flet*\n      ((edit-del\n        (str i j)\n        (concat (substring str 0 i) (substring str j)))\n       (edit-ins\n        (str i a b)\n        (concat (substring str 0 i)\n                (substring str-b a b)\n                (substring str i)))\n       (edit\n        (str edit)\n        (pcase edit\n          (`(ins ,i ,a ,b)\n           (edit-ins str i a b))\n          (`(del ,i ,j)\n           (edit-del str i j)))))\n    (->> (org-ml--diff str-a str-b)\n         (-reduce-from #'edit str-a))))\n\n(defmacro org-ml--test-diff-specs (&rest specs)\n  (declare (indent 1))\n  (let ((forms\n         (->> (-partition 3 specs)\n              (--map\n               (-let (((title a b) it))\n                 `(it ,title\n                    (expect (org-ml--diff-apply ,a ,b) :to-equal ,b)))))))\n    `(progn ,@forms)))\n\n(describe \"better diff algorithm\"\n  (describe \"find SES\"\n    (org-ml--test-diff-specs\n        \"empty strings\" \"\" \"\"\n        \"one identical char\" \"a\" \"a\"\n        \"two identical chars\" \"aa\" \"aa\"\n\n        \"zero chars, insert one (1)\" \"a\" \"\"\n        \"zero chars, insert one (2)\" \"\" \"a\"\n\n        \"one char, insert one (1)\" \"ba\" \"a\"\n        \"one char, insert one (2)\" \"ab\" \"a\"\n        \"one char, insert one (3)\" \"a\" \"ba\"\n        \"one char, insert one (4)\" \"a\" \"ab\"\n        \n        \"one char, insert two (1)\" \"a\" \"abc\"\n        \"one char, insert two (2)\" \"a\" \"acb\"\n        \"one char, insert two (3)\" \"a\" \"cab\"\n        \"one char, insert two (4)\" \"a\" \"cba\"\n        \"one char, insert two (5)\" \"a\" \"bca\"\n        \"one char, insert two (6)\" \"a\" \"bac\"\n\n        \"different chars\" \"a\" \"b\"\n\n        \"two chars, one different (1)\" \"aa\" \"ab\"\n        \"two chars, one different (2)\" \"aa\" \"ba\"\n\n        \"three chars, one different (1)\" \"aaa\" \"baa\"\n        \"three chars, one different (2)\" \"aaa\" \"aba\"\n        \"three chars, one different (3)\" \"aaa\" \"aab\"\n\n        \"three chars, two different (1)\" \"aaa\" \"abc\"\n        \"three chars, two different (2)\" \"aaa\" \"acb\"\n        \"three chars, two different (3)\" \"aaa\" \"bac\"\n        \"three chars, two different (4)\" \"aaa\" \"cab\"\n        \"three chars, two different (5)\" \"aaa\" \"cba\"\n        \"three chars, two different (6)\" \"aaa\" \"bca\"\n        )))\n\n(provide 'org-ml-dev-test)\n;;; org-ml-dev-test.el ends here\n"
  },
  {
    "path": "docs/api-reference.md",
    "content": "# API Reference\n\n## String Conversion\n\n\nConvert nodes to strings.\n\n* [org-ml-to-string](#org-ml-to-string-node) `(node)`\n* [org-ml-to-trimmed-string](#org-ml-to-trimmed-string-node) `(node)`\n* [org-ml-from-string](#org-ml-from-string-type-string) `(type string)`\n\n## Buffer Parsing\n\n\nParse buffers to trees.\n\n* [org-ml-parse-this-buffer](#org-ml-parse-this-buffer-nil) `nil`\n* [org-ml-parse-object-at](#org-ml-parse-object-at-point) `(point)`\n* [org-ml-parse-element-at](#org-ml-parse-element-at-point) `(point)`\n* [org-ml-parse-table-row-at](#org-ml-parse-table-row-at-point) `(point)`\n* [org-ml-parse-headline-at](#org-ml-parse-headline-at-point) `(point)`\n* [org-ml-parse-subtree-at](#org-ml-parse-subtree-at-point) `(point)`\n* [org-ml-parse-item-at](#org-ml-parse-item-at-point) `(point)`\n* [org-ml-parse-section-at](#org-ml-parse-section-at-point) `(point)`\n* [org-ml-parse-this-toplevel-section](#org-ml-parse-this-toplevel-section-nil) `nil`\n* [org-ml-this-buffer-has-headlines](#org-ml-this-buffer-has-headlines-nil) `nil`\n* [org-ml-parse-headlines](#org-ml-parse-headlines-which) `(which)`\n* [org-ml-parse-subtrees](#org-ml-parse-subtrees-which) `(which)`\n\n## Building\n\n\nBuild new nodes.\n\n\n### Leaf Object Nodes\n\n* [org-ml-build-code](#org-ml-build-code-value-key-post-blank) `(value &key post-blank)`\n* [org-ml-build-entity](#org-ml-build-entity-name-key-use-brackets-p-post-blank) `(name &key use-brackets-p post-blank)`\n* [org-ml-build-export-snippet](#org-ml-build-export-snippet-back-end-value-key-post-blank) `(back-end value &key post-blank)`\n* [org-ml-build-inline-babel-call](#org-ml-build-inline-babel-call-call-key-inside-header-arguments-end-header-post-blank) `(call &key inside-header arguments end-header post-blank)`\n* [org-ml-build-inline-src-block](#org-ml-build-inline-src-block-language-key-parameters-value--post-blank) `(language &key parameters (value \"\") post-blank)`\n* [org-ml-build-line-break](#org-ml-build-line-break-key-post-blank) `(&key post-blank)`\n* [org-ml-build-latex-fragment](#org-ml-build-latex-fragment-value-key-post-blank) `(value &key post-blank)`\n* [org-ml-build-macro](#org-ml-build-macro-key-key-args-post-blank) `(key &key args post-blank)`\n* [org-ml-build-statistics-cookie](#org-ml-build-statistics-cookie-value-key-post-blank) `(value &key post-blank)`\n* [org-ml-build-target](#org-ml-build-target-value-key-post-blank) `(value &key post-blank)`\n* [org-ml-build-timestamp](#org-ml-build-timestamp-type-year-start-month-start-day-start-year-end-month-end-day-end-key-range-type-hour-start-minute-start-hour-end-minute-end-repeater-type-repeater-unit-repeater-value-repeater-deadline-unit-repeater-deadline-value-warning-type-warning-unit-warning-value-post-blank) `(type year-start month-start day-start year-end month-end day-end &key range-type hour-start minute-start hour-end minute-end repeater-type repeater-unit repeater-value repeater-deadline-unit repeater-deadline-value warning-type warning-unit warning-value post-blank)`\n* [org-ml-build-verbatim](#org-ml-build-verbatim-value-key-post-blank) `(value &key post-blank)`\n\n### Branch Object Nodes\n\n* [org-ml-build-bold](#org-ml-build-bold-key-post-blank-rest-object-nodes) `(&key post-blank &rest object-nodes)`\n* [org-ml-build-footnote-reference](#org-ml-build-footnote-reference-key-label-post-blank-rest-object-nodes) `(&key label post-blank &rest object-nodes)`\n* [org-ml-build-italic](#org-ml-build-italic-key-post-blank-rest-object-nodes) `(&key post-blank &rest object-nodes)`\n* [org-ml-build-link](#org-ml-build-link-path-key-format-type-fuzzy-post-blank-rest-object-nodes) `(path &key format (type \"fuzzy\") post-blank &rest object-nodes)`\n* [org-ml-build-radio-target](#org-ml-build-radio-target-key-post-blank-rest-object-nodes) `(&key post-blank &rest object-nodes)`\n* [org-ml-build-strike-through](#org-ml-build-strike-through-key-post-blank-rest-object-nodes) `(&key post-blank &rest object-nodes)`\n* [org-ml-build-superscript](#org-ml-build-superscript-key-use-brackets-p-post-blank-rest-object-nodes) `(&key use-brackets-p post-blank &rest object-nodes)`\n* [org-ml-build-subscript](#org-ml-build-subscript-key-use-brackets-p-post-blank-rest-object-nodes) `(&key use-brackets-p post-blank &rest object-nodes)`\n* [org-ml-build-table-cell](#org-ml-build-table-cell-key-post-blank-rest-object-nodes) `(&key post-blank &rest object-nodes)`\n* [org-ml-build-underline](#org-ml-build-underline-key-post-blank-rest-object-nodes) `(&key post-blank &rest object-nodes)`\n\n### Leaf Element Nodes\n\n* [org-ml-build-babel-call](#org-ml-build-babel-call-call-key-inside-header-arguments-end-header-name-plot-header-results-caption-post-blank) `(call &key inside-header arguments end-header name plot header results caption post-blank)`\n* [org-ml-build-clock](#org-ml-build-clock-value-key-post-blank) `(value &key post-blank)`\n* [org-ml-build-comment](#org-ml-build-comment-value-key-post-blank) `(value &key post-blank)`\n* [org-ml-build-comment-block](#org-ml-build-comment-block-key-value--name-plot-header-results-caption-post-blank) `(&key (value \"\") name plot header results caption post-blank)`\n* [org-ml-build-diary-sexp](#org-ml-build-diary-sexp-key-value-name-plot-header-results-caption-post-blank) `(&key value name plot header results caption post-blank)`\n* [org-ml-build-example-block](#org-ml-build-example-block-key-preserve-indent-switches-value--name-plot-header-results-caption-post-blank) `(&key preserve-indent switches (value \"\") name plot header results caption post-blank)`\n* [org-ml-build-export-block](#org-ml-build-export-block-type-value-key-name-plot-header-results-caption-post-blank) `(type value &key name plot header results caption post-blank)`\n* [org-ml-build-fixed-width](#org-ml-build-fixed-width-value-key-name-plot-header-results-caption-post-blank) `(value &key name plot header results caption post-blank)`\n* [org-ml-build-horizontal-rule](#org-ml-build-horizontal-rule-key-name-plot-header-results-caption-post-blank) `(&key name plot header results caption post-blank)`\n* [org-ml-build-keyword](#org-ml-build-keyword-key-value-key-name-plot-header-results-caption-post-blank) `(key value &key name plot header results caption post-blank)`\n* [org-ml-build-latex-environment](#org-ml-build-latex-environment-value-key-name-plot-header-results-caption-post-blank) `(value &key name plot header results caption post-blank)`\n* [org-ml-build-node-property](#org-ml-build-node-property-key-value-key-post-blank) `(key value &key post-blank)`\n* [org-ml-build-planning](#org-ml-build-planning-key-closed-deadline-scheduled-post-blank) `(&key closed deadline scheduled post-blank)`\n* [org-ml-build-src-block](#org-ml-build-src-block-key-value--language-parameters-preserve-indent-switches-name-plot-header-results-caption-post-blank) `(&key (value \"\") language parameters preserve-indent switches name plot header results caption post-blank)`\n\n### Branch Element Nodes with Child Object Nodes\n\n* [org-ml-build-paragraph](#org-ml-build-paragraph-key-name-plot-header-results-caption-post-blank-rest-object-nodes) `(&key name plot header results caption post-blank &rest object-nodes)`\n* [org-ml-build-table-row](#org-ml-build-table-row-key-post-blank-rest-object-nodes) `(&key post-blank &rest object-nodes)`\n* [org-ml-build-verse-block](#org-ml-build-verse-block-key-name-plot-header-results-caption-post-blank-rest-object-nodes) `(&key name plot header results caption post-blank &rest object-nodes)`\n\n### Branch Element Nodes with Child Element Nodes\n\n* [org-ml-build-org-data](#org-ml-build-org-data-rest-nodes) `(&rest nodes)`\n* [org-ml-build-center-block](#org-ml-build-center-block-key-name-plot-header-results-caption-post-blank-rest-element-nodes) `(&key name plot header results caption post-blank &rest element-nodes)`\n* [org-ml-build-drawer](#org-ml-build-drawer-drawer-name-key-name-plot-header-results-caption-post-blank-rest-element-nodes) `(drawer-name &key name plot header results caption post-blank &rest element-nodes)`\n* [org-ml-build-dynamic-block](#org-ml-build-dynamic-block-block-name-key-arguments-name-plot-header-results-caption-post-blank-rest-element-nodes) `(block-name &key arguments name plot header results caption post-blank &rest element-nodes)`\n* [org-ml-build-footnote-definition](#org-ml-build-footnote-definition-label-key-pre-blank-0-name-plot-header-results-caption-post-blank-rest-element-nodes) `(label &key (pre-blank 0) name plot header results caption post-blank &rest element-nodes)`\n* [org-ml-build-headline](#org-ml-build-headline-key-archivedp-commentedp-footnote-section-p-level-1-pre-blank-0-priority-tags-title-todo-keyword-post-blank-rest-element-nodes) `(&key archivedp commentedp footnote-section-p (level 1) (pre-blank 0) priority tags title todo-keyword post-blank &rest element-nodes)`\n* [org-ml-build-item](#org-ml-build-item-key-bullet---pre-blank-0-checkbox-counter-tag-post-blank-rest-element-nodes) `(&key (bullet '-) (pre-blank 0) checkbox counter tag post-blank &rest element-nodes)`\n* [org-ml-build-plain-list](#org-ml-build-plain-list-key-name-plot-header-results-caption-post-blank-rest-element-nodes) `(&key name plot header results caption post-blank &rest element-nodes)`\n* [org-ml-build-property-drawer](#org-ml-build-property-drawer-key-post-blank-rest-element-nodes) `(&key post-blank &rest element-nodes)`\n* [org-ml-build-quote-block](#org-ml-build-quote-block-key-name-plot-header-results-caption-post-blank-rest-element-nodes) `(&key name plot header results caption post-blank &rest element-nodes)`\n* [org-ml-build-section](#org-ml-build-section-key-post-blank-rest-element-nodes) `(&key post-blank &rest element-nodes)`\n* [org-ml-build-special-block](#org-ml-build-special-block-type-key-parameters-name-plot-header-results-caption-post-blank-rest-element-nodes) `(type &key parameters name plot header results caption post-blank &rest element-nodes)`\n* [org-ml-build-table](#org-ml-build-table-key-tblfm-name-plot-header-results-caption-post-blank-rest-element-nodes) `(&key tblfm name plot header results caption post-blank &rest element-nodes)`\n\n### Miscellaneous Builders\n\n* [org-ml-build-secondary-string!](#org-ml-build-secondary-string-string) `(string)`\n* [org-ml-build-table-row-hline](#org-ml-build-table-row-hline-key-post-blank) `(&key post-blank)`\n* [org-ml-build-timestamp-diary](#org-ml-build-timestamp-diary-form-key-start-end-post-blank) `(form &key start end post-blank)`\n\n### Shorthand Builders\n\n\nBuild nodes with more convenient/shorter syntax.\n\n* [org-ml-build-timestamp!](#org-ml-build-timestamp-start-key-end-active-repeater-deadline-warning-collapsed-post-blank) `(start &key end active repeater deadline warning collapsed post-blank)`\n* [org-ml-build-clock!](#org-ml-build-clock-start-key-end-post-blank) `(start &key end post-blank)`\n* [org-ml-build-planning!](#org-ml-build-planning-key-closed-deadline-scheduled-post-blank) `(&key closed deadline scheduled post-blank)`\n* [org-ml-build-property-drawer!](#org-ml-build-property-drawer-key-post-blank-rest-keyvals) `(&key post-blank &rest keyvals)`\n* [org-ml-build-headline!](#org-ml-build-headline-key-level-1-title-text-todo-keyword-tags-pre-blank-priority-commentedp-archivedp-post-blank-planning-statistics-cookie-section-children-rest-subheadlines) `(&key (level 1) title-text todo-keyword tags pre-blank priority commentedp archivedp post-blank planning statistics-cookie section-children &rest subheadlines)`\n* [org-ml-build-item!](#org-ml-build-item-key-post-blank-bullet-checkbox-tag-paragraph-counter-rest-children) `(&key post-blank bullet checkbox tag paragraph counter &rest children)`\n* [org-ml-build-paragraph!](#org-ml-build-paragraph-string-key-post-blank) `(string &key post-blank)`\n* [org-ml-build-table-cell!](#org-ml-build-table-cell-string) `(string)`\n* [org-ml-build-table-row!](#org-ml-build-table-row-row-list) `(row-list)`\n* [org-ml-build-table!](#org-ml-build-table-key-tblfm-post-blank-rest-row-lists) `(&key tblfm post-blank &rest row-lists)`\n\n### Logbook Item Builders\n\n\nBuild item nodes for inclusion in headline logbooks\n\n* [org-ml-build-log-note](#org-ml-build-log-note-unixtime-note) `(unixtime note)`\n* [org-ml-build-log-done](#org-ml-build-log-done-unixtime-optional-note) `(unixtime &optional note)`\n* [org-ml-build-log-refile](#org-ml-build-log-refile-unixtime-optional-note) `(unixtime &optional note)`\n* [org-ml-build-log-state](#org-ml-build-log-state-unixtime-new-state-old-state-optional-note) `(unixtime new-state old-state &optional note)`\n* [org-ml-build-log-deldeadline](#org-ml-build-log-deldeadline-unixtime-old-timestamp-optional-note) `(unixtime old-timestamp &optional note)`\n* [org-ml-build-log-delschedule](#org-ml-build-log-delschedule-unixtime-old-timestamp-optional-note) `(unixtime old-timestamp &optional note)`\n* [org-ml-build-log-redeadline](#org-ml-build-log-redeadline-unixtime-old-timestamp-optional-note) `(unixtime old-timestamp &optional note)`\n* [org-ml-build-log-reschedule](#org-ml-build-log-reschedule-unixtime-old-timestamp-optional-note) `(unixtime old-timestamp &optional note)`\n* [org-ml-build-log-type](#org-ml-build-log-type-type-key-old-new-unixtime-username-full-username-note) `(type &key old new unixtime username full-username note)`\n\n## Type Predicates\n\n\nTest node types.\n\n* [org-ml-get-type](#org-ml-get-type-node-optional-anonymous) `(node &optional anonymous)`\n* [org-ml-is-type](#org-ml-is-type-type-node) `(type node)`\n* [org-ml-is-any-type](#org-ml-is-any-type-types-node) `(types node)`\n* [org-ml-is-element](#org-ml-is-element-node) `(node)`\n* [org-ml-is-branch-node](#org-ml-is-branch-node-node) `(node)`\n* [org-ml-node-may-have-child-objects](#org-ml-node-may-have-child-objects-node) `(node)`\n* [org-ml-node-may-have-child-elements](#org-ml-node-may-have-child-elements-node) `(node)`\n\n## Property Manipulation\n\n\nSet, get, and map properties of nodes.\n\n\n### Generic\n\n* [org-ml-contains-point-p](#org-ml-contains-point-p-point-node) `(point node)`\n* [org-ml-set-property](#org-ml-set-property-prop-value-node) `(prop value node)`\n* [org-ml-get-property](#org-ml-get-property-prop-node) `(prop node)`\n* [org-ml-map-property](#org-ml-map-property-prop-fun-node) `(prop fun node)`\n* [org-ml-toggle-property](#org-ml-toggle-property-prop-node) `(prop node)`\n* [org-ml-shift-property](#org-ml-shift-property-prop-n-node) `(prop n node)`\n* [org-ml-insert-into-property](#org-ml-insert-into-property-prop-index-string-node) `(prop index string node)`\n* [org-ml-remove-from-property](#org-ml-remove-from-property-prop-string-node) `(prop string node)`\n* [org-ml-plist-put-property](#org-ml-plist-put-property-prop-key-value-node) `(prop key value node)`\n* [org-ml-plist-remove-property](#org-ml-plist-remove-property-prop-key-node) `(prop key node)`\n* [org-ml-get-properties](#org-ml-get-properties-props-node) `(props node)`\n* [org-ml-get-all-properties](#org-ml-get-all-properties-node) `(node)`\n* [org-ml-set-properties](#org-ml-set-properties-plist-node) `(plist node)`\n* [org-ml-map-properties](#org-ml-map-properties-plist-node) `(plist node)`\n* [org-ml-get-parents](#org-ml-get-parents-node) `(node)`\n* [org-ml-remove-parent](#org-ml-remove-parent-node) `(node)`\n\n### Clock\n\n* [org-ml-clock-is-running](#org-ml-clock-is-running-clock) `(clock)`\n\n### Entity\n\n* [org-ml-entity-get-replacement](#org-ml-entity-get-replacement-key-entity) `(key entity)`\n\n### Headline\n\n* [org-ml-headline-set-title!](#org-ml-headline-set-title-title-text-stats-cookie-value-headline) `(title-text stats-cookie-value headline)`\n* [org-ml-headline-is-done](#org-ml-headline-is-done-headline) `(headline)`\n* [org-ml-headline-has-tag](#org-ml-headline-has-tag-tag-headline) `(tag headline)`\n* [org-ml-headline-get-statistics-cookie](#org-ml-headline-get-statistics-cookie-headline) `(headline)`\n\n### Item\n\n* [org-ml-item-toggle-checkbox](#org-ml-item-toggle-checkbox-item) `(item)`\n\n### Statistics Cookie\n\n* [org-ml-statistics-cookie-is-complete](#org-ml-statistics-cookie-is-complete-statistics-cookie) `(statistics-cookie)`\n* [org-ml-timestamp-get-start-time](#org-ml-timestamp-get-start-time-timestamp) `(timestamp)`\n* [org-ml-timestamp-get-end-time](#org-ml-timestamp-get-end-time-timestamp) `(timestamp)`\n* [org-ml-timestamp-get-range](#org-ml-timestamp-get-range-timestamp) `(timestamp)`\n* [org-ml-timestamp-is-active](#org-ml-timestamp-is-active-timestamp) `(timestamp)`\n* [org-ml-timestamp-is-ranged](#org-ml-timestamp-is-ranged-timestamp) `(timestamp)`\n* [org-ml-timestamp-range-contains-p](#org-ml-timestamp-range-contains-p-unixtime-timestamp) `(unixtime timestamp)`\n* [org-ml-timestamp-set-collapsed](#org-ml-timestamp-set-collapsed-flag-timestamp) `(flag timestamp)`\n* [org-ml-timestamp-get-warning](#org-ml-timestamp-get-warning-timestamp) `(timestamp)`\n* [org-ml-timestamp-set-warning](#org-ml-timestamp-set-warning-warning-timestamp) `(warning timestamp)`\n* [org-ml-timestamp-map-warning](#org-ml-timestamp-map-warning-fun-timestamp) `(fun timestamp)`\n* [org-ml-timestamp-get-repeater](#org-ml-timestamp-get-repeater-timestamp) `(timestamp)`\n* [org-ml-timestamp-get-deadline](#org-ml-timestamp-get-deadline-timestamp) `(timestamp)`\n* [org-ml-timestamp-set-repeater](#org-ml-timestamp-set-repeater-repeater-timestamp) `(repeater timestamp)`\n* [org-ml-timestamp-set-deadline](#org-ml-timestamp-set-deadline-deadline-timestamp) `(deadline timestamp)`\n* [org-ml-timestamp-map-repeater](#org-ml-timestamp-map-repeater-fun-timestamp) `(fun timestamp)`\n* [org-ml-timestamp-set-start-time](#org-ml-timestamp-set-start-time-time-timestamp) `(time timestamp)`\n* [org-ml-timestamp-set-end-time](#org-ml-timestamp-set-end-time-time-timestamp) `(time timestamp)`\n* [org-ml-timestamp-set-single-time](#org-ml-timestamp-set-single-time-time-timestamp) `(time timestamp)`\n* [org-ml-timestamp-set-double-time](#org-ml-timestamp-set-double-time-time1-time2-timestamp) `(time1 time2 timestamp)`\n* [org-ml-timestamp-set-range](#org-ml-timestamp-set-range-n-timestamp) `(n timestamp)`\n* [org-ml-timestamp-set-active](#org-ml-timestamp-set-active-flag-timestamp) `(flag timestamp)`\n* [org-ml-timestamp-shift](#org-ml-timestamp-shift-n-unit-timestamp) `(n unit timestamp)`\n* [org-ml-timestamp-shift-start](#org-ml-timestamp-shift-start-n-unit-timestamp) `(n unit timestamp)`\n* [org-ml-timestamp-shift-end](#org-ml-timestamp-shift-end-n-unit-timestamp) `(n unit timestamp)`\n* [org-ml-timestamp-toggle-active](#org-ml-timestamp-toggle-active-timestamp) `(timestamp)`\n* [org-ml-timestamp-truncate](#org-ml-timestamp-truncate-timestamp) `(timestamp)`\n* [org-ml-timestamp-truncate-start](#org-ml-timestamp-truncate-start-timestamp) `(timestamp)`\n* [org-ml-timestamp-truncate-end](#org-ml-timestamp-truncate-end-timestamp) `(timestamp)`\n\n### Timestamp (diary)\n\n* [org-ml-timestamp-diary-set-value](#org-ml-timestamp-diary-set-value-form-timestamp-diary) `(form timestamp-diary)`\n* [org-ml-timestamp-diary-set-single-time](#org-ml-timestamp-diary-set-single-time-time-timestamp-diary) `(time timestamp-diary)`\n* [org-ml-timestamp-diary-set-double-time](#org-ml-timestamp-diary-set-double-time-time1-time2-timestamp-diary) `(time1 time2 timestamp-diary)`\n* [org-ml-timestamp-diary-get-start-time](#org-ml-timestamp-diary-get-start-time-timestamp-diary) `(timestamp-diary)`\n* [org-ml-timestamp-diary-set-start-time](#org-ml-timestamp-diary-set-start-time-time-timestamp-diary) `(time timestamp-diary)`\n* [org-ml-timestamp-diary-get-end-time](#org-ml-timestamp-diary-get-end-time-timestamp-diary) `(timestamp-diary)`\n* [org-ml-timestamp-diary-set-end-time](#org-ml-timestamp-diary-set-end-time-time-timestamp-diary) `(time timestamp-diary)`\n* [org-ml-timestamp-diary-set-length](#org-ml-timestamp-diary-set-length-n-unit-timestamp-diary) `(n unit timestamp-diary)`\n* [org-ml-timestamp-diary-shift](#org-ml-timestamp-diary-shift-n-unit-timestamp-diary) `(n unit timestamp-diary)`\n* [org-ml-timestamp-diary-shift-start](#org-ml-timestamp-diary-shift-start-n-unit-timestamp-diary) `(n unit timestamp-diary)`\n* [org-ml-timestamp-diary-shift-end](#org-ml-timestamp-diary-shift-end-n-unit-timestamp-diary) `(n unit timestamp-diary)`\n\n## Branch/Child Manipulation\n\n\nSet, get, and map the children of branch nodes.\n\n\n### Polymorphic\n\n* [org-ml-children-contain-point](#org-ml-children-contain-point-point-branch-node) `(point branch-node)`\n* [org-ml-get-children](#org-ml-get-children-branch-node) `(branch-node)`\n* [org-ml-set-children](#org-ml-set-children-children-branch-node) `(children branch-node)`\n* [org-ml-map-children](#org-ml-map-children-fun-branch-node) `(fun branch-node)`\n* [org-ml-is-childless](#org-ml-is-childless-branch-node) `(branch-node)`\n\n### Object Nodes\n\n* [org-ml-unwrap](#org-ml-unwrap-object-node) `(object-node)`\n* [org-ml-unwrap-types-deep](#org-ml-unwrap-types-deep-types-object-node) `(types object-node)`\n* [org-ml-unwrap-deep](#org-ml-unwrap-deep-object-node) `(object-node)`\n\n### Secondary Strings\n\n* [org-ml-flatten](#org-ml-flatten-secondary-string) `(secondary-string)`\n* [org-ml-flatten-types-deep](#org-ml-flatten-types-deep-types-secondary-string) `(types secondary-string)`\n* [org-ml-flatten-deep](#org-ml-flatten-deep-secondary-string) `(secondary-string)`\n\n### Item\n\n* [org-ml-item-get-paragraph](#org-ml-item-get-paragraph-item) `(item)`\n* [org-ml-item-set-paragraph](#org-ml-item-set-paragraph-secondary-string-item) `(secondary-string item)`\n* [org-ml-item-map-paragraph](#org-ml-item-map-paragraph-fun-item) `(fun item)`\n\n### Headline\n\n* [org-ml-headline-get-section](#org-ml-headline-get-section-headline) `(headline)`\n* [org-ml-headline-set-section](#org-ml-headline-set-section-children-headline) `(children headline)`\n* [org-ml-headline-map-section](#org-ml-headline-map-section-fun-headline) `(fun headline)`\n* [org-ml-headline-get-subheadlines](#org-ml-headline-get-subheadlines-headline) `(headline)`\n* [org-ml-headline-set-subheadlines](#org-ml-headline-set-subheadlines-subheadlines-headline) `(subheadlines headline)`\n* [org-ml-headline-map-subheadlines](#org-ml-headline-map-subheadlines-fun-headline) `(fun headline)`\n\n### Headline (metadata)\n\n* [org-ml-headline-get-planning](#org-ml-headline-get-planning-headline) `(headline)`\n* [org-ml-headline-set-planning](#org-ml-headline-set-planning-planning-headline) `(planning headline)`\n* [org-ml-headline-map-planning](#org-ml-headline-map-planning-fun-headline) `(fun headline)`\n* [org-ml-headline-get-node-properties](#org-ml-headline-get-node-properties-headline) `(headline)`\n* [org-ml-headline-set-node-properties](#org-ml-headline-set-node-properties-node-properties-headline) `(node-properties headline)`\n* [org-ml-headline-map-node-properties](#org-ml-headline-map-node-properties-fun-headline) `(fun headline)`\n* [org-ml-headline-get-node-property](#org-ml-headline-get-node-property-key-headline) `(key headline)`\n* [org-ml-headline-set-node-property](#org-ml-headline-set-node-property-key-value-headline) `(key value headline)`\n* [org-ml-headline-map-node-property](#org-ml-headline-map-node-property-key-fun-headline) `(key fun headline)`\n\n### Headline (logbook and contents)\n\n* [org-ml-headline-get-supercontents](#org-ml-headline-get-supercontents-config-headline) `(config headline)`\n* [org-ml-headline-set-supercontents](#org-ml-headline-set-supercontents-config-supercontents-headline) `(config supercontents headline)`\n* [org-ml-headline-map-supercontents](#org-ml-headline-map-supercontents-config-fun-headline) `(config fun headline)`\n* [org-ml-headline-get-logbook-items](#org-ml-headline-get-logbook-items-config-headline) `(config headline)`\n* [org-ml-headline-set-logbook-items](#org-ml-headline-set-logbook-items-config-items-headline) `(config items headline)`\n* [org-ml-headline-map-logbook-items](#org-ml-headline-map-logbook-items-config-fun-headline) `(config fun headline)`\n* [org-ml-headline-get-logbook-clocks](#org-ml-headline-get-logbook-clocks-config-headline) `(config headline)`\n* [org-ml-headline-set-logbook-clocks](#org-ml-headline-set-logbook-clocks-config-clocks-headline) `(config clocks headline)`\n* [org-ml-headline-map-logbook-clocks](#org-ml-headline-map-logbook-clocks-config-fun-headline) `(config fun headline)`\n* [org-ml-headline-get-contents](#org-ml-headline-get-contents-config-headline) `(config headline)`\n* [org-ml-headline-set-contents](#org-ml-headline-set-contents-config-contents-headline) `(config contents headline)`\n* [org-ml-headline-map-contents](#org-ml-headline-map-contents-config-fun-headline) `(config fun headline)`\n* [org-ml-headline-logbook-append-item](#org-ml-headline-logbook-append-item-config-item-headline) `(config item headline)`\n* [org-ml-headline-logbook-append-open-clock](#org-ml-headline-logbook-append-open-clock-config-unixtime-headline) `(config unixtime headline)`\n* [org-ml-headline-logbook-close-open-clock](#org-ml-headline-logbook-close-open-clock-config-unixtime-note-headline) `(config unixtime note headline)`\n* [org-ml-headline-logbook-convert-config](#org-ml-headline-logbook-convert-config-config1-config2-headline) `(config1 config2 headline)`\n\n### Headline (misc)\n\n* [org-ml-headline-get-path](#org-ml-headline-get-path-headline) `(headline)`\n* [org-ml-headline-update-item-statistics](#org-ml-headline-update-item-statistics-headline) `(headline)`\n* [org-ml-headline-update-todo-statistics](#org-ml-headline-update-todo-statistics-headline) `(headline)`\n* [org-ml-headline-demote-subheadline](#org-ml-headline-demote-subheadline-index-headline) `(index headline)`\n* [org-ml-headline-demote-subtree](#org-ml-headline-demote-subtree-index-headline) `(index headline)`\n* [org-ml-headline-promote-subheadline](#org-ml-headline-promote-subheadline-index-child-index-headline) `(index child-index headline)`\n* [org-ml-headline-promote-all-subheadlines](#org-ml-headline-promote-all-subheadlines-index-headline) `(index headline)`\n\n### Plain List\n\n* [org-ml-plain-list-set-type](#org-ml-plain-list-set-type-type-plain-list) `(type plain-list)`\n* [org-ml-plain-list-indent-item](#org-ml-plain-list-indent-item-index-plain-list) `(index plain-list)`\n* [org-ml-plain-list-indent-item-tree](#org-ml-plain-list-indent-item-tree-index-plain-list) `(index plain-list)`\n* [org-ml-plain-list-outdent-item](#org-ml-plain-list-outdent-item-index-child-index-plain-list) `(index child-index plain-list)`\n* [org-ml-plain-list-outdent-all-items](#org-ml-plain-list-outdent-all-items-index-plain-list) `(index plain-list)`\n\n### Table\n\n* [org-ml-table-get-cell](#org-ml-table-get-cell-row-index-column-index-table) `(row-index column-index table)`\n* [org-ml-table-delete-column](#org-ml-table-delete-column-column-index-table) `(column-index table)`\n* [org-ml-table-delete-row](#org-ml-table-delete-row-row-index-table) `(row-index table)`\n* [org-ml-table-insert-column!](#org-ml-table-insert-column-column-index-column-text-table) `(column-index column-text table)`\n* [org-ml-table-insert-row!](#org-ml-table-insert-row-row-index-row-text-table) `(row-index row-text table)`\n* [org-ml-table-replace-cell!](#org-ml-table-replace-cell-row-index-column-index-cell-text-table) `(row-index column-index cell-text table)`\n* [org-ml-table-replace-column!](#org-ml-table-replace-column-column-index-column-text-table) `(column-index column-text table)`\n* [org-ml-table-replace-row!](#org-ml-table-replace-row-row-index-row-text-table) `(row-index row-text table)`\n\n## Node Matching\n\n\nUse pattern-matching to selectively perform operations on nodes in trees.\n\n* [org-ml-match](#org-ml-match-pattern-node) `(pattern node)`\n* [org-ml-match-delete](#org-ml-match-delete-pattern-node) `(pattern node)`\n* [org-ml-match-extract](#org-ml-match-extract-pattern-node) `(pattern node)`\n* [org-ml-match-map](#org-ml-match-map-pattern-fun-node) `(pattern fun node)`\n* [org-ml-match-mapcat](#org-ml-match-mapcat-pattern-fun-node) `(pattern fun node)`\n* [org-ml-match-replace](#org-ml-match-replace-pattern-node-node) `(pattern node* node)`\n* [org-ml-match-insert-before](#org-ml-match-insert-before-pattern-node-node) `(pattern node* node)`\n* [org-ml-match-insert-after](#org-ml-match-insert-after-pattern-node-node) `(pattern node* node)`\n* [org-ml-match-insert-within](#org-ml-match-insert-within-pattern-index-node-node) `(pattern index node* node)`\n* [org-ml-match-splice](#org-ml-match-splice-pattern-nodes-node) `(pattern nodes* node)`\n* [org-ml-match-splice-before](#org-ml-match-splice-before-pattern-nodes-node) `(pattern nodes* node)`\n* [org-ml-match-splice-after](#org-ml-match-splice-after-pattern-nodes-node) `(pattern nodes* node)`\n* [org-ml-match-splice-within](#org-ml-match-splice-within-pattern-index-nodes-node) `(pattern index nodes* node)`\n* [org-ml-match-do](#org-ml-match-do-pattern-fun-node) `(pattern fun node)`\n\n## Buffer Side Effects\n\n\nMap node manipulations into buffers.\n\n\n### Insert\n\n* [org-ml-insert](#org-ml-insert-point-node) `(point node)`\n* [org-ml-insert-tail](#org-ml-insert-tail-point-node) `(point node)`\n\n### Update\n\n* [org-ml-update](#org-ml-update-fun-node) `(fun node)`\n* [org-ml-update-object-at](#org-ml-update-object-at-point-fun) `(point fun)`\n* [org-ml-update-element-at](#org-ml-update-element-at-point-fun) `(point fun)`\n* [org-ml-update-table-row-at](#org-ml-update-table-row-at-point-fun) `(point fun)`\n* [org-ml-update-item-at](#org-ml-update-item-at-point-fun) `(point fun)`\n* [org-ml-update-headline-at](#org-ml-update-headline-at-point-fun) `(point fun)`\n* [org-ml-update-subtree-at](#org-ml-update-subtree-at-point-fun) `(point fun)`\n* [org-ml-update-section-at](#org-ml-update-section-at-point-fun) `(point fun)`\n* [org-ml-update-headlines](#org-ml-update-headlines-which-fun) `(which fun)`\n* [org-ml-update-subtrees](#org-ml-update-subtrees-which-fun) `(which fun)`\n* [org-ml-update-supercontents](#org-ml-update-supercontents-config-which-fun) `(config which fun)`\n\n### Misc\n\n* [org-ml-fold](#org-ml-fold-node) `(node)`\n* [org-ml-unfold](#org-ml-unfold-node) `(node)`\n## String Conversion\n\n\nConvert nodes to strings.\n\n#### org-ml-to-string `(node)`\n\nReturn **`node`** as an interpreted string without text properties.\n\n```el\n(org-ml-to-string\n  (quote\n    (bold (:begin 1 :end 5 :parent nil :post-blank 0 :post-affiliated nil)\n      \"text\")))\n ;; => \"*text*\"\n\n(org-ml-to-string\n  (quote\n    (bold (:begin 1 :end 5 :parent nil :post-blank 3 :post-affiliated nil)\n      \"text\")))\n ;; => \"*text*   \"\n\n(org-ml-to-string nil)\n ;; => \"\"\n\n```\n\n#### org-ml-to-trimmed-string `(node)`\n\nLike [`org-ml-to-string`](#org-ml-to-string-node) but strip whitespace when returning **`node`**.\n\n```el\n(org-ml-to-trimmed-string\n  (quote\n    (bold (:begin 1 :end 5 :parent nil :post-blank 0 :post-affiliated nil)\n      \"text\")))\n ;; => \"*text*\"\n\n(org-ml-to-trimmed-string\n  (quote\n    (bold (:begin 1 :end 5 :parent nil :post-blank 3 :post-affiliated nil)\n      \"text\")))\n ;; => \"*text*\"\n\n(org-ml-to-trimmed-string nil)\n ;; => \"\"\n\n```\n\n#### org-ml-from-string `(type string)`\n\nConvert **`string`** to a node.\n**`type`** is the node type intended by **`string`**; if **`string`** cannot be\nparsed into **`type`** this function will return nil.\n\n```el\n(->> (org-ml-from-string 'bold \"*text*\") (org-ml-get-type))\n ;; => 'bold\n\n(->> (org-ml-from-string 'bold \"*text*\") (org-ml-get-property :begin))\n ;; => 1\n\n(->> (org-ml-from-string 'bold \"*text*\") (org-ml-get-property :end))\n ;; => 7\n\n(->> (org-ml-from-string 'bold \"*text*\") (org-ml-get-property :post-blank))\n ;; => 0\n\n(->> (org-ml-from-string 'bold \"*text*\")\n  (org-ml-get-property :contents-begin))\n ;; => 2\n\n(->> (org-ml-from-string 'bold \"*text*\") (org-ml-get-property :contents-end))\n ;; => 6\n\n(org-ml-from-string 'italic \"*text*\")\n ;; => nil\n\n```\n\n\n## Buffer Parsing\n\n\nParse buffers to trees.\n\n#### org-ml-parse-this-buffer `nil`\n\nReturn org-data document tree for the current buffer.\nContrary to the org-element specification, the org-data element\nreturned from this function will have :begin and :end properties.\n\n```el\n;; Given the following contents:\n; text\n\n(->> (org-ml-parse-this-buffer) (org-ml-get-property :begin))\n ;; => 1\n\n(->> (org-ml-parse-this-buffer) (org-ml-get-property :end))\n ;; => 5\n\n```\n\n#### org-ml-parse-object-at `(point)`\n\nReturn object node under **`point`** or nil if not on an object.\n\n```el\n;; Given the following contents:\n; *text*\n\n(->> (org-ml-parse-object-at 1) (car))\n ;; => 'bold\n\n;; Given the following contents:\n; [2019-01-01 Tue]\n\n(->> (org-ml-parse-object-at 1) (car))\n ;; => 'timestamp\n\n;; Given the following contents:\n; - notme\n\n;; Return nil when parsing an element\n(org-ml-parse-object-at 1)\n ;; => nil\n\n```\n\n#### org-ml-parse-element-at `(point)`\n\nReturn element node under **`point`** or nil if not on an element.\n\nThis function will return every element available in `org-ml-elements`\nwith the exception of `section`, `item`, and `table-row`. To\nspecifically parse these, use the functions [`org-ml-parse-section-at`](#org-ml-parse-section-at-point),\n[`org-ml-parse-item-at`](#org-ml-parse-item-at-point), and [`org-ml-parse-table-row-at`](#org-ml-parse-table-row-at-point).\n\n```el\n;; Given the following contents:\n; #+call: ktulu()\n\n(->> (org-ml-parse-element-at 1) (car))\n ;; => 'babel-call\n\n;; Given the following contents:\n; - plain-list\n\n;; Give the plain-list, not the item for this function\n(->> (org-ml-parse-element-at 1) (car))\n ;; => 'plain-list\n\n;; Given the following contents:\n; | R | A |\n; | G | E |\n\n;; Return a table, not the table-row for this function\n(->> (org-ml-parse-element-at 1) (car))\n ;; => 'table\n\n```\n\n#### org-ml-parse-table-row-at `(point)`\n\nReturn table-row node under **`point`** or nil if not on a table-row.\n\n```el\n;; Given the following contents:\n; | bow | stroke |\n; |-----+--------|\n; | wob | ekorts |\n\n;; Return the row itself\n(->> (org-ml-parse-table-row-at 1) (car))\n ;; => 'table-row\n\n(->> (org-ml-parse-table-row-at 20) (car))\n ;; => 'table-row\n\n(->> (org-ml-parse-table-row-at 40) (car))\n ;; => 'table-row\n\n;; Also return the row when not at beginning of line\n(->> (org-ml-parse-table-row-at 5) (car))\n ;; => 'table-row\n\n;; Given the following contents:\n; - bow and arrow choke\n\n;; Return nil if not a table-row\n(->> (org-ml-parse-table-row-at 1) (car))\n ;; => nil\n\n```\n\n#### org-ml-parse-headline-at `(point)`\n\nReturn headline node under **`point`** or nil if not on a headline.\n**`point`** does not need to be on the headline itself. Only the headline\nand its section will be returned. To include subheadlines, use\n[`org-ml-parse-subtree-at`](#org-ml-parse-subtree-at-point).\n\n```el\n;; Given the following contents:\n; * headline\n\n;; Return the headline itself\n(->> (org-ml-parse-headline-at 1) (org-ml-to-trimmed-string))\n ;; => \"* headline\"\n\n;; Given the following contents:\n; * headline\n; section crap\n\n;; Return headline and section\n(->> (org-ml-parse-headline-at 1) (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      section crap\"\n\n;; Return headline when point is in the section\n(->> (org-ml-parse-headline-at 12) (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      section crap\"\n\n;; Given the following contents:\n; * headline\n; section crap\n; ** not parsed\n\n;; Don't parse any subheadlines\n(->> (org-ml-parse-headline-at 1) (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      section crap\"\n\n;; Given the following contents:\n; nothing nowhere\n\n;; Return nil if not under a headline\n(->> (org-ml-parse-headline-at 1) (org-ml-to-trimmed-string))\n ;; => \"\"\n\n```\n\n#### org-ml-parse-subtree-at `(point)`\n\nReturn headline node under **`point`** or nil if not on a headline.\n**`point`** does not need to be on the headline itself. Unlike\n[`org-ml-parse-headline-at`](#org-ml-parse-headline-at-point), the returned node will include\nchild headlines.\n\n```el\n;; Given the following contents:\n; * headline\n\n;; Return the headline itself\n(->> (org-ml-parse-subtree-at 1) (org-ml-to-trimmed-string))\n ;; => \"* headline\"\n\n;; Given the following contents:\n; * headline\n; section crap\n\n;; Return headline and section\n(->> (org-ml-parse-subtree-at 1) (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      section crap\"\n\n;; Return headline when point is in the section\n(->> (org-ml-parse-subtree-at 12) (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      section crap\"\n\n;; Given the following contents:\n; * headline\n; section crap\n; ** parsed\n\n;; Return all the subheadlines\n(->> (org-ml-parse-subtree-at 1) (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      section crap\n ;      ** parsed\"\n\n;; Given the following contents:\n; nothing nowhere\n\n;; Return nil if not under a headline\n(->> (org-ml-parse-subtree-at 1) (org-ml-to-trimmed-string))\n ;; => \"\"\n\n```\n\n#### org-ml-parse-item-at `(point)`\n\nReturn item node under **`point`** or nil if not on an item.\nThis will return the item node even if **`point`** is not at the beginning\nof the line.\n\n```el\n;; Given the following contents:\n; - item\n\n;; Return the item itself\n(->> (org-ml-parse-item-at 1) (org-ml-to-trimmed-string))\n ;; => \"- item\"\n\n;; Also return the item when not at beginning of line\n(->> (org-ml-parse-item-at 5) (org-ml-to-trimmed-string))\n ;; => \"- item\"\n\n;; Given the following contents:\n; - item\n;   - item 2\n\n;; Return item and its subitems\n(->> (org-ml-parse-item-at 1) (org-ml-to-trimmed-string))\n ;; => \"- item\n ;        - item 2\"\n\n;; Given the following contents:\n; * not item\n\n;; Return nil if not an item\n(->> (org-ml-parse-item-at 1) (org-ml-to-trimmed-string))\n ;; => \"\"\n\n```\n\n#### org-ml-parse-section-at `(point)`\n\nReturn section node under **`point`** or nil if not on a section.\nIf **`point`** is on or within a headline, return the section under that\nheadline. If **`point`** is before the first headline (if any), return\nthe section at the top of the org buffer.\n\n```el\n;; Given the following contents:\n; over headline\n; * headline\n; under headline\n\n;; Return the section above the headline\n(->> (org-ml-parse-section-at 1) (org-ml-to-trimmed-string))\n ;; => \"over headline\"\n\n;; Return the section under headline\n(->> (org-ml-parse-section-at 25) (org-ml-to-trimmed-string))\n ;; => \"under headline\"\n\n;; Given the following contents:\n; * headline\n; ** subheadline\n\n;; Return nil if no section under headline\n(->> (org-ml-parse-section-at 1) (org-ml-to-trimmed-string))\n ;; => \"\"\n\n;; Given the following contents:\n; \n\n;; Return nil if no section at all\n(->> (org-ml-parse-section-at 1) (org-ml-to-trimmed-string))\n ;; => \"\"\n\n```\n\n#### org-ml-parse-this-toplevel-section `nil`\n\nReturn section node corresponding to the top of the current buffer.\nIf there is no such section, return nil.\n\n```el\n;; Given the following contents:\n; over headline\n; * headline\n; under headline\n\n(->> (org-ml-parse-this-toplevel-section) (org-ml-to-trimmed-string))\n ;; => \"over headline\"\n\n;; Given the following contents:\n; * headline\n; under headline\n\n(->> (org-ml-parse-this-toplevel-section) (org-ml-to-trimmed-string))\n ;; => \"\"\n\n```\n\n#### org-ml-this-buffer-has-headlines `nil`\n\nReturn t if the current buffer has headlines, else return nil.\n\n```el\n;; Given the following contents:\n; not headline\n; * headline\n\n(org-ml-this-buffer-has-headlines)\n ;; => t\n\n;; Given the following contents:\n; not headline\n\n(org-ml-this-buffer-has-headlines)\n ;; => nil\n\n```\n\n#### org-ml-parse-headlines `(which)`\n\nReturn list of headline nodes from current buffer.\n\n**`which`** describes the location of headlines to be parsed and is one\nof the following:\n- `n`: parse up to index `n` headlines (which 0 is the first); if negative\n    start counting from the last headline (which -1 refers to the last)\n- `(m n)`: like `n` but parse after index `m` headlines; `m` and `n` may both\n    be similarly negative\n- [`a` `b`]: parse all headlines whose first point falls between points\n    `a` and `b` in the buffer; if `a` and `b` are nil, use `point-min` and\n    `point-max` respectively.\n- `all`: parse all headlines (equivalent to [nil nil])\n\nEach headline is obtained with [`org-ml-parse-headline-at`](#org-ml-parse-headline-at-point).\n\n```el\n;; Given the following contents:\n; not headline\n; * one\n; * two\n; * three\n\n(->> (org-ml-parse-headlines 'all) (-map #'org-ml-to-string) (s-join \"\"))\n ;; => \"* one\n ;      * two\n ;      * three\n ;      \"\n\n;; Given the following contents:\n; not headline\n\n(->> (org-ml-parse-headlines 'all) (-map #'org-ml-to-string) (s-join \"\"))\n ;; => \"\"\n\n;; Given the following contents:\n; not headline\n; * one\n; ** two\n; *** three\n\n(->> (org-ml-parse-headlines 'all) (-map #'org-ml-to-trimmed-string))\n ;; => '(\"* one\n ;     ** two\n ;     *** three\" \"** two\n ;     *** three\" \"*** three\")\n\n;; Given the following contents:\n; not headline\n; *ignore this*\n; * one\n; * two\n; * three\n\n(->> (org-ml-parse-headlines 0) (-map #'org-ml-to-string) (s-join \"\"))\n ;; => \"* one\n ;      \"\n\n(->> (org-ml-parse-headlines '(0 1)) (-map #'org-ml-to-string) (s-join \"\"))\n ;; => \"* one\n ;      * two\n ;      \"\n\n(->> (org-ml-parse-headlines [23 38]) (-map #'org-ml-to-string) (s-join \"\"))\n ;; => \"* one\n ;      * two\n ;      \"\n\n```\n\n#### org-ml-parse-subtrees `(which)`\n\nReturn list of subtree nodes from current buffer.\n\n**`which`** has analogous meaning to that in [`org-ml-parse-headlines`](#org-ml-parse-headlines-which)\nexcept applied to subtrees not individual headlines.\n\n```el\n;; Given the following contents:\n; not headline\n; * one\n; ** _one\n; * two\n; ** _two\n; * three\n; ** _three\n\n(->> (org-ml-parse-subtrees 'all) (-map #'org-ml-to-string) (s-join \"\"))\n ;; => \"* one\n ;      ** _one\n ;      * two\n ;      ** _two\n ;      * three\n ;      ** _three\n ;      \"\n\n;; Given the following contents:\n; not headline\n\n(->> (org-ml-parse-subtrees 'all) (-map #'org-ml-to-string) (s-join \"\"))\n ;; => \"\"\n\n;; Given the following contents:\n; not headline\n; * one\n; ** _one\n; * two\n; ** _two\n; * three\n; ** _three\n\n(->> (org-ml-parse-subtrees 0) (-map #'org-ml-to-string) (s-join \"\"))\n ;; => \"* one\n ;      ** _one\n ;      \"\n\n(->> (org-ml-parse-subtrees '(0 1)) (-map #'org-ml-to-string) (s-join \"\"))\n ;; => \"* one\n ;      ** _one\n ;      * two\n ;      ** _two\n ;      \"\n\n(->> (org-ml-parse-subtrees [10 30]) (-map #'org-ml-to-string) (s-join \"\"))\n ;; => \"* one\n ;      ** _one\n ;      * two\n ;      ** _two\n ;      \"\n\n```\n\n\n## Building\n\n\nBuild new nodes.\n\n\n### Leaf Object Nodes\n\n#### org-ml-build-code `(value &key post-blank)`\n\nBuild a code object node.\n\nThe following properties are settable:\n- **`value`**: (required) a string\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-code \"text\") (org-ml-to-string))\n ;; => \"~text~\"\n\n```\n\n#### org-ml-build-entity `(name &key use-brackets-p post-blank)`\n\nBuild an entity object node.\n\nThe following properties are settable:\n- **`name`**: (required) a string that makes `org-entity-get` return non-nil\n- **`use-brackets-p`**: nil or t\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-entity \"gamma\") (org-ml-to-string))\n ;; => \"\\\\gamma\"\n\n```\n\n#### org-ml-build-export-snippet `(back-end value &key post-blank)`\n\nBuild an export-snippet object node.\n\nThe following properties are settable:\n- **`back-end`**: (required) a oneline string\n- **`value`**: (required) a string\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-export-snippet \"back\" \"value\") (org-ml-to-string))\n ;; => \"@@back:value@@\"\n\n```\n\n#### org-ml-build-inline-babel-call `(call &key inside-header arguments end-header post-blank)`\n\nBuild an inline-babel-call object node.\n\nThe following properties are settable:\n- **`call`**: (required) a oneline string\n- **`inside-header`**: a plist\n- **`arguments`**: a list of oneline strings\n- **`end-header`**: a plist\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-inline-babel-call \"name\") (org-ml-to-string))\n ;; => \"call_name()\"\n\n(->> (org-ml-build-inline-babel-call \"name\" :arguments '(\"n=4\"))\n  (org-ml-to-string))\n ;; => \"call_name(n=4)\"\n\n(->> (org-ml-build-inline-babel-call \"name\" :inside-header '(:key val))\n  (org-ml-to-string))\n ;; => \"call_name[:key val]()\"\n\n(->> (org-ml-build-inline-babel-call \"name\" :end-header '(:key val))\n  (org-ml-to-string))\n ;; => \"call_name()[:key val]\"\n\n```\n\n#### org-ml-build-inline-src-block `(language &key parameters (value \"\") post-blank)`\n\nBuild an inline-src-block object node.\n\nThe following properties are settable:\n- **`language`**: (required) a oneline string\n- **`parameters`**: a plist\n- **`value`**: (default `\"\"`) a string\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-inline-src-block \"lang\") (org-ml-to-string))\n ;; => \"src_lang{}\"\n\n(->> (org-ml-build-inline-src-block \"lang\" :value \"value\")\n  (org-ml-to-string))\n ;; => \"src_lang{value}\"\n\n(->>\n  (org-ml-build-inline-src-block \"lang\"\n    :value\n    \"value\"\n    :parameters\n    '(:key val))\n  (org-ml-to-string))\n ;; => \"src_lang[:key val]{value}\"\n\n```\n\n#### org-ml-build-line-break `(&key post-blank)`\n\nBuild a line-break object node.\n\nThe following properties are settable:\n\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-line-break) (org-ml-to-string))\n ;; => \"\\\\\\\\\n ;      \"\n\n```\n\n#### org-ml-build-latex-fragment `(value &key post-blank)`\n\nBuild a latex-fragment object node.\n\nThe following properties are settable:\n- **`value`**: (required) a string\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-latex-fragment \"$2+2=5$\") (org-ml-to-string))\n ;; => \"$2+2=5$\"\n\n```\n\n#### org-ml-build-macro `(key &key args post-blank)`\n\nBuild a macro object node.\n\nThe following properties are settable:\n- **`key`**: (required) a oneline string\n- **`args`**: a list of oneline strings\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-macro \"economics\") (org-ml-to-string))\n ;; => \"{{{economics}}}\"\n\n(->> (org-ml-build-macro \"economics\" :args '(\"s=d\")) (org-ml-to-string))\n ;; => \"{{{economics(s=d)}}}\"\n\n```\n\n#### org-ml-build-statistics-cookie `(value &key post-blank)`\n\nBuild a statistics-cookie object node.\n\nThe following properties are settable:\n- **`value`**: (required) a list of non-neg integers like `(perc)` or `(num\n    den)` which make [`num`/`den`] and [`perc`%] respectively\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-statistics-cookie '(nil)) (org-ml-to-string))\n ;; => \"[%]\"\n\n(->> (org-ml-build-statistics-cookie '(nil nil)) (org-ml-to-string))\n ;; => \"[/]\"\n\n(->> (org-ml-build-statistics-cookie '(50)) (org-ml-to-string))\n ;; => \"[50%]\"\n\n(->> (org-ml-build-statistics-cookie '(1 3)) (org-ml-to-string))\n ;; => \"[1/3]\"\n\n```\n\n#### org-ml-build-target `(value &key post-blank)`\n\nBuild a target object node.\n\nThe following properties are settable:\n- **`value`**: (required) a oneline string\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-target \"text\") (org-ml-to-string))\n ;; => \"<<text>>\"\n\n```\n\n#### org-ml-build-timestamp `(type year-start month-start day-start year-end month-end day-end &key range-type hour-start minute-start hour-end minute-end repeater-type repeater-unit repeater-value repeater-deadline-unit repeater-deadline-value warning-type warning-unit warning-value post-blank)`\n\nBuild a timestamp object node.\n\nThe following properties are settable:\n- **`type`**: (required) a symbol from `inactive`, `active`,\n    `inactive-range`, or `active-range`\n- **`year-start`**: (required) a positive integer\n- **`month-start`**: (required) a positive integer\n- **`day-start`**: (required) a positive integer\n- **`year-end`**: (required) a positive integer\n- **`month-end`**: (required) a positive integer\n- **`day-end`**: (required) a positive integer\n- **`range-type`**: either symbol `daterange` or `timerange` or nil\n- **`hour-start`**: a non-negative integer or nil\n- **`minute-start`**: a non-negative integer or nil\n- **`hour-end`**: a non-negative integer or nil\n- **`minute-end`**: a non-negative integer or nil\n- **`repeater-type`**: nil or a symbol from `catch-up`, `restart`, or\n    `cumulate`\n- **`repeater-unit`**: nil or a symbol from `year` `month` `week` `day`, or\n    `hour`\n- **`repeater-value`**: a positive integer or nil\n- **`repeater-deadline-unit`**: nil or a symbol from `year` `month` `week`\n    `day`, or `hour`\n- **`repeater-deadline-value`**: a positive integer or nil\n- **`warning-type`**: nil or a symbol from `all` or `first`\n- **`warning-unit`**: nil or a symbol from `year` `month` `week` `day`, or\n    `hour`\n- **`warning-value`**: a positive integer or nil\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-timestamp 'inactive 2019 1 15 2019 1 15)\n  (org-ml-to-string))\n ;; => \"[2019-01-15 Tue]\"\n\n(->> (org-ml-build-timestamp 'active-range 2019 1 15 2019 1 16)\n  (org-ml-to-string))\n ;; => \"<2019-01-15 Tue>--<2019-01-16 Wed>\"\n\n(->>\n  (org-ml-build-timestamp 'inactive\n    2019\n    1\n    15\n    2019\n    1\n    15\n    :warning-type\n    'all\n    :warning-unit\n    'day\n    :warning-value\n    1)\n  (org-ml-to-string))\n ;; => \"[2019-01-15 Tue -1d]\"\n\n```\n\n#### org-ml-build-verbatim `(value &key post-blank)`\n\nBuild a verbatim object node.\n\nThe following properties are settable:\n- **`value`**: (required) a string\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-verbatim \"text\") (org-ml-to-string))\n ;; => \"=text=\"\n\n```\n\n\n### Branch Object Nodes\n\n#### org-ml-build-bold `(&key post-blank &rest object-nodes)`\n\nBuild a bold object node with **`object-nodes`** as children.\n\nThe following properties are settable:\n\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-bold \"text\") (org-ml-to-string))\n ;; => \"*text*\"\n\n```\n\n#### org-ml-build-footnote-reference `(&key label post-blank &rest object-nodes)`\n\nBuild a footnote-reference object node with **`object-nodes`** as children.\n\nThe following properties are settable:\n- **`label`**: a oneline string or nil\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-footnote-reference) (org-ml-to-string))\n ;; => \"[fn:]\"\n\n(->> (org-ml-build-footnote-reference :label \"label\") (org-ml-to-string))\n ;; => \"[fn:label]\"\n\n(->> (org-ml-build-footnote-reference :label \"label\" \"content\")\n  (org-ml-to-string))\n ;; => \"[fn:label:content]\"\n\n```\n\n#### org-ml-build-italic `(&key post-blank &rest object-nodes)`\n\nBuild an italic object node with **`object-nodes`** as children.\n\nThe following properties are settable:\n\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-italic \"text\") (org-ml-to-string))\n ;; => \"/text/\"\n\n```\n\n#### org-ml-build-link `(path &key format (type \"fuzzy\") post-blank &rest object-nodes)`\n\nBuild a link object node with **`object-nodes`** as children.\n\nThe following properties are settable:\n- **`path`**: (required) a oneline string\n- **`format`**: the symbol `plain`, `bracket` or `angle`\n- **`type`**: (default `\"fuzzy\"`) a oneline string from `org-link-types` or\n    `\"coderef\"`, `\"custorg-ml-id\"`, `\"file\"`, `\"id\"`, `\"radio\"`, or `\"fuzzy\"`\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-link \"target\") (org-ml-to-string))\n ;; => \"[[target]]\"\n\n(->> (org-ml-build-link \"target\" :type \"file\") (org-ml-to-string))\n ;; => \"[[file:target]]\"\n\n(->> (org-ml-build-link \"target\" \"desc\") (org-ml-to-string))\n ;; => \"[[target][desc]]\"\n\n```\n\n#### org-ml-build-radio-target `(&key post-blank &rest object-nodes)`\n\nBuild a radio-target object node with **`object-nodes`** as children.\n\nThe following properties are settable:\n\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-radio-target \"text\") (org-ml-to-string))\n ;; => \"<<<text>>>\"\n\n```\n\n#### org-ml-build-strike-through `(&key post-blank &rest object-nodes)`\n\nBuild a strike-through object node with **`object-nodes`** as children.\n\nThe following properties are settable:\n\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-strike-through \"text\") (org-ml-to-string))\n ;; => \"+text+\"\n\n```\n\n#### org-ml-build-superscript `(&key use-brackets-p post-blank &rest object-nodes)`\n\nBuild a superscript object node with **`object-nodes`** as children.\n\nThe following properties are settable:\n- **`use-brackets-p`**: nil or t\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-superscript \"text\") (org-ml-to-string))\n ;; => \"^text\"\n\n```\n\n#### org-ml-build-subscript `(&key use-brackets-p post-blank &rest object-nodes)`\n\nBuild a subscript object node with **`object-nodes`** as children.\n\nThe following properties are settable:\n- **`use-brackets-p`**: nil or t\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-subscript \"text\") (org-ml-to-string))\n ;; => \"_text\"\n\n```\n\n#### org-ml-build-table-cell `(&key post-blank &rest object-nodes)`\n\nBuild a table-cell object node with **`object-nodes`** as children.\n\nThe following properties are settable:\n\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-table-cell \"text\") (org-ml-to-string))\n ;; => \" text |\"\n\n```\n\n#### org-ml-build-underline `(&key post-blank &rest object-nodes)`\n\nBuild an underline object node with **`object-nodes`** as children.\n\nThe following properties are settable:\n\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-underline \"text\") (org-ml-to-string))\n ;; => \"_text_\"\n\n```\n\n\n### Leaf Element Nodes\n\n#### org-ml-build-babel-call `(call &key inside-header arguments end-header name plot header results caption post-blank)`\n\nBuild a babel-call element node.\n\nThe following properties are settable:\n- **`call`**: (required) a oneline string\n- **`inside-header`**: a plist\n- **`arguments`**: a list of oneline strings\n- **`end-header`**: a plist\n- **`name`**: a string or nil\n- **`plot`**: a string or nil\n- **`header`**: a list of plists where all plist values are strings\n- **`results`**: a list like `(source)` or `(hash source)` where `hash` and `source`\n    are strings.\n- **`caption`**: a list including `(long)` or `(short long)` where `short` and\n    `long` are both strings representing the short and long captions\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-babel-call \"name\") (org-ml-to-trimmed-string))\n ;; => \"#+call: name()\"\n\n(->> (org-ml-build-babel-call \"name\" :arguments '(\"arg=x\"))\n  (org-ml-to-trimmed-string))\n ;; => \"#+call: name(arg=x)\"\n\n(->> (org-ml-build-babel-call \"name\" :inside-header '(:key val))\n  (org-ml-to-trimmed-string))\n ;; => \"#+call: name[:key val]()\"\n\n(->> (org-ml-build-babel-call \"name\" :end-header '(:key val))\n  (org-ml-to-trimmed-string))\n ;; => \"#+call: name() :key val\"\n\n```\n\n#### org-ml-build-clock `(value &key post-blank)`\n\nBuild a clock element node.\n\nThe following properties are settable:\n- **`value`**: (required) a ranged or unranged inactive timestamp node with\n    no warning or repeater\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-clock (org-ml-build-timestamp! '(2019 1 1 0 0)))\n  (org-ml-to-trimmed-string))\n ;; => \"CLOCK: [2019-01-01 Tue 00:00]\"\n\n(->> (org-ml-build-timestamp! '(2019 1 1 0 0) :end '(2019 1 1 1 0))\n  (org-ml-set-property :type 'inactive-range)\n  (org-ml-build-clock)\n  (org-ml-to-trimmed-string))\n ;; => \"CLOCK: [2019-01-01 Tue 00:00]--[2019-01-01 Tue 01:00] =>  1:00\"\n\n```\n\n#### org-ml-build-comment `(value &key post-blank)`\n\nBuild a comment element node.\n\nThe following properties are settable:\n- **`value`**: (required) a string\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-comment \"text\") (org-ml-to-trimmed-string))\n ;; => \"# text\"\n\n(->> (org-ml-build-comment \"text\nless\") (org-ml-to-trimmed-string))\n ;; => \"# text\n ;      # less\"\n\n```\n\n#### org-ml-build-comment-block `(&key (value \"\") name plot header results caption post-blank)`\n\nBuild a comment-block element node.\n\nThe following properties are settable:\n- **`value`**: (default `\"\"`) a string\n- **`name`**: a string or nil\n- **`plot`**: a string or nil\n- **`header`**: a list of plists where all plist values are strings\n- **`results`**: a list like `(source)` or `(hash source)` where `hash` and `source`\n    are strings.\n- **`caption`**: a list including `(long)` or `(short long)` where `short` and\n    `long` are both strings representing the short and long captions\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-comment-block) (org-ml-to-trimmed-string))\n ;; => \"#+begin_comment\n ;      #+end_comment\"\n\n(->> (org-ml-build-comment-block :value \"text\") (org-ml-to-trimmed-string))\n ;; => \"#+begin_comment\n ;      text\n ;      #+end_comment\"\n\n```\n\n#### org-ml-build-diary-sexp `(&key value name plot header results caption post-blank)`\n\nBuild a diary-sexp element node.\n\nThe following properties are settable:\n- **`value`**: a list form or nil\n- **`name`**: a string or nil\n- **`plot`**: a string or nil\n- **`header`**: a list of plists where all plist values are strings\n- **`results`**: a list like `(source)` or `(hash source)` where `hash` and `source`\n    are strings.\n- **`caption`**: a list including `(long)` or `(short long)` where `short` and\n    `long` are both strings representing the short and long captions\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-diary-sexp) (org-ml-to-trimmed-string))\n ;; => \"%%()\"\n\n(->> (org-ml-build-diary-sexp :value '(text)) (org-ml-to-trimmed-string))\n ;; => \"%%(text)\"\n\n```\n\n#### org-ml-build-example-block `(&key preserve-indent switches (value \"\") name plot header results caption post-blank)`\n\nBuild an example-block element node.\n\nThe following properties are settable:\n- **`preserve-indent`**: nil or t\n- **`switches`**: a list of oneline strings\n- **`value`**: (default `\"\"`) a string\n- **`name`**: a string or nil\n- **`plot`**: a string or nil\n- **`header`**: a list of plists where all plist values are strings\n- **`results`**: a list like `(source)` or `(hash source)` where `hash` and `source`\n    are strings.\n- **`caption`**: a list including `(long)` or `(short long)` where `short` and\n    `long` are both strings representing the short and long captions\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-example-block) (org-ml-to-trimmed-string))\n ;; => \"#+begin_example\n ;      #+end_example\"\n\n(->> (org-ml-build-example-block :value \"text\") (org-ml-to-trimmed-string))\n ;; => \"#+begin_example\n ;        text\n ;      #+end_example\"\n\n(->> (org-ml-build-example-block :value \"text\" :switches '(\"switches\"))\n  (org-ml-to-trimmed-string))\n ;; => \"#+begin_example switches\n ;        text\n ;      #+end_example\"\n\n```\n\n#### org-ml-build-export-block `(type value &key name plot header results caption post-blank)`\n\nBuild an export-block element node.\n\nThe following properties are settable:\n- **`type`**: (required) a oneline string\n- **`value`**: (required) a string\n- **`name`**: a string or nil\n- **`plot`**: a string or nil\n- **`header`**: a list of plists where all plist values are strings\n- **`results`**: a list like `(source)` or `(hash source)` where `hash` and `source`\n    are strings.\n- **`caption`**: a list including `(long)` or `(short long)` where `short` and\n    `long` are both strings representing the short and long captions\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-export-block \"type\" \"value\n\") (org-ml-to-trimmed-string))\n ;; => \"#+begin_export type\n ;      value\n ;      #+end_export\"\n\n```\n\n#### org-ml-build-fixed-width `(value &key name plot header results caption post-blank)`\n\nBuild a fixed-width element node.\n\nThe following properties are settable:\n- **`value`**: (required) a oneline string\n- **`name`**: a string or nil\n- **`plot`**: a string or nil\n- **`header`**: a list of plists where all plist values are strings\n- **`results`**: a list like `(source)` or `(hash source)` where `hash` and `source`\n    are strings.\n- **`caption`**: a list including `(long)` or `(short long)` where `short` and\n    `long` are both strings representing the short and long captions\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-fixed-width \"text\") (org-ml-to-trimmed-string))\n ;; => \": text\"\n\n```\n\n#### org-ml-build-horizontal-rule `(&key name plot header results caption post-blank)`\n\nBuild a horizontal-rule element node.\n\nThe following properties are settable:\n- **`name`**: a string or nil\n- **`plot`**: a string or nil\n- **`header`**: a list of plists where all plist values are strings\n- **`results`**: a list like `(source)` or `(hash source)` where `hash` and `source`\n    are strings.\n- **`caption`**: a list including `(long)` or `(short long)` where `short` and\n    `long` are both strings representing the short and long captions\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-horizontal-rule) (org-ml-to-trimmed-string))\n ;; => \"-----\"\n\n```\n\n#### org-ml-build-keyword `(key value &key name plot header results caption post-blank)`\n\nBuild a keyword element node.\n\nThe following properties are settable:\n- **`key`**: (required) a oneline string\n- **`value`**: (required) a oneline string\n- **`name`**: a string or nil\n- **`plot`**: a string or nil\n- **`header`**: a list of plists where all plist values are strings\n- **`results`**: a list like `(source)` or `(hash source)` where `hash` and `source`\n    are strings.\n- **`caption`**: a list including `(long)` or `(short long)` where `short` and\n    `long` are both strings representing the short and long captions\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-keyword \"FILETAGS\" \"tmsu\") (org-ml-to-trimmed-string))\n ;; => \"#+filetags: tmsu\"\n\n```\n\n#### org-ml-build-latex-environment `(value &key name plot header results caption post-blank)`\n\nBuild a latex-environment element node.\n\nThe following properties are settable:\n- **`value`**: (required) a list of strings like `(env body)` or `(env)`\n- **`name`**: a string or nil\n- **`plot`**: a string or nil\n- **`header`**: a list of plists where all plist values are strings\n- **`results`**: a list like `(source)` or `(hash source)` where `hash` and `source`\n    are strings.\n- **`caption`**: a list including `(long)` or `(short long)` where `short` and\n    `long` are both strings representing the short and long captions\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-latex-environment '(\"env\" \"text\"))\n  (org-ml-to-trimmed-string))\n ;; => \"\\\\begin{env}\n ;      text\n ;      \\\\end{env}\"\n\n```\n\n#### org-ml-build-node-property `(key value &key post-blank)`\n\nBuild a node-property element node.\n\nThe following properties are settable:\n- **`key`**: (required) a oneline string\n- **`value`**: (required) a oneline string\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-node-property \"key\" \"val\") (org-ml-to-trimmed-string))\n ;; => \":key:      val\"\n\n```\n\n#### org-ml-build-planning `(&key closed deadline scheduled post-blank)`\n\nBuild a planning element node.\n\nThe following properties are settable:\n- **`closed`**: a zero-range, inactive timestamp node\n- **`deadline`**: a zero-range, active timestamp node\n- **`scheduled`**: a zero-range, active timestamp node\n- **`post-blank`**: a non-negative integer\n\n```el\n(->>\n  (org-ml-build-planning :closed\n    (org-ml-build-timestamp! '(2019 1 1) :active nil))\n  (org-ml-to-trimmed-string))\n ;; => \"CLOSED: [2019-01-01 Tue]\"\n\n(->>\n  (org-ml-build-planning :scheduled\n    (org-ml-build-timestamp! '(2019 1 1) :active t))\n  (org-ml-to-trimmed-string))\n ;; => \"SCHEDULED: <2019-01-01 Tue>\"\n\n(->>\n  (org-ml-build-planning :deadline\n    (org-ml-build-timestamp! '(2019 1 1) :active t))\n  (org-ml-to-trimmed-string))\n ;; => \"DEADLINE: <2019-01-01 Tue>\"\n\n```\n\n#### org-ml-build-src-block `(&key (value \"\") language parameters preserve-indent switches name plot header results caption post-blank)`\n\nBuild a src-block element node.\n\nThe following properties are settable:\n- **`value`**: (default `\"\"`) a string\n- **`language`**: a string or nil\n- **`parameters`**: a plist\n- **`preserve-indent`**: nil or t\n- **`switches`**: a list of oneline strings\n- **`name`**: a string or nil\n- **`plot`**: a string or nil\n- **`header`**: a list of plists where all plist values are strings\n- **`results`**: a list like `(source)` or `(hash source)` where `hash` and `source`\n    are strings.\n- **`caption`**: a list including `(long)` or `(short long)` where `short` and\n    `long` are both strings representing the short and long captions\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-src-block) (org-ml-to-trimmed-string))\n ;; => \"#+begin_src\n ;      #+end_src\"\n\n(->> (org-ml-build-src-block :value \"body\") (org-ml-to-trimmed-string))\n ;; => \"#+begin_src\n ;        body\n ;      #+end_src\"\n\n(->> (org-ml-build-src-block :value \"body\" :language \"emacs-lisp\")\n  (org-ml-to-trimmed-string))\n ;; => \"#+begin_src emacs-lisp\n ;        body\n ;      #+end_src\"\n\n(->> (org-ml-build-src-block :value \"body\" :switches '(\"-n 20\" \"-r\"))\n  (org-ml-to-trimmed-string))\n ;; => \"#+begin_src -n 20 -r\n ;        body\n ;      #+end_src\"\n\n(->> (org-ml-build-src-block :value \"body\" :parameters '(:key val))\n  (org-ml-to-trimmed-string))\n ;; => \"#+begin_src :key val\n ;        body\n ;      #+end_src\"\n\n```\n\n\n### Branch Element Nodes with Child Object Nodes\n\n#### org-ml-build-paragraph `(&key name plot header results caption post-blank &rest object-nodes)`\n\nBuild a paragraph element node with **`object-nodes`** as children.\n\nThe following properties are settable:\n- **`name`**: a string or nil\n- **`plot`**: a string or nil\n- **`header`**: a list of plists where all plist values are strings\n- **`results`**: a list like `(source)` or `(hash source)` where `hash` and `source`\n    are strings.\n- **`caption`**: a list including `(long)` or `(short long)` where `short` and\n    `long` are both strings representing the short and long captions\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-paragraph \"text\") (org-ml-to-trimmed-string))\n ;; => \"text\"\n\n```\n\n#### org-ml-build-table-row `(&key post-blank &rest object-nodes)`\n\nBuild a table-row element node with **`object-nodes`** as children.\n\nThe following properties are settable:\n\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-table-cell \"a\")\n  (org-ml-build-table-row)\n  (org-ml-to-trimmed-string))\n ;; => \"| a |\"\n\n```\n\n#### org-ml-build-verse-block `(&key name plot header results caption post-blank &rest object-nodes)`\n\nBuild a verse-block element node with **`object-nodes`** as children.\n\nThe following properties are settable:\n- **`name`**: a string or nil\n- **`plot`**: a string or nil\n- **`header`**: a list of plists where all plist values are strings\n- **`results`**: a list like `(source)` or `(hash source)` where `hash` and `source`\n    are strings.\n- **`caption`**: a list including `(long)` or `(short long)` where `short` and\n    `long` are both strings representing the short and long captions\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-verse-block \"text\n\") (org-ml-to-trimmed-string))\n ;; => \"#+begin_verse\n ;      text\n ;      #+end_verse\"\n\n```\n\n\n### Branch Element Nodes with Child Element Nodes\n\n#### org-ml-build-org-data `(&rest nodes)`\n\nReturn a new org-data node using **`nodes`**.\n**`nodes`** should be either headline or section nodes.\n\n```el\n(->> (org-ml-build-headline :title '(\"dummy\"))\n  (org-ml-build-org-data)\n  (org-ml-to-trimmed-string))\n ;; => \"* dummy\"\n\n```\n\n#### org-ml-build-center-block `(&key name plot header results caption post-blank &rest element-nodes)`\n\nBuild a center-block element node with **`element-nodes`** as children.\n\nThe following properties are settable:\n- **`name`**: a string or nil\n- **`plot`**: a string or nil\n- **`header`**: a list of plists where all plist values are strings\n- **`results`**: a list like `(source)` or `(hash source)` where `hash` and `source`\n    are strings.\n- **`caption`**: a list including `(long)` or `(short long)` where `short` and\n    `long` are both strings representing the short and long captions\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-center-block) (org-ml-to-trimmed-string))\n ;; => \"#+begin_center\n ;      #+end_center\"\n\n(->> (org-ml-build-paragraph \"text\")\n  (org-ml-build-center-block)\n  (org-ml-to-trimmed-string))\n ;; => \"#+begin_center\n ;      text\n ;      #+end_center\"\n\n```\n\n#### org-ml-build-drawer `(drawer-name &key name plot header results caption post-blank &rest element-nodes)`\n\nBuild a drawer element node with **`element-nodes`** as children.\n\nThe following properties are settable:\n- **`drawer-name`**: (required) a oneline string\n- **`name`**: a string or nil\n- **`plot`**: a string or nil\n- **`header`**: a list of plists where all plist values are strings\n- **`results`**: a list like `(source)` or `(hash source)` where `hash` and `source`\n    are strings.\n- **`caption`**: a list including `(long)` or `(short long)` where `short` and\n    `long` are both strings representing the short and long captions\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-drawer \"NAME\") (org-ml-to-trimmed-string))\n ;; => \":NAME:\n ;      :END:\"\n\n(->> (org-ml-build-paragraph \"text\")\n  (org-ml-build-drawer \"NAME\")\n  (org-ml-to-trimmed-string))\n ;; => \":NAME:\n ;      text\n ;      :END:\"\n\n```\n\n#### org-ml-build-dynamic-block `(block-name &key arguments name plot header results caption post-blank &rest element-nodes)`\n\nBuild a dynamic-block element node with **`element-nodes`** as children.\n\nThe following properties are settable:\n- **`block-name`**: (required) a oneline string\n- **`arguments`**: a plist\n- **`name`**: a string or nil\n- **`plot`**: a string or nil\n- **`header`**: a list of plists where all plist values are strings\n- **`results`**: a list like `(source)` or `(hash source)` where `hash` and `source`\n    are strings.\n- **`caption`**: a list including `(long)` or `(short long)` where `short` and\n    `long` are both strings representing the short and long captions\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-dynamic-block \"empty\") (org-ml-to-trimmed-string))\n ;; => \"#+begin: empty\n ;      #+end:\"\n\n(->> (org-ml-build-comment \"I'm in here\")\n  (org-ml-build-dynamic-block \"notempty\")\n  (org-ml-to-trimmed-string))\n ;; => \"#+begin: notempty\n ;      # I'm in here\n ;      #+end:\"\n\n```\n\n#### org-ml-build-footnote-definition `(label &key (pre-blank 0) name plot header results caption post-blank &rest element-nodes)`\n\nBuild a footnote-definition element node with **`element-nodes`** as children.\n\nThe following properties are settable:\n- **`label`**: (required) a oneline string\n- **`pre-blank`**: a non-negative integer\n- **`name`**: a string or nil\n- **`plot`**: a string or nil\n- **`header`**: a list of plists where all plist values are strings\n- **`results`**: a list like `(source)` or `(hash source)` where `hash` and `source`\n    are strings.\n- **`caption`**: a list including `(long)` or `(short long)` where `short` and\n    `long` are both strings representing the short and long captions\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-paragraph \"footnote contents\")\n  (org-ml-build-footnote-definition \"label\")\n  (org-ml-to-trimmed-string))\n ;; => \"[fn:label] footnote contents\"\n\n```\n\n#### org-ml-build-headline `(&key archivedp commentedp footnote-section-p (level 1) (pre-blank 0) priority tags title todo-keyword post-blank &rest element-nodes)`\n\nBuild a headline element node with **`element-nodes`** as children.\n\nThe following properties are settable:\n- **`archivedp`**: nil or t\n- **`commentedp`**: nil or t\n- **`footnote-section-p`**: nil or t\n- **`level`**: a positive integer\n- **`pre-blank`**: a non-negative integer\n- **`priority`**: an integer between (inclusive) `org-highest-priority` and\n    `org-lowest-priority`\n- **`tags`**: a string list\n- **`title`**: a secondary string\n- **`todo-keyword`**: a oneline string or nil\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-headline) (org-ml-to-trimmed-string))\n ;; => \"*\"\n\n(->> (org-ml-build-headline :level 2 :title '(\"dummy\") :tags '(\"tmsu\"))\n  (org-ml-to-trimmed-string))\n ;; => \"** dummy            :tmsu:\"\n\n(->>\n  (org-ml-build-headline :todo-keyword\n    \"TODO\"\n    :archivedp\n    t\n    :commentedp\n    t\n    :priority\n    65)\n  (org-ml-to-trimmed-string))\n ;; => \"* TODO COMMENT [#A]  :ARCHIVE:\"\n\n```\n\n#### org-ml-build-item `(&key (bullet '-) (pre-blank 0) checkbox counter tag post-blank &rest element-nodes)`\n\nBuild an item element node with **`element-nodes`** as children.\n\nThe following properties are settable:\n- **`bullet`**: (default `-`) a positive integer (ordered) or the symbol `-`\n    (unordered)\n- **`pre-blank`**: a non-negative integer\n- **`checkbox`**: nil or the symbols `on`, `off`, or `trans`\n- **`counter`**: a positive integer or nil\n- **`tag`**: a secondary string\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-paragraph \"item contents\")\n  (org-ml-build-item)\n  (org-ml-to-trimmed-string))\n ;; => \"- item contents\"\n\n(->> (org-ml-build-paragraph \"item contents\")\n  (org-ml-build-item :bullet 1)\n  (org-ml-to-trimmed-string))\n ;; => \"1. item contents\"\n\n(->> (org-ml-build-paragraph \"item contents\")\n  (org-ml-build-item :checkbox 'on)\n  (org-ml-to-trimmed-string))\n ;; => \"- [X] item contents\"\n\n(->> (org-ml-build-paragraph \"item contents\")\n  (org-ml-build-item :tag '(\"tmsu\"))\n  (org-ml-to-trimmed-string))\n ;; => \"- tmsu :: item contents\"\n\n(->> (org-ml-build-paragraph \"item contents\")\n  (org-ml-build-item :counter 10)\n  (org-ml-to-trimmed-string))\n ;; => \"- [@10] item contents\"\n\n```\n\n#### org-ml-build-plain-list `(&key name plot header results caption post-blank &rest element-nodes)`\n\nBuild a plain-list element node with **`element-nodes`** as children.\n\nThe following properties are settable:\n- **`name`**: a string or nil\n- **`plot`**: a string or nil\n- **`header`**: a list of plists where all plist values are strings\n- **`results`**: a list like `(source)` or `(hash source)` where `hash` and `source`\n    are strings.\n- **`caption`**: a list including `(long)` or `(short long)` where `short` and\n    `long` are both strings representing the short and long captions\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-paragraph \"item contents\")\n  (org-ml-build-item)\n  (org-ml-build-plain-list)\n  (org-ml-to-trimmed-string))\n ;; => \"- item contents\"\n\n```\n\n#### org-ml-build-property-drawer `(&key post-blank &rest element-nodes)`\n\nBuild a property-drawer element node with **`element-nodes`** as children.\n\nThe following properties are settable:\n\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-property-drawer) (org-ml-to-trimmed-string))\n ;; => \":PROPERTIES:\n ;      :END:\"\n\n(->> (org-ml-build-node-property \"key\" \"val\")\n  (org-ml-build-property-drawer)\n  (org-ml-to-trimmed-string))\n ;; => \":PROPERTIES:\n ;      :key:      val\n ;      :END:\"\n\n```\n\n#### org-ml-build-quote-block `(&key name plot header results caption post-blank &rest element-nodes)`\n\nBuild a quote-block element node with **`element-nodes`** as children.\n\nThe following properties are settable:\n- **`name`**: a string or nil\n- **`plot`**: a string or nil\n- **`header`**: a list of plists where all plist values are strings\n- **`results`**: a list like `(source)` or `(hash source)` where `hash` and `source`\n    are strings.\n- **`caption`**: a list including `(long)` or `(short long)` where `short` and\n    `long` are both strings representing the short and long captions\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-quote-block) (org-ml-to-trimmed-string))\n ;; => \"#+begin_quote\n ;      #+end_quote\"\n\n(->> (org-ml-build-paragraph \"quoted stuff\")\n  (org-ml-build-quote-block)\n  (org-ml-to-trimmed-string))\n ;; => \"#+begin_quote\n ;      quoted stuff\n ;      #+end_quote\"\n\n```\n\n#### org-ml-build-section `(&key post-blank &rest element-nodes)`\n\nBuild a section element node with **`element-nodes`** as children.\n\nThe following properties are settable:\n\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-paragraph \"text\")\n  (org-ml-build-section)\n  (org-ml-to-trimmed-string))\n ;; => \"text\"\n\n```\n\n#### org-ml-build-special-block `(type &key parameters name plot header results caption post-blank &rest element-nodes)`\n\nBuild a special-block element node with **`element-nodes`** as children.\n\nThe following properties are settable:\n- **`type`**: (required) a oneline string\n- **`parameters`**: a oneline string or nil\n- **`name`**: a string or nil\n- **`plot`**: a string or nil\n- **`header`**: a list of plists where all plist values are strings\n- **`results`**: a list like `(source)` or `(hash source)` where `hash` and `source`\n    are strings.\n- **`caption`**: a list including `(long)` or `(short long)` where `short` and\n    `long` are both strings representing the short and long captions\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-special-block \"monad\") (org-ml-to-trimmed-string))\n ;; => \"#+begin_monad\n ;      #+end_monad\"\n\n(->> (org-ml-build-comment \"Launch missiles\")\n  (org-ml-build-special-block \"monad\")\n  (org-ml-to-trimmed-string))\n ;; => \"#+begin_monad\n ;      # Launch missiles\n ;      #+end_monad\"\n\n```\n\n#### org-ml-build-table `(&key tblfm name plot header results caption post-blank &rest element-nodes)`\n\nBuild a table element node with **`element-nodes`** as children.\n\nThe following properties are settable:\n- **`tblfm`**: a list of oneline strings\n- **`name`**: a string or nil\n- **`plot`**: a string or nil\n- **`header`**: a list of plists where all plist values are strings\n- **`results`**: a list like `(source)` or `(hash source)` where `hash` and `source`\n    are strings.\n- **`caption`**: a list including `(long)` or `(short long)` where `short` and\n    `long` are both strings representing the short and long captions\n- **`post-blank`**: a non-negative integer\n\n```el\n(->> (org-ml-build-table-cell \"cell\")\n  (org-ml-build-table-row)\n  (org-ml-build-table)\n  (org-ml-to-trimmed-string))\n ;; => \"| cell |\"\n\n```\n\n\n### Miscellaneous Builders\n\n#### org-ml-build-secondary-string! `(string)`\n\nReturn a secondary string (list of object nodes) from **`string`**.\n**`string`** is any string that contains a textual representation of\nobject nodes. If the string does not represent a list of object nodes,\nthrow an error.\n\n```el\n(->> (org-ml-build-secondary-string! \"I'm plain\") (-map #'org-ml-get-type))\n ;; => '(plain-text)\n\n(->> (org-ml-build-secondary-string! \"I'm *not* plain\")\n  (-map #'org-ml-get-type))\n ;; => '(plain-text bold plain-text)\n\n(->> (org-ml-build-secondary-string! \"1. I'm *not* a plain list\")\n  (-map #'org-ml-get-type))\n ;; => '(plain-text bold plain-text)\n\n(->> (org-ml-build-secondary-string! \"* I'm not an object\")\n  (-map #'org-ml-get-type))\n ;; => '(plain-text)\n\n```\n\n#### org-ml-build-table-row-hline `(&key post-blank)`\n\nReturn a new rule-typed table-row node.\nOptionally set **`post-blank`** (a positive integer).\n\n```el\n(->>\n  (org-ml-build-table (org-ml-build-table-row (org-ml-build-table-cell \"text\"))\n    (org-ml-build-table-row-hline))\n  (org-ml-to-trimmed-string))\n ;; => \"| text |\n ;      |------|\"\n\n```\n\n#### org-ml-build-timestamp-diary `(form &key start end post-blank)`\n\nReturn a new diary-sexp timestamp node from **`form`**.\n\n`time1` and `time1` are lists like (hour min) which specify the\ntime(s) of the diary timestamp. If `time2` is provided, `time1` must\nalso be provided and the timestamp will be ranged. Optionally set\n**`post-blank`** (a positive integer).\n\n```el\n(->> (org-ml-build-timestamp-diary '(diary-float t 4 2)) (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2)>\"\n\n(->> (org-ml-build-timestamp-diary '(diary-float t 4 2) :start '(0 0))\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2) 00:00>\"\n\n(->>\n  (org-ml-build-timestamp-diary '(diary-float t 4 2)\n    :start\n    '(0 0)\n    :end\n    '(1 0))\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2) 00:00-01:00>\"\n\n```\n\n\n### Shorthand Builders\n\n\nBuild nodes with more convenient/shorter syntax.\n\n#### org-ml-build-timestamp! `(start &key end active repeater deadline warning collapsed post-blank)`\n\nReturn a new timestamp node.\n\n**`start`** specifies the start time and is a list of integers in one of\nthe following forms:\n- `(year month day)`: short form\n- `(year month day nil nil)`: short form\n- `(year month day hour minute)`: long form\n\n**`end`** (if supplied) will add the ending time, and follows the same\nformatting rules as **`start`**.\n\n**`active`** is a boolean where t signifies the type is `active`, else\n`inactive` (the range suffix will be added if an end time is\nsupplied).\n\n**`repeater`**, **`deadline`**, and **`warning`** are lists corresponding to those\nrequired for [`org-ml-timestamp-set-repeater`](#org-ml-timestamp-set-repeater-repeater-timestamp),\n[`org-ml-timestamp-set-deadline`](#org-ml-timestamp-set-deadline-deadline-timestamp), and\n[`org-ml-timestamp-set-warning`](#org-ml-timestamp-set-warning-warning-timestamp) respectively.\n\nBuilding a diary sexp timestamp is not possible with this function.\n\n```el\n(->> (org-ml-build-timestamp! '(2019 1 1)) (org-ml-to-string))\n ;; => \"[2019-01-01 Tue]\"\n\n(->>\n  (org-ml-build-timestamp! '(2019 1 1 12 0)\n    :active\n    t\n    :warning\n    '(all 1 day)\n    :repeater\n    '(cumulate 1 month))\n  (org-ml-to-string))\n ;; => \"<2019-01-01 Tue 12:00 +1m -1d>\"\n\n(->> (org-ml-build-timestamp! '(2019 1 1) :end '(2019 1 2))\n  (org-ml-to-string))\n ;; => \"[2019-01-01 Tue]--[2019-01-02 Wed]\"\n\n```\n\n#### org-ml-build-clock! `(start &key end post-blank)`\n\nReturn a new clock node.\n\n**`start`** and **`end`** follow the same rules as their respective arguments in\n[`org-ml-build-timestamp!`](#org-ml-build-timestamp-start-key-end-active-repeater-deadline-warning-collapsed-post-blank).\n\n```el\n(->> (org-ml-build-clock! '(2019 1 1)) (org-ml-to-trimmed-string))\n ;; => \"CLOCK: [2019-01-01 Tue]\"\n\n(->> (org-ml-build-clock! '(2019 1 1 12 0)) (org-ml-to-trimmed-string))\n ;; => \"CLOCK: [2019-01-01 Tue 12:00]\"\n\n(->> (org-ml-build-clock! '(2019 1 1 12 0) :end '(2019 1 1 13 0))\n  (org-ml-to-trimmed-string))\n ;; => \"CLOCK: [2019-01-01 Tue 12:00]--[2019-01-01 Tue 13:00] =>  1:00\"\n\n```\n\n#### org-ml-build-planning! `(&key closed deadline scheduled post-blank)`\n\nReturn a new planning node.\n\n**`deadline`** and **`scheduled`** are lists with the following structure\n(brackets denote optional members):\n\n`(year minute day [hour] [min]\n [&warning type value unit]\n [&repeater type value unit])`\n\nIn terms of arguments supplied to [`org-ml-build-timestamp!`](#org-ml-build-timestamp-start-key-end-active-repeater-deadline-warning-collapsed-post-blank), the first\nfive members correspond to the list supplied as `time`, and the `type`,\n`value`, and `unit` fields correspond to the lists supplied to `warning` and\n`repeater` arguments. The order of warning and repeater does not\nmatter.\n\n**`closed`** is a similar list to above but does not have &warning or\n&repeater.\n\n```el\n(->> (org-ml-build-planning! :closed '(2019 1 1)) (org-ml-to-trimmed-string))\n ;; => \"CLOSED: [2019-01-01 Tue]\"\n\n(->> (org-ml-build-planning! :closed '(2019 1 1) :scheduled '(2018 1 1))\n  (org-ml-to-trimmed-string))\n ;; => \"SCHEDULED: <2018-01-01 Mon> CLOSED: [2019-01-01 Tue]\"\n\n(->>\n  (org-ml-build-planning! :scheduled\n    '(2019 1 1 &warning all 1 day &repeater cumulate 1 month))\n  (org-ml-to-trimmed-string))\n ;; => \"SCHEDULED: <2019-01-01 Tue +1m -1d>\"\n\n```\n\n#### org-ml-build-property-drawer! `(&key post-blank &rest keyvals)`\n\nReturn a new property-drawer node.\n\nEach member in **`keyvals`** is a list like `(key val)` where `key` and `val`\nare both strings, where each list will generate a node-property\nnode in the property-drawer node like `\":key: val\"`.\n\n```el\n(->> (org-ml-build-property-drawer! '(\"key\" \"val\"))\n  (org-ml-to-trimmed-string))\n ;; => \":PROPERTIES:\n ;      :key:      val\n ;      :END:\"\n\n```\n\n#### org-ml-build-headline! `(&key (level 1) title-text todo-keyword tags pre-blank priority commentedp archivedp post-blank planning statistics-cookie section-children &rest subheadlines)`\n\nReturn a new headline node.\n\n**`title-text`** is a oneline string for the title of the headline.\n\n**`planning`** is a list like `(planning-type args ...)` where\n`planning-type` is one of `:closed`, `:deadline`, or `:scheduled`, and\n`args` are the args supplied to any of the planning types in\n[`org-ml-build-planning!`](#org-ml-build-planning-key-closed-deadline-scheduled-post-blank). Up to all three planning types can be used\nin the same list like `(:closed args :deadline args :scheduled args)`.\n\n**`statistics-cookie`** is a list following the same format as\n[`org-ml-build-statistics-cookie`](#org-ml-build-statistics-cookie-value-key-post-blank).\n\n**`section-children`** is a list of elements that will go in the headline\nsection.\n\n**`subheadlines`** contains zero or more headlines that will go under the\ncreated headline. The level of all members in **`subheadlines`** will\nautomatically be adjusted to **`level`** + 1.\n\nAll arguments not mentioned here follow the same rules as\n[`org-ml-build-headline`](#org-ml-build-headline-key-archivedp-commentedp-footnote-section-p-level-1-pre-blank-0-priority-tags-title-todo-keyword-post-blank-rest-element-nodes)\n\n```el\n(->> (org-ml-build-headline! :title-text \"really impressive title\")\n  (org-ml-to-trimmed-string))\n ;; => \"* really impressive title\"\n\n(->>\n  (org-ml-build-headline! :title-text\n    \"really impressive title\"\n    :statistics-cookie\n    '(0 9000))\n  (org-ml-to-trimmed-string))\n ;; => \"* really impressive title [0/9000]\"\n\n(->>\n  (org-ml-build-headline! :title-text\n    \"really impressive title\"\n    :section-children\n    (list (org-ml-build-property-drawer! '(\"key\" \"val\"))\n      (org-ml-build-paragraph! \"section text\"))\n    (org-ml-build-headline! :title-text \"subhead\"))\n  (org-ml-to-trimmed-string))\n ;; => \"* really impressive title\n ;      :PROPERTIES:\n ;      :key:      val\n ;      :END:\n ;      section text\n ;      ** subhead\"\n\n```\n\n#### org-ml-build-item! `(&key post-blank bullet checkbox tag paragraph counter &rest children)`\n\nReturn a new item node.\n\n**`tag`** is a string representing the tag (make with\n[`org-ml-build-secondary-string!`](#org-ml-build-secondary-string-string)) .\n\n**`paragraph`** is a string that will be the initial text in the item\n(made with [`org-ml-build-paragraph!`](#org-ml-build-paragraph-string-key-post-blank)).\n\n**`children`** contains the nodes that will go under this item after\n**`paragraph`**.\n\nAll other arguments follow the same rules as [`org-ml-build-item`](#org-ml-build-item-key-bullet---pre-blank-0-checkbox-counter-tag-post-blank-rest-element-nodes).\n\n```el\n(->>\n  (org-ml-build-item! :bullet\n    1\n    :tag\n    \"complicated *tag*\"\n    :paragraph\n    \"petulant /frenzy/\"\n    (org-ml-build-plain-list (org-ml-build-item! :bullet '- :paragraph \"below\")))\n  (org-ml-to-trimmed-string))\n ;; => \"1. complicated *tag* :: petulant /frenzy/\n ;           - below\"\n\n```\n\n#### org-ml-build-paragraph! `(string &key post-blank)`\n\nReturn a new paragraph node from **`string`**.\n\n**`string`** is the text to be parsed into a paragraph and must contain\nvalid textual representations of object nodes.\n\n```el\n(->> (org-ml-build-paragraph! \"stuff /with/ *formatting*\" :post-blank 2)\n  (org-ml-to-string))\n ;; => \"stuff /with/ *formatting*\n ;      \n ;      \n ;      \"\n\n(->> (org-ml-build-paragraph! \"* stuff /with/ *formatting*\")\n  (org-ml-to-string))\n ;; => \"* stuff /with/ *formatting*\n ;      \"\n\n```\n\n#### org-ml-build-table-cell! `(string)`\n\nReturn a new table-cell node.\n\n**`string`** is the text to be contained in the table-cell node. It must\ncontain valid textual representations of objects that are allowed in\ntable-cell nodes.\n\n```el\n(->> (org-ml-build-table-cell! \"rage\") (org-ml-to-trimmed-string))\n ;; => \"rage |\"\n\n(->> (org-ml-build-table-cell! \"*rage*\") (org-ml-to-trimmed-string))\n ;; => \"*rage* |\"\n\n```\n\n#### org-ml-build-table-row! `(row-list)`\n\nReturn a new table-row node.\n\n**`row-list`** is a list of strings to be built into table-cell nodes via\n[`org-ml-build-table-cell!`](#org-ml-build-table-cell-string) (see that function for restrictions).\nAlternatively, **`row-list`** may the symbol `hline` instead of a string to\ncreate a rule-typed table-row.\n\n```el\n(->> (org-ml-build-table-row! '(\"R\" \"A\" \"G\" \"E\")) (org-ml-to-trimmed-string))\n ;; => \"| R | A | G | E |\"\n\n(->> (org-ml-build-table-row! '(\"S\" \"\" \"X\")) (org-ml-to-trimmed-string))\n ;; => \"| S |  | X |\"\n\n(->> (org-ml-build-table-row! 'hline) (org-ml-to-trimmed-string))\n ;; => \"|-\"\n\n```\n\n#### org-ml-build-table! `(&key tblfm post-blank &rest row-lists)`\n\nReturn a new table node.\n\nEach member of **`row-lists`** will be converted to a table-row node\nvia [`org-ml-build-table-row!`](#org-ml-build-table-row-row-list) (see that function for\nrestrictions).\n\nAll other arguments follow the same rules as [`org-ml-build-table`](#org-ml-build-table-key-tblfm-name-plot-header-results-caption-post-blank-rest-element-nodes).\n\n```el\n(->> (org-ml-build-table! '(\"R\" \"A\") '(\"G\" \"E\")) (org-ml-to-trimmed-string))\n ;; => \"| R | A |\n ;      | G | E |\"\n\n(->> (org-ml-build-table! '(\"S\" \"\") '(\"\" \"X\")) (org-ml-to-trimmed-string))\n ;; => \"| S |   |\n ;      |   | X |\"\n\n(->> (org-ml-build-table! '(\"L\" \"O\") 'hline '(\"V\" \"E\"))\n  (org-ml-to-trimmed-string))\n ;; => \"| L | O |\n ;      |---+---|\n ;      | V | E |\"\n\n```\n\n\n### Logbook Item Builders\n\n\nBuild item nodes for inclusion in headline logbooks\n\n#### org-ml-build-log-note `(unixtime note)`\n\nReturn an item node for a new note log entry.\n\nThis will format the log entry from the default value for the\n`note` cell in `org-log-note-headings`.\n\n**`unixtime`** is an integer representing the time to be used for all\ntimestamp nodes.\n\n**`note`** is a string for the note text.\n\n```el\n(-> (- 1546300800 (car (current-time-zone)))\n  (org-ml-build-log-note \"noteworthy\")\n  (org-ml-to-trimmed-string))\n ;; => \"- Note taken on [2019-01-01 Tue 00:00] \\\\\\\\\n ;        noteworthy\"\n\n```\n\n#### org-ml-build-log-done `(unixtime &optional note)`\n\nReturn an item node for a done log entry.\n\nThis will format the log entry from the default value for the\n`done` cell in `org-log-note-headings`.\n\n**`unixtime`** is an integer representing the time to be used for all\ntimestamp nodes.\n\nIf string **`note`** is supplied, append a note to the log entry.\n\n```el\n(-> (- 1546300800 (car (current-time-zone)))\n  (org-ml-build-log-done)\n  (org-ml-to-trimmed-string))\n ;; => \"- CLOSING NOTE [2019-01-01 Tue 00:00]\"\n\n(-> (- 1546300800 (car (current-time-zone)))\n  (org-ml-build-log-done \"noteworthy\")\n  (org-ml-to-trimmed-string))\n ;; => \"- CLOSING NOTE [2019-01-01 Tue 00:00] \\\\\\\\\n ;        noteworthy\"\n\n```\n\n#### org-ml-build-log-refile `(unixtime &optional note)`\n\nReturn an item node for a refile log entry.\nThis will format the log entry from the default value for the\n`deldeadline` cell in `org-log-note-headings`.\n\n**`unixtime`** is an integer representing the time to be used for all\ntimestamp nodes.\n\nIf string **`note`** is supplied, append a note to the log entry.\n\n```el\n(-> (- 1546300800 (car (current-time-zone)))\n  (org-ml-build-log-refile)\n  (org-ml-to-trimmed-string))\n ;; => \"- Refiled on [2019-01-01 Tue 00:00]\"\n\n(-> (- 1546300800 (car (current-time-zone)))\n  (org-ml-build-log-refile \"noteworthy\")\n  (org-ml-to-trimmed-string))\n ;; => \"- Refiled on [2019-01-01 Tue 00:00] \\\\\\\\\n ;        noteworthy\"\n\n```\n\n#### org-ml-build-log-state `(unixtime new-state old-state &optional note)`\n\nReturn an item node for a state change log entry.\n\nThis will format the log entry from the default value for the\n`state` cell in `org-log-note-headings`.\n\n**`unixtime`** is an integer representing the time to be used for all\ntimestamp nodes.\n\n**`new-state`** and **`old-state`** are strings for the new and old todo keywords\nrespectively.\n\nIf string **`note`** is supplied, append a note to the log entry.\n\n```el\n(-> (- 1546300800 (car (current-time-zone)))\n  (org-ml-build-log-state \"HOLD\" \"TODO\")\n  (org-ml-to-trimmed-string))\n ;; => \"- State \\\"HOLD\\\"       from \\\"TODO\\\"       [2019-01-01 Tue 00:00]\"\n\n(-> (- 1546300800 (car (current-time-zone)))\n  (org-ml-build-log-state \"HOLD\" \"TODO\" \"noteworthy\")\n  (org-ml-to-trimmed-string))\n ;; => \"- State \\\"HOLD\\\"       from \\\"TODO\\\"       [2019-01-01 Tue 00:00] \\\\\\\\\n ;        noteworthy\"\n\n```\n\n#### org-ml-build-log-deldeadline `(unixtime old-timestamp &optional note)`\n\nReturn an item node for a delete deadline log entry.\n\nThis will format the log entry from the default value for the\n`deldeadline` cell in `org-log-note-headings`.\n\n**`unixtime`** is an integer representing the time to be used for all\ntimestamp nodes.\n\n**`old-timestamp`** is a timestamp node of the deadline that is being\ndeleted. It will always be converted to an inactive timestamp.\n\nIf string **`note`** is supplied, append a note to the log entry.\n\n```el\n(-> (- 1546300800 (car (current-time-zone)))\n  (org-ml-build-log-deldeadline (org-ml-build-timestamp! '(2019 1 2)))\n  (org-ml-to-trimmed-string))\n ;; => \"- Removed deadline, was \\\"[2019-01-02 Wed]\\\" on [2019-01-01 Tue 00:00]\"\n\n(-> (- 1546300800 (car (current-time-zone)))\n  (org-ml-build-log-deldeadline (org-ml-build-timestamp! '(2019 1 2))\n    \"noteworthy\")\n  (org-ml-to-trimmed-string))\n ;; => \"- Removed deadline, was \\\"[2019-01-02 Wed]\\\" on [2019-01-01 Tue 00:00] \\\\\\\\\n ;        noteworthy\"\n\n```\n\n#### org-ml-build-log-delschedule `(unixtime old-timestamp &optional note)`\n\nReturn an item node for a delete schedule log entry.\n\nThis will format the log entry from the default value for the\n`delschedule` cell in `org-log-note-headings`.\n\n**`unixtime`** is an integer representing the time to be used for all\ntimestamp nodes.\n\n**`old-timestamp`** is a timestamp node of the schedule that is being\ndeleted. It will always be converted to an inactive timestamp.\n\nIf string **`note`** is supplied, append a note to the log entry.\n\n```el\n(-> (- 1546300800 (car (current-time-zone)))\n  (org-ml-build-log-delschedule (org-ml-build-timestamp! '(2019 1 2)))\n  (org-ml-to-trimmed-string))\n ;; => \"- Not scheduled, was \\\"[2019-01-02 Wed]\\\" on [2019-01-01 Tue 00:00]\"\n\n(-> (- 1546300800 (car (current-time-zone)))\n  (org-ml-build-log-delschedule (org-ml-build-timestamp! '(2019 1 2))\n    \"noteworthy\")\n  (org-ml-to-trimmed-string))\n ;; => \"- Not scheduled, was \\\"[2019-01-02 Wed]\\\" on [2019-01-01 Tue 00:00] \\\\\\\\\n ;        noteworthy\"\n\n```\n\n#### org-ml-build-log-redeadline `(unixtime old-timestamp &optional note)`\n\nReturn an item node for a new deadline log entry.\n\nThis will format the log entry from the default value for the\n`redeadline` cell in `org-log-note-headings`.\n\n**`unixtime`** is an integer representing the time to be used for all\ntimestamp nodes.\n\n**`old-timestamp`** is a timestamp node of the deadline that is being\ndeleted. It will always be converted to an inactive timestamp.\n\nIf string **`note`** is supplied, append a note to the log entry.\n\n```el\n(-> (- 1546300800 (car (current-time-zone)))\n  (org-ml-build-log-redeadline (org-ml-build-timestamp! '(2019 1 2)))\n  (org-ml-to-trimmed-string))\n ;; => \"- New deadline from \\\"[2019-01-02 Wed]\\\" on [2019-01-01 Tue 00:00]\"\n\n(-> (- 1546300800 (car (current-time-zone)))\n  (org-ml-build-log-redeadline (org-ml-build-timestamp! '(2019 1 2))\n    \"noteworthy\")\n  (org-ml-to-trimmed-string))\n ;; => \"- New deadline from \\\"[2019-01-02 Wed]\\\" on [2019-01-01 Tue 00:00] \\\\\\\\\n ;        noteworthy\"\n\n```\n\n#### org-ml-build-log-reschedule `(unixtime old-timestamp &optional note)`\n\nReturn an item node for a new schedule log entry.\n\nThis will format the log entry from the default value for the\n`reschedule` cell in `org-log-note-headings`.\n\n**`unixtime`** is an integer representing the time to be used for all\ntimestamp nodes.\n\n**`old-timestamp`** is a timestamp node of the schedule that is being\ndeleted. It will always be converted to an inactive timestamp.\n\nIf string **`note`** is supplied, append a note to the log entry.\n\n```el\n(-> (- 1546300800 (car (current-time-zone)))\n  (org-ml-build-log-reschedule (org-ml-build-timestamp! '(2019 1 2)))\n  (org-ml-to-trimmed-string))\n ;; => \"- Rescheduled from \\\"[2019-01-02 Wed]\\\" on [2019-01-01 Tue 00:00]\"\n\n(-> (- 1546300800 (car (current-time-zone)))\n  (org-ml-build-log-reschedule (org-ml-build-timestamp! '(2019 1 2))\n    \"noteworthy\")\n  (org-ml-to-trimmed-string))\n ;; => \"- Rescheduled from \\\"[2019-01-02 Wed]\\\" on [2019-01-01 Tue 00:00] \\\\\\\\\n ;        noteworthy\"\n\n```\n\n#### org-ml-build-log-type `(type &key old new unixtime username full-username note)`\n\nReturn an item for an arbitrary log entry.\n\n**`type`** is a symbol corresponding to the car of one of the cells in\n`org-log-note-headings`. Unlike the other log entry build functions\nin this package, this function will not use the default value of\n`org-log-note-headings` which means it can be used for customly\nformatted log entries.\n\nThe arguments correspond to the following formatting placeholders\n(see `org-log-note-headings` for more information on these\nplaceholders):\n- **`new`**: either a string or timestamp node that will replace the\n    new state/timestamp placeholder (%s)\n- **`old`**: like **`new`** but for the old state/timestamp placeholder (%S)\n- **`unixtime`**: an integer corresponding to the time to be used for the\n    timestamp placeholders (%t/%T/%d/%D)\n- **`username`**: a string for the username (%u)\n- **`full-username`**: a string for the full username (%U)\n\nIf any of these arguments are not supplied but their placeholders\nare present in the heading determined by **`type`**, the placeholders will\nnot be substituted.\n\nIf string **`note`** is supplied, append a note to the log entry.\n\n```el\n(let\n  ((org-log-note-headings '((test . \"Changed %s from %S on %t by %u\")))\n    (ut (- 1546300800 (car (current-time-zone)))))\n  (->>\n    (org-ml-build-log-type 'test\n      :unixtime\n      ut\n      :old\n      \"TODO\"\n      :new\n      \"DONE\"\n      :username\n      \"shadowbrokers\"\n      :note\n      \"We're coming for you\")\n    (org-ml-to-trimmed-string)))\n ;; => \"- Changed \\\"DONE\\\" from \\\"TODO\\\" on [2019-01-01 Tue 00:00] by shadowbrokers \\\\\\\\\n ;        We're coming for you\"\n\n```\n\n\n## Type Predicates\n\n\nTest node types.\n\n#### org-ml-get-type `(node &optional anonymous)`\n\nReturn the type of **`node`**.\n\n```el\n;; Given the following contents:\n; *I'm emboldened*\n\n(->> (org-ml-parse-this-object) (org-ml-get-type))\n ;; => 'bold\n\n;; Given the following contents:\n; * I'm the headliner\n\n(->> (org-ml-parse-this-element) (org-ml-get-type))\n ;; => 'headline\n\n;; Given the following contents:\n; [2112-12-21 Wed]\n\n(->> (org-ml-parse-this-object) (org-ml-get-type))\n ;; => 'timestamp\n\n```\n\n#### org-ml-is-type `(type node)`\n\nReturn t if the type of **`node`** is **`type`** (a symbol).\n\n```el\n;; Given the following contents:\n; *ziltoid*\n\n(->> (org-ml-parse-this-object) (org-ml-is-type 'bold))\n ;; => t\n\n(->> (org-ml-parse-this-object) (org-ml-is-type 'italic))\n ;; => nil\n\n```\n\n#### org-ml-is-any-type `(types node)`\n\nReturn t if the type of **`node`** is in **`types`** (a list of symbols).\n\n```el\n;; Given the following contents:\n; *ziltoid*\n\n(->> (org-ml-parse-this-object) (org-ml-is-any-type '(bold)))\n ;; => t\n\n(->> (org-ml-parse-this-object) (org-ml-is-any-type '(bold italic)))\n ;; => t\n\n(->> (org-ml-parse-this-object) (org-ml-is-any-type '(italic)))\n ;; => nil\n\n```\n\n#### org-ml-is-element `(node)`\n\nReturn t if **`node`** is an element class.\n\n```el\n;; Given the following contents:\n; *ziltoid*\n\n;; Parsing this text as an element node gives a paragraph node\n(->> (org-ml-parse-this-element) (org-ml-is-element))\n ;; => t\n\n;; Parsing the same text as an object node gives a bold node\n(->> (org-ml-parse-this-object) (org-ml-is-element))\n ;; => nil\n\n```\n\n#### org-ml-is-branch-node `(node)`\n\nReturn t if **`node`** is a branch node.\n\n```el\n;; Given the following contents:\n; *ziltoid*\n\n;; Parsing this as an element node gives a paragraph node (a branch node)\n(->> (org-ml-parse-this-element) (org-ml-is-branch-node))\n ;; => t\n\n;; Parsing this as an object node gives a bold node (also a branch node)\n(->> (org-ml-parse-this-object) (org-ml-is-branch-node))\n ;; => t\n\n;; Given the following contents:\n; ~ziltoid~\n\n;; Parsing this as an object node gives a code node (not a branch node)\n(->> (org-ml-parse-this-object) (org-ml-is-branch-node))\n ;; => nil\n\n;; Given the following contents:\n; # ziltoid\n\n;; Parsing this as an element node gives a comment node (also not a branch node)\n(->> (org-ml-parse-this-element) (org-ml-is-branch-node))\n ;; => nil\n\n;; Given the following contents:\n; * I'm so great\n\n;; Parsing this as an element node gives a headline node (a branch node)\n(->> (org-ml-parse-this-element) (org-ml-is-branch-node))\n ;; => t\n\n```\n\n#### org-ml-node-may-have-child-objects `(node)`\n\nReturn t if **`node`** is a branch node that may have child objects.\n\n```el\n;; Given the following contents:\n; *ziltoid*\n\n;; Parsing this as an element node gives a paragraph node (can have child object\n;; nodes)\n(->> (org-ml-parse-this-element) (org-ml-node-may-have-child-objects))\n ;; => t\n\n;; Parsing this as an object node gives a bold node (also can have child object\n;; nodes)\n(->> (org-ml-parse-this-object) (org-ml-node-may-have-child-objects))\n ;; => t\n\n;; Given the following contents:\n; ~ziltoid~\n\n;; Parsing this as an object node gives a code node (not a branch node)\n(->> (org-ml-parse-this-object) (org-ml-node-may-have-child-objects))\n ;; => nil\n\n;; Given the following contents:\n; # ziltoid\n\n;; Parsing this as an element node gives a comment node (not a branch node)\n(->> (org-ml-parse-this-element) (org-ml-node-may-have-child-objects))\n ;; => nil\n\n;; Given the following contents:\n; * I'm so great\n\n;; Parsing this as an element node gives a headline node (can only have child\n;; element nodes)\n(->> (org-ml-parse-this-element) (org-ml-node-may-have-child-objects))\n ;; => nil\n\n```\n\n#### org-ml-node-may-have-child-elements `(node)`\n\nReturn t if **`node`** is a branch node that may have child elements.\n\nNote this implies that **`node`** is also of class element since only\nelements may have other elements as children.\n\n```el\n;; Given the following contents:\n; * I'm so great\n\n;; Parsing this as an element node gives a headline node (can have child element\n;; nodes)\n(->> (org-ml-parse-this-element) (org-ml-node-may-have-child-elements))\n ;; => t\n\n;; Given the following contents:\n; *ziltoid*\n\n;; Parsing this as an element node gives a paragraph node (can only have child\n;; object nodes)\n(->> (org-ml-parse-this-element) (org-ml-node-may-have-child-elements))\n ;; => nil\n\n;; Given the following contents:\n; # ziltoid\n\n;; Parsing this as an element node gives a comment node (not a branch node)\n(->> (org-ml-parse-this-element) (org-ml-node-may-have-child-elements))\n ;; => nil\n\n```\n\n\n## Property Manipulation\n\n\nSet, get, and map properties of nodes.\n\n\n### Generic\n\n#### org-ml-contains-point-p `(point node)`\n\nReturn t if **`point`** is within the boundaries of **`node`**.\n\n```el\n;; Given the following contents:\n; *findme*\n\n(->> (org-ml-parse-this-object) (org-ml-contains-point-p 2))\n ;; => t\n\n(->> (org-ml-parse-this-object) (org-ml-contains-point-p 10))\n ;; => nil\n\n```\n\n#### org-ml-set-property `(prop value node)`\n\nReturn **`node`** with **`prop`** set to **`value`**.\n\nSee builder functions for a list of properties and their rules for\neach type.\n\n```el\n;; Given the following contents:\n; #+call: ktulu()\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-set-property :call \"cthulhu\")\n  (org-ml-set-property :inside-header '(:cache no))\n  (org-ml-set-property :arguments '(\"x=4\"))\n  (org-ml-set-property :end-header '(:exports results))\n  (org-ml-to-trimmed-string))\n ;; => \"#+call: cthulhu[:cache no](x=4) :exports results\"\n\n;; Given the following contents:\n; call_kthulu()\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-set-property :call \"cthulhu\")\n  (org-ml-set-property :inside-header '(:cache no))\n  (org-ml-set-property :arguments '(\"x=4\"))\n  (org-ml-set-property :end-header '(:exports results))\n  (org-ml-to-trimmed-string))\n ;; => \"call_cthulhu[:cache no](x=4)[:exports results]\"\n\n;; Given the following contents:\n; src_emacs{(print 'yeah-boi)}\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-set-property :language \"python\")\n  (org-ml-set-property :parameters '(:cache no))\n  (org-ml-set-property :value \"print \\\"yeah boi\\\"\")\n  (org-ml-to-trimmed-string))\n ;; => \"src_python[:cache no]{print \\\"yeah boi\\\"}\"\n\n;; Given the following contents:\n; - thing\n\n(org-ml->> (org-ml-parse-this-item)\n  (org-ml-set-property :bullet 1)\n  (org-ml-set-property :checkbox 'on)\n  (org-ml-set-property :counter 2)\n  (org-ml-set-property :tag '(\"tmsu\"))\n  (org-ml-to-trimmed-string))\n ;; => \"1. [@2] [X] tmsu :: thing\"\n\n;; Given the following contents:\n; * not valuable\n\n;; Throw error when setting a property that doesn't exist\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-set-property :value \"wtf\")\n  (org-ml-to-trimmed-string))\nError\n\n;; Throw error when setting to an improper type\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-set-property :title 666)\n  (org-ml-to-trimmed-string))\nError\n\n```\n\n#### org-ml-get-property `(prop node)`\n\nReturn the value of **`prop`** of **`node`**.\n\n```el\n;; Given the following contents:\n; #+call: ktulu(x=4) :exports results\n\n(->> (org-ml-parse-this-element) (org-ml-get-property :call))\n ;; => \"ktulu\"\n\n(->> (org-ml-parse-this-element) (org-ml-get-property :inside-header))\n ;; => nil\n\n;; Given the following contents:\n; * not arguable\n\n;; Throw error when requesting a property that doesn't exist\n(->> (org-ml-parse-this-headline) (org-ml-get-property :value))\nError\n\n```\n\n#### org-ml-map-property `(prop fun node)`\n\nReturn **`node`** with **`fun`** applied to the value of **`prop`**.\n\n**`fun`** is a unary function which takes the current value of **`prop`** and\nreturns a new value to which **`prop`** will be set.\n\nSee builder functions for a list of properties and their rules for\neach type.\n\n```el\n;; Given the following contents:\n; ~learn to~\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-map-property :value #'s-upcase)\n  (org-ml-to-trimmed-string))\n ;; => \"~LEARN TO~\"\n\n;; Throw error if property doesn't exist\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-map-property :title #'s-upcase)\n  (org-ml-to-trimmed-string))\nError\n\n;; Throw error if function doesn't return proper type\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-map-property* :value (if it 1 0))\n  (org-ml-to-trimmed-string))\nError\n\n```\n\n#### org-ml-toggle-property `(prop node)`\n\nReturn **`node`** with the value of **`prop`** flipped.\n\nThis function only applies to properties that are booleans.\n\n(fn **`prop`** **`node`**)\n\nThe following types and properties are supported:\n\nentity\n- :use-brackets-p\n\nexample-block\n- :preserve-indent\n\nheadline\n- :archivedp\n- :commentedp\n- :footnote-section-p\n\nsrc-block\n- :preserve-indent\n\nsubscript\n- :use-brackets-p\n\n\n```el\n;; Given the following contents:\n; \\pi\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-toggle-property :use-brackets-p)\n  (org-ml-to-trimmed-string))\n ;; => \"\\\\pi{}\"\n\n;; Given the following contents:\n; - [ ] nope\n\n;; Throw an error when trying to toggle a non-boolean property\n(org-ml->> (org-ml-parse-this-item)\n  (org-ml-toggle-property :checkbox)\n  (org-ml-to-trimmed-string))\nError\n\n```\n\n#### org-ml-shift-property `(prop n node)`\n\nReturn **`node`** with **`prop`** shifted by **`n`** (an integer).\n\nThis only applies the properties that are represented as integers.\n\n(fn **`prop`** **`n`** **`node`**)\n\nThe following types and properties are supported:\n\nall elements\n- :post-blank\n\nfootnote-definition\n- :pre-blank\n\nheadline\n- :level\n- :pre-blank\n- :priority\n\nitem\n\n```el\n;; Given the following contents:\n; * no priorities\n\n;; Do nothing if there is nothing to shift.\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-shift-property :priority 1)\n  (org-ml-to-trimmed-string))\n ;; => \"* no priorities\"\n\n;; Given the following contents:\n; * [#A] priorities\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-shift-property :priority -1)\n  (org-ml-to-trimmed-string))\n ;; => \"* [#B] priorities\"\n\n;; Wrap priority around when crossing the min or max\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-shift-property :priority 1)\n  (org-ml-to-trimmed-string))\n ;; => \"* [#C] priorities\"\n\n;; Given the following contents:\n; * TODO or not todo\n\n;; Throw error when shifting an unshiftable property\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-shift-property :todo-keyword 1)\n  (org-ml-to-string))\nError\n\n```\n\n#### org-ml-insert-into-property `(prop index string node)`\n\nReturn **`node`** with **`string`** inserted at **`index`** into **`prop`**.\n\nThis only applies to properties that are represented as lists of\nstrings.\n\n(fn **`prop`** **`index`** **`string`** **`node`**)\n\nThe following types and properties are supported:\n\nbabel-call\n- :arguments\n\nexample-block\n- :switches\n\nheadline\n- :tags\n\ninline-babel-call\n- :arguments\n\nmacro\n- :args\n\nsrc-block\n- :switches\n\n\n```el\n;; Given the following contents:\n; #+call: ktulu(y=1)\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-insert-into-property :arguments 0 \"x=4\")\n  (org-ml-to-trimmed-string))\n ;; => \"#+call: ktulu(x=4,y=1)\"\n\n;; Do nothing if the string is already in the list\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-insert-into-property :arguments 0 \"y=1\")\n  (org-ml-to-trimmed-string))\n ;; => \"#+call: ktulu(y=1)\"\n\n;; Throw error when inserting into a property that is not a list of strings\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-insert-into-property :end-header 0 \"html\")\n  (org-ml-to-trimmed-string))\nError\n\n```\n\n#### org-ml-remove-from-property `(prop string node)`\n\nReturn **`node`** with **`string`** removed from **`prop`** if present.\n\nThis only applies to properties that are represented as lists of\nstrings.\n\nSee [`org-ml-insert-into-property`](#org-ml-insert-into-property-prop-index-string-node) for a list of supported elements\nand properties that may be used with this function.\n\n```el\n;; Given the following contents:\n; #+call: ktulu(y=1)\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-remove-from-property :arguments \"y=1\")\n  (org-ml-to-trimmed-string))\n ;; => \"#+call: ktulu()\"\n\n;; Do nothing if the string does not exist\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-remove-from-property :arguments \"d=666\")\n  (org-ml-to-trimmed-string))\n ;; => \"#+call: ktulu(y=1)\"\n\n;; Throw error when removing from property that is not a string list\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-remove-from-property :end-header \":results\")\n  (org-ml-to-trimmed-string))\nError\n\n```\n\n#### org-ml-plist-put-property `(prop key value node)`\n\nReturn **`node`** with **`value`** corresponding to **`key`** inserted into **`prop`**.\n\n**`key`** is a keyword and **`value`** is a symbol. This only applies to\nproperties that are represented as plists.\n\n(fn **`prop`** **`key`** **`value`** **`node`**)\n\nThe following types and properties are supported:\n\nbabel-call\n- :inside-header\n- :end-header\n\ndynamic-block\n- :arguments\n\ninline-babel-call\n- :inside-header\n- :end-header\n\ninline-src-block\n- :parameters\n\n\n```el\n;; Given the following contents:\n; #+call: ktulu[:cache no]()\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-plist-put-property :end-header :results 'html)\n  (org-ml-to-trimmed-string))\n ;; => \"#+call: ktulu[:cache no]() :results html\"\n\n;; Change the value of key if it already is present\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-plist-put-property :inside-header :cache 'yes)\n  (org-ml-to-trimmed-string))\n ;; => \"#+call: ktulu[:cache yes]()\"\n\n;; Do nothing if the key and value already exist\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-plist-put-property :inside-header :cache 'no)\n  (org-ml-to-trimmed-string))\n ;; => \"#+call: ktulu[:cache no]()\"\n\n;; Throw error if setting property that isn't a plist\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-plist-put-property :arguments :cache 'no)\n  (org-ml-to-trimmed-string))\nError\n\n```\n\n#### org-ml-plist-remove-property `(prop key node)`\n\nReturn **`node`** with **`key`** and its corresponding value removed from **`prop`**.\n\n**`key`** is a keyword. This only applies to properties that are\nrepresented as plists.\n\nSee [`org-ml-plist-put-property`](#org-ml-plist-put-property-prop-key-value-node) for a list of supported elements\nand properties that may be used with this function.\n\n```el\n;; Given the following contents:\n; #+call: ktulu() :results html\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-plist-remove-property :end-header :results)\n  (org-ml-to-trimmed-string))\n ;; => \"#+call: ktulu()\"\n\n;; Do nothing if the key is not present\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-plist-remove-property :inside-header :cache)\n  (org-ml-to-trimmed-string))\n ;; => \"#+call: ktulu() :results html\"\n\n;; Throw error if trying to remove key from non-plist property\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-plist-remove-property :arguments :cache)\n  (org-ml-to-trimmed-string))\nError\n\n```\n\n#### org-ml-get-properties `(props node)`\n\nReturn all the values of **`props`** from **`node`**.\n**`props`** is a list of all the properties desired, and the returned\nlist will be the values of these properties in the order\nrequested. To get all properties of **`node`**, use\n`org-ml--get-all-properties`.\n\n```el\n;; Given the following contents:\n; call_ktulu[:cache no](x=4)[:exports results]\n\n(->> (org-ml-parse-this-object)\n  (org-ml-get-properties '(:call :inside-header :arguments :end-header)))\n ;; => '(\"ktulu\" (:cache no) (\"x=4\") (:exports results))\n\n```\n\n#### org-ml-get-all-properties `(node)`\n\nReturn the properties list of **`node`**.\n\n```el\n;; Given the following contents:\n; *bold*\n\n(--> (org-ml-parse-this-object)\n  (org-ml-get-all-properties it)\n  (plist-put it :buffer nil)\n  (plist-put it :parent nil))\n ;; => (list :begin 1 :post-affiliated nil :contents-begin 2 :contents-end 6 :end 7 :post-blank 0 :secondary nil :mode nil :granularity nil :cached nil :org-element--cache-sync-key nil :robust-begin nil :robust-end nil :true-level nil :buffer nil :deferred nil :structure nil :parent nil)\n\n```\n\n#### org-ml-set-properties `(plist node)`\n\nReturn **`node`** with all properties set to the values according to **`plist`**.\n\n**`plist`** is a list of property-value pairs that corresponds to the\nproperty list in **`node`**.\n\nSee builder functions for a list of properties and their rules for\neach type.\n\n```el\n;; Given the following contents:\n; - thing\n\n(org-ml->> (org-ml-parse-this-item)\n  (org-ml-set-properties (list :bullet 1 :checkbox 'on :counter 2 :tag '(\"tmsu\")))\n  (org-ml-to-trimmed-string))\n ;; => \"1. [@2] [X] tmsu :: thing\"\n\n;; Given the following contents:\n; - plain\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-set-properties (list :name \"plain name\" :attr_XXX '(\"tmsu\")))\n  (org-ml-to-trimmed-string))\n ;; => \"#+name: plain name\n ;      #+attr_xxx: tmsu\n ;      - plain\"\n\n```\n\n#### org-ml-map-properties `(plist node)`\n\nReturn **`node`** with functions applied to the values of properties.\n\n**`plist`** is a property list where the keys are properties of **`node`** and\nits values are unary functions to be mapped to these properties.\n\nSee builder functions for a list of properties and their rules for\neach type.\n\n```el\n;; Given the following contents:\n; #+KEY: VAL\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-map-properties\n    (list :key\n      (-partial #'s-prepend \"OM_\")\n      :value\n      (-partial #'s-prepend \"OM_\")))\n  (org-ml-to-trimmed-string))\n ;; => \"#+om_key: OM_VAL\"\n\n;; Throw error if any of the properties are invalid\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-map-properties*\n    (:title (s-prepend \"OM_\" it) :value (s-prepend \"OM_\" it)))\n  (org-ml-to-trimmed-string))\nError\n\n```\n\n#### org-ml-get-parents `(node)`\n\nReturn parents of **`node`** as a list.\nThe toplevel parent will be the left-most member, and **`node`** itself\nwill be the rightmost member.\n\n```el\n;; Given the following contents:\n; * one\n; ** two\n; *** three\n\n(->> (org-ml-parse-this-subtree)\n  (org-ml-get-parents)\n  (--map (org-ml-get-property :begin it)))\n ;; => '(1)\n\n(->> (org-ml-parse-this-subtree)\n  (org-ml-headline-get-subheadlines)\n  (car)\n  (org-ml-headline-get-subheadlines)\n  (car)\n  (org-ml-get-parents)\n  (--map (org-ml-get-property :begin it)))\n ;; => '(1 7 14)\n\n```\n\n#### org-ml-remove-parent `(node)`\n\nReturn **`node`** with the :parent property set to nil.\n\nShort synopsis:\n\nUse this function to declutter a node if you are trying to print\nits literal list representation or you are running into infinite\nloops caused by self-referential lists (there are probably other\nvalid reasons but these are the main ones).\n\nGory details:\n\nThe :parent property refers to the node one level higher in the\ntree that contains **`node`** as a child. It will be present in a node\nthat is generated from a parse operation with\n`org-ml-parse-this-buffer` or related. This property offers a\nnice shortcut to traverse up the node tree from a child. Besides\nthis, it is not necessary as the tree structure itself already\nencodes all parent-child relationships. Further, it is not used\nby org-element internally to convert nodes into strings (such as\nwith [`org-ml-to-string`](#org-ml-to-string-node)) and thus can be thought of as a\n`\"read-only\"` property. This is why :parent will be set to nil when\nbuilding a new node with the `\"org-ml-build-\"` family of functions\nand why [`org-ml-set-property`](#org-ml-set-property-prop-value-node) forbids setting this property.\n\nIn many cases, one can safely ignore :parent unless, of course,\none actually needs to read it with [`org-ml-get-parents`](#org-ml-get-parents-node) or\n[`org-ml-get-property`](#org-ml-get-property-prop-node). However, it heavily clutters the list\nrepresentation of nodes, and therefore it is nice to remove this\nproperty whenever literal node lists are printed/visualized (eg\nfor debugging). Note that for deep trees, each parent will itself\nhave a :parent property pointing to its own parent, with this\npattern repeating until the top of the tree.\n\nFurthermore, each parent will itself contain its own child node,\nwhich implies a circular/self-referential list. For the most\npart, this won't matter. However, some functions don't like\ndealing with circular lists and will complain about infinite\nrecursion. If this is happening, the :parent property is likely\nto blame, and setting it to nil has a high probability of fixing\nthe issue.\n\n```el\n;; Given the following contents:\n; one\n\n;; This is actually a paragraph node, but parsing the object will directly\n;; return a plain-text node with the :parent pointing to the paragraph\n(org-ml->> (org-ml-parse-this-object) (org-ml-remove-parent))\n ;; => \"one\"\n\n;; Given the following contents:\n; * headline\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-remove-parents)\n  (org-ml-get-property :parent))\n ;; => nil\n\n;; Given the following contents:\n; - tag :: thingy\n\n\n```\n\n\n### Clock\n\n#### org-ml-clock-is-running `(clock)`\n\nReturn t if **`clock`** element is running (eg is open).\n\n```el\n;; Given the following contents:\n; CLOCK: [2019-01-01 Tue 00:00]\n\n(->> (org-ml-parse-this-element) (org-ml-clock-is-running))\n ;; => t\n\n;; Given the following contents:\n; CLOCK: [2019-01-01 Tue 00:00]--[2019-01-02 Wed 00:00] => 24:00\n\n(->> (org-ml-parse-this-element) (org-ml-clock-is-running))\n ;; => nil\n\n```\n\n\n### Entity\n\n#### org-ml-entity-get-replacement `(key entity)`\n\nReturn replacement string or symbol for **`entity`** node.\n\n**`key`** is one of:\n- `:latex` (the entity's latex representation)\n- `:latex-math-p` (t if the latex representation requires math mode,\n    nil otherwise)\n- `:html` (the entity's html representation)\n- `:ascii` (the entity's ascii representation)\n- `:latin1` (the entity's Latin1 representation)\n- `:utf-8` (the entity's utf8 representation)\n\nAny other keys will trigger an error.\n\n```el\n;; Given the following contents:\n; \\pi{}\n\n(->> (org-ml-parse-this-object) (org-ml-entity-get-replacement :latex))\n ;; => \"\\\\pi\"\n\n(->> (org-ml-parse-this-object)\n  (org-ml-entity-get-replacement :latex-math-p))\n ;; => t\n\n(->> (org-ml-parse-this-object) (org-ml-entity-get-replacement :html))\n ;; => \"&pi;\"\n\n(->> (org-ml-parse-this-object) (org-ml-entity-get-replacement :ascii))\n ;; => \"pi\"\n\n(->> (org-ml-parse-this-object) (org-ml-entity-get-replacement :latin1))\n ;; => \"pi\"\n\n(->> (org-ml-parse-this-object) (org-ml-entity-get-replacement :utf-8))\n ;; => \"π\"\n\n```\n\n\n### Headline\n\n#### org-ml-headline-set-title! `(title-text stats-cookie-value headline)`\n\nReturn **`headline`** node with new title.\n\n**`title-text`** is a string to be parsed into object nodes for the title\nvia [`org-ml-build-secondary-string!`](#org-ml-build-secondary-string-string) (see that function for restrictions)\nand **`stats-cookie-value`** is a list described in\n[`org-ml-build-statistics-cookie`](#org-ml-build-statistics-cookie-value-key-post-blank).\n\n```el\n;; Given the following contents:\n; * really impressive title\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-set-title! \"really *impressive* title\" '(2 3))\n  (org-ml-to-trimmed-string))\n ;; => \"* really *impressive* title [2/3]\"\n\n```\n\n#### org-ml-headline-is-done `(headline)`\n\nReturn t if **`headline`** node has a done todo-keyword.\n\n```el\n;; Given the following contents:\n; * TODO darn\n\n(->> (org-ml-parse-this-headline) (org-ml-headline-is-done))\n ;; => nil\n\n;; Given the following contents:\n; * DONE yay\n\n(->> (org-ml-parse-this-headline) (org-ml-headline-is-done))\n ;; => t\n\n```\n\n#### org-ml-headline-has-tag `(tag headline)`\n\nReturn t if **`headline`** node is tagged with **`tag`**.\n\n```el\n;; Given the following contents:\n; * dummy\n\n(->> (org-ml-parse-this-headline) (org-ml-headline-has-tag \"tmsu\"))\n ;; => nil\n\n;; Given the following contents:\n; * dummy                  :tmsu:\n\n(->> (org-ml-parse-this-headline) (org-ml-headline-has-tag \"tmsu\"))\n ;; => t\n\n```\n\n#### org-ml-headline-get-statistics-cookie `(headline)`\n\nReturn the statistics cookie node from **`headline`** if it exists.\n\n```el\n;; Given the following contents:\n; * statistically significant [10/10]\n\n(->> (org-ml-parse-this-headline)\n  (org-ml-headline-get-statistics-cookie)\n  (org-ml-to-string))\n ;; => \"[10/10]\"\n\n;; Given the following contents:\n; * not statistically significant\n\n(->> (org-ml-parse-this-headline) (org-ml-headline-get-statistics-cookie))\n ;; => nil\n\n```\n\n\n### Item\n\n#### org-ml-item-toggle-checkbox `(item)`\n\nReturn **`item`** node with its checkbox state flipped.\nThis only affects item nodes with checkboxes in the `on` or `off`\nstates; return **`item`** node unchanged if the checkbox property is `trans`\nor nil.\n\n```el\n;; Given the following contents:\n; - [ ] one\n\n(org-ml->> (org-ml-parse-this-item)\n  (org-ml-item-toggle-checkbox)\n  (org-ml-to-trimmed-string))\n ;; => \"- [X] one\"\n\n;; Given the following contents:\n; - [-] one\n\n;; Ignore trans state checkboxes\n(org-ml->> (org-ml-parse-this-item)\n  (org-ml-item-toggle-checkbox)\n  (org-ml-to-trimmed-string))\n ;; => \"- [-] one\"\n\n;; Given the following contents:\n; - one\n\n;; Do nothing if there is no checkbox\n(org-ml->> (org-ml-parse-this-item)\n  (org-ml-item-toggle-checkbox)\n  (org-ml-to-trimmed-string))\n ;; => \"- one\"\n\n```\n\n\n### Statistics Cookie\n\n#### org-ml-statistics-cookie-is-complete `(statistics-cookie)`\n\nReturn t is **`statistics-cookie`** node is complete.\n\n```el\n;; Given the following contents:\n; * statistically significant [10/10]\n\n(->> (org-ml-parse-this-headline)\n  (org-ml-headline-get-statistics-cookie)\n  (org-ml-statistics-cookie-is-complete))\n ;; => t\n\n;; Given the following contents:\n; * statistically significant [1/10]\n\n(->> (org-ml-parse-this-headline)\n  (org-ml-headline-get-statistics-cookie)\n  (org-ml-statistics-cookie-is-complete))\n ;; => nil\n\n;; Given the following contents:\n; * statistically significant [100%]\n\n(->> (org-ml-parse-this-headline)\n  (org-ml-headline-get-statistics-cookie)\n  (org-ml-statistics-cookie-is-complete))\n ;; => t\n\n;; Given the following contents:\n; * statistically significant [33%]\n\n(->> (org-ml-parse-this-headline)\n  (org-ml-headline-get-statistics-cookie)\n  (org-ml-statistics-cookie-is-complete))\n ;; => nil\n\n```\n\n#### org-ml-timestamp-get-start-time `(timestamp)`\n\nReturn the time list for start time of **`timestamp`** node.\nThe return value will be a list as specified by the `time` argument in\n[`org-ml-build-timestamp!`](#org-ml-build-timestamp-start-key-end-active-repeater-deadline-warning-collapsed-post-blank).\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-get-start-time))\n ;; => '(2019 1 1 nil nil)\n\n;; Given the following contents:\n; [2019-01-01 Tue]--[2019-01-02 Wed]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-get-start-time))\n ;; => '(2019 1 1 nil nil)\n\n;; Given the following contents:\n; [2019-01-01 Tue 00:00-12:00]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-get-start-time))\n ;; => '(2019 1 1 0 0)\n\n```\n\n#### org-ml-timestamp-get-end-time `(timestamp)`\n\nReturn the end time list for end time of **`timestamp`** or nil if not a range.\nThe return value will be a list as specified by the `time` argument in\n[`org-ml-build-timestamp!`](#org-ml-build-timestamp-start-key-end-active-repeater-deadline-warning-collapsed-post-blank).\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-get-end-time))\n ;; => nil\n\n;; Given the following contents:\n; [2019-01-01 Tue]--[2019-01-02 Wed]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-get-end-time))\n ;; => '(2019 1 2 nil nil)\n\n;; Given the following contents:\n; [2019-01-01 Tue]--[2019-01-01 Tue]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-get-end-time))\n ;; => '(2019 1 1 nil nil)\n\n;; Given the following contents:\n; [2019-01-01 Tue 00:00-12:00]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-get-end-time))\n ;; => '(2019 1 1 12 0)\n\n```\n\n#### org-ml-timestamp-get-range `(timestamp)`\n\nReturn the range of **`timestamp`** node in seconds as an integer.\n\nIf non-ranged, this function will return 0. If ranged but\nthe start time is in the future relative to end the time, return\na negative integer.\n\nThis function is depreciated. Use `org-ml-timestamp-get-length`\ninstead.\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-get-range))\n ;; => 0\n\n;; Given the following contents:\n; [2019-01-01 Tue]--[2019-01-02 Wed]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-get-range))\n ;; => 86400\n\n;; Given the following contents:\n; [2019-01-01 Tue 00:00-12:00]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-get-range))\n ;; => 43200\n\n```\n\n#### org-ml-timestamp-is-active `(timestamp)`\n\nReturn t if **`timestamp`** node is active.\n\n```el\n;; Given the following contents:\n; <2019-01-01 Tue>\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-is-active))\n ;; => t\n\n;; Given the following contents:\n; [2019-01-01 Tue]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-is-active))\n ;; => nil\n\n```\n\n#### org-ml-timestamp-is-ranged `(timestamp)`\n\nReturn t if **`timestamp`** node is ranged.\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue]--[2019-01-02 Wed]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-is-ranged))\n ;; => t\n\n;; Given the following contents:\n; [2019-01-01 Tue 00:00-12:00]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-is-ranged))\n ;; => t\n\n;; Given the following contents:\n; [2019-01-01 Tue]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-is-ranged))\n ;; => nil\n\n```\n\n#### org-ml-timestamp-range-contains-p `(unixtime timestamp)`\n\nReturn t if **`unixtime`** is between start and end time of **`timestamp`** node.\nThe boundaries are inclusive. If **`timestamp`** has a range of zero, then\nonly return t if **`unixtime`** is the same as **`timestamp`**. **`timestamp`** will be\ninterpreted according to the localtime of the operating system.\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue 00:00]\n\n(let ((ut (org-ml-timelist-to-unixtime '(2019 1 1 0 0))))\n  (->> (org-ml-parse-this-object) (org-ml-timestamp-range-contains-p ut)))\n ;; => t\n\n(let ((ut (org-ml-timelist-to-unixtime '(2019 1 1 0 30))))\n  (->> (org-ml-parse-this-object) (org-ml-timestamp-range-contains-p ut)))\n ;; => nil\n\n;; Given the following contents:\n; [2019-01-01 Tue 00:00-01:00]\n\n(let ((ut (org-ml-timelist-to-unixtime '(2019 1 1 0 30))))\n  (->> (org-ml-parse-this-object) (org-ml-timestamp-range-contains-p ut)))\n ;; => t\n\n```\n\n#### org-ml-timestamp-set-collapsed `(flag timestamp)`\n\nReturn **`timestamp`** with collapsed set to **`flag`**.\n\nCollapsed timestamps are like [yyyy-mm-dd xxx hh:mm-hh:mm].\n\nUncollapsed timestamp are like\n[yyyy-mm-dd xxx hh:mm]--[yyyy-mm-dd xxx hh:mm].\n\n**`flag`** may be one of nil, t, or `force`.\n\nIf nil, uncollapse the timestamp if it is collapsed. The dates in\nthe uncollapsed timestamp will be the same. Has no effect if the\ntimestamp is not collapsed.\n\nIf t, collapse the timestamp from uncollapsed format if the following\nconditions are met:\n1. the dates are the same\n2. start and end hours/minutes are non-nil\n\nHas no effect if timestamp id not uncollapsed and these\nconditions are not met.\n\nIf `force`, ignore condition 1 above. The date in the collapsed\ntimestamp will be taken from the start date and the end date will\nbe ignored.\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue 12:00-13:00]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-collapsed nil)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue 12:00]--[2019-01-01 Tue 13:00]\"\n\n;; Given the following contents:\n; [2019-01-01 Tue 12:00-13:00]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-collapsed nil)\n  (org-ml-timestamp-set-collapsed t)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue 12:00-13:00]\"\n\n;; Given the following contents:\n; [2019-01-01 Tue 12:00]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-collapsed nil)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue 12:00]\"\n\n;; Given the following contents:\n; [2019-01-01 Tue]--[2019-01-02 Wed]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-collapsed nil)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue]--[2019-01-02 Wed]\"\n\n```\n\n#### org-ml-timestamp-get-warning `(timestamp)`\n\nReturn the warning component of **`timestamp`**.\nReturn a list like `(type value unit)`.\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue 12:00]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-get-warning))\n ;; => nil\n\n;; Given the following contents:\n; [2019-01-01 Tue 12:00 -1d]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-get-warning))\n ;; => '(all 1 day)\n\n```\n\n#### org-ml-timestamp-set-warning `(warning timestamp)`\n\nSet the warning of **`timestamp`** to **`warning`**.\n\n**`warning`** is a list like `(type value unit)`. `type` is `all` or\n`first` `value` and is an integer. `unit` is one of `year`, `month`,\n`week`, or `day`.\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue 12:00]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-warning nil)\n  (org-ml-to-string))\n ;; => \"[2019-01-01 Tue 12:00]\"\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-warning '(all 1 day))\n  (org-ml-to-string))\n ;; => \"[2019-01-01 Tue 12:00 -1d]\"\n\n;; Given the following contents:\n; [2019-01-01 Tue 12:00]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-warning nil)\n  (org-ml-to-string))\n ;; => \"[2019-01-01 Tue 12:00]\"\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-warning '(all 1 year))\n  (org-ml-to-string))\n ;; => \"[2019-01-01 Tue 12:00 -1y]\"\n\n```\n\n#### org-ml-timestamp-map-warning `(fun timestamp)`\n\nApply **`fun`** to the warning of **`timestamp`**.\n**`fun`** is a function that takes a warning list like and returns a\nnew warning list. The same rules that apply to\n[`org-ml-timestamp-set-warning`](#org-ml-timestamp-set-warning-warning-timestamp) and [`org-ml-timestamp-get-warning`](#org-ml-timestamp-get-warning-timestamp)\napply here.\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue 12:00 -1d]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-map-warning* (-let (((y v u) it)) `(,y ,(1+ v) ,u)))\n  (org-ml-to-string))\n ;; => \"[2019-01-01 Tue 12:00 -2d]\"\n\n```\n\n#### org-ml-timestamp-get-repeater `(timestamp)`\n\nReturn the repeater component of **`timestamp`**.\nReturn a list like `(type value unit)` or nil.\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue 12:00]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-get-repeater))\n ;; => nil\n\n;; Given the following contents:\n; [2019-01-01 Tue 12:00 +1d]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-get-repeater))\n ;; => '(cumulate 1 day)\n\n;; Given the following contents:\n; [2019-01-01 Tue 12:00 +1d/3d]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-get-repeater))\n ;; => '(cumulate 1 day)\n\n```\n\n#### org-ml-timestamp-get-deadline `(timestamp)`\n\nReturn the repeater component of **`timestamp`**.\nReturn a list like `(value unit)` or nil.\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue 12:00]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-get-deadline))\n ;; => nil\n\n;; Given the following contents:\n; [2019-01-01 Tue 12:00 +1d]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-get-deadline))\n ;; => nil\n\n;; Given the following contents:\n; [2019-01-01 Tue 12:00 +1d/3d]\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-get-deadline))\n ;; => '(3 day)\n\n```\n\n#### org-ml-timestamp-set-repeater `(repeater timestamp)`\n\nSet the repeater of **`timestamp`** to **`repeater`**.\n\n**`repeater`** is a list like `(type value unit)`; `type` is one of\n`cumulate`, `restart`, or `catch-up`. `value` is an integer. `unit`\nis one of `year`, `month`, `week`, or `day`.\n\nSetting **`repeater`** to nil will remove the repeater and its deadline\nif present.\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue 12:00]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-repeater nil)\n  (org-ml-to-string))\n ;; => \"[2019-01-01 Tue 12:00]\"\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-repeater '(restart 1 day))\n  (org-ml-to-string))\n ;; => \"[2019-01-01 Tue 12:00 .+1d]\"\n\n```\n\n#### org-ml-timestamp-set-deadline `(deadline timestamp)`\n\nSet the repeater of **`timestamp`** to **`deadline`**.\n\n**`deadline`** is a list like `(value unit)`; `value` is an integer. `unit`\nis one of `year`, `month`, `week`, or `day`.\n\nSetting **`deadline`** to nil will remove the deadline. Will have no effect\nif repeater is not present.\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue 12:00]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-deadline nil)\n  (org-ml-to-string))\n ;; => \"[2019-01-01 Tue 12:00]\"\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-deadline '(3 day))\n  (org-ml-to-string))\n ;; => \"[2019-01-01 Tue 12:00]\"\n\n;; Given the following contents:\n; [2019-01-01 Tue 12:00 .+1d]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-deadline nil)\n  (org-ml-to-string))\n ;; => \"[2019-01-01 Tue 12:00 .+1d]\"\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-deadline '(3 day))\n  (org-ml-to-string))\n ;; => \"[2019-01-01 Tue 12:00 .+1d/3d]\"\n\n```\n\n#### org-ml-timestamp-map-repeater `(fun timestamp)`\n\nApply **`fun`** to the warning of **`timestamp`**.\n**`fun`** is a function that takes a repeater list like and returns a\nnew repeater list. The same rules that apply to\n[`org-ml-timestamp-set-repeater`](#org-ml-timestamp-set-repeater-repeater-timestamp) and\n[`org-ml-timestamp-get-repeater`](#org-ml-timestamp-get-repeater-timestamp) apply here.\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue 12:00 +1d]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-map-repeater* (-let (((y v u) it)) `(,y ,(1+ v) ,u)))\n  (org-ml-to-string))\n ;; => \"[2019-01-01 Tue 12:00 +2d]\"\n\n```\n\n#### org-ml-timestamp-set-start-time `(time timestamp)`\n\nReturn **`timestamp`** node with start time set to **`time`**.\n**`time`** is a list analogous to the same argument specified in\n[`org-ml-build-timestamp!`](#org-ml-build-timestamp-start-key-end-active-repeater-deadline-warning-collapsed-post-blank).\n\n```el\n;; Given the following contents:\n; [2019-01-02 Wed]\n\n;; If not a range this will turn into a range by moving only the start time.\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-start-time '(2019 1 1))\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue]--[2019-01-02 Wed]\"\n\n;; Set a different time with different precision.\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-start-time '(2019 1 1 10 0))\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue 10:00]--[2019-01-02 Wed]\"\n\n;; Given the following contents:\n; [2019-01-02 Wed 12:00]\n\n;; If not a range and set within a day, use short format\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-start-time '(2019 1 2 0 0))\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-02 Wed 00:00-12:00]\"\n\n```\n\n#### org-ml-timestamp-set-end-time `(time timestamp)`\n\nReturn **`timestamp`** node with end time set to **`time`**.\n**`time`** is a list analogous to the same argument specified in\n[`org-ml-build-timestamp!`](#org-ml-build-timestamp-start-key-end-active-repeater-deadline-warning-collapsed-post-blank).\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue]\n\n;; Add the end time\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-end-time '(2019 1 2))\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue]--[2019-01-02 Wed]\"\n\n;; Given the following contents:\n; [2019-01-01 Tue]--[2019-01-02 Wed]\n\n;; Remove the end time\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-end-time nil)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue]\"\n\n;; Given the following contents:\n; [2019-01-01 Tue 12:00]\n\n;; Use short range format\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-end-time '(2019 1 1 13 0))\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue 12:00-13:00]\"\n\n```\n\n#### org-ml-timestamp-set-single-time `(time timestamp)`\n\nReturn **`timestamp`** node with start and end times set to **`time`**.\n**`time`** is a list analogous to the same argument specified in\n[`org-ml-build-timestamp!`](#org-ml-build-timestamp-start-key-end-active-repeater-deadline-warning-collapsed-post-blank).\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue]\n\n;; Don't make a range\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-single-time '(2019 1 2))\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-02 Wed]\"\n\n;; Given the following contents:\n; [2019-01-01 Tue]--[2019-01-02 Wed]\n\n;; Output is not a range despite input being ranged\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-single-time '(2019 1 3))\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-03 Thu]\"\n\n```\n\n#### org-ml-timestamp-set-double-time `(time1 time2 timestamp)`\n\nReturn **`timestamp`** node with start/end times set to **`time1`**/**`time2`** respectively.\n**`time1`** and **`time2`** are lists analogous to the `time` argument specified in\n[`org-ml-build-timestamp!`](#org-ml-build-timestamp-start-key-end-active-repeater-deadline-warning-collapsed-post-blank).\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue]\n\n;; Make a range\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-double-time '(2019 1 2) '(2019 1 3))\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-02 Wed]--[2019-01-03 Thu]\"\n\n;; Given the following contents:\n; [2019-01-01 Tue]--[2019-01-03 Wed]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-double-time '(2019 1 4) '(2019 1 5))\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-04 Fri]--[2019-01-05 Sat]\"\n\n;; Given the following contents:\n; [2019-01-01 Tue]--[2019-01-03 Wed]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-double-time '(2019 1 1 0 0) '(2019 1 1 1 0))\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue 00:00]--[2019-01-01 Tue 01:00]\"\n\n```\n\n#### org-ml-timestamp-set-range `(n timestamp)`\n\nReturn **`timestamp`** node with range set to **`n`** seconds.\n\nIf **`timestamp`** is ranged, keep start time the same and adjust the end\ntime. If not, make a new end time. The units for `range` are in minutes\nif **`timestamp`** is in long format and days if **`timestamp`** is in short\nformat.\n\nThis function is depreciated. Use `org-ml-timestamp-set-length`\ninstead.\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue]\n\n;; Use days as the unit for short format\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-range 1 'day)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue]--[2019-01-02 Wed]\"\n\n;; Given the following contents:\n; [2019-01-01 Tue 00:00]\n\n;; Use minutes as the unit for long format\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-range 3 'minute)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue 00:00-00:03]\"\n\n;; Given the following contents:\n; [2019-01-01 Tue]--[2019-01-03 Wed]\n\n;; Set range to 0 to remove end time\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-range 0 'day)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue]\"\n\n```\n\n#### org-ml-timestamp-set-active `(flag timestamp)`\n\nReturn **`timestamp`** node with active type if **`flag`** is t.\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-active t)\n  (org-ml-to-trimmed-string))\n ;; => \"<2019-01-01 Tue>\"\n\n;; Given the following contents:\n; <2019-01-01 Tue>\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-set-active nil)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue]\"\n\n```\n\n#### org-ml-timestamp-shift `(n unit timestamp)`\n\nReturn **`timestamp`** node with time shifted by **`n`** **`unit`**`s.\n\nThis function will move the start and end times together; therefore\nranged inputs will always output ranged timestamps and same for\nnon-ranged. To move the start and end time independently, use\n[`org-ml-timestamp-shift-start`](#org-ml-timestamp-shift-start-n-unit-timestamp) or [`org-ml-timestamp-shift-end`](#org-ml-timestamp-shift-end-n-unit-timestamp).\n\n**`n`** is a positive or negative integer and **`unit`** is one of `minute`,\n`hour`, `day`, `month`, or `year`. Overflows will wrap around\ntransparently; for instance, supplying `minute` for **`unit`** and 90 for **`n`**\nwill increase the hour property by 1 and the minute property by 30.\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue 12:00]\n\n;; Change each unit, and wrap around to the next unit as needed.\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-shift 30 'minute)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue 12:30]\"\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-shift 13 'month)\n  (org-ml-to-trimmed-string))\n ;; => \"[2020-02-01 Sat 12:00]\"\n\n;; Given the following contents:\n; [2019-01-01 Tue]\n\n;; Error when shifting hour/minute in short format\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-shift 30 'minute)\n  (org-ml-to-trimmed-string))\nError\n\n```\n\n#### org-ml-timestamp-shift-start `(n unit timestamp)`\n\nReturn **`timestamp`** node with start time shifted by **`n`** **`unit`**`s.\n\n**`n`** and **`unit`** behave the same as those in [`org-ml-timestamp-shift`](#org-ml-timestamp-shift-n-unit-timestamp).\n\nIf **`timestamp`** is not range, the output will be a ranged timestamp with\nthe shifted start time and the end time as that of **`timestamp`**. If this\nbehavior is not desired, use [`org-ml-timestamp-shift`](#org-ml-timestamp-shift-n-unit-timestamp).\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue 12:00]\n\n;; If not a range, change start time and leave implicit end time.\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-shift-start -1 'year)\n  (org-ml-to-trimmed-string))\n ;; => \"[2018-01-01 Mon 12:00]--[2019-01-01 Tue 12:00]\"\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-shift-start -1 'hour)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue 11:00-12:00]\"\n\n;; Given the following contents:\n; [2019-01-01 Tue]--[2019-01-03 Thu]\n\n;; Change only start time if a range\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-shift-start 1 'day)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-02 Wed]--[2019-01-03 Thu]\"\n\n```\n\n#### org-ml-timestamp-shift-end `(n unit timestamp)`\n\nReturn **`timestamp`** node with end time shifted by **`n`** **`unit`**`s.\n\n**`n`** and **`unit`** behave the same as those in [`org-ml-timestamp-shift`](#org-ml-timestamp-shift-n-unit-timestamp).\n\nIf **`timestamp`** is not range, the output will be a ranged timestamp with\nthe shifted end time and the start time as that of **`timestamp`**. If this\nbehavior is not desired, use [`org-ml-timestamp-shift`](#org-ml-timestamp-shift-n-unit-timestamp).\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue]\n\n;; Shift implicit end time if not a range.\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-shift-end 1 'day)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue]--[2019-01-02 Wed]\"\n\n;; Given the following contents:\n; [2019-01-01 Tue]--[2019-01-02 Wed]\n\n;; Move only the second time if a range.\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-shift-end 1 'day)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue]--[2019-01-03 Thu]\"\n\n```\n\n#### org-ml-timestamp-toggle-active `(timestamp)`\n\nReturn **`timestamp`** node with its active/inactive type flipped.\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-toggle-active)\n  (org-ml-to-trimmed-string))\n ;; => \"<2019-01-01 Tue>\"\n\n;; Given the following contents:\n; <2019-01-01 Tue>--<2019-01-02 Wed>\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-toggle-active)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue]--[2019-01-02 Wed]\"\n\n```\n\n#### org-ml-timestamp-truncate `(timestamp)`\n\nReturn **`timestamp`** node with start/end times forced to short format.\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue]--[2019-01-02 Wed]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-truncate)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue]--[2019-01-02 Wed]\"\n\n;; Given the following contents:\n; [2019-01-01 Tue 12:00]--[2019-01-02 Wed 13:00]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-truncate)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue]--[2019-01-02 Wed]\"\n\n```\n\n#### org-ml-timestamp-truncate-start `(timestamp)`\n\nReturn **`timestamp`** node with start time forced to short format.\n\nCollapsed timestamps will become uncollapsed.\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue 12:00]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-truncate-start)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue]\"\n\n;; Given the following contents:\n; [2019-01-01 Tue 12:00]--[2019-01-02 Wed 12:00]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-truncate-start)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue]--[2019-01-02 Wed 12:00]\"\n\n;; Given the following contents:\n; [2019-01-01 Tue]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-truncate-start)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue]\"\n\n```\n\n#### org-ml-timestamp-truncate-end `(timestamp)`\n\nReturn **`timestamp`** node with end time forced to short format.\n\nCollapsed timestamps will become uncollapsed.\n\n```el\n;; Given the following contents:\n; [2019-01-01 Tue]--[2019-01-02 Wed]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-truncate-end)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue]--[2019-01-02 Wed]\"\n\n;; Given the following contents:\n; [2019-01-01 Tue 12:00]--[2019-01-02 Wed 13:00]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-truncate-end)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue 12:00]--[2019-01-02 Wed]\"\n\n;; Given the following contents:\n; [2019-01-01 Tue 12:00]\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-truncate-end)\n  (org-ml-to-trimmed-string))\n ;; => \"[2019-01-01 Tue 12:00]\"\n\n```\n\n\n### Timestamp (diary)\n\n#### org-ml-timestamp-diary-set-value `(form timestamp-diary)`\n\nReturn **`timestamp-diary`** node with value set to **`form`**.\nThe node must have a type `eq` to `diary`. **`form`** is a quoted list.\n\n```el\n;; Given the following contents:\n; <%%(diary-float t 4 2)>\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-set-value '(diary-float 1 3 2))\n  (org-ml-to-string))\n ;; => \"<%%(diary-float 1 3 2)>\"\n\n```\n\n#### org-ml-timestamp-diary-set-single-time `(time timestamp-diary)`\n\nReturn **`timestamp-diary`** node with start/end time set to **`time`**.\nThe node must have a type `eq` to `diary`. **`time`** is a list\nlike (hour min). If **`time`** is nil remove the time.\n\n```el\n;; Given the following contents:\n; <%%(diary-float t 4 2)>\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-set-single-time '(0 0))\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2) 00:00>\"\n\n;; Given the following contents:\n; <%%(diary-float t 4 2) 00:01>\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-set-single-time nil)\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2)>\"\n\n```\n\n#### org-ml-timestamp-diary-set-double-time `(time1 time2 timestamp-diary)`\n\nReturn **`timestamp-diary`** node with time set to **`time1`** and **`time2`**.\nThe node must have a type `eq` to `diary`. **`time1`** and **`time2`** are\nlists like (hour min). Either time may be nil, but if **`time1`** is nil\nthen **`time2`** must also be nil.\n\n```el\n;; Given the following contents:\n; <%%(diary-float t 4 2)>\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-set-double-time '(0 0) '(0 1))\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2) 00:00-00:01>\"\n\n;; Given the following contents:\n; <%%(diary-float t 4 2) 00:00-00:01>\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-set-double-time '(1 0) '(2 0))\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2) 01:00-02:00>\"\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-set-double-time '(1 0) nil)\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2) 01:00>\"\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-set-double-time nil nil)\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2)>\"\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-set-double-time nil '(2 0))\n  (org-ml-to-string))\nError\n\n```\n\n#### org-ml-timestamp-diary-get-start-time `(timestamp-diary)`\n\nReturn start time for  **`timestamp-diary`** or nil.\n\n```el\n;; Given the following contents:\n; <%%(diary-float t 4 2)>\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-diary-get-start-time))\n ;; => nil\n\n;; Given the following contents:\n; <%%(diary-float t 4 2) 12:00-13:00>\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-diary-get-start-time))\n ;; => '(12 0)\n\n```\n\n#### org-ml-timestamp-diary-set-start-time `(time timestamp-diary)`\n\nReturn **`timestamp-diary`** node with start time set to **`time`**.\nThe node must have a type `eq` to `diary`. **`time`** is a list\nlike (hour min). **`time`** may not be nil\n\n```el\n;; Given the following contents:\n; <%%(diary-float t 4 2)>\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-set-start-time '(0 0))\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2) 00:00>\"\n\n;; Given the following contents:\n; <%%(diary-float t 4 2) 12:00>\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-set-start-time '(1 0))\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2) 01:00-12:00>\"\n\n```\n\n#### org-ml-timestamp-diary-get-end-time `(timestamp-diary)`\n\nReturn end time for  **`timestamp-diary`** or nil.\n\n```el\n;; Given the following contents:\n; <%%(diary-float t 4 2)>\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-diary-get-end-time))\n ;; => nil\n\n;; Given the following contents:\n; <%%(diary-float t 4 2) 12:00-13:00>\n\n(->> (org-ml-parse-this-object) (org-ml-timestamp-diary-get-end-time))\n ;; => '(13 0)\n\n```\n\n#### org-ml-timestamp-diary-set-end-time `(time timestamp-diary)`\n\nReturn **`timestamp-diary`** node with end time set to **`time`**.\nThe node must have a type `eq` to `diary`. **`time`** is a list\nlike (hour min). If **`time`** is nil then remove the end time.\nIf start time is not set, return node unchanged.\n\n```el\n;; Given the following contents:\n; <%%(diary-float t 4 2)>\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-set-end-time '(0 0))\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2)>\"\n\n;; Given the following contents:\n; <%%(diary-float t 4 2) 12:00>\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-set-end-time '(13 0))\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2) 12:00-13:00>\"\n\n```\n\n#### org-ml-timestamp-diary-set-length `(n unit timestamp-diary)`\n\nReturn **`timestamp-diary`** node with range set to **`n`** UNITs.\nIf **`timestamp-diary`** is ranged, keep start time the same and adjust\nthe end time. If not, make a new end time.\n\n```el\n;; Given the following contents:\n; <%%(diary-float t 4 2)>\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-set-length 1 'hour)\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2)>\"\n\n;; Given the following contents:\n; <%%(diary-float t 4 2) 12:00>\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-set-length 1 'hour)\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2) 12:00-13:00>\"\n\n;; Given the following contents:\n; <%%(diary-float t 4 2) 12:00-13:00>\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-set-length 0 'hour)\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2) 12:00>\"\n\n```\n\n#### org-ml-timestamp-diary-shift `(n unit timestamp-diary)`\n\nReturn **`timestamp-diary`** node with time shifted by **`n`** UNITs.\n\nThis function will move the start and end times together;\ntherefore ranged inputs will always output ranged timestamps and\nsame for non-ranged. To move the start and end time\nindependently, use [`org-ml-timestamp-diary-shift-start`](#org-ml-timestamp-diary-shift-start-n-unit-timestamp-diary) or\n[`org-ml-timestamp-shift-end`](#org-ml-timestamp-shift-end-n-unit-timestamp).\n\n**`n`** is a positive or negative integer and **`unit`** is one of `minute`,\n`hour`, `day`, `month`, or `year`. Overflows will wrap around\ntransparently; for instance, supplying `minute` for **`unit`** and 90\nfor **`n`** will increase the hour property by 1 and the minute\nproperty by 30.\n\n```el\n;; Given the following contents:\n; <%%(diary-float t 4 2)>\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-shift 1 'hour)\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2)>\"\n\n;; Given the following contents:\n; <%%(diary-float t 4 2) 12:00>\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-shift 1 'hour)\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2) 13:00>\"\n\n```\n\n#### org-ml-timestamp-diary-shift-start `(n unit timestamp-diary)`\n\nReturn **`timestamp-diary`** node with start time shifted by **`n`** UNITs.\n\n**`n`** and **`unit`** behave the same as those in [`org-ml-timestamp-diary-shift`](#org-ml-timestamp-diary-shift-n-unit-timestamp-diary).\n\nIf **`timestamp-diary`** is not range, the output will be a ranged timestamp with\nthe shifted start time and the end time as that of **`timestamp-diary`**. If this\nbehavior is not desired, use [`org-ml-timestamp-diary-shift`](#org-ml-timestamp-diary-shift-n-unit-timestamp-diary).\n\n```el\n;; Given the following contents:\n; <%%(diary-float t 4 2)>\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-shift-start 1 'hour)\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2)>\"\n\n;; Given the following contents:\n; <%%(diary-float t 4 2) 12:00>\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-shift-start -1 'hour)\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2) 11:00-12:00>\"\n\n```\n\n#### org-ml-timestamp-diary-shift-end `(n unit timestamp-diary)`\n\nReturn **`timestamp-diary`** node with end time shifted by **`n`** UNITs.\n\n**`n`** and **`unit`** behave the same as those in [`org-ml-timestamp-diary-shift`](#org-ml-timestamp-diary-shift-n-unit-timestamp-diary).\n\nIf **`timestamp-diary`** is not range, the output will be a ranged timestamp with\nthe shifted end time and the start time as that of **`timestamp-diary`**. If this\nbehavior is not desired, use [`org-ml-timestamp-diary-shift`](#org-ml-timestamp-diary-shift-n-unit-timestamp-diary).\n\n```el\n;; Given the following contents:\n; <%%(diary-float t 4 2)>\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-shift-end 1 'hour)\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2)>\"\n\n;; Given the following contents:\n; <%%(diary-float t 4 2) 12:00>\n\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-timestamp-diary-shift-end 1 'hour)\n  (org-ml-to-string))\n ;; => \"<%%(diary-float t 4 2) 12:00-13:00>\"\n\n```\n\n\n## Branch/Child Manipulation\n\n\nSet, get, and map the children of branch nodes.\n\n\n### Polymorphic\n\n#### org-ml-children-contain-point `(point branch-node)`\n\nReturn t if **`point`** is within the boundaries of **`branch-node`**`s children.\n\n```el\n;; Given the following contents:\n; * headline\n; findme\n\n(->> (org-ml-parse-this-headline) (org-ml-children-contain-point 2))\n ;; => nil\n\n(->> (org-ml-parse-this-headline) (org-ml-children-contain-point 15))\n ;; => t\n\n```\n\n#### org-ml-get-children `(branch-node)`\n\nReturn the children of **`branch-node`** as a list.\n\n```el\n;; Given the following contents:\n; /this/ is a *paragraph*\n\n;; Return child nodes for branch nodes\n(->> (org-ml-parse-this-element)\n  (org-ml-get-children)\n  (-map #'org-ml-get-type))\n ;; => '(italic plain-text bold)\n\n;; Given the following contents:\n; * headline\n\n;; Return nil if no children\n(->> (org-ml-parse-this-subtree)\n  (org-ml-get-children)\n  (-map #'org-ml-get-type))\n ;; => nil\n\n```\n\n#### org-ml-set-children `(children branch-node)`\n\nReturn **`branch-node`** with its children set to **`children`**.\n**`children`** is a list of nodes; the types permitted in this list depend\non the type of `node`.\n\n```el\n;; Given the following contents:\n; /this/ is a *paragraph*\n\n;; Set children for branch object\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-set-children (list \"this is lame\"))\n  (org-ml-to-trimmed-string))\n ;; => \"this is lame\"\n\n;; Given the following contents:\n; * headline\n\n;; Set children for branch element nodes\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-set-children\n    (list (org-ml-build-headline! :title-text \"only me\" :level 2)))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      ** only me\"\n\n```\n\n#### org-ml-map-children `(fun branch-node)`\n\nReturn **`branch-node`** with **`fun`** applied to its children.\n**`fun`** is a unary function that takes the current list of children and\nreturns a modified list of children.\n\n```el\n;; Given the following contents:\n; /this/ is a *paragraph*\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-map-children (lambda (objs) (append objs (list \" ...yeah\"))))\n  (org-ml-to-trimmed-string))\n ;; => \"/this/ is a *paragraph* ...yeah\"\n\n;; Given the following contents:\n; * headline\n; ** subheadline\n\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-map-children* (--map (org-ml-shift-property :level 1 it) it))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      *** subheadline\"\n\n```\n\n#### org-ml-is-childless `(branch-node)`\n\nReturn t if **`branch-node`** has no children.\n\n```el\n;; Given the following contents:\n; * dummy\n; filled with useless knowledge\n\n(->> (org-ml-parse-this-headline) (org-ml-is-childless))\n ;; => nil\n\n;; Given the following contents:\n; * dummy\n\n(->> (org-ml-parse-this-headline) (org-ml-is-childless))\n ;; => t\n\n```\n\n\n### Object Nodes\n\n#### org-ml-unwrap `(object-node)`\n\nReturn the children of **`object-node`** as a secondary string.\nIf **`object-node`** is a plain-text node, wrap it in a list and return.\nElse add the post-blank property of **`object-node`** to the last member\nof its children and return children as a secondary string.\n\n```el\n;; Given the following contents:\n; _1 *2* 3 */4/* 5 /6/_\n\n;; Remove the outer underline formatting\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-unwrap)\n  (apply #'org-ml-build-paragraph)\n  (org-ml-to-trimmed-string))\n ;; => \"1 *2* 3 */4/* 5 /6/\"\n\n```\n\n#### org-ml-unwrap-types-deep `(types object-node)`\n\nReturn the children of **`object-node`** as a secondary string.\nIf **`object-node`** is a plain-text node, wrap it in a list and return.\nElse recursively descend into the children of **`object-node`** and splice\nthe children of nodes with type in **`types`** in place of said node and\nreturn the result as a secondary string.\n\n```el\n;; Given the following contents:\n; _1 *2* 3 */4/* 5 /6/_\n\n;; Remove bold formatting at any level\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-unwrap-types-deep '(bold))\n  (apply #'org-ml-build-paragraph)\n  (org-ml-to-trimmed-string))\n ;; => \"_1 2 3 /4/ 5 /6/_\"\n\n```\n\n#### org-ml-unwrap-deep `(object-node)`\n\nReturn the children of **`object-node`** as plain-text wrapped in a list.\n\n```el\n;; Given the following contents:\n; _1 *2* 3 */4/* 5 /6/_\n\n;; Remove all formatting\n(org-ml->> (org-ml-parse-this-object)\n  (org-ml-unwrap-deep)\n  (apply #'org-ml-build-paragraph)\n  (org-ml-to-trimmed-string))\n ;; => \"1 2 3 4 5 6\"\n\n```\n\n\n### Secondary Strings\n\n#### org-ml-flatten `(secondary-string)`\n\nReturn **`secondary-string`** with its first level unwrapped.\nThe unwrap operation will be done with [`org-ml-unwrap`](#org-ml-unwrap-object-node).\n\n```el\n;; Given the following contents:\n; This (1 *2* 3 */4/* 5 /6/) is randomly formatted\n\n;; Remove first level of formatting\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-map-children #'org-ml-flatten)\n  (org-ml-to-trimmed-string))\n ;; => \"This (1 2 3 /4/ 5 6) is randomly formatted\"\n\n```\n\n#### org-ml-flatten-types-deep `(types secondary-string)`\n\nReturn **`secondary-string`** with object nodes in **`types`** unwrapped.\nThe unwrap operation will be done with [`org-ml-unwrap-types-deep`](#org-ml-unwrap-types-deep-types-object-node).\n\n```el\n;; Given the following contents:\n; This (1 *2* 3 */4/* 5 /6/) is randomly formatted\n\n;; Remove italic formatting at any level\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-map-children* (org-ml-flatten-types-deep '(italic) it))\n  (org-ml-to-trimmed-string))\n ;; => \"This (1 *2* 3 *4* 5 6) is randomly formatted\"\n\n```\n\n#### org-ml-flatten-deep `(secondary-string)`\n\nReturn **`secondary-string`** with all object nodes unwrapped to plain-text.\nThe unwrap operation will be done with [`org-ml-unwrap-deep`](#org-ml-unwrap-deep-object-node).\n\n```el\n;; Given the following contents:\n; This (1 *2* 3 */4/* 5 /6/) is randomly formatted\n\n;; Remove italic formatting at any level\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-map-children #'org-ml-flatten-deep)\n  (org-ml-to-trimmed-string))\n ;; => \"This (1 2 3 4 5 6) is randomly formatted\"\n\n```\n\n\n### Item\n\n#### org-ml-item-get-paragraph `(item)`\n\nReturn the first paragraph's children of **`item`** or nil if none.\n\n```el\n;; Given the following contents:\n; - one\n\n(->> (org-ml-parse-this-item) (org-ml-item-get-paragraph))\n ;; => '(\"one\")\n\n;; Given the following contents:\n; - \n\n(->> (org-ml-parse-this-item) (org-ml-item-get-paragraph))\n ;; => nil\n\n```\n\n#### org-ml-item-set-paragraph `(secondary-string item)`\n\nSet the first paragraph's children of **`item`** to **`secondary-string`**.\n\n```el\n;; Given the following contents:\n; - one\n\n(org-ml->> (org-ml-parse-this-item)\n  (org-ml-item-set-paragraph '(\"two\"))\n  (org-ml-to-string))\n ;; => \"- two\n ;      \"\n\n;; Given the following contents:\n; - one\n\n(org-ml->> (org-ml-parse-this-item)\n  (org-ml-item-set-paragraph nil)\n  (org-ml-to-string))\n ;; => \"- \n ;      \"\n\n```\n\n#### org-ml-item-map-paragraph `(fun item)`\n\nApply **`fun`** to the first paragraph's children in **`item`**.\n**`fun`** is a `unary` function that takes the secondary-string of the\nfirst paragraph and returns modified secondary-string.\n\n```el\n;; Given the following contents:\n; - one\n\n(org-ml->> (org-ml-parse-this-item)\n  (org-ml-item-map-paragraph* (-map #'upcase it))\n  (org-ml-to-string))\n ;; => \"- ONE\n ;      \"\n\n```\n\n\n### Headline\n\n#### org-ml-headline-get-section `(headline)`\n\nReturn children of section node in **`headline`** node or nil if none.\n\n```el\n;; Given the following contents:\n; * headline 1\n; sectional stuff\n; ** headline 2\n; ** headline 3\n\n(->> (org-ml-parse-this-subtree)\n  (org-ml-headline-get-section)\n  (-map #'org-ml-to-trimmed-string))\n ;; => '(\"sectional stuff\")\n\n;; Given the following contents:\n; * headline 1\n; ** headline 2\n; ** headline 3\n\n(->> (org-ml-parse-this-subtree)\n  (org-ml-headline-get-section)\n  (org-ml-to-trimmed-string))\n ;; => \"\"\n\n```\n\n#### org-ml-headline-set-section `(children headline)`\n\nReturn **`headline`** with section node containing **`children`**.\nIf **`children`** is nil, return **`headline`** with no section node.\n\n```el\n;; Given the following contents:\n; * headline\n\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-headline-set-section (list (org-ml-build-paragraph! \"x-section\")))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      x-section\"\n\n;; Given the following contents:\n; * headline\n; x-section\n\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-headline-set-section (list (org-ml-build-paragraph! \"x-guard\")))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      x-guard\"\n\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-headline-set-section nil)\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\"\n\n```\n\n#### org-ml-headline-map-section `(fun headline)`\n\nReturn **`headline`** node with child section node modified by **`fun`**.\n\n**`fun`** is a unary function that takes a section node's children as a list\nreturns a modified child list.\n\n```el\n;; Given the following contents:\n; * headline\n; x-section\n\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-headline-map-section*\n    (cons (org-ml-build-planning! :closed '(2019 1 1)) it))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      CLOSED: [2019-01-01 Tue]\n ;      x-section\"\n\n```\n\n#### org-ml-headline-get-subheadlines `(headline)`\n\nReturn list of child headline nodes in **`headline`** node or nil if none.\n\n```el\n;; Given the following contents:\n; * headline 1\n; sectional stuff\n; ** headline 2\n; ** headline 3\n\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-headline-get-subheadlines)\n  (-map #'org-ml-to-trimmed-string))\n ;; => '(\"** headline 2\" \"** headline 3\")\n\n;; Given the following contents:\n; * headline 1\n; sectional stuff\n\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-headline-get-subheadlines)\n  (-map #'org-ml-to-trimmed-string))\n ;; => nil\n\n```\n\n#### org-ml-headline-set-subheadlines `(subheadlines headline)`\n\nReturn **`headline`** node with **`subheadlines`** set to child subheadlines.\n\n```el\n;; Given the following contents:\n; * headline 1\n; sectional stuff\n; ** headline 2\n; ** headline 3\n\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-headline-set-subheadlines\n    (list (org-ml-build-headline! :level 2 :title-text \"headline x\")))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline 1\n ;      sectional stuff\n ;      ** headline x\"\n\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-headline-set-subheadlines nil)\n  (org-ml-to-trimmed-string))\n ;; => \"* headline 1\n ;      sectional stuff\"\n\n```\n\n#### org-ml-headline-map-subheadlines `(fun headline)`\n\nReturn **`headline`** node with child headline nodes modified by **`fun`**.\n\n**`fun`** is a unary function that takes a list of headlines and returns\na modified list of headlines.\n\n```el\n;; Given the following contents:\n; * headline 1\n; ** headline 2\n; ** headline 3\n\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-headline-map-subheadlines*\n    (--map (org-ml-set-property :todo-keyword \"TODO\" it) it))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline 1\n ;      ** TODO headline 2\n ;      ** TODO headline 3\"\n\n```\n\n\n### Headline (metadata)\n\n#### org-ml-headline-get-planning `(headline)`\n\nReturn the planning node in **`headline`** or nil if none.\n\n```el\n;; Given the following contents:\n; * headline\n; CLOSED: [2019-01-01 Tue]\n\n(->> (org-ml-parse-this-headline) (org-ml-headline-get-planning))\n ;; => '(:closed (2019 1 1 nil nil) :scheduled nil :deadline nil)\n\n;; Given the following contents:\n; * headline\n\n(->> (org-ml-parse-this-headline) (org-ml-headline-get-planning))\n ;; => nil\n\n```\n\n#### org-ml-headline-set-planning `(planning headline)`\n\nReturn **`headline`** node with planning components set to **`planning`** node.\n\n```el\n;; Given the following contents:\n; * headline\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-set-planning '(:closed (2019 1 1)))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      CLOSED: [2019-01-01 Tue]\"\n\n;; Given the following contents:\n; * headline\n; CLOSED: [2019-01-01 Tue]\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-set-planning '(:scheduled (2019 1 1)))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      SCHEDULED: <2019-01-01 Tue>\"\n\n;; Given the following contents:\n; * headline\n; CLOSED: [2019-01-01 Tue]\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-set-planning nil)\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\"\n\n```\n\n#### org-ml-headline-map-planning `(fun headline)`\n\nReturn **`headline`** node with planning node modified by **`fun`**.\n\n**`fun`** is a unary function that takes a planning node and returns a\nmodified planning node.\n\n```el\n;; Given the following contents:\n; * headline\n; CLOSED: [2019-01-01 Tue]\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-map-planning*\n    (list :closed (org-ml-timelist-shift 1 'day (plist-get it :closed))))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      CLOSED: [2019-01-02 Wed]\"\n\n```\n\n#### org-ml-headline-get-node-properties `(headline)`\n\nReturn a list of node-properties nodes in **`headline`** or nil if none.\n\n```el\n;; Given the following contents:\n; * headline\n; :PROPERTIES:\n; :Effort:   1:00\n; :ID:       minesfake\n; :END:\n\n(->> (org-ml-parse-this-headline) (org-ml-headline-get-node-properties))\n ;; => '((\"Effort\" \"1:00\") (\"ID\" \"minesfake\"))\n\n;; Given the following contents:\n; * headline\n\n(->> (org-ml-parse-this-headline)\n  (org-ml-headline-get-node-properties)\n  (-map #'org-ml-to-trimmed-string))\n ;; => nil\n\n```\n\n#### org-ml-headline-set-node-properties `(node-properties headline)`\n\nReturn **`headline`** node with property drawer containing **`node-properties`**.\n**`node-properties`** is a list of (key . value) pairs (both strings).\n\n```el\n;; Given the following contents:\n; * headline\n; :PROPERTIES:\n; :Effort:   1:00\n; :ID:       minesfake\n; :END:\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-set-node-properties '((\"Effort\" \"0:01\") (\"ID\" \"easy\")))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      :PROPERTIES:\n ;      :Effort:   0:01\n ;      :ID:       easy\n ;      :END:\"\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-set-node-properties nil)\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\"\n\n```\n\n#### org-ml-headline-map-node-properties `(fun headline)`\n\nReturn **`headline`** node with property-drawer node modified by **`fun`**.\n\n**`fun`** is a unary function that takes a property-drawer node and returns\na modified property-drawer node.\n\n```el\n;; Given the following contents:\n; * headline\n; :PROPERTIES:\n; :Effort:   1:00\n; :ID:       minesfake\n; :END:\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-map-node-properties* (cons (list \"New\" \"world man\") it))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      :PROPERTIES:\n ;      :New:      world man\n ;      :Effort:   1:00\n ;      :ID:       minesfake\n ;      :END:\"\n\n```\n\n#### org-ml-headline-get-node-property `(key headline)`\n\nReturn value of property with **`key`** in **`headline`** or nil if not found.\nIf multiple properties with **`key`** are present, only return the first.\n\n```el\n;; Given the following contents:\n; * headline\n; :PROPERTIES:\n; :ID:       fake\n; :END:\n\n(->> (org-ml-parse-this-headline) (org-ml-headline-get-node-property \"ID\"))\n ;; => \"fake\"\n\n;; Given the following contents:\n; * headline\n; :PROPERTIES:\n; :ID:       fake\n; :END:\n\n(->> (org-ml-parse-this-headline)\n  (org-ml-headline-get-node-property \"READ_ID\"))\n ;; => nil\n\n```\n\n#### org-ml-headline-set-node-property `(key value headline)`\n\nReturn **`headline`** with node property matching **`key`** set to **`value`**.\nIf a property matching **`key`** is present, set it to **`value`**. If multiple\nproperties matching **`key`** are present, only set the first.\n\n```el\n;; Given the following contents:\n; * headline\n; :PROPERTIES:\n; :ID:       fake\n; :END:\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-set-node-property \"ID\" \"real\")\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      :PROPERTIES:\n ;      :ID:       real\n ;      :END:\"\n\n;; Given the following contents:\n; * headline\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-set-node-property \"ID\" \"real\")\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      :PROPERTIES:\n ;      :ID:       real\n ;      :END:\"\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-set-node-property \"ID\" nil)\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\"\n\n;; Given the following contents:\n; * headline\n; :PROPERTIES:\n; :ID:       real\n; :END:\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-set-node-property \"ID\" nil)\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\"\n\n```\n\n#### org-ml-headline-map-node-property `(key fun headline)`\n\nReturn **`headline`** node with property value matching **`key`** modified by **`fun`**.\n\n**`fun`** is a unary function that takes a node-property value and returns\na modified node-property value.\n\n```el\n;; Given the following contents:\n; * headline\n; :PROPERTIES:\n; :ID:       fake\n; :END:\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-map-node-property \"ID\" #'s-upcase)\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      :PROPERTIES:\n ;      :ID:       FAKE\n ;      :END:\"\n\n```\n\n\n### Headline (logbook and contents)\n\n#### org-ml-headline-get-supercontents `(config headline)`\n\nReturn the supercontents of **`headline`** node.\n\nSupercontents will be a plist like:\n\n(\n    :planning `planning`\n    :node-props `props`\n    :logbook `lb`\n    :blank `blank`\n    :contents `contents`\n)\n\n`planning` is a plist like the analogous argument of\n[`org-ml-build-planning!`](#org-ml-build-planning-key-closed-deadline-scheduled-post-blank) or nil if non-existent.\n\n`props` is a list of node-property nodes.\n\n`lb` is the logbook, which is another plist (see below).\n\n`blank` is the value of any whitespace after the planning,\nproperty-drawer, or logbook (assuming any exist) or the\n:pre-blank value of the encapsulating headline (if they don't\nexist).\n\n`contents` is a list of nodes after all the other stuff above.\n\nThe logbook will be have keys :items, :clocks, and :unknown,\nwhere the first two will include the item and clock nodes of the\nlogbook respectively, and the third will contain anything that\ncould not be identified as a valid logbook entry. Note that items\nare actually stored under a plain-list node but will be returned\nhere as a flat list of items for convenience. Also note that the\n:clocks slot can also include item nodes if clock notes are\nreturned.\n\n**`config`** is a plist representing the logbook configuration to\ntarget and will contain the following keys;\n- :log-into-drawer - corresponds to the value of\n    symbol `org-log-into-drawer` and carriers the same meaning\n- :clock-into-drawer - corresponds to the value of\n    symbol `org-clock-into-drawer` and carriers the same meaning\n- :clock-out-notes - corresponds to the value of\n    `org-log-note-clock-out`\n\nAny values not given will default to nil. Note that there is no\nway to infer what the logbook configuration should be, and thus\nthis controls how the logbook will be parsed; this means it also\ndetermines which nodes will be returned in the :items/:clocks\nslots and which will be deemed :unknown (see above) so be sure\nthis plist is set according to your desired target configuration.\n\n```el\n;; Given the following contents:\n; * headline\n; CLOSED: [2019-01-01 Tue 00:00]\n; :PROPERTIES:\n; :Effort: 0:30\n; :END:\n; :LOGGING:\n; - Note taken on [2018-12-31 Mon 00:00] \\\\\n;   log note\n; :END:\n; :CLOCKING:\n; CLOCK: [2019-01-01 Tue 00:00]\n; :END:\n; contents\n\n(let ((config (list :log-into-drawer \"LOGGING\" :clock-into-drawer \"CLOCKING\")))\n  (->> (org-ml-parse-this-headline)\n    (org-ml-headline-get-supercontents config)\n    (org-ml-supercontents-get-logbook)\n    (org-ml-logbook-get-items)\n    (-map #'org-ml-to-trimmed-string)))\n ;; => '(\"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\n ;       log note\")\n\n(let ((config (list :log-into-drawer \"LOGGING\" :clock-into-drawer \"CLOCKING\")))\n  (->> (org-ml-parse-this-headline)\n    (org-ml-headline-get-supercontents config)\n    (org-ml-supercontents-get-logbook)\n    (org-ml-logbook-get-clocks)\n    (-map #'org-ml-to-trimmed-string)))\n ;; => '(\"CLOCK: [2019-01-01 Tue 00:00]\")\n\n(let ((config (list :log-into-drawer \"LOGGING\" :clock-into-drawer \"CLOCKING\")))\n  (->> (org-ml-parse-this-headline)\n    (org-ml-headline-get-supercontents config)\n    (org-ml-supercontents-get-logbook)\n    (alist-get :unknown)\n    (-map #'org-ml-to-trimmed-string)))\n ;; => nil\n\n(let ((config (list :log-into-drawer \"LOGGING\" :clock-into-drawer \"CLOCKING\")))\n  (->> (org-ml-parse-this-headline)\n    (org-ml-headline-get-supercontents config)\n    (org-ml-supercontents-get-contents)\n    (-map #'org-ml-to-trimmed-string)))\n ;; => '(\"contents\")\n\n```\n\n#### org-ml-headline-set-supercontents `(config supercontents headline)`\n\nSet logbook and contents of **`headline`** according to **`supercontents`**.\nSee [`org-ml-headline-get-supercontents`](#org-ml-headline-get-supercontents-config-headline) for the meaning of **`config`**\nand the structure of the **`supercontents`** list.\n\n```el\n;; Given the following contents:\n; * headline\n; CLOSED: [2019-01-01 Tue 00:00]\n; :PROPERTIES:\n; :Effort:    0:30\n; :END:\n; :LOGGING:\n; - Note taken on [2018-12-31 Mon 00:00] \\\\\n;   log note\n; :END:\n; :CLOCKING:\n; CLOCK: [2019-01-01 Tue 00:00]\n; :END:\n; contents\n\n(let ((config (list :log-into-drawer \"LOGGING\" :clock-into-drawer \"CLOCKING\")))\n  (org-ml->> (org-ml-parse-this-headline)\n    (org-ml-headline-set-supercontents config\n      `(:blank 0 :contents ,(list (org-ml-build-paragraph! \"new contents\"))))\n    (org-ml-to-trimmed-string)))\n ;; => \"* headline\n ;      new contents\"\n\n```\n\n#### org-ml-headline-map-supercontents `(config fun headline)`\n\nMap a function over the supercontents of **`headline`**.\n**`fun`** is a unary function that takes a supercontents list and\nreturns a modified supercontents list. See\n[`org-ml-headline-get-supercontents`](#org-ml-headline-get-supercontents-config-headline) for the meaning of **`config`** and\nthe structure of the supercontents list.\n\n```el\n;; Given the following contents:\n; * headline\n; CLOSED: [2019-01-01 Tue 00:00]\n; :PROPERTIES:\n; :Effort:    0:30\n; :END:\n; :LOGGING:\n; - Note taken on [2018-12-31 Mon 00:00] \\\\\n;   log note\n; :END:\n; :CLOCKING:\n; CLOCK: [2019-01-01 Tue 00:00]\n; :END:\n; contents\n\n(let ((config (list :log-into-drawer \"LOGGING\" :clock-into-drawer \"CLOCKING\")))\n  (org-ml->> (org-ml-parse-this-headline)\n    (org-ml-headline-map-supercontents* config\n      (org-ml-supercontents-map-contents*\n        (cons (org-ml-build-paragraph! \"new contents\") it)\n        it))\n    (org-ml-to-trimmed-string)))\n ;; => \"* headline\n ;      CLOSED: [2019-01-01 Tue 00:00]\n ;      :PROPERTIES:\n ;      :Effort:   0:30\n ;      :END:\n ;      :LOGGING:\n ;      - Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\n ;        log note\n ;      :END:\n ;      :CLOCKING:\n ;      CLOCK: [2019-01-01 Tue 00:00]\n ;      :END:\n ;      new contents\n ;      contents\"\n\n```\n\n#### org-ml-headline-get-logbook-items `(config headline)`\n\nReturn the logbook items of **`headline`**.\nSee [`org-ml-headline-get-supercontents`](#org-ml-headline-get-supercontents-config-headline) for the meaning of\n**`config`**. The returned items will be a flat list of item nodes,\nnot a plain-list node.\n\n```el\n;; Given the following contents:\n; * headline\n; CLOSED: [2019-01-01 Tue 00:00]\n; :PROPERTIES:\n; :Effort: 0:30\n; :END:\n; :LOGGING:\n; - Note taken on [2018-12-31 Mon 00:00] \\\\\n;   log note\n; :END:\n; :CLOCKING:\n; CLOCK: [2019-01-01 Tue 00:00]\n; :END:\n; contents\n\n(let ((config (list :log-into-drawer \"LOGGING\" :clock-into-drawer \"CLOCKING\")))\n  (->> (org-ml-parse-this-headline)\n    (org-ml-headline-get-logbook-items config)\n    (-map #'org-ml-to-trimmed-string)))\n ;; => '(\"- Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\n ;       log note\")\n\n```\n\n#### org-ml-headline-set-logbook-items `(config items headline)`\n\nSet the logbook items of **`headline`** to **`items`**.\nSee [`org-ml-headline-get-supercontents`](#org-ml-headline-get-supercontents-config-headline) for the meaning of\n**`config`**. **`items`** must be supplied as a flat list of valid logbook\nitem nodes, not as a plain-list node.\n\n```el\n;; Given the following contents:\n; * headline\n; CLOSED: [2019-01-01 Tue 00:00]\n; :PROPERTIES:\n; :Effort: 0:30\n; :END:\n; :LOGGING:\n; - Note taken on [2018-12-31 Mon 00:00] \\\\\n;   log note\n; :END:\n; :CLOCKING:\n; CLOCK: [2019-01-01 Tue 00:00]\n; :END:\n; contents\n\n(let ((config (list :log-into-drawer \"LOGGING\" :clock-into-drawer \"CLOCKING\")))\n  (org-ml->> (org-ml-parse-this-headline)\n    (org-ml-headline-set-logbook-items config nil)\n    (org-ml-to-trimmed-string)))\n ;; => \"* headline\n ;      CLOSED: [2019-01-01 Tue 00:00]\n ;      :PROPERTIES:\n ;      :Effort:   0:30\n ;      :END:\n ;      :CLOCKING:\n ;      CLOCK: [2019-01-01 Tue 00:00]\n ;      :END:\n ;      contents\"\n\n```\n\n#### org-ml-headline-map-logbook-items `(config fun headline)`\n\nMap a function over the logbook items of **`headline`**.\n**`fun`** is a unary function that takes a list of item nodes and\nreturns a modified list of item nodes. See\n[`org-ml-headline-get-supercontents`](#org-ml-headline-get-supercontents-config-headline) for the meaning of **`config`**.\n\n```el\n;; Given the following contents:\n; * headline\n; CLOSED: [2019-01-01 Tue 00:00]\n; :PROPERTIES:\n; :Effort: 0:30\n; :END:\n; :LOGGING:\n; - Note taken on [2018-12-31 Mon 00:00] \\\\\n;   log note\n; :END:\n; :CLOCKING:\n; CLOCK: [2019-01-01 Tue 00:00]\n; :END:\n; contents\n\n(let ((config (list :log-into-drawer \"LOGGING\" :clock-into-drawer \"CLOCKING\")))\n  (org-ml->> (org-ml-parse-this-headline)\n    (org-ml-headline-map-logbook-items* config\n      (--map\n        (org-ml-map-children*\n          (--map\n            (org-ml-map-children*\n              (--map-when (org-ml-is-type 'plain-text it) (upcase it) it)\n              it)\n            it)\n          it)\n        it))\n    (org-ml-to-trimmed-string)))\n ;; => \"* headline\n ;      CLOSED: [2019-01-01 Tue 00:00]\n ;      :PROPERTIES:\n ;      :Effort:   0:30\n ;      :END:\n ;      :LOGGING:\n ;      - NOTE TAKEN ON [2018-12-31 Mon 00:00] \\\\\\\\\n ;        LOG NOTE\n ;      :END:\n ;      :CLOCKING:\n ;      CLOCK: [2019-01-01 Tue 00:00]\n ;      :END:\n ;      contents\"\n\n```\n\n#### org-ml-headline-get-logbook-clocks `(config headline)`\n\nReturn the logbook clocks of **`headline`**.\nSee [`org-ml-headline-get-supercontents`](#org-ml-headline-get-supercontents-config-headline) for the meaning of\n**`config`**. The returned list will include clock nodes and maybe item\nnodes if :clock-out-notes is t in **`config`**.\n\n```el\n;; Given the following contents:\n; * headline\n; CLOSED: [2019-01-01 Tue 00:00]\n; :PROPERTIES:\n; :Effort: 0:30\n; :END:\n; :LOGGING:\n; - Note taken on [2018-12-31 Mon 00:00] \\\\\n;   log note\n; :END:\n; :CLOCKING:\n; CLOCK: [2019-01-01 Tue 00:00]\n; :END:\n; contents\n\n(let ((config (list :log-into-drawer \"LOGGING\" :clock-into-drawer \"CLOCKING\")))\n  (->> (org-ml-parse-this-headline)\n    (org-ml-headline-get-logbook-clocks config)\n    (-map #'org-ml-to-trimmed-string)))\n ;; => '(\"CLOCK: [2019-01-01 Tue 00:00]\")\n\n```\n\n#### org-ml-headline-set-logbook-clocks `(config clocks headline)`\n\nSet the logbook clocks of **`headline`** to **`clocks`**.\nSee [`org-ml-headline-get-supercontents`](#org-ml-headline-get-supercontents-config-headline) for the meaning of\n**`config`**. **`clocks`** must be supplied as a flat list of valid clock\nnodes and optionally item nodes if :clock-out-notes is t in\n**`config`**.\n\n```el\n;; Given the following contents:\n; * headline\n; CLOSED: [2019-01-01 Tue 00:00]\n; :PROPERTIES:\n; :Effort: 0:30\n; :END:\n; :LOGGING:\n; - Note taken on [2018-12-31 Mon 00:00] \\\\\n;   log note\n; :END:\n; :CLOCKING:\n; CLOCK: [2019-01-01 Tue 00:00]\n; :END:\n; contents\n\n(let ((config (list :log-into-drawer \"LOGGING\" :clock-into-drawer \"CLOCKING\")))\n  (org-ml->> (org-ml-parse-this-headline)\n    (org-ml-headline-set-logbook-clocks config nil)\n    (org-ml-to-trimmed-string)))\n ;; => \"* headline\n ;      CLOSED: [2019-01-01 Tue 00:00]\n ;      :PROPERTIES:\n ;      :Effort:   0:30\n ;      :END:\n ;      :LOGGING:\n ;      - Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\n ;        log note\n ;      :END:\n ;      contents\"\n\n```\n\n#### org-ml-headline-map-logbook-clocks `(config fun headline)`\n\nMap a function over the logbook clocks of **`headline`**.\n**`fun`** is a unary function that takes a list of clock nodes and\noptionally item nodes to represent the clock notes and returns a\nmodified list of said nodes. [`org-ml-headline-get-supercontents`](#org-ml-headline-get-supercontents-config-headline)\nfor the meaning of **`config`**.\n\n```el\n;; Given the following contents:\n; * headline\n; CLOSED: [2019-01-01 Tue 00:00]\n; :PROPERTIES:\n; :Effort: 0:30\n; :END:\n; :LOGGING:\n; - Note taken on [2018-12-31 Mon 00:00] \\\\\n;   log note\n; :END:\n; :CLOCKING:\n; CLOCK: [2019-01-01 Tue 00:00]\n; :END:\n; contents\n\n(let ((config (list :log-into-drawer \"LOGGING\" :clock-into-drawer \"CLOCKING\")))\n  (org-ml->> (org-ml-parse-this-headline)\n    (org-ml-headline-map-logbook-clocks* config\n      (--map (org-ml-map-property* :value (org-ml-timestamp-shift 1 'day it) it)\n        it))\n    (org-ml-to-trimmed-string)))\n ;; => \"* headline\n ;      CLOSED: [2019-01-01 Tue 00:00]\n ;      :PROPERTIES:\n ;      :Effort:   0:30\n ;      :END:\n ;      :LOGGING:\n ;      - Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\n ;        log note\n ;      :END:\n ;      :CLOCKING:\n ;      CLOCK: [2019-01-02 Wed 00:00]\n ;      :END:\n ;      contents\"\n\n```\n\n#### org-ml-headline-get-contents `(config headline)`\n\nReturn the contents of **`headline`**.\nContents is everything in the headline after the logbook and will\nbe returned as a flat list of nodes. See\n[`org-ml-headline-get-supercontents`](#org-ml-headline-get-supercontents-config-headline) for the meaning of **`config`**.\n\n```el\n;; Given the following contents:\n; * headline\n\n(->> (org-ml-parse-this-headline)\n  (org-ml-headline-get-contents\n    (list :log-into-drawer t :clock-into-drawer t :clock-out-notes t))\n  (-map #'org-ml-to-trimmed-string))\n ;; => nil\n\n;; Given the following contents:\n; * headline\n; something\n\n(->> (org-ml-parse-this-headline)\n  (org-ml-headline-get-contents\n    (list :log-into-drawer t :clock-into-drawer t :clock-out-notes t))\n  (-map #'org-ml-to-trimmed-string))\n ;; => '(\"something\")\n\n;; Given the following contents:\n; * headline\n; CLOSED: [2019-01-01 Tue 00:00]\n; :LOGBOOK:\n; - Note taken on [2018-12-31 Mon 00:00] \\\\\n;   log note\n; CLOCK: [2019-01-01 Tue 00:00]\n; :END:\n; \n; - not log\n\n(->> (org-ml-parse-this-headline)\n  (org-ml-headline-get-contents\n    (list :log-into-drawer t :clock-into-drawer t :clock-out-notes t))\n  (-map #'org-ml-to-trimmed-string))\n ;; => '(\"- not log\")\n\n;; Given the following contents:\n; * headline\n; CLOSED: [2019-01-01 Tue 00:00]\n; :LOGGING:\n; - Note taken on [2018-12-31 Mon 00:00] \\\\\n;   log note\n; :END:\n; :CLOCKING:\n; CLOCK: [2019-01-01 Tue 00:00]\n; :END:\n; \n; - not log\n\n(->> (org-ml-parse-this-headline)\n  (org-ml-headline-get-contents\n    (list :log-into-drawer \"LOGGING\" :clock-into-drawer \"CLOCKING\"))\n  (-map #'org-ml-to-trimmed-string))\n ;; => '(\"- not log\")\n\n```\n\n#### org-ml-headline-set-contents `(config contents headline)`\n\nSet the contents of **`headline`** to **`contents`**.\nContents is everything in the headline after the logbook, and\n**`contents`** must be a flat list of nodes. See\n[`org-ml-headline-get-supercontents`](#org-ml-headline-get-supercontents-config-headline) for the meaning of **`config`**.\n\n```el\n;; Given the following contents:\n; * headline\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-set-contents\n    (list :log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n    (list (org-ml-build-paragraph! \"I'm new\")))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      I'm new\"\n\n;; Given the following contents:\n; * headline\n; something\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-set-contents\n    (list :log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n    (list (org-ml-build-paragraph! \"I'm new\")))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      I'm new\"\n\n;; Given the following contents:\n; * headline\n; :LOGBOOK:\n; - Note taken on [2018-12-31 Mon 00:00] \\\\\n;   log1\n; :END:\n; something\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-set-contents\n    (list :log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n    (list (org-ml-build-paragraph! \"I'm new\")))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      :LOGBOOK:\n ;      - Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\n ;        log1\n ;      :END:\n ;      I'm new\"\n\n;; Given the following contents:\n; * headline\n; :LOGBOOK:\n; - Note taken on [2018-12-31 Mon 00:00] \\\\\n;   log1\n; :END:\n; something\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-set-contents\n    (list :log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n    nil)\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      :LOGBOOK:\n ;      - Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\n ;        log1\n ;      :END:\"\n\n```\n\n#### org-ml-headline-map-contents `(config fun headline)`\n\nMap a function over the contents of **`headline`**.\nContents is everything in the headline after the logbook. **`fun`** is\na unary function that takes a list of nodes representing the\ncontents and returns a modified list of nodes. See\n[`org-ml-headline-get-supercontents`](#org-ml-headline-get-supercontents-config-headline) for the meaning of **`config`**.\n\n```el\n;; Given the following contents:\n; * headline\n; something\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-map-contents*\n    (list :log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n    (cons (org-ml-build-paragraph! \"I'm new\") it))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      I'm new\n ;      something\"\n\n```\n\n#### org-ml-headline-logbook-append-item `(config item headline)`\n\nAppend **`item`** to the logbook of **`headline`**.\nSee [`org-ml-headline-get-supercontents`](#org-ml-headline-get-supercontents-config-headline) for the meaning of\n**`config`**. **`item`** must be a valid logbook item. The logbook will be\nstarted if it does not already exist, else **`item`** will be added in\nchronological order.\n\n```el\n;; Given the following contents:\n; * headline\n\n(let ((ut (- 1546300800 (car (current-time-zone)))))\n  (org-ml->> (org-ml-parse-this-headline)\n    (org-ml-headline-logbook-append-item\n      (list :log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n      (org-ml-build-log-note ut \"new note\"))\n    (org-ml-to-trimmed-string)))\n ;; => \"* headline\n ;      :LOGBOOK:\n ;      - Note taken on [2019-01-01 Tue 00:00] \\\\\\\\\n ;        new note\n ;      :END:\"\n\n;; Given the following contents:\n; * headline\n; :LOGBOOK:\n; - Note taken on [2018-12-31 Mon 00:00] \\\\\n;   old note\n; :END:\n\n(let ((ut (- 1546300800 (car (current-time-zone)))))\n  (org-ml->> (org-ml-parse-this-headline)\n    (org-ml-headline-logbook-append-item\n      (list :log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n      (org-ml-build-log-note ut \"new note\"))\n    (org-ml-to-trimmed-string)))\n ;; => \"* headline\n ;      :LOGBOOK:\n ;      - Note taken on [2019-01-01 Tue 00:00] \\\\\\\\\n ;        new note\n ;      - Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\n ;        old note\n ;      :END:\"\n\n;; Given the following contents:\n; * headline\n; :LOGGING:\n; - Note taken on [2018-12-31 Mon 00:00] \\\\\n;   old note\n; :END:\n; :CLOCKING:\n; CLOCK: [2112-01-01 Fri]\n; :END:\n\n(let ((ut (- 1546300800 (car (current-time-zone)))))\n  (org-ml->> (org-ml-parse-this-headline)\n    (org-ml-headline-logbook-append-item\n      (list :log-into-drawer \"LOGGING\" :clock-into-drawer \"CLOCKING\")\n      (org-ml-build-log-note ut \"new note\"))\n    (org-ml-to-trimmed-string)))\n ;; => \"* headline\n ;      :LOGGING:\n ;      - Note taken on [2019-01-01 Tue 00:00] \\\\\\\\\n ;        new note\n ;      - Note taken on [2018-12-31 Mon 00:00] \\\\\\\\\n ;        old note\n ;      :END:\n ;      :CLOCKING:\n ;      CLOCK: [2112-01-01 Fri]\n ;      :END:\"\n\n```\n\n#### org-ml-headline-logbook-append-open-clock `(config unixtime headline)`\n\nAppend an open clock to the logbook of **`headline`**.\nSee [`org-ml-headline-get-supercontents`](#org-ml-headline-get-supercontents-config-headline) for the meaning of\n**`config`**. **`unixtime`** will set the start time of the clock. The\nlogbook will be started if it does not already exist, else the\nnew clock will be added in chronological order.\n\n```el\n;; Given the following contents:\n; * headline\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-logbook-append-open-clock\n    (list :log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n    (- 1546300800 (car (current-time-zone))))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      :LOGBOOK:\n ;      CLOCK: [2019-01-01 Tue 00:00]\n ;      :END:\"\n\n;; Given the following contents:\n; * headline\n; :LOGBOOK:\n; - note taken on [2018-12-30 Sun 00:00]\n; :END:\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-logbook-append-open-clock\n    (list :log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n    (- 1546300800 (car (current-time-zone))))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      :LOGBOOK:\n ;      CLOCK: [2019-01-01 Tue 00:00]\n ;      - note taken on [2018-12-30 Sun 00:00]\n ;      :END:\"\n\n;; Given the following contents:\n; * headline\n; :LOGGING:\n; - note taken on [2018-12-30 Sun 00:00]\n; :END:\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-logbook-append-open-clock\n    (list :log-into-drawer \"LOGGING\" :clock-into-drawer \"CLOCKING\")\n    (- 1546300800 (car (current-time-zone))))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      :LOGGING:\n ;      - note taken on [2018-12-30 Sun 00:00]\n ;      :END:\n ;      :CLOCKING:\n ;      CLOCK: [2019-01-01 Tue 00:00]\n ;      :END:\"\n\n```\n\n#### org-ml-headline-logbook-close-open-clock `(config unixtime note headline)`\n\nClose an open clock to the logbook of **`headline`**.\nSee [`org-ml-headline-get-supercontents`](#org-ml-headline-get-supercontents-config-headline) for the meaning of\n**`config`**. **`unixtime`** will set the end time of the clock. This will\nonly close an open clock if it is the most recent clock; else it\nwill do nothing. **`note`** is a string representing the clock-out\nnote (or nil if not desired). Note that supplying a non-nil\nclock-note when it is not allowed by **`config`** will trigger an\nerror.\n\n```el\n;; Given the following contents:\n; * headline\n; :LOGBOOK:\n; - note taken on [2018-12-30 Sun 00:00]\n; :END:\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-logbook-close-open-clock\n    (list :log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n    (- 1546300800 (car (current-time-zone)))\n    nil)\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      :LOGBOOK:\n ;      - note taken on [2018-12-30 Sun 00:00]\n ;      :END:\"\n\n;; Given the following contents:\n; * headline\n; :LOGBOOK:\n; CLOCK: [2018-12-31 Mon 00:00]\n; - note taken on [2018-12-30 Sun 00:00]\n; :END:\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-logbook-close-open-clock\n    (list :log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n    (- 1546300800 (car (current-time-zone)))\n    nil)\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      :LOGBOOK:\n ;      CLOCK: [2018-12-31 Mon 00:00]--[2019-01-01 Tue 00:00] => 24:00\n ;      - note taken on [2018-12-30 Sun 00:00]\n ;      :END:\"\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-logbook-close-open-clock\n    (list :log-into-drawer t :clock-into-drawer t :clock-out-notes t)\n    (- 1546300800 (car (current-time-zone)))\n    \"new note\")\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      :LOGBOOK:\n ;      CLOCK: [2018-12-31 Mon 00:00]--[2019-01-01 Tue 00:00] => 24:00\n ;      - new note\n ;      - note taken on [2018-12-30 Sun 00:00]\n ;      :END:\"\n\n;; Given the following contents:\n; * headline\n; :LOGGING:\n; - note taken on [2018-12-30 Sun 00:00]\n; :END:\n; :CLOCKING:\n; CLOCK: [2018-12-31 Mon 00:00]\n; :END:\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-logbook-close-open-clock\n    (list :log-into-drawer\n      \"LOGGING\"\n      :clock-into-drawer\n      \"CLOCKING\"\n      :clock-out-notes\n      t)\n    (- 1546300800 (car (current-time-zone)))\n    nil)\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      :LOGGING:\n ;      - note taken on [2018-12-30 Sun 00:00]\n ;      :END:\n ;      :CLOCKING:\n ;      CLOCK: [2018-12-31 Mon 00:00]--[2019-01-01 Tue 00:00] => 24:00\n ;      :END:\"\n\n```\n\n#### org-ml-headline-logbook-convert-config `(config1 config2 headline)`\n\nConvert the logbook of **`headline`** to a new configuration.\n**`config1`** is the current config and **`config2`** is the target config.\nNote that any logbook nodes that are invalid under **`config1`** will\nbe silently dropped, and nodes which do not conform to **`config2`**\nwill trigger an error. See [`org-ml-headline-get-supercontents`](#org-ml-headline-get-supercontents-config-headline)\nfor the structure of both config lists.\n\n```el\n;; Given the following contents:\n; * headline\n; CLOCK: [2018-12-31 Mon 00:00]--[2019-01-01 Tue 00:00] => 24:00\n; - note taken on [2018-12-30 Sun 00:00]\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-logbook-convert-config nil\n    (list :log-into-drawer t :clock-into-drawer t))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      :LOGBOOK:\n ;      CLOCK: [2018-12-31 Mon 00:00]--[2019-01-01 Tue 00:00] => 24:00\n ;      - note taken on [2018-12-30 Sun 00:00]\n ;      :END:\"\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-logbook-convert-config nil\n    (list :log-into-drawer \"LOGGING\" :clock-into-drawer \"CLOCKING\"))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      :LOGGING:\n ;      - note taken on [2018-12-30 Sun 00:00]\n ;      :END:\n ;      :CLOCKING:\n ;      CLOCK: [2018-12-31 Mon 00:00]--[2019-01-01 Tue 00:00] => 24:00\n ;      :END:\"\n\n;; Given the following contents:\n; * headline\n; :LOGBOOK:\n; CLOCK: [2018-12-31 Mon 00:00]--[2019-01-01 Tue 00:00] => 24:00\n; - note taken on [2018-12-30 Sun 00:00]\n; :END:\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-logbook-convert-config\n    (list :log-into-drawer t :clock-into-drawer t)\n    (list :log-into-drawer \"LOGGING\" :clock-into-drawer \"CLOCKING\"))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline\n ;      :LOGGING:\n ;      - note taken on [2018-12-30 Sun 00:00]\n ;      :END:\n ;      :CLOCKING:\n ;      CLOCK: [2018-12-31 Mon 00:00]--[2019-01-01 Tue 00:00] => 24:00\n ;      :END:\"\n\n```\n\n\n### Headline (misc)\n\n#### org-ml-headline-get-path `(headline)`\n\nReturn tree path of **`headline`** node.\n\nThe return value is a list of headline titles (including that from\n**`headline`**) leading to the root node.\n\n```el\n;; Given the following contents:\n; * one\n; ** two\n; *** three\n\n(->> (org-ml-parse-this-subtree) (org-ml-headline-get-path))\n ;; => '(\"one\")\n\n(->> (org-ml-parse-this-subtree)\n  (org-ml-headline-get-subheadlines)\n  (car)\n  (org-ml-headline-get-subheadlines)\n  (car)\n  (org-ml-headline-get-path))\n ;; => '(\"one\" \"two\" \"three\")\n\n```\n\n#### org-ml-headline-update-item-statistics `(headline)`\n\nReturn **`headline`** node with updated statistics cookie via items.\n\nThe percent/fraction will be computed as the number of checked items\nover the number of items with checkboxes (non-checkbox items will\nnot be considered).\n\n```el\n;; Given the following contents:\n; * statistically significant [/]\n; - irrelevant data\n; - [ ] good data\n; - [X] bad data\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-update-item-statistics)\n  (org-ml-to-trimmed-string))\n ;; => \"* statistically significant [1/2]\n ;      - irrelevant data\n ;      - [ ] good data\n ;      - [X] bad data\"\n\n;; Given the following contents:\n; * statistically significant\n; - irrelevant data\n; - [ ] good data\n; - [X] bad data\n\n;; Do nothing if nothing to update\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-headline-update-item-statistics)\n  (org-ml-to-trimmed-string))\n ;; => \"* statistically significant\n ;      - irrelevant data\n ;      - [ ] good data\n ;      - [X] bad data\"\n\n```\n\n#### org-ml-headline-update-todo-statistics `(headline)`\n\nReturn **`headline`** node with updated statistics cookie via subheadlines.\n\nThe percent/fraction will be computed as the number of done\nsubheadlines over the number of todo subheadlines (eg non-todo\nsubheadlines will not be counted).\n\n```el\n;; Given the following contents:\n; * statistically significant [/]\n; ** irrelevant data\n; ** TODO good data\n; ** DONE bad data\n\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-headline-update-todo-statistics)\n  (org-ml-to-trimmed-string))\n ;; => \"* statistically significant [1/2]\n ;      ** irrelevant data\n ;      ** TODO good data\n ;      ** DONE bad data\"\n\n;; Given the following contents:\n; * statistically significant\n; ** irrelevant data\n; ** TODO good data\n; ** DONE bad data\n\n;; Do nothing if nothing to update\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-headline-update-todo-statistics)\n  (org-ml-to-trimmed-string))\n ;; => \"* statistically significant\n ;      ** irrelevant data\n ;      ** TODO good data\n ;      ** DONE bad data\"\n\n```\n\n#### org-ml-headline-demote-subheadline `(index headline)`\n\nReturn **`headline`** node with child headline at **`index`** demoted.\nUnlike [`org-ml-headline-demote-subtree`](#org-ml-headline-demote-subtree-index-headline) this will not demote the\ndemoted headline node's children.\n\n```el\n;; Given the following contents:\n; * one\n; ** two\n; ** three\n; *** four\n\n(org-ml->> (org-ml-parse-element-at 1)\n  (org-ml-headline-demote-subheadline 0)\n  (org-ml-to-trimmed-string))\nError\n\n(org-ml->> (org-ml-parse-element-at 1)\n  (org-ml-headline-demote-subheadline 1)\n  (org-ml-to-trimmed-string))\n ;; => \"* one\n ;      ** two\n ;      *** three\n ;      *** four\"\n\n```\n\n#### org-ml-headline-demote-subtree `(index headline)`\n\nReturn **`headline`** node with child headline at **`index`** demoted.\nUnlike [`org-ml-headline-demote-subheadline`](#org-ml-headline-demote-subheadline-index-headline) this will also demote the\ndemoted headline node's children.\n\n```el\n;; Given the following contents:\n; * one\n; ** two\n; ** three\n; *** four\n\n(org-ml->> (org-ml-parse-element-at 1)\n  (org-ml-headline-demote-subtree 1)\n  (org-ml-to-trimmed-string))\n ;; => \"* one\n ;      ** two\n ;      *** three\n ;      **** four\"\n\n```\n\n#### org-ml-headline-promote-subheadline `(index child-index headline)`\n\nReturn **`headline`** node with a child headline under **`index`** promoted.\nThe specific child headline to promote is selected by **`child-index`**.\n\n```el\n;; Given the following contents:\n; * one\n; ** two\n; ** three\n; *** four\n; *** four\n; *** four\n\n(org-ml->> (org-ml-parse-element-at 1)\n  (org-ml-headline-promote-subheadline 1 1)\n  (org-ml-to-trimmed-string))\n ;; => \"* one\n ;      ** two\n ;      ** three\n ;      *** four\n ;      ** four\n ;      *** four\"\n\n```\n\n#### org-ml-headline-promote-all-subheadlines `(index headline)`\n\nReturn **`headline`** node with all child headlines under **`index`** promoted.\n\n```el\n;; Given the following contents:\n; * one\n; ** two\n; ** three\n; *** four\n; *** four\n; *** four\n\n(org-ml->> (org-ml-parse-element-at 1)\n  (org-ml-headline-promote-all-subheadlines 1)\n  (org-ml-to-trimmed-string))\n ;; => \"* one\n ;      ** two\n ;      ** three\n ;      ** four\n ;      ** four\n ;      ** four\"\n\n```\n\n\n### Plain List\n\n#### org-ml-plain-list-set-type `(type plain-list)`\n\nReturn **`plain-list`** node with type property set to **`type`**.\n**`type`** is one of the symbols `unordered` or `ordered`.\n\n```el\n;; Given the following contents:\n; - [ ] one\n; - [X] two\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-plain-list-set-type 'ordered)\n  (org-ml-to-trimmed-string))\n ;; => \"1. [ ] one\n ;      2. [X] two\"\n\n;; Given the following contents:\n; 1. [ ] one\n; 2. [X] two\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-plain-list-set-type 'unordered)\n  (org-ml-to-trimmed-string))\n ;; => \"- [ ] one\n ;      - [X] two\"\n\n```\n\n#### org-ml-plain-list-indent-item `(index plain-list)`\n\nReturn **`plain-list`** node with child item at **`index`** indented.\nUnlike `org-ml-item-indent-item-tree` this will not indent the indented\nitem node's children.\n\n```el\n;; Given the following contents:\n; - one\n; - two\n;   - three\n; - four\n\n;; It makes no sense to indent the first item\n(org-ml->> (org-ml-parse-element-at 1)\n  (org-ml-plain-list-indent-item 0)\n  (org-ml-to-trimmed-string))\nError\n\n(org-ml->> (org-ml-parse-element-at 1)\n  (org-ml-plain-list-indent-item 1)\n  (org-ml-to-trimmed-string))\n ;; => \"- one\n ;        - two\n ;        - three\n ;      - four\"\n\n(org-ml->> (org-ml-parse-element-at 1)\n  (org-ml-plain-list-indent-item 2)\n  (org-ml-to-trimmed-string))\n ;; => \"- one\n ;      - two\n ;        - three\n ;        - four\"\n\n```\n\n#### org-ml-plain-list-indent-item-tree `(index plain-list)`\n\nReturn **`plain-list`** node with child item at **`index`** indented.\nUnlike `org-ml-item-indent-item` this will also indent the indented item\nnode's children.\n\n```el\n;; Given the following contents:\n; - one\n;   - one-ish\n; - two\n;   - three\n; - four\n\n(org-ml->> (org-ml-parse-element-at 1)\n  (org-ml-plain-list-indent-item-tree 1)\n  (org-ml-to-trimmed-string))\n ;; => \"- one\n ;        - one-ish\n ;        - two\n ;          - three\n ;      - four\"\n\n```\n\n#### org-ml-plain-list-outdent-item `(index child-index plain-list)`\n\nReturn **`plain-list`** node with a child item under **`index`** outdented.\nThe specific child item to outdent is selected by **`child-index`**.\n\n```el\n;; Given the following contents:\n; - one\n; - two\n;   - three\n;   - three\n;   - three\n; - four\n\n(org-ml->> (org-ml-parse-element-at 1)\n  (org-ml-plain-list-outdent-item 1 0)\n  (org-ml-to-trimmed-string))\n ;; => \"- one\n ;      - two\n ;      - three\n ;        - three\n ;        - three\n ;      - four\"\n\n(org-ml->> (org-ml-parse-element-at 1)\n  (org-ml-plain-list-outdent-item 1 1)\n  (org-ml-to-trimmed-string))\n ;; => \"- one\n ;      - two\n ;        - three\n ;      - three\n ;        - three\n ;      - four\"\n\n(org-ml->> (org-ml-parse-element-at 1)\n  (org-ml-plain-list-outdent-item 2 1)\n  (org-ml-to-trimmed-string))\n ;; => \"- one\n ;      - two\n ;        - three\n ;        - three\n ;        - three\n ;      - four\"\n\n```\n\n#### org-ml-plain-list-outdent-all-items `(index plain-list)`\n\nReturn **`plain-list`** node with all child items under **`index`** outdented.\n\n```el\n;; Given the following contents:\n; - one\n; - two\n;   - three\n;   - three\n;   - three\n; - four\n\n(org-ml->> (org-ml-parse-element-at 1)\n  (org-ml-plain-list-outdent-all-items 1)\n  (org-ml-to-trimmed-string))\n ;; => \"- one\n ;      - two\n ;      - three\n ;      - three\n ;      - three\n ;      - four\"\n\n(org-ml->> (org-ml-parse-element-at 1)\n  (org-ml-plain-list-outdent-all-items 2)\n  (org-ml-to-trimmed-string))\n ;; => \"- one\n ;      - two\n ;        - three\n ;        - three\n ;        - three\n ;      - four\"\n\n;; Given the following contents:\n; - one\n; - two\n;   - three\n;   - three\n;   - three\n;     - three-ish\n; - four\n\n(org-ml->> (org-ml-parse-element-at 1)\n  (org-ml-plain-list-outdent-all-items 1)\n  (org-ml-to-trimmed-string))\n ;; => \"- one\n ;      - two\n ;      - three\n ;      - three\n ;      - three\n ;        - three-ish\n ;      - four\"\n\n```\n\n\n### Table\n\n#### org-ml-table-get-cell `(row-index column-index table)`\n\nReturn table-cell node at **`row-index`** and **`column-index`** in **`table`** node.\nRule-type rows do not count toward row indices.\n\n```el\n;; Given the following contents:\n; | 1 | 2 | 3 |\n; |---+---+---|\n; | a | b | c |\n\n(->> (org-ml-parse-this-element)\n  (org-ml-table-get-cell 0 0)\n  (org-ml-get-children)\n  (car))\n ;; => \"1\"\n\n(->> (org-ml-parse-this-element)\n  (org-ml-table-get-cell 1 1)\n  (org-ml-get-children)\n  (car))\n ;; => \"b\"\n\n(->> (org-ml-parse-this-element)\n  (org-ml-table-get-cell -1 -1)\n  (org-ml-get-children)\n  (car))\n ;; => \"c\"\n\n```\n\n#### org-ml-table-delete-column `(column-index table)`\n\nReturn **`table`** node with column at **`column-index`** deleted.\n\n```el\n;; Given the following contents:\n; | a | b |\n; |---+---|\n; | c | d |\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-table-delete-column 0)\n  (org-ml-to-trimmed-string))\n ;; => \"| b |\n ;      |---|\n ;      | d |\"\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-table-delete-column 1)\n  (org-ml-to-trimmed-string))\n ;; => \"| a |\n ;      |---|\n ;      | c |\"\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-table-delete-column -1)\n  (org-ml-to-trimmed-string))\n ;; => \"| a |\n ;      |---|\n ;      | c |\"\n\n```\n\n#### org-ml-table-delete-row `(row-index table)`\n\nReturn **`table`** node with row at **`row-index`** deleted.\n\n```el\n;; Given the following contents:\n; | a | b |\n; |---+---|\n; | c | d |\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-table-delete-row 0)\n  (org-ml-to-trimmed-string))\n ;; => \"|---+---|\n ;      | c | d |\"\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-table-delete-row 1)\n  (org-ml-to-trimmed-string))\n ;; => \"| a | b |\n ;      | c | d |\"\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-table-delete-row -1)\n  (org-ml-to-trimmed-string))\n ;; => \"| a | b |\n ;      |---+---|\"\n\n```\n\n#### org-ml-table-insert-column! `(column-index column-text table)`\n\nReturn **`table`** node with **`column-text`** inserted at **`column-index`**.\n\n**`column-index`** is the index of the column and **`column-text`** is a list of\nstrings to be made into table-cells to be inserted following the same\nsyntax as [`org-ml-build-table-cell!`](#org-ml-build-table-cell-string).\n\n```el\n;; Given the following contents:\n; | a | b |\n; |---+---|\n; | c | d |\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-table-insert-column! 1 '(\"x\" \"y\"))\n  (org-ml-to-trimmed-string))\n ;; => \"| a | x | b |\n ;      |---+---+---|\n ;      | c | y | d |\"\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-table-insert-column! -1 '(\"x\" \"y\"))\n  (org-ml-to-trimmed-string))\n ;; => \"| a | b | x |\n ;      |---+---+---|\n ;      | c | d | y |\"\n\n```\n\n#### org-ml-table-insert-row! `(row-index row-text table)`\n\nReturn **`table`** node with **`row-text`** inserted at **`row-index`**.\n\n**`row-index`** is the index of the column and **`row-text`** is a list of strings\nto be made into table-cells to be inserted following the same syntax\nas [`org-ml-build-table-row!`](#org-ml-build-table-row-row-list).\n\n```el\n;; Given the following contents:\n; | a | b |\n; |---+---|\n; | c | d |\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-table-insert-row! 1 '(\"x\" \"y\"))\n  (org-ml-to-trimmed-string))\n ;; => \"| a | b |\n ;      | x | y |\n ;      |---+---|\n ;      | c | d |\"\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-table-insert-row! 2 '(\"x\" \"y\"))\n  (org-ml-to-trimmed-string))\n ;; => \"| a | b |\n ;      |---+---|\n ;      | x | y |\n ;      | c | d |\"\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-table-insert-row! -1 '(\"x\" \"y\"))\n  (org-ml-to-trimmed-string))\n ;; => \"| a | b |\n ;      |---+---|\n ;      | c | d |\n ;      | x | y |\"\n\n```\n\n#### org-ml-table-replace-cell! `(row-index column-index cell-text table)`\n\nReturn **`table`** node with a table-cell node replaced by **`cell-text`**.\n\nIf **`cell-text`** is a string, it will replace the children of the\ntable-cell at **`row-index`** and **`column-index`** in **`table`**. **`cell-text`** will be\nprocessed the same as the argument given to [`org-ml-build-table-cell!`](#org-ml-build-table-cell-string).\n\nIf **`cell-text`** is nil, it will set the cell to an empty string.\n\n```el\n;; Given the following contents:\n; | 1 | 2 |\n; |---+---|\n; | a | b |\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-table-replace-cell! 0 0 \"2\")\n  (org-ml-to-trimmed-string))\n ;; => \"| 2 | 2 |\n ;      |---+---|\n ;      | a | b |\"\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-table-replace-cell! 0 0 nil)\n  (org-ml-to-trimmed-string))\n ;; => \"|   | 2 |\n ;      |---+---|\n ;      | a | b |\"\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-table-replace-cell! -1 -1 \"B\")\n  (org-ml-to-trimmed-string))\n ;; => \"| 1 | 2 |\n ;      |---+---|\n ;      | a | B |\"\n\n```\n\n#### org-ml-table-replace-column! `(column-index column-text table)`\n\nReturn **`table`** node with the column at **`column-index`** replaced by **`column-text`**.\n\nIf **`column-text`** is a list of strings, it will replace the table-cells\nat **`column-index`**. Each member of **`column-text`** will be processed the\nsame as the argument given to [`org-ml-build-table-cell!`](#org-ml-build-table-cell-string).\n\nIf **`column-text`** is nil, it will clear all cells at **`column-index`**.\n\n```el\n;; Given the following contents:\n; | a | b |\n; |---+---|\n; | c | d |\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-table-replace-column! 0 '(\"A\" \"B\"))\n  (org-ml-to-trimmed-string))\n ;; => \"| A | b |\n ;      |---+---|\n ;      | B | d |\"\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-table-replace-column! 0 nil)\n  (org-ml-to-trimmed-string))\n ;; => \"|   | b |\n ;      |---+---|\n ;      |   | d |\"\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-table-replace-column! -1 '(\"A\" \"B\"))\n  (org-ml-to-trimmed-string))\n ;; => \"| a | A |\n ;      |---+---|\n ;      | c | B |\"\n\n```\n\n#### org-ml-table-replace-row! `(row-index row-text table)`\n\nReturn **`table`** node with the row at **`row-index`** replaced by **`row-text`**.\n\nIf **`row-text`** is a list of strings, it will replace the cells at\n**`row-index`**. Each member of **`row-text`** will be processed the same as\nthe argument given to [`org-ml-build-table-row!`](#org-ml-build-table-row-row-list).\n\nIf **`row-text`** is nil, it will clear all cells at **`row-index`**.\n\n```el\n;; Given the following contents:\n; | a | b |\n; |---+---|\n; | c | d |\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-table-replace-row! 0 '(\"A\" \"B\"))\n  (org-ml-to-trimmed-string))\n ;; => \"| A | B |\n ;      |---+---|\n ;      | c | d |\"\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-table-replace-row! 0 nil)\n  (org-ml-to-trimmed-string))\n ;; => \"|   |   |\n ;      |---+---|\n ;      | c | d |\"\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-table-replace-row! -1 '(\"A\" \"B\"))\n  (org-ml-to-trimmed-string))\n ;; => \"| a | b |\n ;      |---+---|\n ;      | A | B |\"\n\n```\n\n\n## Node Matching\n\n\nUse pattern-matching to selectively perform operations on nodes in trees.\n\n#### org-ml-match `(pattern node)`\n\nReturn a list of child nodes matching **`pattern`** in **`node`**.\n\n**`pattern`** is a list like `([slicer [x] [y]] [sub1 ...])`.\n\n`slicer` is an optional prefix to the pattern describing how many\nand which matches to return. If not given, all matches are\nreturned. Possible values are:\n\n- `:first` - return the first match\n- `:last` - return the last match\n- `:nth` `x` - return the nth match where `x` is an integer denoting\n    the index to return (starting at 0). `x` may be a negative number\n    to start counting at the end of the match list, in which case\n    -1 is the last index. Using 0 and -1 for `x` is equivalent to\n    using `:first` and `:last` respectively\n- `:sub` `x` `y` - return a sublist between indices `x` and `y`. `x` may\n    not be greater than `y`, and both must either be non-negative\n    integers or negative integers. In the case of negative\n    integers, the indices refer to the same counterparts as\n    described in `:nth`. If `x` and `y` are equal, this slicer has the\n    same behavior as `:nth`.\n\n`subx` denotes subpatterns that that match nodes in the parse tree.\nSubpatterns may either be wildcards or conditions.\n\nConditions match exactly one level of the node tree being\nsearched based on the node's type (the symbol returned by\n[`org-ml-get-type`](#org-ml-get-type-node-optional-anonymous)), properties (the value returned by\n[`org-ml-get-property`](#org-ml-get-property-prop-node) for a valid property keyword), and\nindex (the position of the node in the list returned by\n[`org-ml-get-children`](#org-ml-get-children-branch-node)). For index, both left indices (where zero\nrefers to the left end of the list) and right indices (where -1\nrefers to the right end of the list) are understood. Conditions\nmay either be atomic or compound, where compound conditions are\nthemselves composed of atomic or compound conditions.\n\nThe types of atomic conditions are:\n\n- `type` - match when the node's type is `eq` to `type` (a symbol)\n- `index` - match when the node's index is `=` to `index` (an\n    integer)\n- `(op index)` - match when `(op node-index index)` returns t. `op` is\n    one of `<`, `>`, `<=`, or `>=` and `node-index` is the index of\n    the node being evaluated\n- `(prop val)` - match nodes whose property `prop` (a keyword) is\n    `equal` to `val`; `val` is obtained by evaluating\n    [`org-ml-get-property`](#org-ml-get-property-prop-node) with `prop` and the current node; if `prop`\n    is invalid, an error will be thrown\n- `(:pred pred)` - match when `pred` evaluates to t; `pred` is a symbol\n    for a unary function that takes the current node as its\n    argument\n\nCompound conditions start with an operator followed by their\ncomponent conditions. The types of compound conditions are:\n\n- `(:and c1 c2 [c3 ...])` - match when all ``c`` are true\n- `(:or c1 c2 [c3 ...])` - match when at least one ``c`` is true\n- `(:not c)` - match when ``c`` is not true\n\nIn addition, `subx` may be a wildcard keyword or symbol. These are\nanalogous to the special characters found in `posix` extended\nregular expression syntax. Specifically, `[` and `]` correspond\nto `{` and `}` respectively and `:any` corresponds to the `.`\noperator. All other characters have the same meaning between this\nfunction and `posix` extended regular expressions.:\n\n- `:any` - always match exactly one node\n- `sub` `?` - match `sub` zero or once\n- `sub` `*` - match `sub` zero or more times\n- `sub` `+` - match `sub` one or more times\n- `sub` [`n`] - match `sub` `n` times\n- `sub` [`m` `n`] - match `sub` `m` to `n` times (inclusive); if `m` or `n` is\n    nil, this will match `\"at most `n` times\"` or `\"at least `m` times\"`\n    respectively\n- `(alt-a1 [alt-a2 ...] | alt-b1 [alt-b2 ...] [| ...])` - match\n    any of the `alt` expressions separated by `|` where `alt` is a list\n    of subpatterns as described above or nil to match nothing;\n    these expressions may be nested\n\nIf **`pattern`** is nil, return **`node`**. Likewise, if any wildcard\npatterns match the nil pattern, also return **`node`** along with\nanything else the wildcard matches. Examples of this would\nbe `(sub *)`, `(sub ?)`, and `((nil | sub))`.\n\nFor increased performance, this function (and all others that\nconsume a **`pattern`** parameter) can be memoized using\n`org-ml-memoize-match-patterns`. If nil, **`pattern`** is processed\ninto a lambda form for every function call. If t, the resulting\nlambda forms are cached for each unique **`pattern`**, running\ngeneration step only once if multiple instances of the same\n**`pattern`** are used. Note that `org-ml-memoize-match-patterns` is\nshared between all functions that consume a **`pattern`** parameter.\n\n```el\n;; Given the following contents:\n; * headline 1\n; ** TODO headline 2\n; stuff\n; - item 1\n; - item 2\n; - item 3\n; ** DONE headline 3\n; - item 4\n; - item 5\n; - item 6\n; ** TODO COMMENT headline 4\n; - item 7\n; - item 8\n; - item 9\n\n;; Match items (excluding the first) in headlines that are marked \"TODO\" and not\n;; commented. The :many keyword matches the section and plain-list nodes holding\n;; the items.\n(->> (org-ml-parse-this-subtree)\n  (org-ml-match\n    (quote\n      ((:and (:todo-keyword \"TODO\") (:commentedp nil)) :any\n        *\n        (:and item (> 0)))))\n  (-map #'org-ml-to-trimmed-string))\n ;; => '(\"- item 2\" \"- item 3\")\n\n;; Given the following contents:\n; *one* *two* *three* *four* *five* *six*\n\n;; Return all bold nodes\n(->> (org-ml-parse-this-element)\n  (org-ml-match '(bold))\n  (-map #'org-ml-to-trimmed-string))\n ;; => '(\"*one*\" \"*two*\" \"*three*\" \"*four*\" \"*five*\" \"*six*\")\n\n;; Return first bold node\n(->> (org-ml-parse-this-element)\n  (org-ml-match '(:first bold))\n  (-map #'org-ml-to-trimmed-string))\n ;; => '(\"*one*\")\n\n;; Return last bold node\n(->> (org-ml-parse-this-element)\n  (org-ml-match '(:last bold))\n  (-map #'org-ml-to-trimmed-string))\n ;; => '(\"*six*\")\n\n;; Return a select bold node\n(->> (org-ml-parse-this-element)\n  (org-ml-match '(:nth 2 bold))\n  (-map #'org-ml-to-trimmed-string))\n ;; => '(\"*three*\")\n\n;; Return a sublist of matched bold nodes\n(->> (org-ml-parse-this-element)\n  (org-ml-match '(:sub 1 3 bold))\n  (-map #'org-ml-to-trimmed-string))\n ;; => '(\"*two*\" \"*three*\" \"*four*\")\n\n```\n\n#### org-ml-match-delete `(pattern node)`\n\nReturn **`node`** without children matching **`pattern`**.\n\n**`pattern`** follows the same rules as [`org-ml-match`](#org-ml-match-pattern-node).\n\n```el\n;; Given the following contents:\n; * headline one\n; ** headline two\n; ** headline three\n; ** headline four\n\n;; Selectively delete headlines\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-match-delete '(headline))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline one\"\n\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-match-delete '(:first headline))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline one\n ;      ** headline three\n ;      ** headline four\"\n\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-match-delete '(:last headline))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline one\n ;      ** headline two\n ;      ** headline three\"\n\n```\n\n#### org-ml-match-extract `(pattern node)`\n\nRemove nodes matching **`pattern`** from **`node`**.\nReturn cons cell where the car is a list of all removed nodes and\nthe cdr is the modified **`node`**.\n\n**`pattern`** follows the same rules as [`org-ml-match`](#org-ml-match-pattern-node).\n\n```el\n;; Given the following contents:\n; pull me /under/\n\n(--> (org-ml-parse-this-element)\n  (org-ml-match-extract '(:any * italic) it)\n  (cons (-map #'org-ml-to-trimmed-string (car it))\n    (org-ml-to-trimmed-string (cdr it))))\n ;; => '((\"/under/\") . \"pull me\")\n\n```\n\n#### org-ml-match-map `(pattern fun node)`\n\nReturn **`node`** with **`fun`** applied to children matching **`pattern`**.\n**`fun`** is a unary function that takes a node and returns a new node\nwhich will replace the original.\n\n**`pattern`** follows the same rules as [`org-ml-match`](#org-ml-match-pattern-node).\n\n```el\n;; Given the following contents:\n; * headline one\n; ** TODO headline two\n; ** headline three\n; ** headline four\n\n;; Selectively mark headlines as DONE\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-match-map '(headline)\n    (lambda (it) (org-ml-set-property :todo-keyword \"DONE\" it)))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline one\n ;      ** DONE headline two\n ;      ** DONE headline three\n ;      ** DONE headline four\"\n\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-match-map* '(:first headline)\n    (org-ml-set-property :todo-keyword \"DONE\" it))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline one\n ;      ** DONE headline two\n ;      ** headline three\n ;      ** headline four\"\n\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-match-map '(:last headline)\n    (-partial #'org-ml-set-property :todo-keyword \"DONE\"))\n  (org-ml-to-trimmed-string))\n ;; => \"* headline one\n ;      ** TODO headline two\n ;      ** headline three\n ;      ** DONE headline four\"\n\n```\n\n#### org-ml-match-mapcat `(pattern fun node)`\n\nReturn **`node`** with **`fun`** applied to children matching **`pattern`**.\n**`fun`** is a unary function that takes a node and returns a list of new\nnodes which will be spliced in place of the original node.\n\n**`pattern`** follows the same rules as [`org-ml-match`](#org-ml-match-pattern-node).\n\n```el\n;; Given the following contents:\n; * one\n; ** two\n\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-match-mapcat* '(:first headline)\n    (list (org-ml-build-headline! :title-text \"1.5\" :level 2) it))\n  (org-ml-to-trimmed-string))\n ;; => \"* one\n ;      ** 1.5\n ;      ** two\"\n\n```\n\n#### org-ml-match-replace `(pattern node* node)`\n\nReturn **`node`** with **`node*`*** in place of children matching **`pattern`**.\n\n**`pattern`** follows the same rules as [`org-ml-match`](#org-ml-match-pattern-node).\n\n```el\n;; Given the following contents:\n; *1* 2 *3* 4 *5* 6 *7* 8 *9* 10\n\n(org-ml->> (org-ml-parse-this-element)\n  (org-ml-match-replace '(:any * bold) (org-ml-build-bold :post-blank 1 \"0\"))\n  (org-ml-to-trimmed-string))\n ;; => \"*0* 2 *0* 4 *0* 6 *0* 8 *0* 10\"\n\n```\n\n#### org-ml-match-insert-before `(pattern node* node)`\n\nReturn **`node`** with **`node*`*** inserted before children matching **`pattern`**.\n\n**`pattern`** follows the same rules as [`org-ml-match`](#org-ml-match-pattern-node).\n\n```el\n;; Given the following contents:\n; * one\n; ** two\n; ** three\n\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-match-insert-before '(headline)\n    (org-ml-build-headline! :title-text \"new\" :level 2))\n  (org-ml-to-trimmed-string))\n ;; => \"* one\n ;      ** new\n ;      ** two\n ;      ** new\n ;      ** three\"\n\n```\n\n#### org-ml-match-insert-after `(pattern node* node)`\n\nReturn **`node`** with **`node*`*** inserted after children matching **`pattern`**.\n\n**`pattern`** follows the same rules as [`org-ml-match`](#org-ml-match-pattern-node).\n\n```el\n;; Given the following contents:\n; * one\n; ** two\n; ** three\n\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-match-insert-after '(headline)\n    (org-ml-build-headline! :title-text \"new\" :level 2))\n  (org-ml-to-trimmed-string))\n ;; => \"* one\n ;      ** two\n ;      ** new\n ;      ** three\n ;      ** new\"\n\n```\n\n#### org-ml-match-insert-within `(pattern index node* node)`\n\nReturn **`node`** with **`node*`*** inserted at **`index`** in children matching **`pattern`**.\n\n**`pattern`** follows the same rules as [`org-ml-match`](#org-ml-match-pattern-node) with the exception\nthat **`pattern`** may be nil. In this case **`node*`*** will be inserted at **`index`**\nin the immediate, top level children of **`node`**.\n\n```el\n;; Given the following contents:\n; * one\n; ** two\n; ** three\n\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-match-insert-within '(headline)\n    0\n    (org-ml-build-headline! :title-text \"new\" :level 3))\n  (org-ml-to-trimmed-string))\n ;; => \"* one\n ;      ** two\n ;      *** new\n ;      ** three\n ;      *** new\"\n\n;; The nil pattern denotes top-level element\n(org-ml->> (org-ml-parse-this-subtree)\n  (org-ml-match-insert-within nil\n    1\n    (org-ml-build-headline! :title-text \"new\" :level 2))\n  (org-ml-to-trimmed-string))\n ;; => \"* one\n ;      ** two\n ;      ** new\n ;      ** three\"\n\n```\n\n#### org-ml-match-splice `(pattern nodes* node)`\n\nReturn **`node`** with **`nodes*`*** spliced in place of children matching **`pattern`**.\n**`nodes*`*** is a list of nodes.\n\n**`pattern`** follows the same rules as [`org-ml-match`](#org-ml-match-pattern-node).\n\n```el\n;; Given the following contents:\n; * one\n; ** two\n; ** three\n\n(let\n  ((L (list (org-ml-build-headline! :title-text \"new0\" :level 2) (org-ml-build-headline! :title-text \"new1\" :level 2))))\n  (org-ml->> (org-ml-parse-this-subtree)\n    (org-ml-match-splice '(0) L)\n    (org-ml-to-trimmed-string)))\n ;; => \"* one\n ;      ** new0\n ;      ** new1\n ;      ** three\"\n\n```\n\n#### org-ml-match-splice-before `(pattern nodes* node)`\n\nReturn **`node`** with **`nodes*`*** spliced before children matching **`pattern`**.\n**`nodes*`*** is a list of nodes.\n\n**`pattern`** follows the same rules as [`org-ml-match`](#org-ml-match-pattern-node).\n\n```el\n;; Given the following contents:\n; * one\n; ** two\n; ** three\n\n(let\n  ((L (list (org-ml-build-headline! :title-text \"new0\" :level 2) (org-ml-build-headline! :title-text \"new1\" :level 2))))\n  (org-ml->> (org-ml-parse-this-subtree)\n    (org-ml-match-splice-before '(0) L)\n    (org-ml-to-trimmed-string)))\n ;; => \"* one\n ;      ** new0\n ;      ** new1\n ;      ** two\n ;      ** three\"\n\n```\n\n#### org-ml-match-splice-after `(pattern nodes* node)`\n\nReturn **`node`** with **`nodes*`*** spliced after children matching **`pattern`**.\n**`nodes*`*** is a list of nodes.\n\n**`pattern`** follows the same rules as [`org-ml-match`](#org-ml-match-pattern-node).\n\n```el\n;; Given the following contents:\n; * one\n; ** two\n; ** three\n\n(let\n  ((L (list (org-ml-build-headline! :title-text \"new0\" :level 2) (org-ml-build-headline! :title-text \"new1\" :level 2))))\n  (org-ml->> (org-ml-parse-this-subtree)\n    (org-ml-match-splice-after '(0) L)\n    (org-ml-to-trimmed-string)))\n ;; => \"* one\n ;      ** two\n ;      ** new0\n ;      ** new1\n ;      ** three\"\n\n```\n\n#### org-ml-match-splice-within `(pattern index nodes* node)`\n\nReturn **`node`** with **`nodes*`*** spliced at **`index`** in children matching **`pattern`**.\n**`nodes*`*** is a list of nodes.\n\n**`pattern`** follows the same rules as [`org-ml-match`](#org-ml-match-pattern-node) with the exception\nthat **`pattern`** may be nil. In this case **`nodes*`*** will be inserted at **`index`**\nin the immediate, top level children of **`node`**.\n\n```el\n;; Given the following contents:\n; * one\n; ** two\n; ** three\n; *** four\n\n(let\n  ((L (list (org-ml-build-headline! :title-text \"new0\" :level 3) (org-ml-build-headline! :title-text \"new1\" :level 3))))\n  (org-ml->> (org-ml-parse-this-subtree)\n    (org-ml-match-splice-within '(headline) 0 L)\n    (org-ml-to-trimmed-string)))\n ;; => \"* one\n ;      ** two\n ;      *** new0\n ;      *** new1\n ;      ** three\n ;      *** new0\n ;      *** new1\n ;      *** four\"\n\n(let\n  ((L (list (org-ml-build-headline! :title-text \"new0\" :level 2) (org-ml-build-headline! :title-text \"new1\" :level 2))))\n  (org-ml->> (org-ml-parse-this-subtree)\n    (org-ml-match-splice-within nil 1 L)\n    (org-ml-to-trimmed-string)))\n ;; => \"* one\n ;      ** two\n ;      ** new0\n ;      ** new1\n ;      ** three\n ;      *** four\"\n\n```\n\n#### org-ml-match-do `(pattern fun node)`\n\nLike [`org-ml-match-map`](#org-ml-match-map-pattern-fun-node) but for side effects only.\n**`fun`** is a unary function that has side effects and is applied to the\nmatches from **`node`** using **`pattern`**. This function itself returns nil.\n\n**`pattern`** follows the same rules as [`org-ml-match`](#org-ml-match-pattern-node).\n\n```el\nno examples :(\n```\n\n\n## Buffer Side Effects\n\n\nMap node manipulations into buffers.\n\n\n### Insert\n\n#### org-ml-insert `(point node)`\n\nConvert **`node`** to a string and insert at **`point`** in the current buffer.\n**`node`** may be a node or a list of nodes. Return **`node`**.\n\n```el\n;; Given the following contents:\n; * one\n; \n\n;; Insert single node\n(->> (org-ml-build-headline! :title-text \"two\") (org-ml-insert (point-max)))\n ;; Output these buffer contents\n ;; $> \"* one\n ;      * two\"\n\n;; Insert multiple nodes\n(->> (org-ml-build-headline! :title-text \"two\")\n  (list (org-ml-build-headline! :title-text \"more\"))\n  (org-ml-insert (point-max)))\n ;; Output these buffer contents\n ;; $> \"* one\n ;      * more\n ;      * two\"\n\n;; Given the following contents:\n; a *game* or a /boy/\n\n(->> (org-ml-build-paragraph! \"we don't care if you're\")\n  (org-ml-insert (point-min)))\n ;; Output these buffer contents\n ;; $> \"we don't care if you're\n ;      a *game* or a /boy/\"\n\n```\n\n#### org-ml-insert-tail `(point node)`\n\nLike [`org-ml-insert`](#org-ml-insert-point-node) but insert **`node`** at **`point`** and move to end of insertion.\n\n```el\nno examples :(\n```\n\n\n### Update\n\n#### org-ml-update `(fun node)`\n\nReplace **`node`** in the current buffer with a new one.\n**`fun`** is a unary function that takes **`node`** and returns a modified node\nor list of nodes.\n\nThe modified **`node`** will be converted to a string and then compared\nto the old buffer string using the Myers diff algorithm. This has\nan average time complexity of `o`(`m`+`n`+`d`^2) where `m` and `n` are the\nlengths of the old and new strings respectively and `d` is the\nnumber of inserts or deletes required to change one into the\nother. At the cost of performance, only the parts of the buffer\nthat need to be modified will actually be changed, which is less\nlikely to disturb overlays and move the cursor (and is also more\nlike how org-mode's build-in imperative functions behave).\n\nIf one does not need this level of precision, use the function\n`org-ml~update` and supply nil for the `diff-mode` argument. This\nwill simply replace the old node's string representation with the\nmodified node's string in its entirety. This will likely be\nfaster but could destroy overlays (eg folding) and will\nreposition the cursor to the beginning of **`node`** if it is in the\nmiddle of **`node`**.\n\n```el\n;; Given the following contents:\n; * TODO win grammy\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-update (lambda (hl) (org-ml-set-property :todo-keyword \"DONE\" hl))))\n ;; Output these buffer contents\n ;; $> \"* DONE win grammy\"\n\n;; Given the following contents:\n; * win grammy [0/0]\n; - [ ] write punk song\n; - [ ] get new vocalist\n; - [ ] sell 2 singles\n\n(org-ml->> (org-ml-parse-this-headline)\n  (org-ml-update*\n    (->> (org-ml-match-map '(:any * item) #'org-ml-item-toggle-checkbox it)\n      (org-ml-headline-update-item-statistics))))\n ;; Output these buffer contents\n ;; $> \"* win grammy [3/3]\n ;      - [X] write punk song\n ;      - [X] get new vocalist\n ;      - [X] sell 2 singles\"\n\n```\n\n#### org-ml-update-object-at `(point fun)`\n\nUpdate object under **`point`** using **`fun`**.\n**`fun`** takes an object and returns a modified object\n\nThis function uses the Myers diff algorithm.\nSee [`org-ml-update`](#org-ml-update-fun-node) for what this means.\n\n```el\n;; Given the following contents:\n; [[http://example.com][desc]]\n\n(org-ml-update-object-at* (point)\n  (org-ml-set-property :path \"//buymoreram.com\" it))\n ;; Output these buffer contents\n ;; $> \"[[http://buymoreram.com][desc]]\"\n\n```\n\n#### org-ml-update-element-at `(point fun)`\n\nUpdate element under **`point`** using **`fun`**.\n**`fun`** takes an element and returns a modified element\n\nThis function uses the Myers diff algorithm.\nSee [`org-ml-update`](#org-ml-update-fun-node) for what this means.\n\n```el\n;; Given the following contents:\n; #+call: ktulu()\n\n(org-ml-update-element-at* (point)\n  (org-ml-set-properties\n    (list :call\n      \"cthulhu\"\n      :inside-header\n      '(:cache no)\n      :arguments\n      '(\"x=4\")\n      :end-header\n      '(:results html))\n    it))\n ;; Output these buffer contents\n ;; $> \"#+call: cthulhu[:cache no](x=4) :results html\"\n\n```\n\n#### org-ml-update-table-row-at `(point fun)`\n\nUpdate table-row under **`point`** using **`fun`**.\n**`fun`** takes an table-row and returns a modified table-row\n\nThis function uses the Myers diff algorithm.\nSee [`org-ml-update`](#org-ml-update-fun-node) for what this means.\n\n```el\n;; Given the following contents:\n; | a | b |\n\n(org-ml-update-table-row-at* (point)\n  (org-ml-map-children* (cons (org-ml-build-table-cell! \"0\") it) it))\n ;; Output these buffer contents\n ;; $> \"| 0 | a | b |\"\n\n```\n\n#### org-ml-update-item-at `(point fun)`\n\nUpdate item under **`point`** using **`fun`**.\n**`fun`** takes an item and returns a modified item\n\nThis function uses the Myers diff algorithm.\nSee [`org-ml-update`](#org-ml-update-fun-node) for what this means.\n\n```el\n;; Given the following contents:\n; - [ ] thing\n\n(org-ml-update-item-at* (point) (org-ml-item-toggle-checkbox it))\n ;; Output these buffer contents\n ;; $> \"- [X] thing\"\n\n```\n\n#### org-ml-update-headline-at `(point fun)`\n\nUpdate headline under **`point`** using **`fun`**.\n**`fun`** takes an headline and returns a modified headline\n\nThis function uses the Myers diff algorithm.\nSee [`org-ml-update`](#org-ml-update-fun-node) for what this means.\n\n```el\n;; Given the following contents:\n; * TODO might get done\n; * DONE no need to update\n\n(org-ml-update-headline-at* (point)\n  (org-ml-set-property :todo-keyword \"DONE\" it))\n ;; Output these buffer contents\n ;; $> \"* DONE might get done\n ;      * DONE no need to update\"\n\n```\n\n#### org-ml-update-subtree-at `(point fun)`\n\nUpdate subtree under **`point`** using **`fun`**.\n**`fun`** takes an subtree and returns a modified subtree\n\nThis function uses the Myers diff algorithm.\nSee [`org-ml-update`](#org-ml-update-fun-node) for what this means.\n\n```el\n;; Given the following contents:\n; * one\n; ** two\n; ** three\n; * not updated\n\n(org-ml-update-subtree-at* (point) (org-ml-headline-demote-subheadline 1 it))\n ;; Output these buffer contents\n ;; $> \"* one\n ;      ** two\n ;      *** three\n ;      * not updated\"\n\n```\n\n#### org-ml-update-section-at `(point fun)`\n\nUpdate section under **`point`** using **`fun`**.\n**`fun`** takes an section and returns a modified section\n\nThis function uses the Myers diff algorithm.\nSee [`org-ml-update`](#org-ml-update-fun-node) for what this means.\n\n```el\n;; Given the following contents:\n; #+key1: VAL1\n; #+key2: VAL2\n; * irrelevant headline\n\n;; Update the top buffer section before the headlines start\n(org-ml-update-section-at* (point)\n  (org-ml-map-children* (--map (org-ml-map-property :value #'s-downcase it) it)\n    it))\n ;; Output these buffer contents\n ;; $> \"#+key1: val1\n ;      #+key2: val2\n ;      * irrelevant headline\"\n\n```\n\n#### org-ml-update-headlines `(which fun)`\n\nUpdate some headlines in the current using **`fun`**.\n\nSee [`org-ml-parse-headlines`](#org-ml-parse-headlines-which) for the meaning of **`which`**.\n\nHeadlines are updated using `org-ml~update` with `diff-arg` set to\nnil (see this for use and meaning of **`fun`**).\n\n```el\n;; Given the following contents:\n; * one\n; * two\n; * three\n\n(org-ml-update-headlines* 0 (org-ml-set-property :todo-keyword \"DONE\" it))\n ;; Output these buffer contents\n ;; $> \"* DONE one\n ;      * two\n ;      * three\"\n\n(org-ml-update-headlines* '(0 1)\n  (org-ml-set-property :todo-keyword \"DONE\" it))\n ;; Output these buffer contents\n ;; $> \"* DONE one\n ;      * DONE two\n ;      * three\"\n\n(org-ml-update-headlines* [2 nil]\n  (org-ml-set-property :todo-keyword \"DONE\" it))\n ;; Output these buffer contents\n ;; $> \"* one\n ;      * DONE two\n ;      * DONE three\"\n\n(org-ml-update-headlines* [2 10]\n  (org-ml-set-property :todo-keyword \"DONE\" it))\n ;; Output these buffer contents\n ;; $> \"* one\n ;      * DONE two\n ;      * three\"\n\n;; Given the following contents:\n; * one\n; * two\n; * three\n\n(org-ml-update-headlines* 'all (org-ml-set-property :todo-keyword \"DONE\" it))\n ;; Output these buffer contents\n ;; $> \"* DONE one\n ;      * DONE two\n ;      * DONE three\"\n\n```\n\n#### org-ml-update-subtrees `(which fun)`\n\nUpdate some toplevel subtrees in the current buffer using **`fun`**.\n\nSee [`org-ml-parse-subtrees`](#org-ml-parse-subtrees-which) for the meaning of **`which`**.\n\nSubtrees are updated using `org-ml~update` with `diff-arg` set to\nnil (see this for use and meaning of **`fun`**).\n\n```el\n;; Given the following contents:\n; * one [/]\n; ** DONE _one\n; * two [/]\n; ** DONE _one\n; * three [/]\n; ** DONE _one\n\n(org-ml-update-subtrees* 0 (org-ml-headline-update-todo-statistics it))\n ;; Output these buffer contents\n ;; $> \"* one [1/1]\n ;      ** DONE _one\n ;      * two [/]\n ;      ** DONE _one\n ;      * three [/]\n ;      ** DONE _one\"\n\n(org-ml-update-subtrees* '(0 1) (org-ml-headline-update-todo-statistics it))\n ;; Output these buffer contents\n ;; $> \"* one [1/1]\n ;      ** DONE _one\n ;      * two [1/1]\n ;      ** DONE _one\n ;      * three [/]\n ;      ** DONE _one\"\n\n(org-ml-update-subtrees* [2 nil] (org-ml-headline-update-todo-statistics it))\n ;; Output these buffer contents\n ;; $> \"* one [/]\n ;      ** DONE _one\n ;      * two [1/1]\n ;      ** DONE _one\n ;      * three [1/1]\n ;      ** DONE _one\"\n\n(org-ml-update-subtrees* [nil 5] (org-ml-headline-update-todo-statistics it))\n ;; Output these buffer contents\n ;; $> \"* one [1/1]\n ;      ** DONE _one\n ;      * two [/]\n ;      ** DONE _one\n ;      * three [/]\n ;      ** DONE _one\"\n\n;; Given the following contents:\n; * one [/]\n; ** DONE _one\n; ** DONE _two\n; * two [/]\n; ** DONE _one\n; ** DONE _two\n\n```\n\n#### org-ml-update-supercontents `(config which fun)`\n\nUpdate some headline supercontents in the current using **`fun`**.\n\nSee [`org-ml-parse-headlines`](#org-ml-parse-headlines-which) for the meaning of **`which`**.\n\nHeadlines are updated using `org-ml~update` with `diff-arg` set to\nnil (see this for use and meaning of **`fun`**).\n\n```el\n;; Given the following contents:\n; * one\n\n(let ((pl '(:scheduled (2000 1 1))))\n  (org-ml-wrap-impure\n    (org-ml-update-supercontents* nil\n      'all\n      (org-ml-supercontents-set-planning pl it))))\n ;; Output these buffer contents\n ;; $> \"* one\n ;      SCHEDULED: <2000-01-01 Sat>\"\n\n;; Given the following contents:\n; * one\n; \n; something\n\n(let ((pl '(:scheduled (2000 1 1))))\n  (org-ml-wrap-impure\n    (org-ml-update-supercontents* nil\n      'all\n      (org-ml-supercontents-set-planning pl it))))\n ;; Output these buffer contents\n ;; $> \"* one\n ;      SCHEDULED: <2000-01-01 Sat>\n ;      \n ;      something\"\n\n;; Given the following contents:\n; * one\n; ** two\n\n(let ((pl '(:scheduled (2000 1 1))))\n  (org-ml-wrap-impure\n    (org-ml-update-supercontents* nil\n      'all\n      (org-ml-supercontents-set-planning pl it))))\n ;; Output these buffer contents\n ;; $> \"* one\n ;      SCHEDULED: <2000-01-01 Sat>\n ;      ** two\n ;      SCHEDULED: <2000-01-01 Sat>\"\n\n;; Given the following contents:\n; * one\n; ** two\n; stuff\n\n(let ((pl '(:scheduled (2000 1 1))))\n  (org-ml-wrap-impure\n    (org-ml-update-supercontents* nil\n      'all\n      (org-ml-supercontents-set-planning pl it))))\n ;; Output these buffer contents\n ;; $> \"* one\n ;      SCHEDULED: <2000-01-01 Sat>\n ;      ** two\n ;      SCHEDULED: <2000-01-01 Sat>\n ;      stuff\"\n\n;; Given the following contents:\n; * one\n; stuff\n\n(let ((pl '(:scheduled (2000 1 1))))\n  (org-ml-wrap-impure\n    (org-ml-update-supercontents* nil\n      'all\n      (org-ml-supercontents-set-planning pl it))))\n ;; Output these buffer contents\n ;; $> \"* one\n ;      SCHEDULED: <2000-01-01 Sat>\n ;      stuff\"\n\n```\n\n\n### Misc\n\n#### org-ml-fold `(node)`\n\nFold the children of **`node`** if they exist.\n\n```el\nno examples :(\n```\n\n#### org-ml-unfold `(node)`\n\nUnfold the children of **`node`** if they exist.\n\n```el\nno examples :(\n```\nVersion: 5.8.8"
  },
  {
    "path": "docs/cookbook.md",
    "content": "# org-ml cookbook\n\nThe following are a list of common use cases and formulationsfor `org-ml`. If a function is not available straight from theAPI it may be here.\n\n## Adding created time\n\nThis will add a property called CREATED with a timestamp (which could be modified to hold the current time)..\n\n```el\n;; Given the following contents:\n; * headine\n\n(let ((ts (org-ml-to-string (org-ml-build-timestamp! '(2020 1 1 0 0)))))\n  (->> (org-ml-parse-this-headline)\n    (org-ml-headline-set-node-property \"CREATED\" ts)\n    (org-ml-to-string)))\n ;; => \"* headline\n ;      :PROPERTIES:\n ;      :CREATED:  [2020-01-01 Wed 00:00]\n ;      :END:\"\n```\n"
  },
  {
    "path": "env-28.2.yml",
    "content": "name: org-ml-28.2\nchannels:\n  - conda-forge\ndependencies:\n  - _libgcc_mutex=0.1=conda_forge\n  - _openmp_mutex=4.5=2_gnu\n  - adwaita-icon-theme=46.2=unix_0\n  - at-spi2-atk=2.38.0=h0630a04_3\n  - at-spi2-core=2.40.3=h0630a04_0\n  - atk-1.0=2.38.0=h04ea711_2\n  - bzip2=1.0.8=hd590300_5\n  - ca-certificates=2024.6.2=hbcca054_0\n  - cairo=1.18.0=h3faef2a_0\n  - dbus=1.13.6=h5008d03_3\n  - emacs=28.2=h41efbed_4\n  - epoxy=1.5.10=h166bdaf_1\n  - expat=2.6.2=h59595ed_0\n  - font-ttf-dejavu-sans-mono=2.37=hab24e00_0\n  - font-ttf-inconsolata=3.000=h77eed37_0\n  - font-ttf-source-code-pro=2.038=h77eed37_0\n  - font-ttf-ubuntu=0.83=h77eed37_2\n  - fontconfig=2.14.2=h14ed4e7_0\n  - fonts-conda-ecosystem=1=0\n  - fonts-conda-forge=1=0\n  - freetype=2.12.1=h267a509_2\n  - fribidi=1.0.10=h36c2ea0_0\n  - gdk-pixbuf=2.42.10=h6b639ba_2\n  - gettext=0.22.5=h59595ed_2\n  - gettext-tools=0.22.5=h59595ed_2\n  - giflib=5.2.2=hd590300_0\n  - glib=2.80.2=h8a4344b_1\n  - glib-tools=2.80.2=h73ef956_1\n  - gmp=6.3.0=h59595ed_1\n  - gnutls=3.7.9=hb077bed_0\n  - graphite2=1.3.13=h59595ed_1003\n  - gtk3=3.24.41=h280cfa0_0\n  - harfbuzz=8.5.0=hfac3d4d_0\n  - hicolor-icon-theme=0.17=ha770c72_2\n  - icu=73.2=h59595ed_0\n  - keyutils=1.6.1=h166bdaf_0\n  - krb5=1.21.2=h659d440_0\n  - ld_impl_linux-64=2.40=hf3520f5_7\n  - lerc=4.0.0=h27087fc_0\n  - libasprintf=0.22.5=h661eb56_2\n  - libasprintf-devel=0.22.5=h661eb56_2\n  - libcups=2.3.3=h4637d8d_4\n  - libdeflate=1.18=h0b41bf4_0\n  - libedit=3.1.20191231=he28a2e2_2\n  - libexpat=2.6.2=h59595ed_0\n  - libffi=3.4.2=h7f98852_5\n  - libgcc-ng=13.2.0=h77fa898_10\n  - libgettextpo=0.22.5=h59595ed_2\n  - libgettextpo-devel=0.22.5=h59595ed_2\n  - libglib=2.80.2=h8a4344b_1\n  - libgomp=13.2.0=h77fa898_10\n  - libiconv=1.17=hd590300_2\n  - libidn2=2.3.7=hd590300_0\n  - libjpeg-turbo=2.1.5.1=hd590300_1\n  - libnsl=2.0.1=hd590300_0\n  - libpng=1.6.43=h2797004_0\n  - librsvg=2.58.0=hadf69e7_1\n  - libsqlite=3.46.0=hde9e2c9_0\n  - libstdcxx-ng=13.2.0=hc0a3c3a_10\n  - libtasn1=4.19.0=h166bdaf_0\n  - libtiff=4.5.1=h8b53f26_1\n  - libunistring=0.9.10=h7f98852_0\n  - libuuid=2.38.1=h0b41bf4_0\n  - libwebp-base=1.4.0=hd590300_0\n  - libxcb=1.15=h0b41bf4_0\n  - libxcrypt=4.4.36=hd590300_1\n  - libxkbcommon=1.7.0=h662e7e4_0\n  - libxml2=2.12.7=hc051c1a_1\n  - libzlib=1.3.1=h4ab18f5_1\n  - ncurses=6.5=h59595ed_0\n  - nettle=3.9.1=h7ab15ed_0\n  - openssl=3.3.1=h4ab18f5_0\n  - p11-kit=0.24.1=hc5aa10d_0\n  - pango=1.54.0=h84a9a3c_0\n  - pcre2=10.44=h0f59acf_0\n  - pip=24.0=pyhd8ed1ab_0\n  - pixman=0.43.2=h59595ed_0\n  - pthread-stubs=0.4=h36c2ea0_1001\n  - python=3.12.4=h194c7f8_0_cpython\n  - readline=8.2=h8228510_1\n  - setuptools=70.0.0=pyhd8ed1ab_0\n  - tk=8.6.13=noxft_h4845f30_101\n  - tzdata=2024a=h0c530f3_0\n  - wayland=1.23.0=h5291e77_0\n  - wheel=0.43.0=pyhd8ed1ab_1\n  - xkeyboard-config=2.42=h4ab18f5_0\n  - xorg-compositeproto=0.4.2=h7f98852_1001\n  - xorg-damageproto=1.2.1=h7f98852_1002\n  - xorg-fixesproto=5.0=h7f98852_1002\n  - xorg-inputproto=2.3.2=h7f98852_1002\n  - xorg-kbproto=1.0.7=h7f98852_1002\n  - xorg-libice=1.1.1=hd590300_0\n  - xorg-libsm=1.2.4=h7391055_0\n  - xorg-libx11=1.8.9=h8ee46fc_0\n  - xorg-libxau=1.0.11=hd590300_0\n  - xorg-libxaw=1.0.14=h7f98852_1\n  - xorg-libxcomposite=0.4.6=h0b41bf4_1\n  - xorg-libxcursor=1.2.0=h0b41bf4_1\n  - xorg-libxdamage=1.1.5=h7f98852_1\n  - xorg-libxdmcp=1.1.3=h7f98852_0\n  - xorg-libxext=1.3.4=h0b41bf4_2\n  - xorg-libxfixes=5.0.3=h7f98852_1004\n  - xorg-libxft=2.3.8=hf69aa0a_0\n  - xorg-libxi=1.7.10=h7f98852_0\n  - xorg-libxinerama=1.1.5=h27087fc_0\n  - xorg-libxmu=1.1.3=h4ab18f5_1\n  - xorg-libxpm=3.5.17=hd590300_0\n  - xorg-libxrandr=1.5.2=h7f98852_1\n  - xorg-libxrender=0.9.11=hd590300_0\n  - xorg-libxt=1.3.0=hd590300_1\n  - xorg-libxtst=1.2.3=h7f98852_1002\n  - xorg-randrproto=1.5.0=h7f98852_1001\n  - xorg-recordproto=1.14.2=h7f98852_1002\n  - xorg-renderproto=0.11.1=h7f98852_1002\n  - xorg-util-macros=1.19.3=h7f98852_0\n  - xorg-xextproto=7.3.0=h0b41bf4_1003\n  - xorg-xineramaproto=1.2.1=h7f98852_1001\n  - xorg-xproto=7.0.31=h7f98852_1007\n  - xz=5.2.6=h166bdaf_0\n  - zlib=1.3.1=h4ab18f5_1\n  - zstd=1.5.6=ha6fb4c9_0\n"
  },
  {
    "path": "env-29.3.yml",
    "content": "name: org-ml-29.3\nchannels:\n  - conda-forge\ndependencies:\n  - _libgcc_mutex=0.1=conda_forge\n  - _openmp_mutex=4.5=2_gnu\n  - adwaita-icon-theme=46.2=unix_0\n  - at-spi2-atk=2.38.0=h0630a04_3\n  - at-spi2-core=2.40.3=h0630a04_0\n  - atk-1.0=2.38.0=h04ea711_2\n  - bzip2=1.0.8=hd590300_5\n  - ca-certificates=2024.6.2=hbcca054_0\n  - cairo=1.18.0=h3faef2a_0\n  - dbus=1.13.6=h5008d03_3\n  - emacs=29.3=hc93ec10_0\n  - epoxy=1.5.10=h166bdaf_1\n  - expat=2.6.2=h59595ed_0\n  - font-ttf-dejavu-sans-mono=2.37=hab24e00_0\n  - font-ttf-inconsolata=3.000=h77eed37_0\n  - font-ttf-source-code-pro=2.038=h77eed37_0\n  - font-ttf-ubuntu=0.83=h77eed37_2\n  - fontconfig=2.14.2=h14ed4e7_0\n  - fonts-conda-ecosystem=1=0\n  - fonts-conda-forge=1=0\n  - freetype=2.12.1=h267a509_2\n  - fribidi=1.0.10=h36c2ea0_0\n  - gdk-pixbuf=2.42.12=hb9ae30d_0\n  - gettext=0.22.5=h59595ed_2\n  - gettext-tools=0.22.5=h59595ed_2\n  - giflib=5.2.2=hd590300_0\n  - glib=2.80.2=h8a4344b_1\n  - glib-tools=2.80.2=h73ef956_1\n  - gmp=6.3.0=h59595ed_1\n  - gnutls=3.7.9=hb077bed_0\n  - graphite2=1.3.13=h59595ed_1003\n  - gtk3=3.24.42=h6d40eaa_0\n  - harfbuzz=8.5.0=hfac3d4d_0\n  - hicolor-icon-theme=0.17=ha770c72_2\n  - icu=73.2=h59595ed_0\n  - jansson=2.14=h0b41bf4_1\n  - keyutils=1.6.1=h166bdaf_0\n  - krb5=1.21.2=h659d440_0\n  - ld_impl_linux-64=2.40=hf3520f5_7\n  - lerc=4.0.0=h27087fc_0\n  - libasprintf=0.22.5=h661eb56_2\n  - libasprintf-devel=0.22.5=h661eb56_2\n  - libcups=2.3.3=h4637d8d_4\n  - libdeflate=1.20=hd590300_0\n  - libedit=3.1.20191231=he28a2e2_2\n  - libexpat=2.6.2=h59595ed_0\n  - libffi=3.4.2=h7f98852_5\n  - libgcc-ng=13.2.0=h77fa898_10\n  - libgettextpo=0.22.5=h59595ed_2\n  - libgettextpo-devel=0.22.5=h59595ed_2\n  - libglib=2.80.2=h8a4344b_1\n  - libgomp=13.2.0=h77fa898_10\n  - libiconv=1.17=hd590300_2\n  - libidn2=2.3.7=hd590300_0\n  - libjpeg-turbo=3.0.0=hd590300_1\n  - libnsl=2.0.1=hd590300_0\n  - libpng=1.6.43=h2797004_0\n  - librsvg=2.58.1=hadf69e7_0\n  - libsqlite=3.46.0=hde9e2c9_0\n  - libstdcxx-ng=13.2.0=hc0a3c3a_10\n  - libtasn1=4.19.0=h166bdaf_0\n  - libtiff=4.6.0=h1dd3fc0_3\n  - libtree-sitter=0.20.8=hd590300_0\n  - libunistring=0.9.10=h7f98852_0\n  - libuuid=2.38.1=h0b41bf4_0\n  - libwebp-base=1.4.0=hd590300_0\n  - libxcb=1.15=h0b41bf4_0\n  - libxcrypt=4.4.36=hd590300_1\n  - libxkbcommon=1.7.0=h662e7e4_0\n  - libxml2=2.12.7=hc051c1a_1\n  - libzlib=1.3.1=h4ab18f5_1\n  - ncurses=6.5=h59595ed_0\n  - nettle=3.9.1=h7ab15ed_0\n  - openssl=3.3.1=h4ab18f5_0\n  - p11-kit=0.24.1=hc5aa10d_0\n  - pango=1.54.0=h84a9a3c_0\n  - pcre2=10.44=h0f59acf_0\n  - pip=24.0=pyhd8ed1ab_0\n  - pixman=0.43.2=h59595ed_0\n  - pthread-stubs=0.4=h36c2ea0_1001\n  - python=3.12.4=h194c7f8_0_cpython\n  - readline=8.2=h8228510_1\n  - setuptools=70.0.0=pyhd8ed1ab_0\n  - tk=8.6.13=noxft_h4845f30_101\n  - tzdata=2024a=h0c530f3_0\n  - wayland=1.23.0=h5291e77_0\n  - wheel=0.43.0=pyhd8ed1ab_1\n  - xkeyboard-config=2.42=h4ab18f5_0\n  - xorg-compositeproto=0.4.2=h7f98852_1001\n  - xorg-damageproto=1.2.1=h7f98852_1002\n  - xorg-fixesproto=5.0=h7f98852_1002\n  - xorg-inputproto=2.3.2=h7f98852_1002\n  - xorg-kbproto=1.0.7=h7f98852_1002\n  - xorg-libice=1.1.1=hd590300_0\n  - xorg-libsm=1.2.4=h7391055_0\n  - xorg-libx11=1.8.9=h8ee46fc_0\n  - xorg-libxau=1.0.11=hd590300_0\n  - xorg-libxaw=1.0.14=h7f98852_1\n  - xorg-libxcomposite=0.4.6=h0b41bf4_1\n  - xorg-libxcursor=1.2.0=h0b41bf4_1\n  - xorg-libxdamage=1.1.5=h7f98852_1\n  - xorg-libxdmcp=1.1.3=h7f98852_0\n  - xorg-libxext=1.3.4=h0b41bf4_2\n  - xorg-libxfixes=5.0.3=h7f98852_1004\n  - xorg-libxft=2.3.8=hf69aa0a_0\n  - xorg-libxi=1.7.10=h7f98852_0\n  - xorg-libxinerama=1.1.5=h27087fc_0\n  - xorg-libxmu=1.1.3=h4ab18f5_1\n  - xorg-libxpm=3.5.17=hd590300_0\n  - xorg-libxrandr=1.5.2=h7f98852_1\n  - xorg-libxrender=0.9.11=hd590300_0\n  - xorg-libxt=1.3.0=hd590300_1\n  - xorg-libxtst=1.2.3=h7f98852_1002\n  - xorg-randrproto=1.5.0=h7f98852_1001\n  - xorg-recordproto=1.14.2=h7f98852_1002\n  - xorg-renderproto=0.11.1=h7f98852_1002\n  - xorg-util-macros=1.19.3=h7f98852_0\n  - xorg-xextproto=7.3.0=h0b41bf4_1003\n  - xorg-xineramaproto=1.2.1=h7f98852_1001\n  - xorg-xproto=7.0.31=h7f98852_1007\n  - xz=5.2.6=h166bdaf_0\n  - zlib=1.3.1=h4ab18f5_1\n  - zstd=1.5.6=ha6fb4c9_0\n"
  },
  {
    "path": "env-30.1.yml",
    "content": "name: org-ml-30.1\nchannels:\n  - conda-forge\ndependencies:\n  - _libgcc_mutex=0.1=conda_forge\n  - _openmp_mutex=4.5=2_gnu\n  - adwaita-icon-theme=48.0=unix_0\n  - at-spi2-atk=2.38.0=h0630a04_3\n  - at-spi2-core=2.40.3=h0630a04_0\n  - atk-1.0=2.38.0=h04ea711_2\n  - binutils=2.43=h4852527_4\n  - binutils_impl_linux-64=2.43=h4bf12b8_4\n  - bzip2=1.0.8=h4bc722e_7\n  - ca-certificates=2025.1.31=hbcca054_0\n  - cairo=1.18.4=h3394656_0\n  - dbus=1.13.6=h5008d03_3\n  - emacs=30.1=h9508cbc_4\n  - epoxy=1.5.10=h166bdaf_1\n  - expat=2.7.0=h5888daf_0\n  - font-ttf-dejavu-sans-mono=2.37=hab24e00_0\n  - font-ttf-inconsolata=3.000=h77eed37_0\n  - font-ttf-source-code-pro=2.038=h77eed37_0\n  - font-ttf-ubuntu=0.83=h77eed37_3\n  - fontconfig=2.15.0=h7e30c49_1\n  - fonts-conda-ecosystem=1=0\n  - fonts-conda-forge=1=0\n  - freetype=2.13.3=h48d6fc4_0\n  - fribidi=1.0.10=h36c2ea0_0\n  - gdk-pixbuf=2.42.12=hb9ae30d_0\n  - gettext=0.23.1=h5888daf_0\n  - gettext-tools=0.23.1=h5888daf_0\n  - giflib=5.2.2=hd590300_0\n  - glib=2.84.1=h07242d1_0\n  - glib-tools=2.84.1=h4833e2c_0\n  - gmp=6.3.0=hac33072_2\n  - gnutls=3.8.9=h5746830_0\n  - graphite2=1.3.13=h59595ed_1003\n  - gtk3=3.24.43=h0c6a113_5\n  - harfbuzz=11.0.0=h76408a6_0\n  - hicolor-icon-theme=0.17=ha770c72_2\n  - icu=75.1=he02047a_0\n  - jansson=2.14.1=hb9d3cd8_0\n  - kernel-headers_linux-64=3.10.0=he073ed8_18\n  - keyutils=1.6.1=h166bdaf_0\n  - krb5=1.21.3=h659f571_0\n  - ld_impl_linux-64=2.43=h712a8e2_4\n  - lerc=4.0.0=h27087fc_0\n  - libasprintf=0.23.1=h8e693c7_0\n  - libasprintf-devel=0.23.1=h8e693c7_0\n  - libcups=2.3.3=h4637d8d_4\n  - libdeflate=1.23=h4ddbbb0_0\n  - libedit=3.1.20250104=pl5321h7949ede_0\n  - libexpat=2.7.0=h5888daf_0\n  - libffi=3.4.6=h2dba641_1\n  - libgcc=14.2.0=h767d61c_2\n  - libgcc-ng=14.2.0=h69a702a_2\n  - libgettextpo=0.23.1=h5888daf_0\n  - libgettextpo-devel=0.23.1=h5888daf_0\n  - libglib=2.84.1=h2ff4ddf_0\n  - libgomp=14.2.0=h767d61c_2\n  - libiconv=1.18=h4ce23a2_1\n  - libidn2=2.3.8=ha4ef2c3_0\n  - libjpeg-turbo=3.0.0=hd590300_1\n  - liblzma=5.8.1=hb9d3cd8_0\n  - libmpdec=4.0.0=h4bc722e_0\n  - libpng=1.6.47=h943b412_0\n  - librsvg=2.58.4=he92a37e_3\n  - libsqlite=3.49.1=hee588c1_2\n  - libstdcxx=14.2.0=h8f9b012_2\n  - libstdcxx-ng=14.2.0=h4852527_2\n  - libtasn1=4.20.0=hb9d3cd8_0\n  - libtiff=4.7.0=hd9ff511_3\n  - libtree-sitter=0.25.3=hd0c01bc_0\n  - libunistring=0.9.10=h7f98852_0\n  - libuuid=2.38.1=h0b41bf4_0\n  - libwebp-base=1.5.0=h851e524_0\n  - libxcb=1.17.0=h8a09558_0\n  - libxkbcommon=1.8.1=hc4a0caf_0\n  - libxml2=2.13.7=h4bc477f_1\n  - libzlib=1.3.1=hb9d3cd8_2\n  - mpc=1.3.1=h24ddda3_1\n  - mpfr=4.2.1=h90cbb55_3\n  - ncurses=6.5=h2d0b736_3\n  - nettle=3.9.1=h7ab15ed_0\n  - openssl=3.4.1=h7b32b05_0\n  - p11-kit=0.24.1=hc5aa10d_0\n  - packaging=24.2=pyhd8ed1ab_2\n  - pango=1.56.3=h9ac818e_1\n  - pcre2=10.44=hba22ea6_2\n  - pip=25.0.1=pyh145f28c_0\n  - pixman=0.44.2=h29eaf8c_0\n  - pthread-stubs=0.4=hb9d3cd8_1002\n  - python=3.13.2=hf636f53_101_cp313\n  - python_abi=3.13=6_cp313\n  - readline=8.2=h8c095d6_2\n  - sysroot_linux-64=2.17=h0157908_18\n  - tk=8.6.13=noxft_h4845f30_101\n  - tzdata=2025b=h78e105d_0\n  - wayland=1.23.1=h3e06ad9_0\n  - xkeyboard-config=2.43=hb9d3cd8_0\n  - xorg-libice=1.1.2=hb9d3cd8_0\n  - xorg-libsm=1.2.6=he73a12e_0\n  - xorg-libx11=1.8.12=h4f16b4b_0\n  - xorg-libxau=1.0.12=hb9d3cd8_0\n  - xorg-libxaw=1.0.16=hb9d3cd8_0\n  - xorg-libxcomposite=0.4.6=hb9d3cd8_2\n  - xorg-libxcursor=1.2.3=hb9d3cd8_0\n  - xorg-libxdamage=1.1.6=hb9d3cd8_0\n  - xorg-libxdmcp=1.1.5=hb9d3cd8_0\n  - xorg-libxext=1.3.6=hb9d3cd8_0\n  - xorg-libxfixes=6.0.1=hb9d3cd8_0\n  - xorg-libxft=2.3.8=ha04879e_1\n  - xorg-libxi=1.8.2=hb9d3cd8_0\n  - xorg-libxinerama=1.1.5=h5888daf_1\n  - xorg-libxmu=1.2.1=hb9d3cd8_1\n  - xorg-libxpm=3.5.17=hb9d3cd8_1\n  - xorg-libxrandr=1.5.4=hb9d3cd8_0\n  - xorg-libxrender=0.9.12=hb9d3cd8_0\n  - xorg-libxt=1.3.1=hb9d3cd8_0\n  - xorg-libxtst=1.2.5=hb9d3cd8_3\n  - xorg-xorgproto=2024.1=hb9d3cd8_1\n  - zlib=1.3.1=hb9d3cd8_2\n  - zstd=1.5.7=hb8e6e7a_2\n"
  },
  {
    "path": "init.el",
    "content": "(defun fix-null-term (s)\n  \"Fix string S with extra wonky null terminators.\n\nFor whatever reason this affects certain strings in the conda\npackage for Emacs. These look like `blabla\\0\\0\\0\\0\\0\\0\\0`.\"\n  (declare (pure t) (side-effect-free t))\n  (save-match-data\n    (if (string-match \"\\0+\" s)\n        (replace-match \"\" t t s)\n      s)))\n\n;; HACK stuff won't install unless this string is fixed\n(if (< (round (string-to-number emacs-version)) 29)\n    (setq Info-default-directory-list\n          (cons\n           (fix-null-term (car Info-default-directory-list))\n           (cdr Info-default-directory-list)))\n  (setq configure-info-directory (fix-null-term configure-info-directory)))\n\n(setq package-enable-at-startup nil)\n\n(setq user-emacs-directory\n      (file-name-concat (expand-file-name \".emacs\") emacs-version))\n\n(defvar bootstrap-version)\n\n(let ((bootstrap-file\n       (file-name-concat\n        user-emacs-directory\n        \"straight/repos/straight.el/bootstrap.el\"))\n      (bootstrap-version 7))\n  (unless (file-exists-p bootstrap-file)\n    (with-current-buffer\n        (url-retrieve-synchronously\n         \"https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el\"\n         'silent 'inhibit-cookies)\n      (goto-char (point-max))\n      (eval-print-last-sexp)))\n  (load bootstrap-file nil 'nomessage))\n\n(straight-use-package 's)\n(straight-use-package 'dash)\n(straight-use-package 'buttercup)\n(straight-use-package 'org)\n\n(defun compile-target ()\n  \"Compile org-ml.\"\n  (byte-compile-file \"org-ml-macs.el\")\n  (byte-compile-file \"org-ml.el\"))\n"
  },
  {
    "path": "org-ml-macs.el",
    "content": ";;; org-ml-macs.el --- Macros for org-ml -*- lexical-binding: t; -*-\n\n;; Author: Nathan Dwarshuis <ndwar@yavin4.ch>\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\n;;; Commentary:\n\n;; This file contains macros essential for the `org-ml' package. The\n;; following functionality is implemented:\n;; - automatic anaphoric form generation (`org-ml--defun*'): this macro will\n;;   define an anaphoric form along with a regular function definition\n;; - defun with &rest + &keys support (`org-ml--defun-kw'): this macro allows\n;;   writing function definitions that accept &key and &rest arguments at the\n;;   same time, which `cl-defun' does not (and likely will never) support\n\n;;; Code:\n\n(require 'dash)\n(require 's)\n\n(eval-when-compile\n  (require 'cl-lib))\n\n;;; ANAPHORIC FUNCTIONS\n\n(defun org-ml--defun-partition-body (body)\n  \"Return ARGS as a list like (DOCSTRING DECLS BODY).\nDOCSTRING is the first string in BODY if present and it succeeded by\nmore forms. DECLS is a list of declarations in the DECLARE statement\nif present after the docstring. Everything else is BODY.\"\n  ;; macroexp-parse-body doesn't seem to retain declare\n  (cl-flet\n      ((is-declare\n        (form)\n        (eq 'declare (car form))))\n    (let ((first (car body))\n          (second (cadr body))\n          (rest (cddr body)))\n      (cond\n       ((and (stringp first) (is-declare second))\n        (list first (cdr second) rest))\n       ((and (stringp first) second)\n        (list first nil (cons second rest)))\n       ((and (is-declare first) second)\n        (list nil (cdr first) (cons second rest)))\n       (t\n        (list nil nil body))))))\n\n(defun org-ml--defun-make-indent-declare (decl pos)\n  \"Return declare form with indent set to POS if not present already.\nDECL is a list of declarations.\"\n  (let ((indent (or (assoc 'indent decl) `(indent ,pos)))\n        (decl (--remove (eq 'indent (car it)) decl)))\n    `(declare ,@decl ,indent)))\n\n(defun org-ml--defun-make-anaphoric-docstring (name docstring)\n  \"Return DOCSTRING adapted for anaphoric version of definition NAME.\nThis includes adding a short string to the front indicating it is an\nanaphoric version and replacing all instances of \\\"FUN\\\" with \\\"FORM\\\".\"\n  (let ((case-fold-search nil))\n    (->> (s-replace \"FUN\" \"FORM\" docstring)\n         (format \"Anaphoric form of `%s'.\\n\\n%s\" name))))\n\n(defmacro org-ml--defun* (name arglist &rest args)\n  \"Return a function definition for NAME, ARGLIST, and ARGS.\nThis will also make a mirrored anaphoric form macro definition. This\nassumes that `fun' represents a unary function which will be used\nsomewhere in the definition's body. When making the anaphoric form,\n`fun' will be replaced by the symbol `form', and `form' will be\nwrapped in a lambda call binding the unary argument to the symbol\n`it'.\"\n  (declare (doc-string 3) (indent 2))\n  (-let* (((docstring decls body) (org-ml--defun-partition-body args))\n          (name* (intern (format \"%s*\" name)))\n          (arglist* (-replace 'fun 'form arglist))\n          (docstring* (org-ml--defun-make-anaphoric-docstring name docstring))\n          (funargs (--map (if (eq it 'fun) '(lambda (it) (\\, form))\n                            (cons '\\, (list it)))\n                          arglist))\n          (body* (cdr (backquote-process (backquote (,name ,@funargs)))))\n          (debug* (->> arglist\n                       (--map (if (eq it 'fun) 'def-form 'form))\n                       (list 'debug)))\n          (dec (org-ml--defun-make-indent-declare\n                decls (-elem-index 'fun arglist)))\n          (dec* (org-ml--defun-make-indent-declare\n                 (cons debug* decls) (-elem-index 'fun arglist))))\n    `(progn\n       (defmacro ,name* ,arglist*\n         ,docstring*\n         ,dec*\n         ,body*)\n       (defun ,name ,arglist\n         ,docstring\n         ,dec\n         ,@body))))\n\n(defun org-ml--replace-funcall (sym form)\n  \"Replace all instances of (funcall fun X) with SYM in FORM.\"\n  (pcase form\n    (`(funcall fun it) (list '\\, sym))\n    (`(funcall fun ,f) (list 'let `((it ,f)) (list '\\, sym)))\n    ((pred consp) (--map (org-ml--replace-funcall sym it) form))\n    (f f)))\n\n(defun org-ml--get-let-symbols (let-syms form)\n  \"Return the symbols that should be bound in let forms from FORM.\nThe symbols to search for are LET-SYMS, and the returned list will\ncontain all symbols in LET-SYMS that appear more than once in\nFORM.\"\n  (->> (-tree-seq #'consp #'identity form)\n       (-filter #'symbolp)\n       (-remove #'keywordp)\n       (-remove #'fboundp)\n       (--filter (memq it let-syms))\n       (-group-by #'identity)\n       (--filter (< 1 (length (cdr it))))\n       (-map #'car)))\n\n(defun org-ml--replace-syms (let-syms privatize? form)\n  \"Replace symbols in FORM.\nThe symbols to replace are in LET-SYMS, and the value to replace\nthe symbol will with be (\\\\, SYM). If PRIVATIZE? is non-nil, also\nprivatize any sym along with replacing it.\"\n  (pcase form\n    ((pred consp) (--map (org-ml--replace-syms let-syms privatize? it) form))\n    ((pred symbolp) (if (not (memq form let-syms)) form\n                      (list '\\, (if (not privatize?) form\n                                  (org-ml--make-private-sym form)))))\n    (f f)))\n\n(defun org-ml--make-private-sym (sym)\n  \"Return SYM prefixed with two dashes.\"\n  (intern (format \"--%s\" sym)))\n\n(defun org-ml--make-anaphoric-form (arglist body)\n  \"Make an anaphoric from from BODY.\nARGLIST is the argument list from the non-anaphoric form.\"\n  (let* ((body* (->> (-map #'macroexpand-all body)\n                     (org-ml--replace-funcall 'form)))\n         (arglist* (-remove-item 'fun arglist)))\n    (-if-let (let-syms (org-ml--get-let-symbols arglist* body*))\n        (let* ((nonlet-syms (-difference arglist* let-syms))\n               (body** (->> (org-ml--replace-syms nonlet-syms nil body*)\n                            (org-ml--replace-syms let-syms t)))\n               (private-syms (-map #'org-ml--make-private-sym let-syms))\n               (mk-sym-forms (--map `(make-symbol ,(symbol-name it)) private-syms))\n               (outer-forms (--zip-with `(,it ,other) private-syms mk-sym-forms))\n               (inner-forms (--zip-with `(,(list '\\, it) ,(list '\\, other))\n                                        private-syms let-syms)))\n          `(let (,@outer-forms)\n             (backquote\n              (let (,@inner-forms)\n                ,@body**))))\n      (let ((body** (org-ml--replace-syms arglist* nil body*)))\n        `(backquote ,body**)))))\n\n(defmacro org-ml--defun-anaphoric* (name arglist &rest args)\n  \"Return a function definition for NAME, ARGLIST, and ARGS.\nThis will also make a mirrored anaphoric form macro definition. This\nassumes that `fun' represents a unary function which will be used\nsomewhere in the definition's body. When making the anaphoric form,\n`fun' will be replaced by the symbol `form', and `form' will be\nwrapped in a lambda call binding the unary argument to the symbol\n`it'.\"\n  (declare (doc-string 3) (indent 2))\n  (-let* (((docstring decls body) (org-ml--defun-partition-body args))\n          (dec (org-ml--defun-make-indent-declare\n                decls (-elem-index 'fun arglist)))\n          (name* (intern (format \"%s*\" name)))\n          (arglist* (-replace 'fun 'form arglist))\n          (docstring* (org-ml--defun-make-anaphoric-docstring name docstring))\n          (debug* (->> arglist\n                       (--map (if (eq it 'fun) 'def-form 'form))\n                       (list 'debug)))\n          (dec* (org-ml--defun-make-indent-declare\n                 (cons debug* decls) (-elem-index 'fun arglist)))\n          (body* (org-ml--make-anaphoric-form arglist body)))\n    `(progn\n       (defmacro ,name* ,arglist*\n         ,docstring*\n         ,dec*\n         ;; (backquote ,body*))\n         ,body*)\n       (defun ,name ,arglist\n         ,docstring\n         ,dec\n         ,@body))))\n\n;;; BETTER CL-DEFUN\n\n;; Some functions here require a clean way to use &rest and &key at the same\n;; time, which `cl-defun' does not do. For a given external function signature\n;; like (P1 ... &key K1 ... &rest R), this framework will make a function with\n;; the internal signature (P1 ... &rest --rest-args) where PX are positional\n;; arguments matching exactly those in the external signature and --rest-args\n;; will bind the list contain the key-val pairs and rest arguments. This will be\n;; partitioned into keyword arguments like KX VAL rest arguments R internally.\n\n(defun org-ml--symbol-to-keyword (symbol)\n  \"Convert SYMBOL to keyword if not already.\"\n  (if (keywordp symbol) symbol\n    (->> (symbol-name symbol)\n         (s-prepend \":\")\n         (intern))))\n\n(defun org-ml--process-pos-args (pos-args)\n  \"Process POS-ARGS and return if valid.\"\n  (if (--all? (or (symbolp it) (consp it)) pos-args) pos-args\n    (error \"Positional args must be either cons cells or symbols\")))\n\n(defun org-ml--process-rest-arg (rest-arg)\n  \"Process REST-ARG and return if valid.\"\n  (pcase rest-arg\n    (`(,(and (pred symbolp) sym) . nil) sym)\n    (`nil nil)\n    (_ (error \"Rest argument must only have one symbol\"))))\n\n(defun org-ml--make-kwarg-let (kws-sym kwarg)\n  \"Return cell for KWARG like (KW . LET-FORM).\nKWARG is a keyword argument in the signature of a function definition\n\\(see `org-ml--defun-kw' for valid configurations of this). In the returned\ncell, KW is keyword representing the key to be used in a function\ncall, and LET-FORM is a form to be used in a let binding that will\nretrieve the value for KW from a plist bound to KWS-SYM (which is\na non-interned symbol to be bound to the keywords in a function\ncall).\"\n  (cl-flet\n      ((make-plist\n        (arg init)\n        (let* ((kw (org-ml--symbol-to-keyword arg))\n               (kw-get `(cadr (plist-member ,kws-sym ',kw)))\n               (val (if init `(or ,kw-get ,init) kw-get)))\n          (cons kw `(,arg ,val)))))\n    (pcase kwarg\n      (`(,arg ,init)\n       (make-plist arg init))\n      ((and (pred symbolp) arg)\n       (make-plist arg nil))\n      (_ (error \"Invalid keyword argument: %s\" kwarg)))))\n\n(defmacro org-ml--throw-kw-error (msg kws)\n  \"Throw an error with MSG with formatted list of KWS.\"\n  `(when ,kws\n     (->> (-map #'symbol-name ,kws)\n          (s-join \", \")\n          (error (concat ,msg \": %s\")))))\n\n(defmacro org-ml--partition-rest-args (args)\n  \"Partition ARGS into two keyword and rest argument lists.\nThe keyword list is determined by partitioning all keyword-value\npairs until this pattern is broken. Whatever is left is put into the\nrest list. Return a list like (KEYARGS RESTARGS).\"\n  `(let ((rest ,args) acc-plist acc-keys)\n     (while (and rest (keywordp (car rest)))\n       (setq acc-plist `(,(cadr rest) ,(car rest) ,@acc-plist)\n             acc-keys (cons (car rest) acc-keys)\n             rest (cddr rest)))\n     (list (nreverse acc-keys) (nreverse acc-plist) rest)))\n\n(defmacro org-ml--make-rest-partition-form (argsym kws use-rest?)\n  \"Return a form that will partition the args in ARGSYM.\nARGSYM is a symbol which is bound to the rest argument list of a\nfunction call. KWS is a list of valid keywords to use when deciding\nwhich in the argument values is a keyword-value pair, and USE-REST?\nis a boolean that determines if rest arguments are to be considered.\"\n  ;; these `make-symbol' calls probably aren't necessary but they\n  ;; ensure the let bindings are leak-proof\n  (let* ((k (make-symbol \"--kpart\"))\n         (y (make-symbol \"--keys\"))\n         (r (make-symbol \"--rpart\"))\n         (inv-msg \"Invalid keyword(s) found\")\n         (dup-msg \"Keyword(s) used multiple times\")\n         (rest-msg\n          (s-join \" \" '(\"Keyword-value pairs must be immediately\"\n                        \"after positional arguments. These keywords\"\n                        \"were interpreted as rest arguments\")))\n         (tests\n          `((let (invalid unique dups)\n              (--each ,y\n                (if (memq it ',kws)\n                    (if (memq it unique)\n                        (!cons it dups)\n                      (!cons it unique))\n                  (if (memq it invalid)\n                      (!cons it dups)\n                    (!cons it invalid))))\n              (when invalid\n                (org-ml--throw-kw-error ,inv-msg invalid))\n              (when dups\n                (org-ml--throw-kw-error ,dup-msg dups)))\n            ;; ensure that keyword pairs are only used\n            ;; immediately after positional arguments\n            (->> (-filter #'keywordp ,r)\n                 (org-ml--throw-kw-error ,rest-msg))))\n         ;; if rest arguments are used but not allowed in function\n         ;; call, throw error\n         (tests (if use-rest? tests\n                  (-snoc\n                   tests\n                   `(when ,r\n                      (error \"Too many arguments supplied\")))))\n         ;; return a cons cell of (KEY REST) argument values or\n         ;; just KEY if rest is not used in the function call\n         (return (if (not use-rest?) k `(cons ,k ,r))))\n    `(-let (((,y ,k ,r) (org-ml--partition-rest-args ,argsym)))\n       ,@tests\n       ,return)))\n\n(defun org-ml--make-usage-args (arglist)\n  \"Return ARGLIST as it should appear in the usage signature.\nThis will uppercase all symbol names and remove all type keys.\"\n  (cl-flet*\n      ((ucase-sym\n        (sym)\n        (-> sym (symbol-name) (upcase) (make-symbol)))\n       (unwrap-form-maybe\n        (arg)\n        (ucase-sym (if (consp arg) (cadr arg) arg)))\n       (unwrap-kw-form-maybe\n        (arg)\n        (pcase arg\n          ;; ((PRED KEY) INITFORM)\n          (`((,(and (pred keywordp) _) ,arg) ,init)\n           (list (ucase-sym arg) init))\n          ;; ((PRED KEY))\n          (`((,(and (pred keywordp) _) ,arg))\n           (ucase-sym arg))\n          ;; (KEY INITFORM)\n          (`(,arg ,init)\n           (list (ucase-sym arg) init))\n          ;; KEY\n          ((and (pred symbolp) arg)\n           (ucase-sym arg))\n          (_ (error \"This shouldn't happen\")))))\n    (let* ((part(-partition-before-pred\n                 (lambda (it) (memq it '(&pos &rest &key)))\n                 (cons '&pos arglist)))\n           (pos (-some->> (alist-get '&pos part)\n                          (-map #'unwrap-form-maybe)))\n           (kw (-some->> (alist-get '&key part)\n                         (-map #'unwrap-kw-form-maybe)\n                         (cons '&key)))\n           (rest (-some->> (alist-get '&rest part)\n                           (-map #'unwrap-form-maybe)\n                           (cons '&rest))))\n      (append pos kw rest))))\n\n(defun org-ml--make-header (body arglist)\n  \"Return a header using docstring from BODY and ARGLIST.\"\n  (let ((header (caar (macroexp-parse-body body))))\n    ;; Macro expansion can take place in the middle of\n    ;; apparently harmless computation, so it should not\n    ;; touch the match-data.\n    (save-match-data\n      (let ((print-gensym nil)\n            (print-quoted t)\n            (print-escape-newlines t))\n        (->> (org-ml--make-usage-args arglist)\n             (cons 'fn)\n             (format \"%S\")\n             (help--docstring-quote)\n             (help-add-fundoc-usage header))))))\n\n(defun org-ml--transform-lambda (arglist body name)\n  \"Make a form for a keyword/rest composite function definition.\nARGLIST is the argument signature. BODY is the function body. NAME\nis the NAME of the function definition.\n\nThis acts much like `cl-defun' except that it only considers &rest\nand &key slots. The way the final function call will work beneath the\nsurface is that all positional arguments will be bound to their\nsymbols in ARGLIST (analogous to `defun' and `cl-defun'), and the key\nand rest arguments will be captured in one rest argument to be\npartitioned on the fly into key and rest bindings that can be used\nin BODY.\"\n  ;; assume &key will always be present if this function is called\n  (let* ((a (make-symbol \"--arg-cell\"))\n         (k (make-symbol \"--kw-args\"))\n         (kr (make-symbol \"--key-and-rest-args\"))\n         (partargs (-partition-before-pred\n                    (lambda (it) (memq it '(&pos &rest &key)))\n                    (cons '&pos arglist)))\n         (pos-args (->> (alist-get '&pos partargs)\n                        (org-ml--process-pos-args)))\n         (kw-alist (->> (alist-get '&key partargs)\n                        (--map (org-ml--make-kwarg-let k it))))\n         (rest-arg (->> (alist-get '&rest partargs)\n                        (org-ml--process-rest-arg)))\n         (kws (-map #'car kw-alist))\n         (kw-lets (-map #'cdr kw-alist))\n         (arg-form `(,@pos-args &rest ,kr))\n         (header (org-ml--make-header body arglist))\n         (let-forms\n          (if rest-arg\n              `((,a (org-ml--make-rest-partition-form ,kr ,kws t))\n                (,k (car ,a))\n                (,rest-arg (cdr ,a))\n                ,@kw-lets)\n            `((,k (org-ml--make-rest-partition-form ,kr ,kws nil))\n              ,@kw-lets)))\n         (body (->> (macroexp-parse-body body)\n                    (cdr)\n                    (append `(cl-block ,name)))))\n    ;; if &key is used but no keywords are actually used, slap the\n    ;; programmer in the face\n    (unless kw-alist (error \"No keywords used\"))\n    `(,arg-form\n      ,header\n      ,(macroexp-let*\n        let-forms\n        (macroexp-progn `(,body))))))\n\n(def-edebug-spec org-ml--defun-key\n  ([&or arg (arg sexp)]))\n\n(def-edebug-spec org-ml--defun-lambda-kw-list\n  (([&rest arg]\n    [&optional [\"&key\" org-ml--defun-key &rest org-ml--defun-key]]\n    &optional [\"&rest\" arg])))\n\n(defmacro org-ml--defun-kw (name arglist &rest body)\n  \"Define NAME as a function with BODY.\n\nThis is like `cl-defun' except it allows &key to be used in\nconjunction with &rest without freaking out. ARGLIST can be specified\nusing the following syntax:\n\n\\([VAR] ...\n [&key (VAR [INITFORM])...]\n [&rest VAR])\n\nwhere VAR is a symbol for the variable identifier and INITFORM is an\natom or form that will be the default value for keyword VAR if it is\nnot give in a function call.\n\nWhen calling functions defined with this, keywords can be given in any\norder as long as they are after all positional arguments, and rest\narguments will be interpreted as anything not belonging to a key-val\npair (but only if &rest was used to define the function). This implies\nthat keywords may not be used as values for the rest argument in\nfunction calls.\"\n  (declare (doc-string 3) (indent 2)\n           (debug (&define name\n                           org-ml--defun-lambda-kw-list\n                           lambda-doc\n                           [&optional (\"declare\" &rest sexp)]\n                           def-body)))\n  (if (memq '&key arglist)\n      (let ((res (org-ml--transform-lambda arglist body name)))\n        `(defun ,name ,@res))\n    (error \"&key not used, use regular defun\")))\n\n;; COMPILER MACROS\n\n(defmacro org-ml--defconst (symbol form &optional docstring)\n  \"Like `defconst' but wrapped in `eval-and-compile'.\nSYMBOL and DOCSTRING have the same meaning as `defconst'.\nFORM is used to set the init value and is wrapped in\n`eval-when-compile.'\"\n  (declare (indent 1))\n  `(eval-and-compile (defconst ,symbol (eval-when-compile ,form) ,docstring)))\n\n(defmacro org-ml--defvaralias (new-alias base-variable &optional docstring)\n  \"Like `defvaralias' but wrapped in `eval-and-compile'.\nNEW-ALIAS, BASE-VARIABLE, and DOCSTRING have the same meaning as `defconst'.\"\n  (declare (indent 1))\n  `(eval-and-compile (defvaralias ,new-alias ,base-variable ,docstring)))\n\n;; FUNCTORS\n\n(defmacro org-ml--map-first* (form list)\n  \"Return LIST with FORM applied to the first member.\nThe first element is `it' in FORM which returns the modified member.\"\n  (let ((x (make-symbol \"--list\")))\n    `(let ((,x ,list))\n       (when ,x\n         (cons (let ((it (car ,x))) ,form) (cdr ,x))))))\n\n(defmacro org-ml--map-last* (form list)\n    \"Return LIST with FORM applied to the last member.\nThe last element is `it' in FORM which returns the modified member.\"\n  (let ((x (make-symbol \"--list\")))\n    `(let ((,x ,list))\n       (when ,x\n         (nreverse (org-ml--map-first* ,form (nreverse ,x)))))))\n\n(defmacro org-ml--map-at* (n form list)\n  \"Return LIST with FORM applied to the member at index N.\nThe nth element is `it' in FORM which returns the modified member.\"\n  (declare (indent 1))\n  `(-replace-at ,n (let ((it (nth ,n ,list))) ,form) ,list))\n\n;; LIST OPERATIONS\n\n(defmacro org-ml--reduce2-from* (form init list)\n  \"Like `--reduce-from' but iterate over every pair of items in LIST.\nIn FORM, the first of the pair is bound to `it-key' and the\nsecond is bound to `it'. INIT has the same meaning.\"\n  (let ((l (make-symbol \"list\")))\n    `(let ((acc ,init)\n           (,l ,list)\n           it it-key)\n       (while ,l\n         (setq it (cadr ,l)\n               it-key (car ,l)\n               acc ,form\n               ,l (cdr (cdr ,l))))\n       acc)))\n\n(provide 'org-ml-macs)\n;;; org-ml-macs.el ends here\n"
  },
  {
    "path": "org-ml.el",
    "content": ";;; org-ml.el --- Functional Org Mode API -*- lexical-binding: t; -*-\n\n;; Copyright (C) 2020  Nathan Dwarshuis\n\n;; Author: Nathan Dwarshuis <ndwar@yavin4.ch>\n;; Keywords: org-mode, outlines\n;; Homepage: https://github.com/ndwarshuis/org-ml\n;; Package-Requires: ((emacs \"27.1\") (org \"9.7\") (dash \"2.17\") (s \"1.12\"))\n;; Version: 6.0.2\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\n;;; Commentary:\n\n;; This is a functional API for org-mode primarily using the `org-element'\n;; library. `org-element.el' provides the means for converting an org buffer to\n;; a parse-tree data structure. This library contains functions to modify this\n;; parse-tree in a more-or-less 'purely' functional manner (with the exception\n;; of parsing from the buffer and writing back to the buffer). For the purpose\n;; of this package, the resulting parse tree is composed of 'nodes'.\n\n;; This library exposes the following types of functions:\n;; - builder: build new nodes to be inserted into a parse tree\n;; - property functions: return either property values (get) or nodes with\n;;   modified properties (set and map)\n;; - children functions: return either children of nodes (get) or return a node\n;;   with modified children (set and map)\n;; - node predicates: return t if node meets a condition\n;; - pattern matching: return nodes based on a pattern that matches the parse\n;;   tree (and perform operations on those nodes depending on the function)\n;; - parsers: parse a buffer (optionally at current point) and return a parse\n;;   tree\n;; - writers: insert/update the contents of a buffer given a parse tree\n\n;; For examples please see full documentation at:\n;; https://github.com/ndwarshuis/org-ml\n\n;;; Code:\n\n(require 'org-element)\n(require 'dash)\n(require 's)\n(require 'inline)\n\n(eval-when-compile\n  (require 'org-ml-macs))\n\n;;; NODE TYPE SETS\n\n;; When only considering types, nodes can be arranged in the following\n;; sets (where nested sets are mutually exclusive)\n\n;; +---------------------------------------------------+\n;; | nodes                                             |\n;; |                                                   |\n;; | +-----------------------------------------------+ |\n;; | | element nodes                                 | |\n;; | | (`org-element-all-elements' + 'org-data')     | |\n;; | |                                               | |\n;; | | +-------------------------------------------+ | |\n;; | | | leaf nodes                                | | |\n;; | | +-------------------------------------------+ | |\n;; | |                                               | |\n;; | | +-------------------------------------------+ | |\n;; | | | branch nodes                              | | |\n;; | | |                                           | | |\n;; | | | +---------------------------------------+ | | |\n;; | | | | permitting child element nodes        | | | |\n;; | | | | (aka \"greater elements\")              | | | |\n;; | | | | (`org-element-greater-elements'       | | | |\n;; | | | | + 'org-data)                          | | | |\n;; | | | +---------------------------------------+ | | |\n;; | | |                                           | | |\n;; | | | +---------------------------------------+ | | |\n;; | | | | permitting child object nodes         | | | |\n;; | | | | (`org-element-object-containers' -    | | | |\n;; | | | | `org-element-recursive-objects')      | | | |\n;; | | | +---------------------------------------+ | | |\n;; | | +-------------------------------------------+ | |\n;; | +-----------------------------------------------+ |\n;; |                                                   |\n;; | +-----------------------------------------------+ |\n;; | | object nodes                                  | |\n;; | | (`org-element-all-objects' + 'plain-text')    | |\n;; | |                                               | |\n;; | | +-------------------------------------------+ | |\n;; | | | leaf nodes                                | | |\n;; | | +-------------------------------------------+ | |\n;; | |                                               | |\n;; | | +-------------------------------------------+ | |\n;; | | | branch nodes permitting child object      | | |\n;; | | | nodes (aka \"recursive objects\")           | | |\n;; | | | (`org-element-recursive-objects')         | | |\n;; | | +-------------------------------------------+ | |\n;; | +-----------------------------------------------+ |\n;; +---------------------------------------------------+\n\n;; In `org-element.el' the types 'plain-text' and 'org-data' are\n;; not mentioned but are required here to make the sets complete.\n;; 'plain-text' is consider a leaf node of class object and 'org-data'\n;; is considered a branch node of class element that is permitted to\n;; hold other element nodes as children\n\n(org-ml--defconst org-ml-elements\n  (cons 'org-data org-element-all-elements)\n  \"List of all element types including `org-data'.\")\n\n(org-ml--defconst org-ml-objects\n  (cons 'plain-text org-element-all-objects)\n  \"List of all object types including `plain-text'.\")\n\n(eval-and-compile\n  (defconst org-ml-nodes\n    (append org-ml-elements org-ml-objects)\n    \"List of all node types.\"))\n\n(org-ml--defvaralias 'org-ml-branch-nodes-permitting-child-objects\n  'org-element-object-containers\n  \"List of node types that can have objects as children.\nThese are also known as \\\"object containers\\\" in `org-element.el'\")\n\n(org-ml--defconst org-ml-branch-elements-permitting-child-objects\n  (-intersection org-ml-branch-nodes-permitting-child-objects org-ml-elements)\n  \"List of element types that can have objects as children.\")\n\n(org-ml--defconst org-ml-branch-elements-permitting-child-elements\n  (cons 'org-data org-element-greater-elements)\n  \"List of element types that can have elements as children.\nThese are also known as \\\"greater elements\\\" in `org-element.el'\")\n\n(org-ml--defconst org-ml-branch-elements\n  (append org-ml-branch-elements-permitting-child-objects\n          org-ml-branch-elements-permitting-child-elements)\n  \"List of element types that can have children.\")\n\n(org-ml--defvaralias 'org-ml-branch-objects\n  'org-element-recursive-objects\n  \"List of object types that can have objects as children.\nThese are also known as \\\"recursive objects\\\" in `org-element.el'\")\n\n(org-ml--defconst org-ml-branch-nodes\n  (append org-ml-branch-elements org-ml-branch-objects)\n  \"List of node types that can have children.\")\n\n;;; BRANCH NODE CHILD TYPE RESTRICTIONS\n\n;; `org-element.el' specifies which object nodes may be children of other object\n;; nodes but does not have the same thing for element nodes; implement here\n\n(org-ml--defconst org-ml--object-restrictions\n  (->>\n   org-element-object-restrictions\n   ;; remove non-object nodes\n   (--remove (memq (car it) '(inlinetask item headline keyword)))\n   ;; add plain-text type to everything except table-row\n   (--map-when (not (eq (car it) 'table-row)) (-snoc it 'plain-text)))\n  \"Alist of object node type restrictions for object branch nodes.\nThe types in the cdr of each entry may be children of the type held at\nthe car.\")\n\n(org-ml--defconst org-ml--element-restrictions\n  ;; TODO add inlinetask\n  ;; this includes all elements except those that are restricted\n  ;; (see comments below)\n  (let ((standard '(babel-call center-block clock comment\n                               comment-block diary-sexp drawer\n                               dynamic-block example-block\n                               export-block fixed-width\n                               footnote-definition horizontal-rule\n                               keyword latex-environment paragraph\n                               plain-list planning property-drawer\n                               quote-block special-block src-block\n                               table verse-block)))\n    `((center-block ,@(remove 'center-block standard))\n      (drawer ,@(remove 'drawer standard))\n      (dynamic-block ,@(remove 'dynamic-block standard))\n      (footnote-definition ,@(remove 'footnote-definition standard))\n      ;; headlines can only have headlines and sections\n      (headline headline section)\n      (item ,@standard)\n      ;; plain-lists can only have items\n      (plain-list item)\n      ;; property-drawers can only have node-properties\n      (property-drawer node-property)\n      (quote-block ,@(remove 'quote-block standard))\n      (section ,@standard)\n      (special-block ,@standard)\n      ;; tables can only have table-rows\n      (table table-row)\n      (org-data headline section)))\n  \"Alist of element node type restrictions for element branch nodes.\nThe types in the cdr of each entry may be children of the type held at\nthe car.\")\n\n(defconst org-ml--node-restrictions\n  (eval-when-compile\n    (append org-ml--element-restrictions org-ml--object-restrictions))\n  \"Alist of all restrictions for containers.\")\n\n(defconst org-ml--item-tag-restrictions\n  (eval-when-compile\n    (->> org-element-object-restrictions\n         (alist-get 'item)\n         (cons 'plain-text)))\n  \"List of node types which may be used in item node tag properties.\")\n\n(defconst org-ml--headline-title-restrictions\n  (eval-when-compile\n    (->> org-element-object-restrictions\n         (alist-get 'headline)\n         (cons 'plain-text)))\n  \"List of node types which may be used in item headline title properties.\")\n\n;;; CUSTOM\n\n(defcustom org-ml-memoize-match-patterns nil\n  \"Memoize patterns in `org-ml-match' and friends.\n\nThese functions all take a PATTERN parameter that is used to\ngenerate a lambda function, which is then used to computationally\nsearch for the desired matches. Generating these lambda forms has\nsome overhead (and will increase with increasing pattern\ncomplexity). Therefore, this value can be used to memoize (cache)\neach unique lambda form for each pattern. When enabled, calls to\nany of the match function using a unique pattern will generate\nthe corresponding lambda form only once, and then subsequent\ncalls will retrieve the form from the cache. This can increase\nperformance if relatively few patterns are used relative to the\ncalls made to pattern-consuming functions.\n\nThe following values are understood:\n- nil: do not memoize anything\n- `compiled': memoize byte-compiled lambda forms\n- any other non-nil: memoize non-compiled lambda forms\"\n  :type 'boolean\n  :group 'org-ml)\n\n(defcustom org-ml-memoize-builders nil\n  \"Memoize `org-ml-build-*' functions.\n\nThese functions are pure and thus can be easily memoized.\n\nOne may wish to do this if one needs to create many nodes that\nare the same, as node creation is relatively expensive. These\nfunctions are also used internally by other parts of `org-ml',\nthus memoizing these functions can achieve significant speed\nincreases in many scenerios. The downside is each unique node\nwill be stored, which takes space.\n\nThis variable globally controls memoization for these functions.\nTo control memoization on a per-type basis, see\n`org-ml-memoize-builder-types'.\"\n  :type 'boolean\n  :group 'org-ml)\n\n(org-ml--defconst org-ml-builder-types\n   (append org-element-all-objects org-element-all-elements))\n\n(defcustom org-ml-memoize-builder-types\n  (-difference org-ml-builder-types org-ml-branch-nodes)\n  \"Specify the types for `org-ml-memoize-builders'.\"\n  :type `(set ,@(--map (list 'const it) org-ml-builder-types))\n  :group 'org-ml)\n\n(defcustom org-ml-memoize-shorthand-builders nil\n  \"Memoize `org-ml-build-*!' functions.\n\nLike the `org-ml-build-*' functions (no exclamation point), these\nfunctions are pure and thus can be easily memoized.\n\nOne may wish to do this if one needs to create many nodes that\nare the same, as node creation is relatively expensive. These\nfunctions are also used internally by other parts of `org-ml',\nthus memoizing these functions can achieve significant speed\nincreases in many scenerios. The downside is each unique node\nwill be stored, which takes space.\n\nThis variable globally controls memoization for these functions.\nTo control memoization on a per-type basis, see\n`org-ml-memoize-shorthand-builder-types'.\"\n  :type 'boolean\n  :group 'org-ml)\n\n(org-ml--defconst org-ml-shorthand-builder-types\n  '(timestamp clock planning headline paragraph secondary-string item\n              property-drawer table-cell table-row table))\n\n(defcustom org-ml-memoize-shorthand-builder-types\n  (-difference org-ml-shorthand-builder-types '(headline item))\n  \"Specify the types for `org-ml-memoize-shorthand-builders'.\n\nNote that these don't perfectly correspond to `org-ml-nodes' since\nsome of these functions are composite node builders.\n\nAll in `org-ml-shorthand-builder-types' are enabled by default except for\n`headline' and `item' since these take child nodes are arguments\nwhich therefore lead to large key sizes.\"\n  :type `(set ,@(--map (list 'const it) org-ml-shorthand-builder-types))\n  :group 'org-ml)\n\n(defcustom org-ml-use-impure nil\n  \"Run functions in impure mode.\n\nFor now this means that no function will make a copy of a node,\nso all changes will be via side effect.\"\n  :type 'boolean\n  :group 'org-ml)\n\n(defcustom org-ml-disable-checks nil\n  \"Run functions without checking nodes for proper types.\"\n  :type 'boolean\n  :group 'org-ml)\n\n;;; AFFILIATED KEYWORD NODES\n\n(org-ml--defconst org-ml--element-nodes-with-affiliated\n  (eval-when-compile\n    (-difference org-ml-elements\n                 '(org-data comment clock headline inlinetask item\n                            node-property planning property-drawer\n                            section table-row))))\n\n;;; LIST OPERATIONS (EXTENDING DASH.el)\n\n(defun org-ml--pad-or-truncate (length pad list)\n  \"Return padded or truncated list starting from LIST.\n\nIf length of LIST is greater than LENGTH, truncate LIST to LENGTH\nand return. If LIST is longer than LENGTH, add PAD to the end\nof LIST until it's length equals LENGTH and return. Do nothing if\nlength of LIST is equal to LENGTH initially.\"\n  (let ((blanks (- length (length list))))\n    (if (< blanks 0) (-take length list)\n      (append list (-repeat blanks pad)))))\n\n;;; plist operations\n\n(defun org-ml--plist-get-keys (plist)\n  \"Get the keys for PLIST.\"\n  (-slice plist 0 nil 2))\n\n(defun org-ml--plist-get-vals (plist)\n  \"Get the values for PLIST.\"\n  (-slice plist 1 nil 2))\n\n(defun org-ml--plist-map-values (fun plist)\n  \"Map FUN over the values in PLIST.\nFUN is a unary function that returns a modified value.\"\n  (nreverse\n   (org-ml--reduce2-from* (cons (funcall fun it) (cons it-key acc))\n     nil plist)))\n\n(defun org-ml--is-plist (x)\n  \"Return t if X is a plist.\"\n  (declare (pure t) (side-effect-free t))\n  (when (listp x)\n    (let ((is-plist t))\n      (while (and is-plist (cdr x))\n        (setq is-plist (keywordp (car x))\n              x (cdr (cdr x))))\n      (and (not x) is-plist))))\n\n(defun org-ml--plist-remove (key plist)\n  \"Return PLIST with KEY and its value removed.\"\n  (nreverse\n   (org-ml--reduce2-from* (if (eq key it-key) acc (cons it (cons it-key acc)))\n     nil plist)))\n\n;;; inter-index operations\n\n;; The \"inter-index\" alludes to the fact that these list operations\n;; use an index value that refers to spaces between list members.\n;; These functions are enhanced versions of what is provided in\n;; `dash.el' and native emacs that handle negative indices and have\n;; switches to handle out of bounds errors\n\n(defun org-ml--convert-inter-index (n list &optional use-oor)\n  \"Return absolute index given N and LIST.\n\nN is relative index where positions in LIST are given by the following:\n- 0: before first member\n- 1: before second member (and so on)\n- -1: after last member\n- -2: after penultimate member (and so on)\n\nThe absolute index to be returned will be N mapped to a positive\ninteger that refers to the same space in LIST.\n\nIf USE-OOR (use out-of-range) is t, return the closest valid index\nif N refers to a location that is outside LIST. Otherwise throw an\nerror.\"\n  (let* ((N (length list))\n         (upper N)\n         (lower (- (- N) 1)))\n    (cond\n     ((<= 0 n upper) n)\n     ((>= -1 n lower) (+ 1 N n))\n     ((and use-oor (< upper n)) upper)\n     ((and use-oor (< n lower)) lower)\n     (t (org-ml--arg-error\n         \"Index (%s) out of range; must be between %s and %s\"\n         n lower upper)))))\n\n(defun org-ml--insert-at (n x list &optional use-oor)\n  \"Like `-insert-at' but can insert X at negative indices N in LIST.\nSee `org-ml--convert-inter-index' for the meaning of N and USE-OOR.\"\n  (-insert-at (org-ml--convert-inter-index n list use-oor) x list))\n\n(defun org-ml--split-at (n list &optional use-oor)\n  \"Like `-split-at' except allow negative indices in LIST.\nSee `org-ml--convert-inter-index' for the meaning of N and USE-OOR.\"\n  (let ((n* (org-ml--convert-inter-index n list use-oor)))\n    (when list\n      (-split-at n* list))))\n\n(defun org-ml--splice-at (n list* list &optional use-oor)\n  \"Return LIST with LIST* spliced at index N.\nSee `org-ml--convert-inter-index' for the meaning of N and USE-OOR.\"\n  (--> (-map #'list list)\n       (org-ml--insert-at n list* it use-oor)\n       (apply #'append it)))\n\n;;; intra-index operations\n\n;; The \"intra-index\" alludes to the fact that these list operations\n;; use an index value that refers to explicit list members.\n;; These functions are enhanced versions of what is provided in\n;; `dash.el' and native emacs that handle negative indices and have\n;; switches to handle out of bounds errors\n\n(defun org-ml--convert-intra-index (n list &optional use-oor)\n  \"Return absolute index given N and LIST.\n\nN is relative index where positions in LIST are given by the following:\n- 0: first member\n- 1: second member (and so on)\n- -1: last member\n- -2: penultimate member (and so on)\n\nThe absolute index to be returned is N mapped to a positive integer\nthat refers to the same member in LIST.\n\nIf USE-OOR (use out-of-range) is non-nil, return the closest valid\nindex if N refers to a position outside of LIST.\n\nIn cases where LIST is nil, N is meaningless since it will never\nrefer to anything. In this case, return nil if USE-OOR is\n`permit-empty', and throw an error otherwise (even if USE-OOR it\nnon-nil).\"\n  (let* ((N (length list))\n         (upper (1- N))\n         (lower (- N)))\n    (cond\n     ((= N 0) (unless (eq use-oor 'permit-empty)\n                (error \"List is empty, index is meaningless\")))\n     ((<= 0 n upper) n)\n     ((>= -1 n lower) (+ N n))\n     ((and use-oor (< upper n)) upper)\n     ((and use-oor (< n lower)) 0)\n     (t (org-ml--arg-error\n         \"Index (%s) out of range; must be between %s and %s\"\n         n lower upper)))))\n\n(defun org-ml--remove-at (n list &optional use-oor)\n  \"Like `-remove-at' but honors negative indices N in LIST.\nSee `org-ml--convert-intra-index' for the meaning of N and USE-OOR.\"\n  (-some-> (org-ml--convert-intra-index n list use-oor)\n           (-remove-at list)))\n\n(defun org-ml--replace-at (n x list &optional use-oor)\n  \"Like `-replace-at' but can substitute X at negative indices N in LIST.\nSee `org-ml--convert-intra-index' for the meaning of N and USE-OOR.\"\n  (-some-> (org-ml--convert-intra-index n list use-oor)\n           (-replace-at x list)))\n\n(defun org-ml--nth (n list &optional use-oor)\n  \"Like `nth' but honors negative indices N in LIST.\nSee `org-ml--convert-intra-index' for the meaning of N and USE-OOR.\"\n  (-some-> (org-ml--convert-intra-index n list use-oor)\n           (nth list)))\n\n;;; INTERNAL TYPE FUNCTIONS\n\n(define-error 'arg-type-error \"Argument type error\")\n\n(defun org-ml--arg-error (string &rest args)\n  \"Signal an `arg-type-error'.\nSTRING and ARGS are analogous to `error'.\"\n  (signal 'arg-type-error `(,(apply #'format-message string args))))\n\n(defun org-ml--is-any-type (types node)\n  \"Return t if the type of NODE is in TYPES (a list of symbols).\"\n  (declare (pure t) (side-effect-free t))\n  (if (memq (org-ml-get-type node) types) t))\n\n(defun org-ml--is-node (list)\n  \"Return t if LIST is a node.\"\n  (declare (pure t) (side-effect-free t))\n  (org-ml--is-any-type org-ml-nodes list))\n\n(defun org-ml--is-type (type node)\n  \"Return t if the type of NODE is TYPE (a symbol).\"\n  (declare (pure t) (side-effect-free t))\n  (eq (org-ml-get-type node) type))\n\n(defun org-ml--is-table-row (node)\n  \"Return t if NODE is a standard table-row node.\"\n  (declare (pure t) (side-effect-free t))\n  (and (org-ml--is-type 'table-row node)\n       (org-ml--property-is-eq :type 'standard node)))\n\n(defun org-ml--filter-type (type node)\n  \"Return NODE if it is TYPE or nil otherwise.\"\n  (declare (pure t) (side-effect-free t))\n  (and (org-ml--is-type type node) node))\n\n(defun org-ml--filter-types (types node)\n  \"Return NODE if it is one of TYPES or nil otherwise.\"\n  (declare (pure t) (side-effect-free t))\n  (and (org-ml-is-any-type types node) node))\n\n(defun org-ml--is-secondary-string (list)\n  \"Return t if LIST is a secondary string.\"\n  (declare (pure t) (side-effect-free t))\n  (--none? (org-ml--is-any-type org-ml-elements it) list))\n\n(defun org-ml--check-type (type node)\n  \"Check that NODE is TYPE; throw error if not.\"\n  (unless org-ml-disable-checks\n    (let ((y (org-ml-get-type node)))\n      (unless (equal y type)\n        (org-ml--arg-error \"Node must be a %s, got a %s\" type y)))))\n\n(defun org-ml--check-types (types node)\n  \"Check that NODE is one of TYPES; throw error if not.\"\n  (unless org-ml-disable-checks\n    (let ((y (org-ml-get-type node)))\n      (unless (memq y types)\n        (org-ml--arg-error \"Node must be one of %s, got a %s\" types y)))))\n\n;;; MISC HELPER FUNCTIONS\n\n(defun org-ml--get-head (node)\n  \"Return the type and properties cells of NODE.\"\n  (declare (pure t) (side-effect-free t))\n  (if (stringp node) node (list (car node) (cadr node))))\n\n(defun org-ml--from-string (string)\n  \"Convert STRING to org-element representation.\"\n  (with-temp-buffer\n    (insert string)\n    (-> (org-ml-parse-this-buffer) (org-element-contents) (car))))\n\n(define-inline org-ml-copy (node &optional keep)\n  \"Copy NODE if running in pure mode.\n\nKEEP is passed to `org-element-copy'.\"\n  (inline-letevals (node)\n    (inline-quote\n     (if org-ml-use-impure ,node (org-element-copy ,node ,keep)))))\n\n(defmacro org-ml-wrap-impure (&rest body)\n  \"Run BODY in impure mode.\"\n  `(let ((org-ml-use-impure t))\n     ,@body))\n\n(defmacro org-ml-wrap-check (&rest body)\n  \"Run BODY without node type checking.\"\n  `(let ((org-ml-disable-checks t))\n     ,@body))\n\n(defmacro org-ml-> (&rest forms)\n  \"Thread FORMS using `->' and run in impure mode.\"\n  (declare (indent 1))\n  `(org-ml-wrap-impure (-> ,@forms)))\n\n(defmacro org-ml->> (&rest forms)\n  \"Thread FORMS using `->>' and run in impure mode.\"\n  (declare (indent 1))\n  `(org-ml-wrap-impure (->> ,@forms)))\n\n(defmacro org-ml--> (&rest forms)\n  \"Thread FORMS using `-->' and run in impure mode.\"\n  (declare (indent 1))\n  `(org-ml-wrap-impure (--> ,@forms)))\n\n(defmacro org-ml-some-> (&rest forms)\n  \"Thread FORMS using `-some->' and run in impure mode.\"\n  (declare (indent 1))\n  `(org-ml-wrap-impure (-some-> ,@forms)))\n\n(defmacro org-ml-some->> (&rest forms)\n  \"Thread FORMS using `-some->>' and run in impure mode.\"\n  (declare (indent 1))\n  `(org-ml-wrap-impure (-some->> ,@forms)))\n\n(defmacro org-ml-some--> (&rest forms)\n  \"Thread FORMS using `-some-->' and run in impure mode.\"\n  (declare (indent 1))\n  `(org-ml-wrap-impure (-some--> ,@forms)))\n\n(defmacro org-ml-as-> (&rest forms)\n  \"Thread FORMS using `-some-->' and run in impure mode.\"\n  (declare (indent 1))\n  `(org-ml-wrap-impure (-some--> ,@forms)))\n\n;;; INTERNAL PREDICATES\n\n(defun org-ml--is-oneline-string (x)\n  \"Return t if X is a string with no newlines.\"\n  (declare (pure t))\n  (and (stringp x) (not (s-contains? \"\\n\" x))))\n\n(defun org-ml--is-oneline-string-or-nil (x)\n  \"Return t if X is a string with no newlines or nil.\"\n  (declare (pure t))\n  (or (null x) (org-ml--is-oneline-string x)))\n\n(defun org-ml--is-non-neg-integer (x)\n  \"Return t if X is a non-negative integer.\"\n  (declare (pure t))\n  (and (integerp x) (<= 0 x)))\n\n(defun org-ml--is-non-neg-integer-or-nil (x)\n  \"Return t if X is a non-negative integer or nil.\"\n  (declare (pure t))\n  (or (null x) (org-ml--is-non-neg-integer x)))\n\n(defun org-ml--is-pos-integer (x)\n  \"Return t if X is a positive integer.\"\n  (declare (pure t))\n  (and (integerp x) (< 0 x)))\n\n(defun org-ml--is-pos-integer-or-nil (x)\n  \"Return t if X is a positive integer or nil.\"\n  (declare (pure t))\n  (or (null x) (org-ml--is-pos-integer x)))\n\n(defun org-ml--is-string-list (x)\n  \"Return t if X is a list of strings without newlines or nil.\"\n  (declare (pure t))\n  (or (null x) (and (listp x) (-all? #'org-ml--is-oneline-string x))))\n\n;;; INTERNAL NODE PROPERTY FUNCTIONS\n\n(defun org-ml--get-nonstandard-properties (node)\n  \"Return the non-standard properties list of NODE.\"\n  (if (stringp node) (text-properties-at 0 node) (cddr (nth 1 node))))\n\n(defun org-ml-get-all-properties (node)\n  \"Return the properties list of NODE.\"\n  (if (stringp node) (text-properties-at 0 node)\n    (let ((arr-props (->> org-element--standard-properties\n                          (--map (list it (org-element-property it node)))\n                          (-flatten-n 1)))\n          (plist-props (->> (nth 1 node)\n                            (cddr)\n                            (org-ml--plist-get-keys)\n                            (--map (list it (org-element-property it node)))\n                            (-flatten-n 1))))\n      (append arr-props plist-props))))\n\n(define-inline org-ml--get-post-blank-text (plain-text)\n  \"Return number of trailing spaces in PLAIN-TEXT.\"\n  (inline-quote\n   (length (car (s-match \"[ ]*$\" ,plain-text)))))\n\n(define-inline org-ml--get-post-blank-textsafe (node)\n  \"Return number of trailing spaces in PLAIN-TEXT.\"\n  (inline-letevals (node)\n    (inline-quote\n     (if (stringp ,node) (org-ml--get-post-blank-text ,node)\n       (org-element-post-blank ,node)))))\n\n(define-inline org-ml--set-post-blank (post-blank node)\n  (inline-quote (org-element-put-property-2 :post-blank ,post-blank ,node)))\n\n(define-inline org-ml--set-last-post-blank (pb nodes)\n  \"Set post-blank of last of NODES by PB.\"\n  (inline-quote (org-ml--map-last* (org-ml--set-post-blank ,pb it) ,nodes)))\n\n(defmacro org-ml--set-properties-raw (node &rest plist)\n  \"Set all properties in NODE to the values corresponding to PLIST.\nPLIST is a list of property-value pairs that correspond to the\nproperty list in NODE.\n\nThis is not meant for plain-text.\"\n  (declare (indent 1))\n  (let* ((n (make-symbol \"it-node\"))\n         (forms (->> (-partition 2 plist)\n                     (--map `(org-element-put-property ,n ,(car it) ,(cadr it))))))\n    `(let ((,n ,node))\n       ,@forms\n       ,n)))\n\n(eval-when-compile\n  (defmacro org-ml--map-property-raw* (prop form node)\n    \"Return NODE with FUN applied to the value in PROP.\nFUN is a form that returns a modified value and contains `it'\nbound to the property value.\"\n    (declare (indent 1))\n    (let ((node* (make-symbol \"node\")))\n      `(let ((,node* ,node))\n         (let ((it (org-element-property-raw ,prop ,node*)))\n           (org-element-put-property-2 ,prop ,form ,node*))))))\n\n(define-inline org-ml--shift-post-blank (n node)\n  \"ADD N spaces to the end of NODE.\n\nThis will not work if NODE is a string.\"\n  (inline-quote\n   (org-ml--map-property-raw* :post-blank (+ it ,n) ,node)))\n\n(define-inline org-ml--shift-last-post-blank (pb nodes)\n  \"Shift post-blank of last of NODES by PB.\"\n  (inline-quote (org-ml--map-last* (org-ml--shift-post-blank ,pb it) ,nodes)))\n\n(defun org-ml--shift-post-blank-textsafe (n node)\n  \"ADD N spaces to the end of NODE.\n\nThis will work if NODE is a string.\"\n  (if (stringp node)\n      (let ((pb (org-ml--get-post-blank-text node)))\n        (concat (substring node (- pb)) (make-string (+ pb n) ?\\ )))\n    (org-ml--shift-post-blank n node)))\n\n(define-inline org-ml--property-is-eq (prop val node)\n  \"Return t if PROP in NODE is `eq' to VAL.\"\n  (inline-quote (eq ,val (org-element-property-raw ,prop ,node))))\n\n;;; NODE PROPERTY TRANSLATION AND CHECKING FRAMEWORK\n\n;; This code provides the internal framework for the following\n;; operations where NODE is any node, PROP is a property of NODE,\n;; and VALUE is the value of PROP:\n\n;; Get: f(PROP NODE) -> VALUE\n;; Set: f(PROP VALUE NODE) -> NODE'\n;; Map: f(PROP FUN NODE) -> NODE' where FUN is a function that\n;;      modifies the value of PROP in NODE and is like:\n;;      f(VALUE) -> VALUE'\n\n;; Get -> 'read', Set -> 'write', Map -> 'read/write'\n\n;; `org-element.el' doesn't always store values as their native types (like some\n;; strings look like plists converted to strings). Here, we make a distinction\n;; between VALUE and its internal representation IVALUE (which is actually the\n;; value stored in the node list and understood by `org-element.el'), where\n;; VALUE may not always be `equal' to IVALUE. When performing any of the\n;; operations above, this framework will transparently translate between VALUE\n;; and IVALUE (using so called encoders and decodes). Furthermore, the VALUE for\n;; any PROP must conform to a 'type' which is enforced by this framework.\n\n;; The center of this framework is the constant\n;; `org-ml--property-alist' which holds the relationship of all node\n;; types and their properties, type checkers, and encoders/decoders.\n;; This alist has the following structure:\n\n;; - car of each member is the type of NODE\n;; - cdr of each member is the property alist for the node type\n;;   - the car of the property alist is the keyword for PROP\n;;   - the cdr of the property alist is an attribute plist, and the\n;;     keys of this plist include:\n;;     - :pred - a predicate function that returns t if VALUE is the correct\n;;       type for PROP\n;;     - :type-desc - a string describing the data type for PROP\n;;     - :encode - a unary function that converts VALUE to IVALUE; if not given\n;;       this is the identity function\n;;     - :decode - a function that inverts the function at :encode, if not given\n;;       this is the identity function\n;;     - :cis - a unary function that takes NODE and returns a modified NODE;\n;;       the point of this it to \"update\" other properties when PROP is changed\n;;     - :const - a value that PROP should always have\n;;     - :shift - a binary function that shifts PROP; the first argument takes\n;;       an integer describing the magnitude and direction of the shift and the\n;;       second argument is VALUE for PROP; return a new VALUE; this only makes\n;;       sense the type of PROP is an integer\n;;     - :require - a boolean telling if PROP is required to be specified when\n;;       creating a NODE of this type\n;;     - :string-list - a boolean telling if the type of PROP is a list of\n;;       strings\n;;     - :plist - a boolean telling if the type of PROP is a plist\n;;     - :toggle - a boolean telling if the type of PROP is a boolean\n\n;; In terms of property attributes, the three property operations can be\n;; described by the following pseudo code:\n\n;; get: GET(PROP VALUE) -> NODE\n;; 1) DECODE(IGET(PROP, NODE)) -> VALUE where IGET retrieves the IVALUE of PROP\n;;    from NODE\n\n;; set: SET(PROP VALUE NODE) -> NODE\n;; 1) if PRED(VALUE) -> t, proceed to 2), else throw error\n;; 2) ISET(PROP, ENCODE(VALUE), NODE)) -> NODE' where ISET sets the PROP of NODE\n;;    to IVALUE\n;; 3) If CIS is non-nil, run CIS(NODE') -> NODE'', else return NODE'\n\n;; map: MAP(PROP FUN NODE) -> NODE'\n;; 1) GET(PROP NODE) -> VALUE\n;; 2) FUN(VALUE) -> VALUE'\n;; 3) if PRED(VALUE') -> t proceed to 4), else throw error\n;; 4) SET(PROP VALUE' NODE) -> NODE'\n\n;; Thus GET only requires that the property exist in the type (which may be\n;; nil, in which case GET returns nil). The decoder doesn't need to be present\n;; as the identity function will be used if it isn't present.\n;;\n;; SET requires that the :pred attribute exists, since it needs to check that\n;; the incoming value to assign is valid. If :encoder or :cis are unspecified\n;; then these will be identity.\n;;\n;; MAP obviously requires both.\n\n;;; property value predicates (type specific)\n\n(defun org-ml--is-valid-link-format (x)\n  \"Return t if X is an allowed value for a link node format property.\"\n  (memq x '(nil plain angle bracket)))\n\n(defun org-ml--is-valid-link-type (x)\n  \"Return t if X is an allowed value for a link node type property.\"\n  (->> '(\"coderef\" \"custorg-ml-id\" \"file\" \"id\" \"radio\" \"fuzzy\")\n       (append (org-link-types))\n       (member x)))\n\n(defun org-ml--is-valid-item-checkbox (x)\n  \"Return t if X is an allowed value for an item node checkbox property.\"\n  (memq x '(nil on off trans)))\n\n(defun org-ml--is-valid-item-tag (x)\n  \"Return t if X is an allowed value for an item node tag property.\"\n  (and (listp x)\n       (--all? (org-ml--is-any-type org-ml--item-tag-restrictions it) x)))\n\n(defun org-ml--is-valid-item-bullet (x)\n  \"Return t if X is an allowed value for a item node bullet property.\"\n  ;; NOTE org mode 9.1.9 has the following limitations:\n  ;; - \"+\" will be converted to \"-\" when interpreted\n  ;; - \"1)\" will be converted to \"1.\" when interpreted\n  ;; - alphanumeric symbols make the interpreter crash\n  (pcase x ((or '- (pred integerp)) t)))\n\n(defun org-ml--is-valid-clock-timestamp (x)\n  \"Return t if X is an allowed value for a clock node value property.\"\n  (and (org-ml--is-type 'timestamp x)\n       (memq (org-element-property-raw :type x) '(inactive inactive-range))\n       (not (org-element-property-raw :repeater-type x))))\n\n(defun org-ml--is-valid-planning-unclosed-timestamp (x)\n  \"Return t if X is an allowed value for a planning node timestamp property.\"\n  (or (null x) (and (org-ml--is-type 'timestamp x)\n                    (org-ml--property-is-eq :type 'active x))))\n\n(defun org-ml--is-valid-planning-closed-timestamp (x)\n  \"Return t if X is an allowed value for a planning node timestamp property.\"\n  (or (null x) (and (org-ml--is-type 'timestamp x)\n                    (org-ml--property-is-eq :type 'inactive x))))\n\n(defun org-ml--is-valid-entity-name (x)\n  \"Return t if X is an allowed value for an entity node name property.\"\n  (org-entity-get x))\n\n(defun org-ml--is-valid-headline-tags (x)\n  \"Return t if X is an allowed value for a headline node tags property.\"\n  (and (listp x)\n       (-all? #'org-ml--is-oneline-string x)\n       (not (member org-archive-tag x))))\n\n(defun org-ml--is-valid-headline-priority (x)\n  \"Return t if X is an allowed value for a headline node priority property.\"\n  (or (null x) (and (integerp x)\n                    (>= org-lowest-priority x org-highest-priority))))\n\n(defun org-ml--is-valid-headline-title (x)\n  \"Return t if X is an allowed value for a headline node title property.\"\n  (and\n   (listp x)\n   (--all? (org-ml--is-any-type org-ml--headline-title-restrictions it) x)))\n\n(defun org-ml--is-valid-timestamp-type (x)\n  \"Return t if X is an allowed value for a timestamp node type property.\"\n  (memq x '(inactive inactive-range active active-range)))\n\n(defun org-ml--is-valid-timestamp-range-type (x)\n  \"Return t if X is an allowed value for a timestamp node range-type property.\"\n  (memq x '(nil daterange timerange)))\n\n(defun org-ml--is-valid-timestamp-repeater-type (x)\n  \"Return t if X is an allowed value for a timestamp node repeater-type property.\"\n  (memq x '(nil catch-up restart cumulate)))\n\n(defun org-ml--is-valid-timestamp-warning-type (x)\n  \"Return t if X is an allowed value for a timestamp node warning-type property.\"\n  (memq x '(nil all first)))\n\n(defun org-ml--is-valid-timestamp-unit (x)\n  \"Return t if X is an allowed value for a timestamp node unit property.\"\n  (memq x '(nil year month week day hour)))\n\n(defun org-ml--is-valid-latex-environment-value (x)\n  \"Return t if X is an allowed value for a latex-environment node value property.\"\n  (pcase x\n    ((or `(,(pred org-ml--is-oneline-string))\n         `(,(pred org-ml--is-oneline-string) ,(pred stringp)))\n     t)))\n\n(defun org-ml--is-valid-statistics-cookie-value (x)\n  \"Return t if X is an allowed value for a statistics-cookie node value property.\"\n  (pcase x\n    ((or `(nil) `(nil nil)) t)\n    (`(,(and (pred integerp) percent))\n     (<= 0 percent 100))\n    (`(,(and (pred integerp) numerator)\n       ,(and (pred integerp) denominator))\n     (and (org-ml--is-non-neg-integer numerator)\n          (org-ml--is-non-neg-integer denominator)\n          (<= numerator denominator)))))\n\n(defun org-ml--is-valid-diary-sexp-value (x)\n  \"Return t if X is an allowed value for a diary-sexp node value property.\"\n  (or (null x) (listp x)))\n\n(defun org-ml--is-valid-header (x)\n  \"Return t if X is an allowed value for a header affiliated keyword property.\"\n  (and (listp x) (--all? (org-ml--is-plist it) x)))\n\n(defun org-ml--is-valid-results (x)\n  \"Return t if X is an allowed value for a results affiliated keyword property.\"\n  (pcase x\n    (`nil t)\n    (`(,(pred stringp) ,(pred stringp)) t)\n    (`(,(pred stringp)) t)))\n\n(defun org-ml--is-valid-caption (x)\n  \"Return t if X is an allowed value for a caption affiliated keyword property.\"\n  (and (listp x)\n       (--all? (pcase it\n                 ((pred stringp) t)\n                 (`(,(pred stringp) ,(pred stringp)) t))\n               x)))\n\n;;; encode/decode (general)\n\n(defun org-ml--decode-boolean (bool)\n  \"Return BOOL as either t or nil.\"\n  (and bool t))\n\n(defun org-ml--encode-string-or-nil (string)\n  \"Return STRING as either itself or \\\"\\\" if nil.\"\n  (if (null string) \"\" string))\n\n(defun org-ml--decode-string-or-nil (string)\n  \"Return STRING without text properties if not nil.\"\n  (when string (substring-no-properties string)))\n\n(defun org-ml--encode-string-list-delim (string-list delim)\n  \"Return STRING-LIST as string joined by DELIM.\"\n  (-some->> string-list (s-join delim)))\n\n(defun org-ml--decode-string-list-delim (string delim)\n  \"Return STRING as list of strings split by DELIM.\"\n  (-some->> string (s-split delim)))\n\n(defun org-ml--encode-string-list-space-delim (string-list)\n  \"Return STRING-LIST as string joined by spaces.\"\n  (org-ml--encode-string-list-delim string-list \" \"))\n\n(defun org-ml--decode-string-list-space-delim (string)\n  \"Return STRING as list of strings split by spaces.\"\n  (org-ml--decode-string-list-delim string \" \"))\n\n(defun org-ml--encode-string-list-comma-delim (string-list)\n  \"Return STRING-LIST as string joined by commas.\"\n  (org-ml--encode-string-list-delim string-list \",\"))\n\n(defun org-ml--decode-string-list-comma-delim (string)\n  \"Return STRING as list of strings split by commas.\"\n  (org-ml--decode-string-list-delim string \",\"))\n\n(defun org-ml--encode-plist (plist)\n  \"Return PLIST as string joined by spaces.\"\n  (-some->> (--map (format \"%S\" it) plist) (s-join \" \")))\n\n(defun org-ml--decode-plist (string)\n  \"Return STRING as plist split by spaces.\"\n  (-map #'intern (org-ml--decode-string-list-space-delim string)))\n\n;;; encode/decode (type specific)\n\n(defun org-ml--encode-latex-environment-value (value)\n  \"Return VALUE as a string representing a latex-environment.\nVALUE is a list conforming to `org-ml--is-valid-latex-environment-value'.\"\n  (-let (((env body) value))\n    (if body (format \"\\\\begin{%1$s}\\n%2$s\\n\\\\end{%1$s}\" env body)\n      (format \"\\\\begin{%1$s}\\n\\\\end{%1$s}\" env))))\n\n(defun org-ml--decode-latex-environment-value (value)\n  \"Return VALUE as a list representing a latex-environment.\nThe return value is a list conforming to\n`org-ml--is-valid-latex-environment-value'.\"\n  (let ((m (car (s-match-strings-all \"\\\\\\\\begin{\\\\(.+\\\\)}\\n\\\\(.*\\\\)\\n?\\\\\\\\end{\\\\(.+\\\\)}\" value))))\n    (list (nth 1 m) (nth 2 m))))\n\n(defun org-ml--encode-item-bullet (bullet)\n  \"Return BULLET as a formatted string.\nBULLET must conform to `org-ml--is-valid-item-bullet'.\"\n  ;; assume bullet conforms to pcase statement below\n  (pcase bullet\n    ('- \"- \")\n    ((pred integerp) (format \"%s. \" bullet))\n    (_ (error \"This should not happen\"))))\n\n(defun org-ml--decode-item-bullet (bullet)\n  \"Return BULLET as a symbol from a formatted string.\nReturn value will conform to `org-ml--is-valid-item-bullet'.\"\n  ;; NOTE this must conform to the full range of item bullets since anything\n  ;; could be parsed from an org file. Anything \"invalid\" should be converted to\n  ;; it's closest \"legal\" bullet\n  (if (s-matches? \"^\\\\(-\\\\|+\\\\)\" bullet) '-\n    (let* ((case-fold-search nil)) ; need case-sensitivity\n      (or (-some->> (s-match \"^[0-9]+\" bullet)\n                    (car)\n                    (string-to-number))\n          ;; convert letters to numbers if they are used\n          (-some->> (s-match \"^[a-z]+\" bullet)\n                    (car)\n                    (string-to-char)\n                    (+ -96))\n          (-some->> (s-match \"^[A-Z]+\" bullet)\n                    (car)\n                    (string-to-char)\n                    (+ -64))\n          (org-ml--arg-error \"Invalid bullet found: %s\" bullet)))))\n\n(defun org-ml--decode-headline-tags (tags)\n  \"Return TAGS with `org-archive-tag' removed.\"\n  (-map #'substring-no-properties (remove org-archive-tag tags)))\n\n(defun org-ml--encode-statistics-cookie-value (value)\n  \"Return VALUE as formatted string representing the cookie.\nVALUE must conform to `org-ml--is-valid-statistics-cookie-value'.\"\n  (cl-flet\n      ((mk-stat\n        (v)\n        (pcase v\n          (`(nil) \"%\")\n          (`(nil nil) \"/\")\n          (`(,percent . nil)\n           (format \"%s%%\" percent))\n          (`(,numerator . (,denominator . nil))\n           (format \"%s/%s\" numerator denominator))\n          (_ (error \"This should never happen\")))))\n    (format \"[%s]\" (mk-stat value))))\n\n(defun org-ml--decode-statistics-cookie-value (value)\n  \"Return VALUE as a list representing the cookie.\nReturn value will conform to `org-ml--is-valid-statistics-cookie-value'.\"\n  (cond\n   ((equal \"[%]\" value) '(nil))\n   ((equal \"[/]\" value) '(nil nil))\n   (t\n    (->>\n     (or (s-match-strings-all \"\\\\[\\\\([0-9]+\\\\)/\\\\([0-9]+\\\\)\\\\]\" value)\n         (s-match-strings-all \"\\\\[\\\\([0-9]+\\\\)%\\\\]\" value)\n         (org-ml--arg-error \"Invalid stats-cookie: %s\" value))\n     (cdar)\n     (-map #'string-to-number)))))\n\n(defun org-ml--encode-diary-sexp-value (value)\n  \"Return VALUE as a string.\nVALUE must conform to `org-ml--is-valid-diary-sexp-value'.\"\n  (if value (format \"%%%%%S\" value) \"%%()\"))\n\n(defun org-ml--decode-diary-sexp-value (value)\n  \"Return VALUE as a form.\nReturn value will conform to `org-ml--is-valid-diary-sexp-value'.\"\n  (->> (s-chop-prefix \"%%\" value) (read)))\n\n(defun org-ml--encode-header (plists)\n  \"Return PLISTS as a list of strings.\"\n  (--map\n   (-some->> it\n     (-partition 2)\n     (--map (format \"%S %s\" (car it) (cadr it)))\n     (s-join \" \"))\n   plists))\n\n(defun org-ml--decode-header (headers)\n  \"Return HEADERS (a list of strings) as a list of plists.\"\n  (--map\n   (->> (org-ml--decode-string-list-space-delim it)\n        (--map-indexed (if (cl-evenp it-index) (intern it) it)))\n   headers))\n\n(defun org-ml--encode-results (results)\n  \"Return a encoded results affiliated keyword value.\nRESULTS should conform to `org-ml--is-valid-caption'.\"\n  (-let (((hash source) results))\n    (when source `(,source . ,hash))))\n\n(defun org-ml--decode-results (internal-results)\n  \"Return a decoded results affiliated keyword value.\nThe returned list will conform to `org-ml--is-valid-caption' given\nINTERNAL-RESULTS stored in a node.\"\n  (-let (((source . hash) internal-results))\n    (if hash (list hash source) source)))\n\n(defun org-ml--encode-caption (caption)\n  \"Return a encoded caption affiliated keyword value.\nCAPTION should conform to `org-ml--is-valid-caption'.\"\n  (->> (reverse caption)\n       (--reduce-from\n        (pcase it\n          ((and (pred stringp) long)\n           (cons `((,long)) acc))\n          (`(,short ,long)\n           (when long\n             (cons (if short `((,long) ,short) `((,long))) acc))))\n        nil)\n       (-non-nil)))\n\n(defun org-ml--decode-caption (internal-caption)\n  \"Return a decoded caption affiliated keyword value.\nThe returned list will conform to `org-ml--is-valid-caption' given\nINTERNAL-CAPTION stored in a node.\"\n  (--reduce-from\n   (-let ((((long) short) it))\n     (-> (if short (list (substring-no-properties short)\n                         (substring-no-properties long))\n           (substring-no-properties long))\n         (cons acc)))\n   nil (reverse internal-caption)))\n\n;;; cis functions\n\n(defun org-ml--update-macro-value (macro)\n  \"Return MACRO node with its value property updated.\nThis will be based on MACRO's key and value properties.\"\n  (let* ((k (org-element-property-raw :key macro))\n         (as (org-element-property-raw :args macro))\n         (v (if as (format \"%s(%s)\" k (s-join \",\" as)) k)))\n    (org-element-put-property-2 :value (format \"{{{%s}}}\" v) macro)))\n\n(defun org-ml--update-clock-duration-and-status (clock)\n  \"Return CLOCK node with its duration and status properties updated.\nThis will be based on CLOCK's value property.\"\n  (let* ((ts (org-element-property-raw :value clock))\n         (seconds (org-ml--timestamp-get-length ts)))\n    (if (= seconds 0)\n        (org-ml--set-properties-raw clock\n          :duration nil\n          :status 'running)\n      (let* ((h (-> seconds (/ 3600) (floor)))\n             (m (-> seconds (- (* h 3600)) (/ 60) (floor))))\n        (org-ml--set-properties-raw clock\n          :duration (format \"%2d:%02d\" h m)\n          :status 'closed\n          ;; if the clock is going from non-ranged to ranged, it may not be in\n          ;; collapsed form; ensure it is not in collapsed form\n          :value (org-ml--timestamp-set-collapsed nil ts))))))\n\n(defun org-ml--update-headline-tags (headline)\n  \"Return HEADLINE node with its tags updated.\nThis will be based on HEADLINE's archivedp property.\"\n  (org-ml--map-property-raw* :tags\n    (let ((tags* (remove org-archive-tag it)))\n      (if (org-element-property :archivedp headline)\n          (-snoc tags* org-archive-tag)\n        tags*))\n    (org-element-properties-resolve headline)))\n\n(defun org-ml--link-update-type-explicit (link)\n  \"Return LINK with `:type-explicit-p' updated.\"\n  (let ((x (-> (org-element-property-raw :type link)\n               (member (org-link-types))\n               (null)\n               (not))))\n    (org-element-put-property-2 :type-explicit-p x link)))\n\n;;; shifters\n\n(defun org-ml--shift-pos-integer (n x)\n  \"Return X shifted by N (both are integers).\nIf the value to return is less than 1, return 1.\"\n  (when x\n    (let ((x* (+ x n)))\n      (if (< 0 x*) x* 1))))\n\n(defun org-ml--shift-non-neg-integer (n x)\n  \"Return X shifted by N (both are integers).\nIf the value to return is less than 0, return 0.\"\n  (when x\n    (let ((x* (+ x n)))\n      (if (<= 0 x*) x* 0))))\n\n(defun org-ml--shift-headline-priority (n priority)\n  \"Return PRIORITY shifted by N (an integer).\nIf the final value is outside the bounds of `org-highest-priority'\nand `org-lowest-priority', return as if cycling and wrapping\nbetween the priority bounds until the return value is inside the\nbounds.\"\n  (when priority\n    (let ((diff (1+ (- org-lowest-priority org-highest-priority)))\n          (offset (- priority org-highest-priority)))\n      (-> (- offset n)\n          (mod diff)\n          (- offset)\n          (+ priority)))))\n\n;;; property alist\n\n(org-ml--defconst org-ml--property-alist\n  (let* ((bool (list :pred #'booleanp\n                     :decode 'org-ml--decode-boolean\n                     :type-desc \"nil or t\"\n                     :toggle t))\n         (pos-int (list :pred #'org-ml--is-pos-integer\n                        :type-desc \"a positive integer\"))\n         (pos-int-nil (list :pred #'org-ml--is-pos-integer-or-nil\n                            :type-desc \"a positive integer or nil\"))\n         (nn-int (list :pred #'org-ml--is-non-neg-integer\n                       :type-desc \"a non-negative integer\"))\n         (nn-int-nil (list :pred #'org-ml--is-non-neg-integer-or-nil\n                           :type-desc \"a non-negative integer or nil\"))\n         (str (list :pred #'stringp\n                    :type-desc \"a string\"))\n         (str-nil (list :pred #'string-or-null-p\n                        :type-desc \"a string or nil\"))\n         (ol-str (list :pred #'org-ml--is-oneline-string\n                       :type-desc \"a oneline string\"))\n         (ol-str-nil (list :pred #'org-ml--is-oneline-string-or-nil\n                           :type-desc \"a oneline string or nil\"))\n         (plist (list :encode 'org-ml--encode-plist\n                      :pred #'org-ml--is-plist\n                      :decode 'org-ml--decode-plist\n                      :plist t\n                      :type-desc \"a plist\"))\n         (slist (list :pred #'org-ml--is-string-list\n                      :string-list t\n                      :type-desc \"a list of oneline strings\"))\n         (slist-com (list :encode 'org-ml--encode-string-list-comma-delim\n                          :decode 'org-ml--decode-string-list-comma-delim\n                          :pred #'org-ml--is-string-list\n                          :string-list t\n                          :type-desc \"a list of oneline strings\"))\n         (slist-spc (list :encode 'org-ml--encode-string-list-space-delim\n                          :decode 'org-ml--decode-string-list-space-delim\n                          :pred #'org-ml--is-string-list\n                          :string-list t\n                          :type-desc \"a list of oneline strings\"))\n         (planning-unclosed (list :pred #'org-ml--is-valid-planning-unclosed-timestamp\n                                  :type-desc \"a zero-range, active timestamp node\"))\n         (planning-closed (list :pred #'org-ml--is-valid-planning-closed-timestamp\n                                :type-desc \"a zero-range, inactive timestamp node\"))\n         (ts-unit (list :pred #'org-ml--is-valid-timestamp-unit\n                        :type-desc '(\"nil or a symbol from `year' `month'\"\n                                     \"`week' `day', or `hour'\"))))\n    (->>\n     `((babel-call (:call ,@ol-str :require t)\n                   (:inside-header ,@plist)\n                   (:arguments ,@slist-com)\n                   (:end-header ,@plist)\n                   (:value))\n       (bold)\n       (center-block)\n       (clock (:value :pred org-ml--is-valid-clock-timestamp\n                      :cis org-ml--update-clock-duration-and-status\n                      :type-desc (\"a ranged or unranged inactive timestamp\"\n                                  \"node with no warning or repeater\")\n                      :require t)\n              (:status)\n              (:duration))\n       (code (:value ,@str :require t))\n       (comment (:value ,@str :require t))\n       (comment-block (:value ,@str :decode s-trim-right :require \"\"))\n       (drawer (:drawer-name ,@ol-str :require t))\n       (diary-sexp (:value :encode org-ml--encode-diary-sexp-value\n                           :pred org-ml--is-valid-diary-sexp-value\n                           :decode org-ml--decode-diary-sexp-value\n                           :type-desc \"a list form or nil\"))\n       (dynamic-block (:arguments ,@plist)\n                      (:block-name ,@ol-str :require t))\n       (entity (:name :pred org-ml--is-valid-entity-name\n                      :type-desc \"a string that makes `org-entity-get' return non-nil\"\n                      :require t)\n               (:use-brackets-p ,@bool)\n               (:latex)\n               (:latex-math-p)\n               (:html)\n               (:ascii)\n               (:latin1)\n               (:utf-8))\n       (example-block (:preserve-indent ,@bool)\n                      (:switches ,@slist-spc)\n                      (:value ,@str :require \"\" :decode s-trim-right)\n                      ;; TODO some of these are tied to switches, it\n                      ;; may be good to set them directly\n                      (:number-lines)\n                      (:retain-labels)\n                      (:use-labels)\n                      (:label-fmt))\n       (export-block (:type ,@ol-str :require t)\n                     (:value ,@str :require t))\n       (export-snippet (:back-end ,@ol-str :require t)\n                       (:value ,@str :require t))\n       (fixed-width (:value ,@ol-str :decode s-trim-right :require t))\n       (footnote-definition (:label ,@ol-str :require t)\n                            (:pre-blank ,@nn-int\n                                        :shift org-ml--shift-non-neg-integer\n                                        :require 0))\n       (footnote-reference (:label ,@ol-str-nil)\n                           (:type))\n       (headline (:archivedp ,@bool :cis org-ml--update-headline-tags)\n                 (:commentedp ,@bool)\n                 (:footnote-section-p ,@bool)\n                 (:level ,@pos-int\n                         :shift org-ml--shift-pos-integer\n                         :require 1)\n                 (:pre-blank ,@nn-int\n                             :shift org-ml--shift-non-neg-integer\n                             :require 0)\n                 ;; ,@robust\n                 (:priority :pred org-ml--is-valid-headline-priority\n                            :shift org-ml--shift-headline-priority\n                            :type-desc (\"an integer between (inclusive)\"\n                                        \"`org-highest-priority' and\"\n                                        \"`org-lowest-priority'\"))\n                 (:tags :pred org-ml--is-valid-headline-tags\n                        :decode org-ml--decode-headline-tags\n                        :cis org-ml--update-headline-tags\n                        :type-desc \"a string list\"\n                        :string-list t)\n                 (:title :pred org-ml--is-valid-headline-title\n                         :type-desc \"a secondary string\")\n                 (:todo-keyword ,@ol-str-nil\n                                :decode org-ml--decode-string-or-nil)\n                 (:raw-value)\n                 (:todo-type))\n       (horizontal-rule)\n       (inline-babel-call (:call ,@ol-str :require t)\n                          (:inside-header ,@plist)\n                          (:arguments ,@slist-com)\n                          (:end-header ,@plist)\n                          (:value))\n       (inline-src-block (:language ,@ol-str :require t)\n                         (:parameters ,@plist)\n                         (:value ,@str :require \"\"))\n       ;; (inlinetask)\n       (italic)\n       (item (:bullet :encode org-ml--encode-item-bullet\n                      :pred org-ml--is-valid-item-bullet\n                      :decode org-ml--decode-item-bullet\n                      :type-desc (\"a positive integer (ordered)\"\n                                  \"or the symbol `-' (unordered)\")\n                      :require '-)\n             (:pre-blank ,@nn-int\n                         :shift org-ml--shift-non-neg-integer\n                         :require 0)\n             (:checkbox :pred org-ml--is-valid-item-checkbox\n                        :type-desc \"nil or the symbols `on', `off', or `trans'\")\n             (:counter ,@pos-int-nil :shift org-ml--shift-pos-integer)\n             (:tag :pred org-ml--is-valid-item-tag\n                   :type-desc \"a secondary string\"))\n             ;; (:structure))\n       (keyword (:key ,@ol-str :require t)\n                (:value ,@ol-str :require t))\n       (latex-environment (:value :encode org-ml--encode-latex-environment-value\n                                  :pred org-ml--is-valid-latex-environment-value\n                                  :decode org-ml--decode-latex-environment-value\n                                  :type-desc \"a list of strings like (ENV BODY) or (ENV)\"\n                                  :require t))\n       (latex-fragment (:value ,@str :require t))\n       (line-break)\n       (link (:path ,@ol-str :require t)\n             (:format :pred org-ml--is-valid-link-format\n                      :type-desc \"the symbol `plain', `bracket' or `angle'\")\n             (:type :pred org-ml--is-valid-link-type\n                    :cis org-ml--link-update-type-explicit\n                    :type-desc (\"a oneline string from `org-link-types'\"\n                                \"or \\\"coderef\\\", \\\"custorg-ml-id\\\",\"\n                                \"\\\"file\\\", \\\"id\\\", \\\"radio\\\", or\"\n                                \"\\\"fuzzy\\\"\")\n                    ;; TODO is fuzzy a good default?\n                    :require \"fuzzy\")\n             (:raw-link)\n             (:application)\n             (:search-option))\n       (macro (:args ,@slist :cis org-ml--update-macro-value)\n              (:key ,@ol-str :cis org-ml--update-macro-value :require t)\n              (:value))\n       (node-property (:key ,@ol-str :require t)\n                      (:value ,@ol-str :require t))\n       (paragraph)\n       (plain-list ;;(:structure)\n                   (:type))\n       (plain-text)\n       (planning (:closed ,@planning-closed)\n                 (:deadline ,@planning-unclosed)\n                 (:scheduled ,@planning-unclosed))\n       (property-drawer)\n       (quote-block)\n       ;; TODO this should not have multiline strings in it\n       (radio-target (:value))\n       (section)\n       (special-block (:type ,@ol-str :require t) (:parameters ,@ol-str-nil))\n       (src-block (:value ,@str :decode s-trim-right :require \"\")\n                  (:language ,@str-nil)\n                  (:parameters ,@plist)\n                  (:preserve-indent ,@bool)\n                  (:switches ,@slist-spc)\n                  (:number-lines)\n                  (:retain-labels)\n                  (:use-labels)\n                  (:label-fmt))\n       (statistics-cookie (:value\n                           :encode org-ml--encode-statistics-cookie-value\n                           :pred org-ml--is-valid-statistics-cookie-value\n                           :decode org-ml--decode-statistics-cookie-value\n                           :type-desc (\"a list of non-neg integers\"\n                                       \"like (PERC) or (NUM DEN)\"\n                                       \"which make [NUM/DEN] and\"\n                                       \"[PERC%] respectively\")\n                           :require t))\n       (strike-through)\n       ;; TODO these should only allow multiline strings if bracketed\n       (subscript (:use-brackets-p ,@bool))\n       (superscript (:use-brackets-p ,@bool))\n       (table (:tblfm ,@slist)\n              (:type :const 'org)\n              (:value))\n       ;; TODO this should not have multiline strings in it\n       (table-cell)\n       (table-row (:type :const 'standard))\n       (target (:value ,@ol-str :require t))\n       (timestamp (:type :pred org-ml--is-valid-timestamp-type\n                         :type-desc (\"a symbol from `inactive',\"\n                                     \"`active', `inactive-range', or\"\n                                     \"`active-range'\")\n                         :require t)\n                  (:range-type :pred org-ml--is-valid-timestamp-range-type\n                               :type-desc (\"either symbol `daterange' or\"\n                                           \"`timerange' or nil\"))\n                  (:year-start ,@pos-int :require t)\n                  (:month-start ,@pos-int :require t)\n                  (:day-start ,@pos-int :require t)\n                  (:year-end ,@pos-int :require t)\n                  (:month-end ,@pos-int :require t)\n                  (:day-end ,@pos-int :require t)\n                  (:hour-start ,@nn-int-nil)\n                  (:minute-start ,@nn-int-nil)\n                  (:hour-end ,@nn-int-nil)\n                  (:minute-end ,@nn-int-nil)\n                  (:repeater-type :pred org-ml--is-valid-timestamp-repeater-type\n                                  :type-desc (\"nil or a symbol from\"\n                                              \"`catch-up', `restart',\"\n                                              \"or `cumulate'\"))\n                  (:repeater-unit ,@ts-unit)\n                  (:repeater-value ,@pos-int-nil)\n                  (:repeater-deadline-unit ,@ts-unit)\n                  (:repeater-deadline-value ,@pos-int-nil)\n                  (:warning-type :pred org-ml--is-valid-timestamp-warning-type\n                                 :type-desc (\"nil or a symbol from\"\n                                             \"`all' or `first'\"))\n                  (:warning-unit ,@ts-unit)\n                  (:warning-value ,@pos-int-nil)\n                  (:raw-value))\n       (underline)\n       (verbatim (:value ,@str :require t))\n       (verse-block))\n     (--map-when (memq (car it) org-ml--element-nodes-with-affiliated)\n                 (append it\n                         `((:name ,@str-nil)\n                           (:plot ,@str-nil)\n                           (:header :encode org-ml--encode-header\n                                    :pred org-ml--is-valid-header\n                                    :decode org-ml--decode-header\n                                    :type-desc (\"a list of plists where all\"\n                                                \"plist values are strings\"))\n                           (:results :encode org-ml--encode-results\n                                     :pred org-ml--is-valid-results\n                                     :decode org-ml--decode-results\n                                     :type-desc (\"a list like (SOURCE) or\"\n                                                 \"(HASH SOURCE) where HASH\"\n                                                 \"and SOURCE are strings.\"))\n                           (:caption :encode org-ml--encode-caption\n                                     :pred org-ml--is-valid-caption\n                                     :decode org-ml--decode-caption\n                                     :type-desc (\"a list including (LONG) or\"\n                                                 \"(SHORT LONG) where SHORT and\"\n                                                 \"LONG are both strings representing\"\n                                                 \"the short and long captions\"))))))))\n\n;;; node property operations\n\n;; alist functions\n\n(eval-when-compile\n  (defun org-ml--flatten-attribute (attr)\n    (->> org-ml--property-alist\n         (--map (cons (car it)\n                      (->> (cdr it)\n                           (--map (cons (car it) (plist-get (cdr it) attr)))\n                           (-filter #'cdr))))\n         (-filter #'cdr)))\n\n  (defun org-ml--flatten-attribute-boolean (attr)\n    (->> org-ml--property-alist\n         (--map (cons (car it)\n                      (->> (cdr it)\n                           (--filter (plist-get (cdr it) attr))\n                           (-map #'car))))\n         (-filter #'cdr))))\n\n(org-ml--defconst org-ml--property-decoder-functions\n  (--map (cons (car it)\n               (--map (cons (car it) (plist-get (cdr it) :decode))\n                      (cdr it)))\n         org-ml--property-alist))\n\n(org-ml--defconst org-ml--property-encoder-functions\n  (org-ml--flatten-attribute :encode))\n\n(org-ml--defconst org-ml--property-predicate-functions\n  (org-ml--flatten-attribute :pred))\n\n(org-ml--defconst org-ml--property-shifter-functions\n  (org-ml--flatten-attribute :shift))\n\n(org-ml--defconst org-ml--property-updater-functions\n  (org-ml--flatten-attribute :cis))\n\n(org-ml--defconst org-ml--property-type-descriptions\n  (cl-flet\n      ((map-cdr\n         (cell f)\n         (cons (car cell) (funcall f (cdr cell)))))\n    (->> (org-ml--flatten-attribute :type-desc)\n         (--map (map-cdr it (lambda (props)\n                              (--map (map-cdr it\n                                              (lambda (desc)\n                                                (if (listp desc)\n                                                    (s-join \" \" desc)\n                                                  desc)))\n                                     props)))))))\n\n(org-ml--defconst org-ml--properties-with-toggle\n  (org-ml--flatten-attribute-boolean :toggle))\n\n(org-ml--defconst org-ml--properties-with-string-list\n  (org-ml--flatten-attribute-boolean :string-list))\n\n(org-ml--defconst org-ml--properties-with-plist\n  (org-ml--flatten-attribute-boolean :plist))\n\n(defun org-ml--get-property-encoder (type prop)\n  \"Return the encoder for PROP of node TYPE.\"\n  (->> (alist-get type org-ml--property-encoder-functions)\n       (alist-get prop)))\n\n(defun org-ml--get-property-decoder (type prop)\n  \"Return the decoder function for PROP of node TYPE.\nIf TYPE does not exist, return error. If PROP does not exist for\nTYPE, also return error. If type does exist, return the decoder\nfunction or nil if there is none.\"\n    (-if-let (ps (alist-get type org-ml--property-decoder-functions))\n        (-if-let (f (assq prop ps))\n            (cdr f)\n          (org-ml--arg-error \"Type '%s' does not have property '%s'\" type prop))\n      (org-ml--arg-error \"Tried to query '%s' for non-existent '%s'\" prop type)))\n\n(defun org-ml--get-property-updater (type prop)\n  \"Return the updater for PROP of node TYPE.\"\n  (->> (alist-get type org-ml--property-updater-functions)\n       (alist-get prop)))\n\n(defun org-ml--get-property-type-desc (type prop)\n  \"Return the description for PROP of node TYPE.\"\n  (->> (alist-get type org-ml--property-type-descriptions)\n       (alist-get prop)))\n\n(defun org-ml--get-property-shifter (type prop)\n  \"Lookup shifter function for TYPE and PROP.\"\n  (if (eq prop :post-blank) #'org-ml--shift-non-neg-integer\n    (->> (alist-get type org-ml--property-shifter-functions)\n         (alist-get prop))))\n\n(defun org-ml--get-property-predicate (type prop)\n  \"Lookup shifter function for TYPE and PROP.\"\n  (if (eq prop :post-blank) #'org-ml--is-non-neg-integer\n    (->> (alist-get type org-ml--property-predicate-functions)\n         (alist-get prop))))\n\n(defun org-ml--property-memq (alist type prop)\n  \"Return t if PROP is in the cdr of TYPE in ALIST.\"\n  (memq prop (alist-get type alist)))\n\n(defun org-ml--property-error-unsettable (prop type)\n  \"Throw error signifying that PROP is unsettable of node TYPE.\"\n  (org-ml--arg-error \"Property '%s' is unsettable for type '%s'\" prop type))\n\n(defun org-ml--property-error-wrong-type (prop type value)\n  \"Throw error signifying that VALUE is wrong for PROP of node TYPE.\"\n  (let ((msg \"Property '%s' in node of type '%s' must be %s. Got '%S'\")\n        (correct-type (org-ml--get-property-type-desc type prop)))\n    (org-ml--arg-error msg prop type correct-type value)))\n\n(defun org-ml--property-encode (prop value type)\n  \"Given TYPE and PROP, return encoded VALUE.\"\n  (-if-let (pred (org-ml--get-property-predicate type prop))\n      (if (funcall pred value)\n          (-if-let (encode-fun (org-ml--get-property-encoder type prop))\n              (funcall encode-fun value)\n            value)\n        (org-ml--property-error-wrong-type prop type value))\n    (org-ml--property-error-unsettable prop type)))\n\n;;; INTERNAL BRANCH/CHILD MANIPULATION\n\n(defun org-ml--get-descendent (indices node)\n  \"Return the nested children of NODE as given by INDICES.\nINDICES is a list of integers specifying the index and level of the\nnested element to return.\"\n  (if (not indices) node\n    (->> (org-element-contents node)\n         (nth (car indices))\n         (org-ml--get-descendent (cdr indices)))))\n\n(defun org-ml--set-children-nocheck (children node)\n  \"Return NODE with children set to CHILDREN.\"\n   (let ((head (org-ml--get-head node)))\n     (if children (append head children) head)))\n\n(eval-when-compile\n  (defmacro org-ml--map-children-nocheck* (form node)\n    \"Return NODE with FORM applied to its children.\n\nFORM is a form with `it' bound to the list of children and\nreturns a modified list of children.\"\n    (declare (debug (form form)))\n    (let ((n (make-symbol \"--node\")))\n    `(let* ((,n ,node)\n            (it (org-element-contents ,n)))\n       (org-ml--set-children-nocheck ,form ,n)))))\n\n(defun org-ml--set-children-throw-error (type child-types illegal)\n  \"Throw an `arg-type-error' for TYPE.\nIn the message specify that allowed child types are CHILD-TYPES\nand ILLEGAL types were attempted to be set.\"\n  (cl-flet\n      ((format-types\n        (type-list)\n        (->> type-list (-map #'symbol-name) (s-join \", \"))))\n    (let ((fmt (->> '(\"Setting illegal child types for node type '%s'\"\n                      \"illegal types found: %s\"\n                      \"allowed types are: %s\")\n                    (s-join \"; \")))\n          (illegal (format-types illegal))\n          (child-types (format-types child-types)))\n      (org-ml--arg-error fmt type illegal child-types))))\n\n;;; BASE BUILDER FUNCTIONS\n\n;;; build helpers\n\n(eval-and-compile\n  (defun org-ml--build-bare-node (type post-blank props children)\n    \"Return new node of TYPE with POST-BLANK, PROPS and CHILDREN.\nTYPE is a symbol and POST-BLANK is a positive integer.\"\n    ;; don't set children in the function itself a) so I can check for valid\n    ;; types and b) because `org-element-create' will add :parent\n    (let ((node (org-element-create type `(:post-blank ,(or post-blank 0) ,@props))))\n      ;; Use this function here so we get child type checks\n      (if children (org-ml-set-children children node) node))))\n\n(defmacro org-ml--build-blank-node (type post-blank)\n  \"Return new node of TYPE with POST-BLANK and all properties set to nil.\"\n  (let ((ips (->> (alist-get type org-ml--property-alist)\n                  (-map #'car)\n                  (--mapcat (list it nil))\n                  (cons 'list))))\n    `(org-ml--build-bare-node ',type ,post-blank ,ips nil)))\n\n;;; base builders\n\n;; define all base builders using this automated monstrosity\n\n(defmacro org-ml--with-cache (table switch valid type key body)\n  \"Run BODY with a memoization cache.\n\nTABLE is a symbol with an alist indexed by TYPE. SWITCH is a\nsymbol which will be dynamically read at runtime to determine if\nthe cache should be used. VALID is a symbol bound to a list of\nvalid types (at runtime) which should use the cache. KEY is the\nlookup key to be used in the cache (which is actually a hash\ntable) and is assumed to correspond to the inputs to BODY.\n\nIf KEY is not in the cache, run BODY and put the result in the\ncache under KEY. If KEY is in the cache, return whatever that is.\"\n  (let* ((k (make-symbol \"--key\"))\n         (n (make-symbol \"--node\"))\n         (c (make-symbol \"--cached\"))\n         (h (alist-get type (eval table))))\n    (unless h\n      (error \"Failed to get cache table for %s\" type))\n    `(let* ((,k ,key)\n            (,c (and ,switch (memq ',type ,valid) (gethash ,k ,h))))\n       (if ,c (org-ml-copy ,c t)\n         ;; turn off memoizer internally since some shorthand builders\n         ;; call other shorthand builders and caching each layer is probably\n         ;; overkill since none of these functions have that many arguments\n         ;; to vary\n         (let ((,switch nil))\n           (let ((,n ,body))\n             (puthash ,k (org-ml-copy ,n t) ,h)\n             ,n))))))\n\n(eval-and-compile\n  (defvar org-ml--builder-cache\n    (--map (cons it (make-hash-table :test #'equal)) org-ml-builder-types)\n    \"Alist of hash tables to store builder results.\"))\n\n(defun org-ml-clear-builder-cache ()\n  \"Clear the memoization cache for node builders.\"\n  (interactive)\n  (--each org-ml--builder-cache\n    (clrhash (cdr it))))\n\n(defmacro org-ml--with-builder-cache (type key body)\n  \"Run BODY with builder cache.\nSee org-ml--with-cache' for meaning of TYPE and KEY.\"\n  (declare (indent 1))\n  `(org-ml--with-cache\n     org-ml--builder-cache\n     org-ml-memoize-builders\n     org-ml-memoize-builder-types\n     ,type ,key ,body))\n\n(eval-when-compile\n  (defun org-ml--autodef-kwd-to-sym (keyword)\n    \"Return KEYWORD as a string with no leading colon.\"\n    (->> (symbol-name keyword) (s-chop-prefix \":\") (intern)))\n\n  (defun org-ml--autodef-prepend-article (string)\n    \"Return STRING starting with \\\"a\\\" or \\\"an\\\" depending on first word.\"\n    (let ((a (--> (symbol-name string)\n                  (s-left 1 it)\n                  (if (member it '(\"a\" \"e\" \"i\" \"o\" \"u\")) \"an\" \"a\"))))\n      (format \"%s %s\" a string)))\n\n  (defun org-ml--autodef-categorize-prop (prop)\n    \"Return category for PROP.\"\n    (-let (((&plist :require :pred :const) (cdr prop)))\n      (cond\n       (const 'const)\n       ((not pred) 'null)\n       ((eq require t) 'req)\n       (t 'key))))\n\n  (defun org-ml--autodef-prop-form (len fun-0 fun-n props)\n    \"Return form to set properties to PROPS.\nIf list PROPS is length LEN, use FUN-0, otherwise FUN-N.\"\n    (declare (indent 1))\n    (if (= len (length props)) `(,fun-0 ,@props) `(,fun-n (list ,@props))))\n\n  (defun org-ml--indent-doc (s)\n    (with-temp-buffer\n      (insert s)\n      (fill-paragraph)\n      (buffer-string)))\n\n  (defun org-ml--autodef-make-docstring (type rest-arg props)\n    \"Return docstring for PROPS.\nTYPE is the type of the node in question and REST-ARG is the\nsymbol for the rest argument.\"\n    (let ((class (if (memq type org-element-all-elements) \"element\" \"object\"))\n          (end (if (not rest-arg) \".\"\n                 (->> (symbol-name rest-arg)\n                      (s-upcase)\n                      (format \" with %s as children.\"))))\n          ;; (post-blank (if element? \"newlines\" \"spaces\"))\n          (prop\n           (-some->>\n            (append (alist-get 'req props) (alist-get 'key props))\n            (--map (let ((p (->> (car it)\n                                 (symbol-name)\n                                 (s-chop-prefix \":\")\n                                 (s-upcase)))\n                         (r (-->\n                             (plist-get (cdr it) :require)\n                             (pcase it\n                               ((pred stringp)\n                                (format \"(default %S)\" it))\n                               (`(quote ,s)\n                                (format \"(default `%s')\" s))\n                               ((guard (eq it t))\n                                \"(required)\")\n                               (_ \"\"))))\n                         (d (plist-get (cdr it) :type-desc)))\n                     (unless d\n                       (error \"No type-desc: %s %s\" type p))\n                     (->> (if (listp d) (s-join \" \" d) d)\n                          (format \"- %s: %s %s\" p r)\n                          (org-ml--indent-doc))))\n            (s-join \"\\n\"))))\n      (concat\n       (format \"Build %s %s node\" (org-ml--autodef-prepend-article type) class)\n       end\n       \"\\n\\nThe following properties are settable:\\n\"\n       prop \"\\n- POST-BLANK: a non-negative integer\")))\n\n  (defun org-ml--autodef-build-node-form (entry)\n    \"Return defun form for ENTRY.\"\n    (let* ((type (car entry))\n           (name (intern (format \"org-ml-build-%s\" type)))\n           (props (->> (cdr entry)\n                       (--remove (eq :post-blank (car it)))\n                       (-non-nil)\n                       (-group-by #'org-ml--autodef-categorize-prop)))\n           (pos-args (->> (alist-get 'req props)\n                          (--map (org-ml--autodef-kwd-to-sym (car it)))))\n           (kw-args (->> (alist-get 'key props)\n                         (--map (let ((prop (org-ml--autodef-kwd-to-sym (car it)))\n                                      (default (plist-get (cdr it) :require)))\n                                  (if default `(,prop ,default) prop)))))\n           (rest-arg (cond\n                      ((memq type org-element-greater-elements) 'element-nodes)\n                      ((memq type org-element-object-containers) 'object-nodes)))\n           (args (let ((a `(,@pos-args &key ,@kw-args post-blank)))\n                   (if rest-arg `(,@a &rest ,rest-arg) a)))\n           (const-props (->> (alist-get 'const props)\n                             (--mapcat (list (car it)\n                                             (plist-get (cdr it) :const)))))\n           (nil-props (->> (alist-get 'null props)\n                           (-map #'car)\n                           (--mapcat (list it nil))))\n           (strict-props\n            (->> (append (alist-get 'key props)\n                         (alist-get 'req props))\n                 (-map #'car)\n                 (--mapcat (list it\n                                 `(org-ml--property-encode\n                                   ,it ,(org-ml--autodef-kwd-to-sym it) ',type)))))\n           (all-props (-some->> (append strict-props nil-props const-props)\n                       (cons 'list)))\n           (updaters (->> (alist-get type org-ml--property-updater-functions)\n                          (-map #'cdr)\n                          (-uniq)))\n           (doc (org-ml--autodef-make-docstring type rest-arg props))\n           (inner-body `(org-ml--build-bare-node\n                         ',type post-blank\n                         ,all-props\n                         ,rest-arg))\n           (prop-syms (->> (alist-get 'key props)\n                           (append (alist-get 'req props))\n                           (-map #'car)\n                           (-map #'org-ml--autodef-kwd-to-sym)\n                           (cons 'post-blank)))\n           (memoizer-key\n            (cond\n             ((and rest-arg (not prop-syms)) rest-arg)\n             ((and rest-arg (= (length prop-syms) 1)) `(cons ,@prop-syms ,rest-arg))\n             (rest-arg `(append (list ,@prop-syms) ,rest-arg))\n             ((not prop-syms) nil)\n             ((= (length prop-syms) 1) (car prop-syms))\n             (t `(list ,@prop-syms))))\n           (body (if updaters\n                     (let ((us (--map `(funcall #',it node) updaters)))\n                       `(let ((node ,inner-body))\n                          ,@us))\n                   inner-body))\n           (memoized-body\n            (if memoizer-key\n                `(org-ml--with-builder-cache ,type ,memoizer-key ,body)\n              body)))\n      (macroexpand `(org-ml--defun-kw ,name ,args ,doc ,memoized-body))))\n\n  (defmacro org-ml--autodef-build-node-functions ()\n    \"Define all build node functions.\"\n    (let ((forms (--> (--remove (eq 'plain-text (car it)) org-ml--property-alist)\n                      (--map (org-ml--autodef-build-node-form it) it))))\n      `(progn ,@forms))))\n\n(org-ml--autodef-build-node-functions)\n\n;; INTERNAL TYPE-SPECIFIC PROPERTY FUNCTIONS\n\n;;; object nodes\n;;\n;; statistics-cookie\n\n(defun org-ml--statistics-cookie-get-format (statistics-cookie)\n  \"Return format of STATISTICS-COOKIE as a symbol.\nIf fractional cookie, return `fraction'; if percentage cookie return\n`percent', else throw error (which should never happen).\"\n  (let ((value (org-element-property-raw :value statistics-cookie)))\n    (cond ((s-contains? \"/\" value) 'fraction)\n          ((s-contains? \"%\" value) 'percent)\n          (t (org-ml--arg-error \"Unparsable statistics cookie: %s\" value)))))\n\n;; timestamp (auxiliary functions)\n\n;; terminology (in haskell types)\n;; type Date = (Y, M, D)\n;; type Time = (H, M)\n;; type DateTime = (Y, M, D, H, M)\n;; type TimeList = (Y, M, D, (Maybe H), (Maybe M))\n\n(defun org-ml-is-time-p (time)\n  \"Return t if TIME is a list like (hour min).\"\n  (pcase time (`(,(pred integerp) ,(pred integerp)) t)))\n\n(defun org-ml-timelist-has-time (timelist)\n  \"Return t if TIMELIST has a time.\"\n  (pcase timelist\n    (`(,(pred integerp) ,(pred integerp) ,(pred integerp)\n       ,(pred integerp) ,(pred integerp))\n     t)))\n\n;; make these public, not sure where else to put them\n(defun org-ml-timelist-to-unixtime (timelist)\n  \"Return the unix time (integer seconds) of TIMELIST.\nThe returned value is dependent on the time zone of the operating\nsystem.\"\n  (->> (-let (((y m d H M) timelist))\n         (list 0 (or M 0) (or H 0) d m y nil -1 (current-time-zone)))\n       (encode-time)\n       (float-time)\n       (round)))\n\n(defun org-ml-unixtime-to-timelist (has-time unixtime)\n  \"Return the long time list of UNIXTIME.\n\nThe list will be formatted like (YEAR MONTH DAY HOUR MIN) unless\nHAS-TIME is nil, in which case HOUR and MIN will be set to nil.\"\n  (-let (((M H d m y) (-slice (decode-time unixtime (current-time-zone)) 1 6)))\n    (if has-time (list y m d H M) (list y m d nil nil))))\n\n(defun org-ml-unixtime-to-datetime (unixtime)\n  \"Return the long time list of UNIXTIME.\nThe list will be formatted like (YEAR MONTH DAY HOUR MIN).\"\n  (reverse (-slice (decode-time unixtime (current-time-zone)) 1 6)))\n\n(defun org-ml-unixtime-to-date (unixtime)\n  \"Return the short time list of UNIXTIME.\nThe list will be formatted like (YEAR MONTH DAY nil nil).\"\n  (append (-take 3 (org-ml-unixtime-to-datetime unixtime)) '(nil nil)))\n\n(defun org-ml--timelist-truncate (timelist)\n  \"Return the date of TIMELIST with hour amd minute fields nil-ed.\"\n  `(,@(-take 3 timelist) nil nil))\n\n(defun org-ml-timelist-split (timelist)\n  \"Return TIMELIST split into ((Y M D) (H M)).\n\nThe second member will be nil if either hours or minutes is nil.\"\n  (-let (((ymd (h m)) (-split-at 3 timelist)))\n    (list ymd (if (and h m) (list h m) nil))))\n\n(defun org-ml-times-equal-date-p (time1 time2)\n  \"Return t if the dates of TIME1 and TIME2 are the same.\"\n  (equal (org-ml--timelist-truncate time1) (org-ml--timelist-truncate time2)))\n\n(defun org-ml--timelists-get-range-type (timelist1 timelist2 original)\n  \"Return range type of TIMELIST1 and TIMELIST2.\n\nValid return values are nil (unranged), `daterange' (ranged with\ndifferent dates), or `timerange' (ranged with same date).\n\nORIGINAL can be any of the return values above. If TIME1 and TIME2 are\na timerange as defined above, return ORIGINAL if it is non-nil.\"\n  (-let (((d1 t1) (org-ml-timelist-split timelist1))\n         ((d2 t2) (org-ml-timelist-split timelist2)))\n    (if (equal d1 d2)\n        (if (equal t1 t2) nil (or original 'timerange))\n      'daterange)))\n\n;; ASSUME any \"impossible datetimes\" will be corrected when the timelist is\n;; parsed back into a timestamp (or however it will be used). Ie if I add 10000\n;; days to any timestamp assume this will be reflected sensibly in the month\n;; and year of the final result downstream.\n(defun org-ml-timelist-shift (n unit timelist)\n  \"Return modified TIMELIST shifted N UNIT's.\n\nUNIT is one of `day', `week', `month', `year', `minute', or `hour'.\nN is an integer.\"\n  (-let (((i s) (cond\n                 ((eq unit 'year) `(0 ,n))\n                 ((eq unit 'month) `(1 ,n))\n                 ((eq unit 'week) `(2 ,(* 7 n)))\n                 ((eq unit 'day) `(2 ,n))\n                 ((and (eq unit 'hour) (org-ml-timelist-has-time timelist)) `(3 ,n))\n                 ((and (eq unit 'minute) (org-ml-timelist-has-time timelist)) `(4 ,n))\n                 (t (org-ml--arg-error \"Invalid time unit: %S\" unit)))))\n    (org-ml--map-at* i (+ s it) timelist)))\n\n(defun org-ml--time-shift (n unit time)\n  \"Return modified TIME shifted N UNITs (modulo).\n\nUNIT is `minute', or `hour'. N is an integer.\"\n  (-let* ((f (pcase unit\n               (`hour 60)\n               (`minute 1)\n               (_ (org-ml--arg-error \"Invalid time unit: %S\" unit))))\n          ((H M) time)\n          (s (+ (* n f) (* H 60) M)))\n    (list (mod (/ s 60) 24) (mod s 60))))\n\n;; timestamp (regular)\n\n;; ASSUME the source of truth for if a timestamp is ranged or not is in the\n;; :ranged-type property. This is much faster than querying each piece of the\n;; timestamp and inferring if it is ranged or not. It also is less ambiguous for\n;; in cases where the timestamp may be collapsed.\n\n(defun org-ml--check-time (H M)\n  \"Check time, decomposed into H and M.\"\n  (unless (and (integerp H) (integerp M))\n    (org-ml--arg-error \"Invalid time %s\" (list H M))))\n\n(defun org-ml--check-time-from-list (time)\n  \"Check TIME.\"\n  (if (consp time)\n      (-let (((H M) time))\n        (org-ml--check-time H M))\n    (org-ml--arg-error \"Time must not be nil\")))\n\n(defun org-ml--check-timelist (y m d H M)\n  \"Check timelist, decomposed into Y M D H and M.\"\n  (unless (and (integerp y)\n               (integerp m)\n               (integerp d)\n               (or (not H) (integerp H))\n               (or (not M) (integerp M)))\n    (org-ml--arg-error \"Invalid timelist %s\" (list y m d H M))))\n\n(defun org-ml--check-timelist-from-list (timelist)\n  \"Check TIMELIST.\"\n  (if (consp timelist)\n      (-let (((y m d H M) timelist))\n        (org-ml--check-timelist y m d H M))\n    (org-ml--arg-error \"Timelist must not be nil\")))\n\n(defun org-ml--check-warning (type value unit)\n  \"Check that warning (TYPE VALUE UNIT) is valid.\"\n  (unless (and (org-ml--is-valid-timestamp-warning-type type)\n               (integerp value)\n               (org-ml--is-valid-timestamp-unit unit))\n    (org-ml--arg-error \"Invalid warning %s\" (list type value unit))))\n\n(defun org-ml--check-repeater (type value unit)\n  \"Check that repeater (TYPE VALUE UNIT) is valid.\"\n  (unless (and (org-ml--is-valid-timestamp-repeater-type type)\n               (integerp value)\n               (org-ml--is-valid-timestamp-unit unit))\n    (org-ml--arg-error \"Invalid repeater %s\" (list type value unit))))\n\n(defun org-ml--check-deadline (value unit)\n  \"Check that deadline (VALUE UNIT) is valid.\"\n  (unless (and (integerp value) (org-ml--is-valid-timestamp-unit unit))\n    (org-ml--arg-error \"Invalid deadline %s\" (list value unit))))\n\n(defun org-ml--check-warning-from-list (warning)\n  \"Check that WARNING is valid.\"\n  (if (consp warning)\n      (-let (((y v u) warning))\n        (org-ml--check-warning y v u))\n    (org-ml--arg-error \"Warning must not be nil\")))\n\n(defun org-ml--check-repeater-from-list (repeater)\n  \"Check that REPEATER is valid.\"\n  (if (consp repeater)\n      (-let (((y v u) repeater))\n        (org-ml--check-repeater y v u))\n    (org-ml--arg-error \"Repeater must not be nil\")))\n\n(defun org-ml--check-deadline-from-list (deadline)\n  \"Check that DEADLINE is valid.\"\n  (if (consp deadline)\n      (-let (((v u) deadline))\n        (org-ml--check-deadline v u))\n    (org-ml--arg-error \"Repeater must not be nil\")))\n\n(defun org-ml--timestamp-get-start-timelist (timestamp)\n  \"Return the timelist of the start time in TIMESTAMP.\"\n  (-let (((&plist :minute-start n :hour-start h :day-start d\n                  :month-start m :year-start y)\n          (org-ml--get-nonstandard-properties timestamp)))\n    `(,y ,m ,d ,h ,n)))\n\n(defun org-ml--timestamp-get-start-date (timestamp)\n  \"Return the start date of TIMESTAMP.\"\n  (-let (((&plist :year-start y :month-start m :day-start d)\n          (org-ml--get-nonstandard-properties timestamp)))\n    `(,y ,m ,d)))\n\n(defun org-ml--timestamp-get-start-time (timestamp)\n  \"Return the start time of TIMESTAMP or nil if not set.\"\n  (-let (((&plist :minute-start m :hour-start h)\n          (org-ml--get-nonstandard-properties timestamp)))\n    (if (and h m) `(,h ,m) nil)))\n\n(defun org-ml--timestamp-get-end-timelist (timestamp)\n  \"Return the timelist of the end time in TIMESTAMP.\"\n  (-let (((&plist :minute-end n :hour-end h :day-end d\n                  :month-end m :year-end y)\n          (org-ml--get-nonstandard-properties timestamp)))\n    `(,y ,m ,d ,h ,n)))\n\n(defun org-ml--timestamp-get-end-date (timestamp)\n  \"Return the end date of TIMESTAMP.\"\n  (-let (((&plist :year-end y :month-end m :day-end d)\n          (org-ml--get-nonstandard-properties timestamp)))\n    `(,y ,m ,d)))\n\n(defun org-ml--timestamp-get-end-time (timestamp)\n  \"Return the end time of TIMESTAMP or nil if not set.\"\n  (-let (((&plist :minute-end m :hour-end h)\n          (org-ml--get-nonstandard-properties timestamp)))\n    (if (and h m) `(,h ,m) nil)))\n\n(defun org-ml--timestamp-get-start-unixtime (timestamp)\n  \"Return the unixtime of the start time in TIMESTAMP.\"\n  (->> (org-ml--timestamp-get-start-timelist timestamp)\n       (org-ml-timelist-to-unixtime)))\n\n(defun org-ml--timestamp-get-end-unixtime (timestamp)\n  \"Return the unixtime of the end time in TIMESTAMP.\"\n  (->> (org-ml--timestamp-get-end-timelist timestamp)\n       (org-ml-timelist-to-unixtime)))\n\n(defun org-ml--timestamp-has-equal-dates-p (timestamp)\n  \"Return t if start and end dates of TIMESTAMP are the same.\"\n  (equal (org-ml--timestamp-get-start-date timestamp)\n         (org-ml--timestamp-get-end-date timestamp)))\n\n(defun org-ml--timestamp-get-length (timestamp)\n  \"Return the range of TIMESTAMP in seconds.\"\n  (- (org-ml--timestamp-get-end-unixtime timestamp)\n     (org-ml--timestamp-get-start-unixtime timestamp)))\n\n(defun org-ml--timestamp-is-active (timestamp)\n  \"Return t if TIMESTAMP is an active type.\"\n  (memq (org-element-property-raw :type timestamp) '(active active-range)))\n\n(defun org-ml--timestamp-is-range-type (timestamp)\n  \"Return t if TIMESTAMP has a range type.\"\n  (memq (org-element-property-raw :type timestamp)\n        '(active-range inactive-range)))\n\n(defun org-ml--timestamp-is-ranged (timestamp)\n  \"Return t if TIMESTAMP has a range greater than 0 seconds.\"\n  (/= 0 (org-ml--timestamp-get-length timestamp)))\n\n(defun org-ml--timestamp-set-start-timelist-nocheck (timelist timestamp)\n  \"Set the start of TIMESTAMP using TIMELIST. Does not set type.\"\n  (-let (((y m d H M) timelist))\n    (org-ml--set-properties-raw timestamp\n      :year-start y\n      :month-start m\n      :day-start d\n      :hour-start H\n      :minute-start M)))\n\n(defun org-ml--timestamp-set-start-timelist (timelist timestamp)\n  \"Return TIMESTAMP with start time set according to TIMELIST.\"\n  (->> (org-ml--timestamp-set-start-timelist-nocheck timelist timestamp)\n       (org-ml--timestamp-update-type-ranged)))\n\n(defun org-ml--timestamp-set-end-timelist-nocheck (timelist timestamp)\n  \"Set the end of TIMESTAMP using TIMELIST. Does not set type.\n\nSet end to start if TIMELIST is nil.\"\n  (-let (((y m d H M)\n          (or timelist (org-ml--timestamp-get-start-timelist timestamp))))\n    (org-ml--set-properties-raw timestamp\n      :year-end y\n      :month-end m\n      :day-end d\n      :hour-end H\n      :minute-end M)))\n\n(defun org-ml--timestamp-set-end-timelist (timelist timestamp)\n  \"Return TIMESTAMP with end set according to TIMELIST.\"\n  (->> (org-ml--timestamp-set-end-timelist-nocheck timelist timestamp)\n       (org-ml--timestamp-update-type-ranged)))\n\n(defun org-ml--timestamp-set-single-timelist (timelist timestamp)\n  \"Return TIMESTAMP with start/end set to TIMELIST.\"\n  (->> (org-ml--timestamp-set-start-timelist-nocheck timelist timestamp)\n       (org-ml--timestamp-set-end-timelist-nocheck timelist)\n       (org-ml--timestamp-set-range-type nil)))\n\n(defun org-ml--timestamp-set-double-timelist (timelist1 timelist2 timestamp)\n  \"Return TIMESTAMP with start/end set to TIMELIST1/TIMELIST2.\"\n  (->> (org-ml--timestamp-set-start-timelist-nocheck timelist1 timestamp)\n       (org-ml--timestamp-set-end-timelist-nocheck timelist2)\n       (org-ml--timestamp-update-type-ranged)))\n\n(defun org-ml--timestamp-set-type-ranged (is-ranged timestamp)\n  \"Return TIMESTAMP with `:type' set according to IS-RANGED.\"\n  (org-ml--map-property-raw* :type\n    (pcase it\n      ((or `active `active-range)\n       (if is-ranged 'active-range 'active))\n      ((or `inactive `inactive-range)\n       (if is-ranged 'inactive-range 'inactive))\n      (e (org-ml--arg-error \"Invalid timestamp type: %s\" e)))\n    timestamp))\n\n(defun org-ml--timestamp-set-range-type (range-type timestamp)\n  \"Return TIMESTAMP updated to reflect RANGE-TYPE.\nSpecifically update `:range-type' and `:type'.\"\n  (->> (org-ml--timestamp-set-type-ranged range-type timestamp)\n       (org-element-put-property-2 :range-type range-type)))\n\n(defun org-ml--timestamp-set-length (n unit timestamp)\n  \"Return TIMESTAMP with end time shifted to N UNITs from start time.\"\n  (let* ((t1 (org-ml--timestamp-get-start-timelist timestamp))\n         (has-time (org-ml-timelist-has-time t1))\n         ;; convert to unixtime and back to fix any overflow values\n         (t2 (->> (org-ml-timelist-shift n unit t1)\n                  (org-ml-timelist-to-unixtime)\n                  (org-ml-unixtime-to-timelist has-time)))\n         (rt (->> (org-element-property-raw :range-type timestamp)\n                  (org-ml--timelists-get-range-type t1 t2))))\n    (->> (org-ml--timestamp-set-end-timelist-nocheck t2 timestamp)\n         (org-ml--timestamp-update-type-ranged)\n         (org-ml--timestamp-set-range-type rt))))\n\n(defun org-ml--timestamp-update-type-ranged (timestamp)\n  \"Return TIMESTAMP with updated `:type' and `:range-type'.\n\nSpecifically, this assumes that the start and/or end of TIMESTAMP\nhave just been updated, and that the `:type' and `:range-type'\nare now out of sync with the range between start/end. If deciding\nbetween `timerange' or `daterange', prefer the original value of\nTIMESTAMP if possible.\"\n  (let* ((t1 (org-ml--timestamp-get-start-timelist timestamp))\n         (t2 (org-ml--timestamp-get-end-timelist timestamp))\n         (rt (->> (org-element-property-raw :range-type timestamp)\n                  (org-ml--timelists-get-range-type t1 t2))))\n    (org-ml--timestamp-set-range-type rt timestamp)))\n\n(defun org-ml--timestamp-set-active (flag timestamp)\n  \"Return TIMESTAMP with active type if FLAG is t.\"\n  (let ((type (if (org-element-property-raw :range-type timestamp)\n                  (if flag 'active-range 'inactive-range)\n                (if flag 'active 'inactive))))\n    (org-element-put-property-2 :type type timestamp)))\n\n(defun org-ml--timestamp-get-warning (timestamp)\n  \"Return the warning component of TIMESTAMP.\nReturn a list like (TYPE VALUE UNIT) or nil.\"\n  (-let (((&plist :warning-type y :warning-value v :warning-unit u)\n          (org-ml--get-nonstandard-properties timestamp)))\n    (when (and y v u) `(,y ,v, u))))\n\n(defun org-ml--timestamp-set-warning (warning timestamp)\n  \"Return TIMESTAMP with warning properties set to WARNING list.\"\n  (-let (((type value unit) warning))\n    (org-ml--set-properties-raw timestamp\n      :warning-type type\n      :warning-value value\n      :warning-unit unit)))\n\n(defun org-ml--timestamp-get-repeater (timestamp)\n  \"Return the repeater component of TIMESTAMP.\nReturn a list like (TYPE VALUE UNIT) or nil.\"\n  (-let (((&plist :repeater-type y :repeater-value v :repeater-unit u)\n          (org-ml--get-nonstandard-properties timestamp)))\n    (when (and y v u) `(,y ,v, u))))\n\n(defun org-ml--timestamp-set-repeater (repeater timestamp)\n  \"Return TIMESTAMP with repeater properties set to REPEATER.\"\n  (unless repeater\n    (org-ml--set-properties-raw timestamp\n      :repeater-deadline-value nil\n      :repeater-deadline-unit nil))\n  (-let (((type value unit) repeater))\n    (org-ml--set-properties-raw timestamp\n      :repeater-type type\n      :repeater-value value\n      :repeater-unit unit)))\n\n(defun org-ml--timestamp-set-deadline (deadline timestamp)\n  \"Return TIMESTAMP with repeater properties set to DEADLINE.\"\n  (if (not (org-ml--timestamp-get-repeater timestamp)) timestamp\n    (-let (((value unit) deadline))\n      (org-ml--set-properties-raw timestamp\n        :repeater-deadline-value value\n        :repeater-deadline-unit unit))))\n\n(defun org-ml--timestamp-set-collapsed (flag timestamp)\n  \"Return TIMESTAMP with collapsed set to FLAG.\"\n  (pcase (org-element-property-raw :range-type timestamp)\n    ;; collapsed\n    (`timerange\n     (if flag timestamp\n       (->> (org-ml-copy timestamp)\n            (org-element-put-property-2 :range-type 'daterange))))\n    ;; uncollapsed\n    (`daterange\n     (if (and (org-ml--timestamp-get-start-time timestamp)\n              (org-ml--timestamp-get-end-time timestamp))\n         (cond\n          ((and (eq flag t) (org-ml--timestamp-has-equal-dates-p timestamp))\n           (org-element-put-property-2 :range-type 'timerange timestamp))\n          ((and (eq flag 'force))\n           (let ((s (org-ml--timestamp-get-start-timelist timestamp)))\n             (->> (org-ml-copy timestamp)\n                  (org-ml--timestamp-set-end-timelist-nocheck s)\n                  (org-element-put-property-2 :range-type 'timerange))))\n          (t\n           timestamp))\n       timestamp))\n    ;; neither\n    (`nil\n     timestamp)\n    (e\n     (error \"Invalid range-type %s\" e))))\n\n(defun org-ml--timestamp-set-start-time (time timestamp-diary)\n  \"Set the start of TIMESTAMP-DIARY to TIME. Does not set type.\"\n  (-let (((H M) time))\n    (org-ml--set-properties-raw timestamp-diary\n      :hour-start H\n      :minute-start M)))\n\n(defun org-ml--timestamp-set-end-time (time timestamp-diary)\n  \"Set the end of TIMESTAMP-DIARY to TIME. Does not set type.\"\n  (-let (((H M) time))\n    (org-ml--set-properties-raw timestamp-diary\n      :hour-end H\n      :minute-end M)))\n\n(defun org-ml--timestamp-update-type-ranged-timeonly (timestamp-diary)\n  \"Return TIMESTAMP-DIARY with updated `:range-type'.\"\n  (let* ((t1 (org-ml--timestamp-get-start-time timestamp-diary))\n         (t2 (org-ml--timestamp-get-end-time timestamp-diary))\n         (rt (if (equal t1 t2) nil 'timerange)))\n    (org-element-put-property-2 :range-type rt timestamp-diary)))\n\n;; timestamp (diary sexp)\n\n;;; element nodes\n;;\n;; item\n\n(defun org-ml--item-get-subcomponents (item)\n  \"Return the children of ITEM broken down into subcomponents.\nThe returned list will be of the form (HEAD SUBITEMS POST-BLANK\nREST) where HEAD consists of all nodes before the first nested\nplain-list, SUBITEMS will be all items in the nested plain-list,\nPOST-BLANK will be the post-blank of the nested plain-list, and\nREST will be everything after the plain-list (which should be nil\nfor all sensible items).\n\nExample item showing how this breaks down:\n\n- HEAD\n  - SUBITEM1\n  - SUBITEM2 (with POST-BLANK 1 below)\n\n  REST\n\nREST may itself contain more plain lists, but (for now at least)\nlet's consider these cases extremely rare. This function will\nstill do the right thing, but any plain list in REST will be\noff-limits for the indent/outdent functions that use this\nfunction.\"\n  (-let* (((h (s . r)) (->> (org-element-contents item)\n                            (--split-with (not (org-ml--is-type 'plain-list it)))))\n          (pb (if s (org-element-post-blank s) 0))\n          (i (org-element-contents s)))\n    (list h i pb r)))\n\n(defun org-ml--item-set-subcomponents (subcomponents item)\n  \"Return the child subcomponents of ITEM.\nSUBCOMPONENTS is a list like that returned by\n`org-ml--item-get-subcomponents'.\"\n  (-let* (((head subitems sub-pb rest) subcomponents))\n    (-when-let (pb (cond\n                    (rest (org-element-post-blank (-last-item rest)))\n                    (subitems sub-pb)\n                    (head (org-element-post-blank (-last-item head)))))\n      ;; TODO why did I do this?\n      (let ((rest* (org-ml--set-last-post-blank 0 rest))\n            (sublist (apply #'org-ml-build-plain-list\n                            :post-blank (if rest sub-pb 0)\n                            subitems)))\n        (->> (org-ml--set-children-nocheck `(,@head ,sublist ,@rest*) item)\n             (org-ml--shift-post-blank-textsafe pb))))))\n\n(defmacro org-ml--item-map-subcomponents* (form item)\n  \"Return ITEM with subcomponents modified.\nFORM is a form where the subcomponents of item are bound to the\nsymbol `it' and returns modified subcomponents. The subcomponents\nwill conform to those given in `org-ml--item-get-subcomponents'.\"\n  (declare (debug (form form)))\n  (let ((i (make-symbol \"item\")))\n    `(let ((,i ,item))\n       (let ((it (org-ml--item-get-subcomponents ,i)))\n         (org-ml--item-set-subcomponents ,form ,i)))))\n\n(defmacro org-ml--item-map-subcomponents-cond*\n    (head-form subitem-form rest-form item)\n  \"Return ITEM with subcomponents modified.\n\nFirst, split ITEM using `org-ml--item-get-subcomponents' and\nassign each of the four outputs to `it-head', `it-subitems',\n`it-rest-blank', and `it-rest' respectively.\n\nREST-FORM will run with all `it' variables are non-nil.\nThis should return a modified `it-rest' analogue.\n\nSUBITEM-FORM will run if `it-rest' is nil and the rest are\nnon-nil. This should return a list (SUBITEMS REST-BLANK REST).\n\nHEAD-FORM will run if `it-rest' and `it-subitems' are nil and the\nothers are non-nil. This should return a list to be fed into\n`org-ml--item-set-subcomponents'.\"\n  (declare (indent 3))\n  (let ((h (make-symbol \"--head\"))\n        (s (make-symbol \"--subitems\"))\n        (b (make-symbol \"--blank\"))\n        (r (make-symbol \"--rest\")))\n  `(org-ml--item-map-subcomponents*\n    (-let (((,h ,s ,b ,r) it))\n      (cond\n       (,r\n        (let ((it-rest ,r))\n          (list ,h ,s ,b ,rest-form)))\n       (,s\n        (let ((it-subitems ,s))\n          (cons ,h ,subitem-form)))\n       (t\n        (let ((it-head ,h))\n          ,head-form))))\n    ,item)))\n\n(defun org-ml--item-get-subitems (item)\n  \"Return the subitems of ITEM.\"\n  (-let (((_ s _ _) (org-ml--item-get-subcomponents item)))\n    s))\n\n(defun org-ml--item-set-subitems (subitems item)\n  \"Return ITEM with subitems set to SUBITEMS.\"\n  (org-ml--item-map-subcomponents*\n   (-let (((h _ p r) it))\n     (list h subitems p r))\n   item))\n\n(defmacro org-ml--item-map-subitems* (form item)\n  \"Return a ITEM with FORM applied to its sublist if present.\nFORM is a Lisp form in which the symbol `it' is bound to the\nitems in the sub plain-list, and returns a modified list of\nitems.\"\n  (declare (debug (form form)))\n  (let ((h (make-symbol \"h\"))\n        (p (make-symbol \"p\"))\n        (r (make-symbol \"r\")))\n    `(org-ml--item-map-subcomponents*\n      (-let (((,h it ,p ,r) it))\n        (list ,h ,form ,p ,r))\n      ,item)))\n\n;; headline\n\n(defun org-ml--headline-shift-level (n headline)\n  \"Return HEADLINE node with the level property shifted by N.\nIf the level is less then one after shifting, set level to one.\"\n  (->> (org-element-properties-resolve headline)\n       (org-ml--map-property-raw* :level (org-ml--shift-pos-integer n it))))\n\n(defun org-ml--headline-set-statistics-cookie (value headline)\n  \"Return HEADLINE node with statistics cookie set by VALUE.\nVALUE is a list conforming to `org-ml--is-valid-statistics-cookie-value'\nor nil to erase the statistics cookie if present.\"\n  (org-ml--map-property-raw*\n   :title\n   (let ((last? (org-ml--is-type 'statistics-cookie (-last-item it))))\n     (cond\n      ((and last? value)\n       ;; NOTE use full property setter here since this will call the encoder\n       (org-ml--map-last* (org-ml-set-property :value value it) (org-ml-copy it)))\n      ((and last? (not value))\n       (-drop-last 1 it))\n      (value\n       (-snoc it (org-ml-build-statistics-cookie value)))\n      (t it)))\n   (org-element-properties-resolve headline)))\n\n(defun org-ml--headline-set-statistics-cookie-fraction (done total headline)\n  \"Return HEADLINE node with statistics cookie set by DONE and TOTAL.\n\nDONE and TOTAL are integers representing the numerator and denominator\nrespectively of the statistics-cookie's fractional value. Both must\nbe greater than zero, and DONE must be less than or equal to TOTAL.\"\n  (-if-let (cookie (org-ml-headline-get-statistics-cookie headline))\n      (let* ((format (org-ml--statistics-cookie-get-format cookie))\n             (value (if (eq 'fraction format) `(,done ,total)\n                      (-> (float done)\n                          (/ total)\n                          (* 100)\n                          (round)\n                          (list)))))\n        (org-ml--headline-set-statistics-cookie value headline))\n    headline))\n\n;; planning\n\n(defun org-ml--build-planning-timestamp (active timelist)\n  \"Build a planning timestamp.\n\nACTIVE is a boolean. TIMELIST is a list like (year month date\n[hour] [minute]).\n\nNote this is a more optimized version of `org-ml-build-timestamp!'\"\n  (-let (((y m d H M) timelist))\n    (org-ml--check-timelist y m d H M)\n    (org-ml--set-properties-raw (org-ml--build-blank-node timestamp 0)\n      :year-start y\n      :month-start m\n      :day-start d\n      :hour-start H\n      :minute-start M\n      :year-end y\n      :month-end m\n      :day-end d\n      :hour-end H\n      :minute-end M\n      :type (if active 'active 'inactive))))\n\n(defun org-ml--planning-list-to-timestamp (planning-list)\n  \"Return timestamp node from PLANNING-LIST.\nSee `org-ml-build-planning!' for syntax of PLANNING-LIST.\"\n  (-let* ((p (-partition-before-pred\n              (lambda (it) (memq it '(&warning &repeater)))\n              planning-list))\n          (ts (org-ml--build-planning-timestamp t (car p))))\n    (-when-let (w (alist-get '&warning p))\n      (org-ml--check-warning-from-list w)\n      (org-ml--timestamp-set-warning w ts))\n    (-when-let (r (alist-get '&repeater p))\n      (org-ml--check-repeater-from-list r)\n      (org-ml--timestamp-set-repeater r ts))\n    ts))\n\n(defun org-ml--timestamp-to-planning-list (timestamp)\n  \"Return TIMESTAMP as planning list.\nSee `org-ml-build-planning!' for syntax of PLANNING-LIST. This is\nonly meant for deadline or scheduled timestamps, since the list\nfor closed is trival.\"\n  (let ((timelist (org-ml--timestamp-get-start-timelist timestamp))\n        (warning (org-ml--timestamp-get-warning timestamp))\n        (repeater (org-ml--timestamp-get-repeater timestamp)))\n    (append timelist\n            (and warning (cons '&warning warning))\n            (and repeater (cons '&repeater repeater)))))\n\n;; clock\n\n(defun org-ml--build-clock-timestamp (start end)\n  \"Build clock timestamp from START and END.\n\nBoth arguments are lists like (year month date hour minute).\n\nThis is a more optimized version of `org-ml-build-timestamp!'.\"\n  (-let (((y0 m0 d0 H0 M0) start)\n         ((y1 m1 d1 H1 M1) (or end start)))\n    (org-ml--check-timelist y0 m0 d0 H0 M0)\n    (when end\n      (org-ml--check-timelist y1 m1 d1 H1 M1))\n    (org-ml--set-properties-raw (org-ml--build-blank-node timestamp 0)\n      :year-start y0\n      :month-start m0\n      :day-start d0\n      :hour-start H0\n      :minute-start M0\n      :year-end y1\n      :month-end m1\n      :day-end d1\n      :hour-end H1\n      :minute-end M1\n      :range-type (and end 'daterange)\n      :type (if end 'inactive-range 'inactive))))\n\n;;; INTERNAL TYPE-SPECIFIC BRANCH/CHILD FUNCTIONS\n\n;;; headline\n\n(defun org-ml-headline-get-section (headline)\n  \"Return children of section node in HEADLINE node or nil if none.\"\n  (--> (car (org-element-contents headline))\n       (when (org-ml--is-type 'section it) (org-element-contents it))))\n\n(defun org-ml-headline-set-section (children headline)\n  \"Return HEADLINE with section node containing CHILDREN.\nIf CHILDREN is nil, return HEADLINE with no section node.\"\n  (org-ml--map-children-nocheck*\n    (if (org-ml--is-type 'section (car it))\n        (cons (org-ml-set-children children (car it)) (cdr it))\n      (cons (apply #'org-ml-build-section children) it))\n    headline))\n\n(org-ml--defun-anaphoric* org-ml-headline-map-section (fun headline)\n  \"Return HEADLINE node with child section node modified by FUN.\n\nFUN is a unary function that takes a section node's children as a list\nreturns a modified child list.\"\n  (--> (org-ml-headline-get-section headline)\n       (org-ml-headline-set-section (funcall fun it) headline)))\n\n(defun org-ml-headline-get-subheadlines (headline)\n  \"Return list of child headline nodes in HEADLINE node or nil if none.\"\n  (let ((children (org-element-contents headline)))\n    (if (org-ml--is-type 'section (car children)) (cdr children) children)))\n\n(defun org-ml-headline-set-subheadlines (subheadlines headline)\n  \"Return HEADLINE node with SUBHEADLINES set to child subheadlines.\"\n  (org-ml--map-children-nocheck*\n    (-if-let (section (assq 'section it))\n        (cons section subheadlines)\n      subheadlines)\n    headline))\n\n(org-ml--defun-anaphoric* org-ml-headline-map-subheadlines (fun headline)\n  \"Return HEADLINE node with child headline nodes modified by FUN.\n\nFUN is a unary function that takes a list of headlines and returns\na modified list of headlines.\"\n  (--> (org-ml-headline-get-subheadlines headline)\n       (org-ml-headline-set-subheadlines (funcall fun it) headline)))\n\n(defun org-ml--headline-subtree-shift-level (n headline)\n  \"Return HEADLINE node with its level shifted by N.\nAlso shift all HEADLINE node's child headline nodes by N.\nIf the final shifted level is less one, set level to one (for parent\nand child nodes).\"\n  (->> (org-ml-copy headline)\n       (org-ml--headline-shift-level n)\n       (org-ml-headline-map-subheadlines*\n         (--map (org-ml--headline-subtree-shift-level n it) it))))\n\n(defun org-ml--headline-set-level (level headline)\n  \"Return HEADLINE node with its level set to LEVEL.\nAdditionally set all child headline nodes to be (+ 1 level) for\nfirst layer, (+ 2 level) for second, and so on.\"\n  ;; NOTE full setter needed since this is called from the headline builder\n  (->> (org-element-put-property-2 :level level headline)\n       (org-ml-headline-map-subheadlines*\n         (--map (org-ml--headline-set-level (1+ level) it) it))))\n\n;;; table\n\n(defun org-ml--table-get-width (table)\n  \"Return the width of TABLE as an integer.\nThis effectively is the maximum of all table-row lengths.\"\n  (->> (org-element-contents table)\n       (--map (length (org-element-contents it)))\n       (-max)))\n\n(defun org-ml--table-pad-or-truncate (length list)\n  \"Pad or truncate LIST of table-cell nodes by LENGTH.\nBehavior is the same as `org-ml--pad-or-truncate' where the padded value\nis a blank table-cell node.\"\n  (let ((pad (org-ml-build-table-cell \"\")))\n    (org-ml--pad-or-truncate length pad list)))\n\n(defun org-ml--column-map-down-rows (fun column-index table)\n  \"Return TABLE node with FUN applied down the rows at COLUMN-INDEX.\n\nFUN is a unary function that takes a table-cell node and returns\na modified table-cell node.\"\n  (cl-flet\n      ((zip-into-rows\n        (row new-cell)\n        (if (org-ml--property-is-eq :type 'rule row) row\n          (org-ml--map-children-nocheck* (funcall fun new-cell it) row))))\n    (org-ml--map-children-nocheck*\n     (->> (--find-indices (org-ml--property-is-eq :type 'rule it) it)\n          (--reduce-from (-insert-at it nil acc) column-index)\n          (org-ml--table-pad-or-truncate (length it))\n          (-zip-with #'zip-into-rows it))\n     table)))\n\n(defun org-ml--table-get-row (row-index table)\n  \"Return the table-row node at ROW-INDEX within TABLE.\nRule-type table-row nodes do not factor when counting the index.\"\n  (->> (org-element-contents table)\n       (--filter (org-ml--property-is-eq :type 'standard it))\n       (org-ml--nth row-index)))\n\n(defun org-ml--table-replace-column (column-index column-cells table)\n  \"Return TABLE with COLUMN-CELLS in place of original cells at COLUMN-INDEX.\"\n  (org-ml--column-map-down-rows\n   (lambda (new-cell cells) (org-ml--replace-at column-index new-cell cells))\n   column-cells\n   table))\n\n(defun org-ml--table-row-pad-maybe (table table-row)\n  \"Return TABLE-ROW with row truncated or padded.\nSee `org-ml--table-pad-or-truncate' for how padding and truncation is\nperformed. TABLE is used to get the table width.\"\n  (if (org-ml--property-is-eq :type 'rule table-row) table-row\n    (let ((width (org-ml--table-get-width table)))\n      (org-ml--map-children-nocheck*\n       (org-ml--table-pad-or-truncate width it)\n       table-row))))\n\n(defun org-ml--table-replace-row (row-index table-row table)\n  \"Return TABLE node with row at ROW-INDEX replaced by TABLE-ROW.\"\n  (let ((table-row (org-ml--table-row-pad-maybe table table-row)))\n    (org-ml--map-children-nocheck*\n     (org-ml--replace-at row-index table-row it)\n     table)))\n\n(defun org-ml--table-clear-row (row-index table)\n  \"Return TABLE with table-cells in row at ROW-INDEX filled with blanks.\"\n  (org-ml--table-replace-row row-index (org-ml-build-table-row! '(\" \")) table))\n\n(defun org-ml--table-clear-column (column-index table)\n  \"Return TABLE with table-cells in column at COLUMN-INDEX filled with blanks.\"\n  (org-ml--table-replace-column column-index `(,(org-ml-build-table-cell \" \")) table))\n\n;;; COMPOSITE BUILDERS\n\n;;; misc builders\n\n(org-ml--defun-kw org-ml-build-timestamp-diary (form &key start end post-blank)\n  \"Return a new diary-sexp timestamp node from FORM.\n\nTIME1 and TIME1 are lists like (hour min) which specify the\ntime(s) of the diary timestamp. If TIME2 is provided, TIME1 must\nalso be provided and the timestamp will be ranged. Optionally set\nPOST-BLANK (a positive integer).\"\n  ;; TODO this isn't very efficient\n  (->> (org-ml--build-blank-node timestamp post-blank)\n       (org-element-put-property-2 :type 'diary)\n       (org-ml-timestamp-diary-set-value form)\n       (org-ml-timestamp-diary-set-double-time start end)))\n\n(org-ml--defun-kw org-ml-build-table-row-hline (&key post-blank)\n  \"Return a new rule-typed table-row node.\nOptionally set POST-BLANK (a positive integer).\"\n  (->> (org-ml--build-blank-node table-row post-blank)\n       (org-element-put-property-2 :type 'rule)))\n\n;;; shorthand builders\n\n;; These function offer a shorter and more convenient way of building\n;; nodes. They all end in '!' (and all associated functions later\n\n(eval-and-compile\n  (defvar org-ml--shorthand-builder-cache\n    (--map (cons it (make-hash-table :test #'equal))\n           org-ml-shorthand-builder-types)\n    \"Alist of hash tables to store shorthand builder results.\"))\n\n(defun org-ml-clear-shorthand-builder-cache ()\n  \"Clear the memoization cache for shorthand node builders.\"\n  (interactive)\n  (--each org-ml--shorthand-builder-cache\n    (clrhash (cdr it))))\n\n(defmacro org-ml--with-shorthand-builder-cache (type key body)\n  \"Run BODY with shorthand builder cache.\nSee org-ml--with-cache' for meaning of TYPE and KEY.\"\n  (declare (indent 1))\n  `(org-ml--with-cache\n     org-ml--shorthand-builder-cache\n     org-ml-memoize-shorthand-builders\n     org-ml-memoize-shorthand-builder-types\n     ,type ,key ,body))\n\n\n(defun org-ml-build-secondary-string! (string)\n  \"Return a secondary string (list of object nodes) from STRING.\nSTRING is any string that contains a textual representation of\nobject nodes. If the string does not represent a list of object nodes,\nthrow an error.\"\n  (org-ml--with-shorthand-builder-cache secondary-string\n    string\n    ;; add space to prevent leading stars from parsing as headlines\n    (-if-let (d (->> (org-ml--from-string (concat \" \" string))\n                     (org-ml--get-descendent '(0))))\n        ;; special case, anything starting with what looks like a bullet will\n        ;; be parsed as a list with one item\n        (if (org-ml--is-type 'plain-list d)\n            (-let* ((i (org-ml--get-descendent '(0) d))\n                    ((first . rest) (->> (org-ml--get-descendent '(0) i)\n                                         (org-element-contents)))\n                    (bullet (org-element-property-raw :bullet i)))\n              (if (org-ml--is-type 'plain-text first)\n                  `(,(concat bullet first) ,@rest)\n                `(,bullet ,first ,@rest)))\n          (if-let (ss (org-element-contents d))\n              (cond\n               ((not (org-ml--is-secondary-string ss))\n                (org-ml--arg-error \"Secondary string must only contain objects\"))\n               ((equal (car ss) \" \")\n                (-drop 1 ss))\n               (t (org-ml--map-first* (substring it 1) ss)))\n            (org-ml--arg-error \"Could not make secondary string from %S\" string)))\n      (org-ml--arg-error \"Could not make secondary string from %S\" string))))\n\n(org-ml--defun-kw org-ml-build-timestamp! (start &key end active repeater\n                                                 deadline warning collapsed\n                                                 post-blank)\n  \"Return a new timestamp node.\n\nSTART specifies the start time and is a list of integers in one of\nthe following forms:\n- (YEAR MONTH DAY): short form\n- (YEAR MONTH DAY nil nil): short form\n- (YEAR MONTH DAY HOUR MINUTE): long form\n\nEND (if supplied) will add the ending time, and follows the same\nformatting rules as START.\n\nACTIVE is a boolean where t signifies the type is `active', else\n`inactive' (the range suffix will be added if an end time is\nsupplied).\n\nREPEATER, DEADLINE, and WARNING are lists corresponding to those\nrequired for `org-ml-timestamp-set-repeater',\n`org-ml-timestamp-set-deadline', and\n`org-ml-timestamp-set-warning' respectively.\n\nBuilding a diary sexp timestamp is not possible with this function.\"\n  (org-ml--check-timelist-from-list start)\n  (when end\n    (org-ml--check-timelist-from-list end))\n  (when repeater\n    (org-ml--check-repeater-from-list repeater))\n  (when warning\n    (org-ml--check-warning-from-list warning))\n  (when deadline\n    (org-ml--check-deadline-from-list deadline))\n  (org-ml--with-shorthand-builder-cache timestamp\n    (list start end active repeater deadline warning collapsed post-blank)\n    (org-ml->> (org-ml--build-blank-node timestamp post-blank)\n      (org-ml--timestamp-set-start-timelist-nocheck start)\n      (org-ml--timestamp-set-end-timelist-nocheck end)\n      (org-ml--timestamp-set-active active)\n      (org-ml--timestamp-update-type-ranged)\n      (org-ml--timestamp-set-warning warning)\n      (org-ml--timestamp-set-repeater repeater)\n      (org-ml--timestamp-set-deadline deadline)\n      (org-ml--timestamp-set-collapsed (or collapsed t)))))\n\n(org-ml--defun-kw org-ml-build-clock! (start &key end post-blank)\n  \"Return a new clock node.\n\nSTART and END follow the same rules as their respective arguments in\n`org-ml-build-timestamp!'.\"\n  (org-ml--with-shorthand-builder-cache clock\n    (list start end post-blank)\n    (let ((ts (org-ml--build-clock-timestamp start end)))\n      (org-ml-build-clock ts :post-blank post-blank))))\n\n(org-ml--defun-kw org-ml-build-planning! (&key closed deadline scheduled\n                                               post-blank)\n  \"Return a new planning node.\n\nDEADLINE and SCHEDULED are lists with the following structure\n\\(brackets denote optional members):\n\n\\(YEAR MINUTE DAY [HOUR] [MIN]\n [&warning TYPE VALUE UNIT]\n [&repeater TYPE VALUE UNIT])\n\nIn terms of arguments supplied to `org-ml-build-timestamp!', the first\nfive members correspond to the list supplied as TIME, and the TYPE,\nVALUE, and UNIT fields correspond to the lists supplied to WARNING and\nREPEATER arguments. The order of warning and repeater does not\nmatter.\n\nCLOSED is a similar list to above but does not have &warning or\n&repeater.\"\n  (org-ml--with-shorthand-builder-cache planning\n    (list closed deadline scheduled post-blank)\n    (let ((node (org-ml--build-blank-node planning (or post-blank 0))))\n      (when closed\n        (->> (org-ml--build-planning-timestamp nil closed)\n             (org-element-put-property node :closed)))\n      (when deadline\n        (->> (org-ml--planning-list-to-timestamp deadline)\n             (org-element-put-property node :deadline)))\n      (when scheduled\n        (->> (org-ml--planning-list-to-timestamp scheduled)\n             (org-element-put-property node :scheduled)))\n      node)))\n\n(org-ml--defun-kw org-ml-build-property-drawer! (&key post-blank &rest keyvals)\n  \"Return a new property-drawer node.\n\nEach member in KEYVALS is a list like (KEY VAL) where KEY and VAL\nare both strings, where each list will generate a node-property\nnode in the property-drawer node like \\\":key: val\\\".\"\n  (org-ml--with-shorthand-builder-cache property-drawer\n    (cons post-blank keyvals)\n    (->> keyvals\n         (--map (let ((key (car it))\n                      (val (cadr it)))\n                  (org-ml-build-node-property key val)))\n         (apply #'org-ml-build-property-drawer :post-blank post-blank))))\n\n(org-ml--defun-kw org-ml-build-headline! (&key (level 1) title-text\n                                               todo-keyword tags pre-blank\n                                               priority commentedp archivedp\n                                               post-blank planning\n                                               statistics-cookie\n                                               section-children\n                                               &rest subheadlines)\n  \"Return a new headline node.\n\nTITLE-TEXT is a oneline string for the title of the headline.\n\nPLANNING is a list like (PLANNING-TYPE ARGS ...) where\nPLANNING-TYPE is one of `:closed', `:deadline', or `:scheduled', and\nARGS are the args supplied to any of the planning types in\n`org-ml-build-planning!'. Up to all three planning types can be used\nin the same list like (:closed ARGS :deadline ARGS :scheduled ARGS).\n\nSTATISTICS-COOKIE is a list following the same format as\n`org-ml-build-statistics-cookie'.\n\nSECTION-CHILDREN is a list of elements that will go in the headline\nsection.\n\nSUBHEADLINES contains zero or more headlines that will go under the\ncreated headline. The level of all members in SUBHEADLINES will\nautomatically be adjusted to LEVEL + 1.\n\nAll arguments not mentioned here follow the same rules as\n`org-ml-build-headline'\"\n  (org-ml--with-shorthand-builder-cache headline\n    (append (list level title-text todo-keyword tags pre-blank priority\n                  commentedp archivedp post-blank planning statistics-cookie\n                  section-children)\n            subheadlines)\n    (let* ((planning (-some->> planning (apply #'org-ml-build-planning!)))\n           (section (-some->>  (if planning (cons planning section-children)\n                                 section-children)\n                      (apply #'org-ml-build-section)))\n           (shls (--map (org-ml--headline-set-level (1+ level) it) subheadlines))\n           (nodes (--> shls (if section (cons section it) it))))\n      (->> (apply #'org-ml-build-headline\n                  :todo-keyword todo-keyword\n                  :level level\n                  :tags tags\n                  :post-blank post-blank\n                  :pre-blank pre-blank\n                  :priority priority\n                  :commentedp commentedp\n                  :archivedp archivedp\n                  nodes)\n           (org-ml-headline-set-title! title-text statistics-cookie)))))\n\n(org-ml--defun-kw org-ml-build-paragraph! (string &key post-blank)\n  \"Return a new paragraph node from STRING.\n\nSTRING is the text to be parsed into a paragraph and must contain\nvalid textual representations of object nodes.\"\n  ;; ASSUME all children coming from `org-ml-build-secondary-string!' will be\n  ;; valid so bypass type checking overhead.\n  (org-ml--with-shorthand-builder-cache paragraph\n    (list string post-blank)\n    (->> (org-ml--build-blank-node paragraph post-blank)\n         (org-ml-set-children (org-ml-build-secondary-string! string)))))\n\n(org-ml--defun-kw org-ml-build-item! (&key post-blank bullet checkbox tag\n                                           paragraph counter &rest children)\n  \"Return a new item node.\n\nTAG is a string representing the tag (make with\n`org-ml-build-secondary-string!') .\n\nPARAGRAPH is a string that will be the initial text in the item\n\\(made with `org-ml-build-paragraph!').\n\nCHILDREN contains the nodes that will go under this item after\nPARAGRAPH.\n\nAll other arguments follow the same rules as `org-ml-build-item'.\"\n  (org-ml--with-shorthand-builder-cache item\n    (append (list post-blank bullet checkbox tag paragraph counter) children)\n    (let ((children* (or (-some-> paragraph\n                           (org-ml-build-paragraph!)\n                           (cons children))\n                         children))\n          (tag (-some->> tag (org-ml-build-secondary-string!))))\n      (apply #'org-ml-build-item\n             :post-blank post-blank\n             :bullet bullet\n             :checkbox checkbox\n             :counter counter\n             :tag tag\n             children*))))\n\n(defun org-ml-build-table-cell! (string)\n  \"Return a new table-cell node.\n\nSTRING is the text to be contained in the table-cell node. It must\ncontain valid textual representations of objects that are allowed in\ntable-cell nodes.\"\n  (org-ml--with-shorthand-builder-cache table-cell\n    string\n    (apply #'org-ml-build-table-cell (org-ml-build-secondary-string! string))))\n\n(defun org-ml-build-table-row! (row-list)\n  \"Return a new table-row node.\n\nROW-LIST is a list of strings to be built into table-cell nodes via\n`org-ml-build-table-cell!' (see that function for restrictions).\nAlternatively, ROW-LIST may the symbol `hline' instead of a string to\ncreate a rule-typed table-row.\"\n  (org-ml--with-shorthand-builder-cache table-row\n    row-list\n    (if (eq row-list 'hline) (org-ml-build-table-row-hline)\n      (->> (-map #'org-ml-build-table-cell! row-list)\n           (apply #'org-ml-build-table-row)))))\n\n(org-ml--defun-kw org-ml-build-table! (&key tblfm post-blank &rest row-lists)\n  \"Return a new table node.\n\nEach member of ROW-LISTS will be converted to a table-row node\nvia `org-ml-build-table-row!' (see that function for\nrestrictions).\n\nAll other arguments follow the same rules as `org-ml-build-table'.\"\n  (org-ml--with-shorthand-builder-cache table\n    (append (list tblfm post-blank) row-lists)\n    (->> (-map #'org-ml-build-table-row! row-lists)\n         (apply #'org-ml-build-table :tblfm tblfm :post-blank post-blank))))\n\n(defun org-ml-build-org-data (&rest nodes)\n  \"Return a new org-data node using NODES.\nNODES should be either headline or section nodes.\"\n  (->> (org-ml--build-blank-node org-data nil)\n       (org-ml-set-children nodes)))\n\n;;; logbook items\n\n;; internal\n\n(defun org-ml--log-replace (placeholder string heading)\n  \"Return HEADING with PLACEHOLDER replaced by STRING.\"\n  (->> (cons placeholder string)\n       (list)\n       (org-replace-escapes heading)))\n\n(defun org-ml--log-replace-new (string heading)\n  \"Return HEADING with placeholder \\\"%s\\\" replaced by STRING.\"\n  (--> (format \"\\\"%s\\\"\" string)\n       (org-ml--log-replace \"%s\" it heading)))\n\n(defun org-ml--log-replace-old (string heading)\n  \"Return HEADING with placeholder \\\"%S\\\" replaced by STRING.\"\n  (--> (format \"\\\"%s\\\"\" string)\n       (org-ml--log-replace \"%S\" it heading)))\n\n(defun org-ml--log-replace-new-state (state heading)\n  \"Return HEADING with placeholder \\\"%s\\\" replaced by string STATE.\"\n  (org-ml--log-replace-new state heading))\n\n(defun org-ml--log-replace-old-state (state heading)\n  \"Return HEADING with placeholder \\\"%S\\\" replaced by string STATE.\"\n  (org-ml--log-replace-old state heading))\n\n(defun org-ml--log-replace-new-timestamp (timestamp heading)\n  \"Return HEADING with placeholder \\\"%s\\\" replaced by TIMESTAMP.\nTIMESTAMP is a timestamp node and will be converted to an inactive\ntimestamp if active.\"\n  (-> (org-ml-timestamp-set-active nil timestamp)\n      (org-ml-to-string)\n      (org-ml--log-replace-new heading)))\n\n(defun org-ml--log-replace-old-timestamp (timestamp heading)\n  \"Return HEADING with placeholder \\\"%S\\\" replaced by TIMESTAMP.\nTIMESTAMP is a timestamp node and will be converted to an inactive\ntimestamp if active.\"\n  (-> (org-ml-timestamp-set-active nil timestamp)\n      (org-ml-to-string)\n      (org-ml--log-replace-old heading)))\n\n(defun org-ml--log-replace-timestamp (unixtime active-p long-p heading)\n  \"Return HEADING with timestamp placeholders replaced by a timestamp.\n\nUNIXTIME is an integer to be converted to a timestamp.\n\nThe type of timestamp and the placeholders that are replaced depend\non the boolean values of ACTIVE-P and LONG-P:\n- ACTIVE-P and LONG-P are t: long active timestamp replacing \\\"T\\\"\n- ACTIVE-P is t: short active timestamp replacing \\\"D\\\"\n- LONG-P is t: long inactive timestamp replacing \\\"t\\\"\n- both nil: short inactive timestamp replacing \\\"d\\\"\"\n  (let ((key (cond ((and active-p long-p) \"%T\")\n                   (active-p \"%D\")\n                   (long-p \"%t\")\n                   (t \"%d\")))\n        (time (if long-p (org-ml-unixtime-to-datetime unixtime)\n                (org-ml-unixtime-to-date unixtime))))\n    ;; TODO this can likely be optimized\n    (--> (org-ml-build-timestamp! time :active active-p)\n         (org-ml-to-string it)\n         (org-ml--log-replace key it heading))))\n\n(defun org-ml--log-replace-username (username heading)\n  \"Return HEADING with \\\"%u\\\" replaced by symbol USERNAME.\"\n  (org-ml--log-replace \"%u\" username heading))\n\n(defun org-ml--log-replace-full-username (full-username heading)\n  \"Return HEADING with \\\"%U\\\" replaced by symbol FULL-USERNAME.\"\n  (org-ml--log-replace \"%U\" full-username heading))\n\n(defun org-ml--log-get (type)\n  \"Return the log heading associated with symbol TYPE.\nThis function will only use the default value of\n`org-log-note-headings' and is thus a pure function.\"\n  (alist-get type (default-value 'org-log-note-headings)))\n\n(defun org-ml--build-log-item (note heading)\n  \"Return an item with string HEADING as its first line.\nIf string NOTE is supplied, append this after a newline object node\nin the first paragraph of the returned item.\"\n  (->> (if note (format \"%s \\\\\\\\\\n  %s\" heading note) heading)\n       (org-ml-build-paragraph!)\n       (org-ml-build-item)))\n\n(defun org-ml--build-log-item-trans (type unixtime old-timestamp note)\n  \"Return an item for any of the transition log entry types.\nThese are re/del-schedule/deadline (specified with TYPE) transitioning\nfrom OLD-TIMESTAMP at UNIXTIME with optionally supplied NOTE.\"\n  (->> (org-ml--log-get type)\n       (org-ml--log-replace-old-timestamp old-timestamp)\n       (org-ml--log-replace-timestamp unixtime nil t)\n       (org-ml--build-log-item note)))\n\n;; public\n\n(defun org-ml-build-log-done (unixtime &optional note)\n  \"Return an item node for a done log entry.\n\nThis will format the log entry from the default value for the\n`done' cell in `org-log-note-headings'.\n\nUNIXTIME is an integer representing the time to be used for all\ntimestamp nodes.\n\nIf string NOTE is supplied, append a note to the log entry.\"\n  (->> (org-ml--log-get 'done)\n       (org-ml--log-replace-timestamp unixtime nil t)\n       (org-ml--build-log-item note)))\n\n(defun org-ml-build-log-state (unixtime new-state old-state &optional note)\n  \"Return an item node for a state change log entry.\n\nThis will format the log entry from the default value for the\n`state' cell in `org-log-note-headings'.\n\nUNIXTIME is an integer representing the time to be used for all\ntimestamp nodes.\n\nNEW-STATE and OLD-STATE are strings for the new and old todo keywords\nrespectively.\n\nIf string NOTE is supplied, append a note to the log entry.\"\n  (->> (org-ml--log-get 'state)\n       (org-ml--log-replace-new-state new-state)\n       (org-ml--log-replace-old-state old-state)\n       (org-ml--log-replace-timestamp unixtime nil t)\n       (org-ml--build-log-item note)))\n\n(defun org-ml-build-log-note (unixtime note)\n  \"Return an item node for a new note log entry.\n\nThis will format the log entry from the default value for the\n`note' cell in `org-log-note-headings'.\n\nUNIXTIME is an integer representing the time to be used for all\ntimestamp nodes.\n\nNOTE is a string for the note text.\"\n  (->> (org-ml--log-get 'note)\n       (org-ml--log-replace-timestamp unixtime nil t)\n       (org-ml--build-log-item note)))\n\n(defun org-ml-build-log-reschedule (unixtime old-timestamp &optional note)\n  \"Return an item node for a new schedule log entry.\n\nThis will format the log entry from the default value for the\n`reschedule' cell in `org-log-note-headings'.\n\nUNIXTIME is an integer representing the time to be used for all\ntimestamp nodes.\n\nOLD-TIMESTAMP is a timestamp node of the schedule that is being\ndeleted. It will always be converted to an inactive timestamp.\n\nIf string NOTE is supplied, append a note to the log entry.\"\n  (org-ml--build-log-item-trans 'reschedule unixtime old-timestamp note))\n\n(defun org-ml-build-log-delschedule (unixtime old-timestamp &optional note)\n  \"Return an item node for a delete schedule log entry.\n\nThis will format the log entry from the default value for the\n`delschedule' cell in `org-log-note-headings'.\n\nUNIXTIME is an integer representing the time to be used for all\ntimestamp nodes.\n\nOLD-TIMESTAMP is a timestamp node of the schedule that is being\ndeleted. It will always be converted to an inactive timestamp.\n\nIf string NOTE is supplied, append a note to the log entry.\"\n  (org-ml--build-log-item-trans 'delschedule unixtime old-timestamp note))\n\n(defun org-ml-build-log-redeadline (unixtime old-timestamp &optional note)\n  \"Return an item node for a new deadline log entry.\n\nThis will format the log entry from the default value for the\n`redeadline' cell in `org-log-note-headings'.\n\nUNIXTIME is an integer representing the time to be used for all\ntimestamp nodes.\n\nOLD-TIMESTAMP is a timestamp node of the deadline that is being\ndeleted. It will always be converted to an inactive timestamp.\n\nIf string NOTE is supplied, append a note to the log entry.\"\n  (org-ml--build-log-item-trans 'redeadline unixtime old-timestamp note))\n\n(defun org-ml-build-log-deldeadline (unixtime old-timestamp &optional note)\n  \"Return an item node for a delete deadline log entry.\n\nThis will format the log entry from the default value for the\n`deldeadline' cell in `org-log-note-headings'.\n\nUNIXTIME is an integer representing the time to be used for all\ntimestamp nodes.\n\nOLD-TIMESTAMP is a timestamp node of the deadline that is being\ndeleted. It will always be converted to an inactive timestamp.\n\nIf string NOTE is supplied, append a note to the log entry.\"\n  (org-ml--build-log-item-trans 'deldeadline unixtime old-timestamp note))\n\n(defun org-ml-build-log-refile (unixtime &optional note)\n  \"Return an item node for a refile log entry.\nThis will format the log entry from the default value for the\n`deldeadline' cell in `org-log-note-headings'.\n\nUNIXTIME is an integer representing the time to be used for all\ntimestamp nodes.\n\nIf string NOTE is supplied, append a note to the log entry.\"\n  (->> (org-ml--log-get 'refile)\n       (org-ml--log-replace-timestamp unixtime nil t)\n       (org-ml--build-log-item note)))\n\n(org-ml--defun-kw org-ml-build-log-type (type &key old new unixtime username\n                                              full-username note)\n  \"Return an item for an arbitrary log entry.\n\nTYPE is a symbol corresponding to the car of one of the cells in\n`org-log-note-headings'. Unlike the other log entry build functions\nin this package, this function will not use the default value of\n`org-log-note-headings' which means it can be used for customly\nformatted log entries.\n\nThe arguments correspond to the following formatting placeholders\n(see `org-log-note-headings' for more information on these\nplaceholders):\n- NEW: either a string or timestamp node that will replace the\n  new state/timestamp placeholder (%s)\n- OLD: like NEW but for the old state/timestamp placeholder (%S)\n- UNIXTIME: an integer corresponding to the time to be used for the\n  timestamp placeholders (%t/%T/%d/%D)\n- USERNAME: a string for the username (%u)\n- FULL-USERNAME: a string for the full username (%U)\n\nIf any of these arguments are not supplied but their placeholders\nare present in the heading determined by TYPE, the placeholders will\nnot be substituted.\n\nIf string NOTE is supplied, append a note to the log entry.\"\n  ;; TODO this can likely be made faster (if desired) by not relying on the\n  ;; individual replacement functions; doing it this way will call\n  ;; `org-replace-escapes' multiple times, which is likely not as fast\n  (cl-flet\n      ((replace-note\n        (old-p rep note)\n        (if (not rep) note\n          (let ((fun\n                 (cond\n                  ((org-ml--is-type 'timestamp rep)\n                   (if old-p #'org-ml--log-replace-old-timestamp\n                     #'org-ml--log-replace-new-timestamp))\n                  ((stringp rep)\n                   (if old-p #'org-ml--log-replace-old-state\n                     #'org-ml--log-replace-new-state))\n                  (t\n                   (org-ml--arg-error \"Must be string or timestamp: Got %S\" rep)))))\n            (funcall fun rep note))))\n       (replace-timestamps\n        (heading)\n        (if (not unixtime) heading\n          (->> heading\n               (org-ml--log-replace-timestamp unixtime nil nil)\n               (org-ml--log-replace-timestamp unixtime t nil)\n               (org-ml--log-replace-timestamp unixtime nil t)\n               (org-ml--log-replace-timestamp unixtime t t)))))\n    (--> (alist-get type org-log-note-headings)\n         (replace-timestamps it)\n         (replace-note t old it)\n         (replace-note nil new it)\n         (if username (org-ml--log-replace-username username it) it)\n         (if full-username\n             (org-ml--log-replace-full-username full-username it)\n           it)\n         (org-ml--build-log-item note it))))\n\n;;; PUBLIC TYPE FUNCTIONS\n\n(defalias 'org-ml-get-type #'org-element-type\n  \"Return the type of NODE.\")\n\n(defun org-ml-is-type (type node)\n  \"Return t if the type of NODE is TYPE (a symbol).\"\n  (declare (pure t))\n  (unless (memq type org-ml-nodes)\n    (org-ml--arg-error \"Argument 'type' must be in `org-ml-nodes': Got %s\" type))\n  (org-ml--is-type type node))\n\n(defun org-ml-is-any-type (types node)\n  \"Return t if the type of NODE is in TYPES (a list of symbols).\"\n  (declare (pure t))\n  (-some->>\n   (-difference types org-ml-nodes)\n   (org-ml--arg-error\n    \"All in 'types' must be in `org-ml-nodes'; these were not: %s\"))\n  (org-ml--is-any-type types node))\n\n(defun org-ml-is-element (node)\n  \"Return t if NODE is an element class.\"\n  (org-ml--is-any-type org-ml-elements node))\n\n(defun org-ml-is-branch-node (node)\n  \"Return t if NODE is a branch node.\"\n  (org-ml--is-any-type org-ml-branch-nodes node))\n\n(defun org-ml-node-may-have-child-objects (node)\n  \"Return t if NODE is a branch node that may have child objects.\"\n  (org-ml--is-any-type org-ml-branch-nodes-permitting-child-objects node))\n\n(defun org-ml-node-may-have-child-elements (node)\n  \"Return t if NODE is a branch node that may have child elements.\n\nNote this implies that NODE is also of class element since only\nelements may have other elements as children.\"\n  (org-ml--is-any-type org-ml-branch-elements-permitting-child-elements node))\n\n;;; PUBLIC PROPERTY FUNCTIONS\n\n;;; polymorphic\n\n(defun org-ml-contains-point-p (point node)\n  \"Return t if POINT is within the boundaries of NODE.\"\n  (-let ((b (org-element-begin node))\n         (e (org-element-end node)))\n    (if (and (integerp b) (integerp e))\n        (<= b point e)\n      (error \"Node boundaries are not defined\"))))\n\n(defun org-ml--property-is-attribute (prop)\n  \"Return t if PROP is of the form :attr_X where X is anything.\"\n  (and (keywordp prop) (s-prefix-p \":attr_\" (symbol-name prop) t)))\n\n(defun org-ml-set-property (prop value node)\n  \"Return NODE with PROP set to VALUE.\n\nSee builder functions for a list of properties and their rules for\neach type.\"\n  (let ((type (org-ml-get-type node)))\n    ;; Specialized code to handle :attr_X properties which can't be put in\n    ;; `org-ml--property-alist'. Values for these can only be lists of strings\n    ;; and have no encoder or decoder.\n    (cond\n     ((and (memq type org-ml--element-nodes-with-affiliated)\n           (org-ml--property-is-attribute prop))\n      (if (org-ml--is-string-list value)\n          (org-element-put-property-2 prop value node)\n        (org-ml--arg-error \"All attributes like '%s' must be a list of strings. Got '%S'\"\n                           prop value)))\n     ((eq type 'plain-text)\n      (if (eq prop :post-blank)\n          (concat (s-trim-right node) (make-string value ?\\ ))\n        (org-add-props node nil prop value)))\n     (t\n      (let* ((value* (org-ml--property-encode prop value type))\n             (node* (->> (if (eq type 'headline)\n                             (org-element-properties-resolve node)\n                           node)\n                         (org-ml-copy)\n                         (org-element-put-property-2 prop value*))))\n        (-if-let (update-fun (org-ml--get-property-updater type prop))\n            (funcall update-fun node*)\n          node*))))))\n\n(defun org-ml-set-properties (plist node)\n  \"Return NODE with all properties set to the values according to PLIST.\n\nPLIST is a list of property-value pairs that corresponds to the\nproperty list in NODE.\n\nSee builder functions for a list of properties and their rules for\neach type.\"\n  (cl-flet\n      ((split-keyvals-maybe\n        (type keyvals)\n        (if (not (memq type org-ml--element-nodes-with-affiliated))\n            (list keyvals nil)\n          (--> keyvals\n               (--group-by (org-ml--property-is-attribute (car it)) it)\n               (list (alist-get nil it) (alist-get t it))))))\n    (if (not (org-ml--is-plist plist))\n        (org-ml--arg-error \"Not a plist: %S\" plist)\n      (-let* ((type (org-ml-get-type node))\n              ;; this will divide the keywords to those that are of the form\n              ;; :attr_X which must be set differently\n              ((kv kv-attrs) (split-keyvals-maybe type (-partition 2 plist)))\n              (update-funs\n               (->> (--map (org-ml--get-property-updater type (car it)) kv)\n                    (-uniq)\n                    (-non-nil)))\n              (node* (org-ml-copy node)))\n        (--each kv (->> (org-ml--property-encode (car it) (cadr it) type)\n                        (org-element-put-property node* (car it))))\n        (--each kv-attrs (org-element-put-property node* (car it) (cadr it)))\n        (--each update-funs (funcall it node*))\n        node*))))\n\n(defun org-ml-get-property (prop node)\n  \"Return the value of PROP of NODE.\"\n  (let ((type (org-ml-get-type node)))\n    (if (and (eq type 'plain-text) (eq prop :post-blank))\n        (org-ml--get-post-blank-text node)\n      (let ((decoder-fun (unless (or (and (memq type org-ml--element-nodes-with-affiliated)\n                                          (org-ml--property-is-attribute prop))\n                                     (memq prop org-element--standard-properties))\n                           (org-ml--get-property-decoder type prop)))\n            (value (org-element-property prop node)))\n        (if decoder-fun (funcall decoder-fun value) value)))))\n\n(defun org-ml-get-properties (props node)\n  \"Return all the values of PROPS from NODE.\nPROPS is a list of all the properties desired, and the returned\nlist will be the values of these properties in the order\nrequested. To get all properties of NODE, use\n`org-ml--get-all-properties'.\"\n  (--map (org-ml-get-property it node) props))\n\n(org-ml--defun-anaphoric* org-ml-map-property (prop fun node)\n  \"Return NODE with FUN applied to the value of PROP.\n\nFUN is a unary function which takes the current value of PROP and\nreturns a new value to which PROP will be set.\n\nSee builder functions for a list of properties and their rules for\neach type.\"\n  (--> (org-ml-get-property prop node)\n       (org-ml-set-property prop (funcall fun it) node)))\n\n(defun org-ml-map-properties (plist node)\n  \"Return NODE with functions applied to the values of properties.\n\nPLIST is a property list where the keys are properties of NODE and\nits values are unary functions to be mapped to these properties.\n\nSee builder functions for a list of properties and their rules for\neach type.\"\n  ;; TODO this is slow since it will copy the node for each property iteration\n  (cond\n   ((not plist) node)\n   ((org-ml--is-plist plist)\n    (->> (org-ml-map-property (nth 0 plist) (nth 1 plist) node)\n         (org-ml-map-properties (-drop 2 plist))))\n   (t (org-ml--arg-error \"Not a plist: %s\" plist))))\n\n(defmacro org-ml-map-properties* (plist node)\n  \"Anaphoric form of `org-ml-map-properties'.\n\nPLIST is a property list where the keys are properties of NODE and\nits values are forms to be mapped to these properties.\"\n  (declare (debug (form form)))\n  (let ((p (make-symbol \"plist*\")))\n    `(let ((,p (org-ml--plist-map-values (lambda (form) `(lambda (it) ,form)) ',plist)))\n       (org-ml-map-properties ,p ,node))))\n\n(defun org-ml-toggle-property (prop node)\n  \"Return NODE with the value of PROP flipped.\n\nThis function only applies to properties that are booleans.\"\n  (let ((type (org-ml-get-type node)))\n    (if (org-ml--property-memq org-ml--properties-with-toggle type prop)\n        (org-ml-map-property prop #'not node)\n      (org-ml--arg-error \"Not a toggle-able property\"))))\n\n(defun org-ml-shift-property (prop n node)\n  \"Return NODE with PROP shifted by N (an integer).\n\nThis only applies the properties that are represented as integers.\"\n  (let* ((type (org-ml-get-type node))\n         (fun (org-ml--get-property-shifter type prop)))\n    (if fun (org-ml-map-property* prop (funcall fun n it) node)\n      (org-ml--arg-error \"'%s' not a shiftable for '%s'\" prop type))))\n\n(defun org-ml-insert-into-property (prop index string node)\n  \"Return NODE with STRING inserted at INDEX into PROP.\n\nThis only applies to properties that are represented as lists of\nstrings.\"\n  (let ((type (org-ml-get-type node)))\n    (if (org-ml--property-memq org-ml--properties-with-string-list type prop)\n        (org-ml-map-property* prop\n          (if (member string it) it (org-ml--insert-at index string it))\n          node)\n      (org-ml--arg-error \"Property '%s' in node of type '%s' is not a string-list\"\n                         prop type))))\n\n(defun org-ml-remove-from-property (prop string node)\n  \"Return NODE with STRING removed from PROP if present.\n\nThis only applies to properties that are represented as lists of\nstrings.\n\nSee `org-ml-insert-into-property' for a list of supported elements\nand properties that may be used with this function.\"\n  (let ((type (org-ml-get-type node)))\n    (if (org-ml--property-memq org-ml--properties-with-string-list type prop)\n        (org-ml-map-property* prop (-remove-item string it) node)\n      (org-ml--arg-error \"Property '%s' in node of type '%s' is not a string-list\"\n                     prop type))))\n\n(defun org-ml-plist-put-property (prop key value node)\n  \"Return NODE with VALUE corresponding to KEY inserted into PROP.\n\nKEY is a keyword and VALUE is a symbol. This only applies to\nproperties that are represented as plists.\"\n  (if (org-ml--property-memq org-ml--properties-with-plist (org-ml-get-type node) prop)\n      (org-ml-map-property* prop (plist-put it key value) node)\n    (org-ml--arg-error \"Not a plist property\")))\n\n(defun org-ml-plist-remove-property (prop key node)\n  \"Return NODE with KEY and its corresponding value removed from PROP.\n\nKEY is a keyword. This only applies to properties that are\nrepresented as plists.\n\nSee `org-ml-plist-put-property' for a list of supported elements\nand properties that may be used with this function.\"\n  (if (org-ml--property-memq org-ml--properties-with-plist (org-ml-get-type node) prop)\n      (org-ml-map-property* prop (org-ml--plist-remove key it) node)\n    (org-ml--arg-error \"Not a plist property\")))\n\n;; update polymorphic property function documentation:\n;;\n;; For the functions immediately above, modify the docstrings to inform the user\n;; which node types and property combinations may be used. This information is\n;; stored in `org-ml--property-alist'.\n\n(defun org-ml--get-types-with-property-attribute (attr)\n  \"Return alist of all nodes types that contain ATTR.\nReturn a list like ((TYPE (PROP1 ...)) ...) where TYPE is the\nnode type and PROPX are the properties that contain ATTR.\"\n  (->> org-ml--property-alist\n       (--map (cons (car it) (--filter (plist-get (cdr it) attr) (cdr it))))\n       (-filter #'cdr)))\n\n(defun org-ml--format-alist-operations (type-alist)\n  \"Return a formatted string of TYPE-ALIST.\nTYPE-ALIST is a list like that given by\n`org-ml--format-alist-operations'.\"\n  (->> type-alist\n       (--map (cons (car it) (-map #'car (cdr it))))\n       (--map (format \"\\n%s\\n%s\"\n                      (car it)\n                      (s-join \"\\n\" (--map (format \"- %S\" it) (cdr it)))))\n       (s-join \"\\n\")))\n\n(defun org-ml--append-documentation (fun string)\n  \"Append STRING to the docstring of FUN.\"\n  (let ((msg \"\\n\\nThe following types and properties are supported:\\n\")\n        (doc (documentation fun)))\n    ;; ensure we only update once, otherwise reloads will keep adding to the\n    ;; docstrings\n    (unless (s-contains? msg doc)\n      (->> (concat doc msg string)\n           (function-put fun 'function-documentation)))))\n\n(->> (org-ml--get-types-with-property-attribute :toggle)\n     (org-ml--format-alist-operations)\n     (org-ml--append-documentation 'org-ml-toggle-property))\n\n(->> (org-ml--get-types-with-property-attribute :shift)\n     (--map (cons (car it) (--remove (eq :post-blank (car it)) (cdr it))))\n     (-filter #'cdr)\n     (org-ml--format-alist-operations)\n     (concat \"\\nall elements\\n- :post-blank\\n\")\n     (org-ml--append-documentation 'org-ml-shift-property))\n\n(->> (org-ml--get-types-with-property-attribute :string-list)\n     (org-ml--format-alist-operations)\n     (org-ml--append-documentation 'org-ml-insert-into-property))\n\n(->> (org-ml--get-types-with-property-attribute :plist)\n     (org-ml--format-alist-operations)\n     (org-ml--append-documentation 'org-ml-plist-put-property))\n\n(defun org-ml-get-parents (node)\n  \"Return parents of NODE as a list.\nThe toplevel parent will be the left-most member, and NODE itself\nwill be the rightmost member.\"\n  (cl-labels\n      ((get-parents\n        (acc node)\n        (if (or (null node) (eq 'org-data (car node))) acc\n          (get-parents (cons node acc) (org-element-parent node)))))\n    (get-parents nil node)))\n\n(defun org-ml-remove-parent (node)\n  \"Return NODE with the :parent property set to nil.\n\nShort synopsis:\n\nUse this function to declutter a node if you are trying to print\nits literal list representation or you are running into infinite\nloops caused by self-referential lists (there are probably other\nvalid reasons but these are the main ones).\n\nGory details:\n\nThe :parent property refers to the node one level higher in the\ntree that contains NODE as a child. It will be present in a node\nthat is generated from a parse operation with\n`org-ml-parse-this-buffer' or related. This property offers a\nnice shortcut to traverse up the node tree from a child. Besides\nthis, it is not necessary as the tree structure itself already\nencodes all parent-child relationships. Further, it is not used\nby org-element internally to convert nodes into strings (such as\nwith `org-ml-to-string') and thus can be thought of as a\n\\\"read-only\\\" property. This is why :parent will be set to nil when\nbuilding a new node with the \\\"org-ml-build-\\\" family of functions\nand why `org-ml-set-property' forbids setting this property.\n\nIn many cases, one can safely ignore :parent unless, of course,\none actually needs to read it with `org-ml-get-parents' or\n`org-ml-get-property'. However, it heavily clutters the list\nrepresentation of nodes, and therefore it is nice to remove this\nproperty whenever literal node lists are printed/visualized (eg\nfor debugging). Note that for deep trees, each parent will itself\nhave a :parent property pointing to its own parent, with this\npattern repeating until the top of the tree.\n\nFurthermore, each parent will itself contain its own child node,\nwhich implies a circular/self-referential list. For the most\npart, this won't matter. However, some functions don't like\ndealing with circular lists and will complain about infinite\nrecursion. If this is happening, the :parent property is likely\nto blame, and setting it to nil has a high probability of fixing\nthe issue.\"\n  (if (stringp node)\n      (progn (remove-text-properties 0 (length node) '(:parent) node) node)\n    (org-element-put-property-2 :parent nil node)))\n\n(defun org-ml--caption-remove-parents (node)\n  \"Remove parents from CAPTION property in NODE if present.\"\n  (cl-flet*\n      ((remove-ss\n        (ss)\n        (-map #'org-ml-remove-parents ss))\n       (remove-from-caption\n        (caption)\n        (pcase caption\n          (`(,(pred org-ml--is-secondary-string))\n           (list (remove-ss (car caption))))\n          (`(,(pred org-ml--is-secondary-string)\n             . ,(pred org-ml--is-secondary-string))\n           (-let (((long . short) caption))\n             (cons (remove-ss long) (remove-ss short))))\n          ;; TODO error here?\n          (_ caption))))\n    (if (and (org-ml--is-any-type org-ml--element-nodes-with-affiliated node)\n             (org-element-property-raw :caption node))\n        (org-ml--map-property-raw* :caption\n          (-map #'remove-from-caption it)\n          node)\n      node)))\n\n(defun org-ml-remove-parents (node)\n  \"Like `org-ml-remove-parent' but for children of NODE as well.\n\nSee `org-ml-remove-parent' for why you might want this.\"\n  (cl-flet*\n      ((remove-recursive\n        (nodes)\n        (--map (org-ml-remove-parents it) nodes))\n       (remove-within-prop\n        (prop node)\n        (org-ml--map-property-raw* prop\n          (remove-recursive it)\n          node)))\n    (->>\n     ;; remove parents from secondary strings (if necessary)\n     (pcase (org-ml-get-type node)\n       (`headline (->> (org-element-properties-resolve node)\n                       (remove-within-prop :title)))\n       (`item (remove-within-prop :tag node))\n       (_ node))\n     (org-ml--caption-remove-parents)\n     (org-ml-remove-parent)\n     (org-ml--map-children-nocheck* (remove-recursive it)))))\n\n;;; object nodes\n;;\n;; entity\n\n(defun org-ml-entity-get-replacement (key entity)\n  \"Return replacement string or symbol for ENTITY node.\n\nKEY is one of:\n- `:latex' (the entity's latex representation)\n- `:latex-math-p' (t if the latex representation requires math mode,\n  nil otherwise)\n- `:html' (the entity's html representation)\n- `:ascii' (the entity's ascii representation)\n- `:latin1' (the entity's Latin1 representation)\n- `:utf-8' (the entity's utf8 representation)\n\nAny other keys will trigger an error.\"\n  (org-ml--check-type 'entity entity)\n  (-if-let (index (-elem-index key (list :latex :latex-math-p :html\n                                         :ascii :latin1 :utf-8)))\n      (->> (org-element-property-raw :name entity)\n           (org-entity-get)\n           (cdr)\n           (nth index))\n    (org-ml--arg-error \"Invalid encoding requested: %s\" index)))\n\n;; statistics-cookie\n\n(defun org-ml-statistics-cookie-is-complete (statistics-cookie)\n  \"Return t is STATISTICS-COOKIE node is complete.\"\n  (org-ml--check-type 'statistics-cookie statistics-cookie)\n  (let ((val (org-element-property-raw :value statistics-cookie)))\n    (or (-some->>\n         (s-match \"\\\\([[:digit:]]+\\\\)%\" val)\n         (nth 1)\n         (string-to-number)\n         (= 100))\n        (-some->>\n         (s-match \"\\\\([[:digit:]]+\\\\)/\\\\([[:digit:]]+\\\\)\" val)\n         (cdr)\n         (-map #'string-to-number)\n         (apply #'=)))))\n\n;; timestamp (standard)\n\n(defun org-ml-timestamp-get-start-time (timestamp)\n  \"Return the time list for start time of TIMESTAMP node.\nThe return value will be a list as specified by the TIME argument in\n`org-ml-build-timestamp!'.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (org-ml--timestamp-get-start-timelist timestamp))\n\n(defun org-ml-timestamp-get-end-time (timestamp)\n  \"Return the end time list for end time of TIMESTAMP or nil if not a range.\nThe return value will be a list as specified by the TIME argument in\n`org-ml-build-timestamp!'.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (and (org-ml--timestamp-is-range-type timestamp)\n       (org-ml--timestamp-get-end-timelist timestamp)))\n\n(defun org-ml-timestamp-get-length (timestamp)\n  \"Return the length of TIMESTAMP node in seconds as an integer.\nIf non-ranged, this function will return 0. If ranged but\nthe start time is in the future relative to end the time, return\na negative integer.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (org-ml--timestamp-get-length timestamp))\n\n(defun org-ml-timestamp-is-active (timestamp)\n  \"Return t if TIMESTAMP node is active.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (let ((y (org-element-property-raw :type timestamp)))\n    (if (memq y '(active active-range)) t)))\n\n(defun org-ml-timestamp-is-ranged (timestamp)\n  \"Return t if TIMESTAMP node is ranged.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (let ((y (org-element-property-raw :type timestamp)))\n    (if (memq y '(active-ranged inactive-range)) t)))\n\n(defun org-ml-timestamp-range-contains-p (unixtime timestamp)\n  \"Return t if UNIXTIME is between start and end time of TIMESTAMP node.\nThe boundaries are inclusive. If TIMESTAMP has a range of zero, then\nonly return t if UNIXTIME is the same as TIMESTAMP. TIMESTAMP will be\ninterpreted according to the localtime of the operating system.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (let ((ut1 (org-ml--timestamp-get-start-unixtime timestamp))\n        (ut2 (org-ml--timestamp-get-end-unixtime timestamp)))\n    (<= ut1 unixtime ut2)))\n\n(defun org-ml-timestamp-set-start-time (time timestamp)\n  \"Return TIMESTAMP node with start time set to TIME.\nTIME is a list analogous to the same argument specified in\n`org-ml-build-timestamp!'.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (org-ml--check-timelist-from-list time)\n  (org-ml--timestamp-set-start-timelist time (org-ml-copy timestamp)))\n\n(defun org-ml-timestamp-set-end-time (time timestamp)\n  \"Return TIMESTAMP node with end time set to TIME.\nTIME is a list analogous to the same argument specified in\n`org-ml-build-timestamp!'.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (when time\n    (org-ml--check-timelist-from-list time))\n  (org-ml--timestamp-set-end-timelist time (org-ml-copy timestamp)))\n\n(defun org-ml-timestamp-set-single-time (time timestamp)\n  \"Return TIMESTAMP node with start and end times set to TIME.\nTIME is a list analogous to the same argument specified in\n`org-ml-build-timestamp!'.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (org-ml--check-timelist-from-list time)\n  (org-ml--timestamp-set-single-timelist time (org-ml-copy timestamp)))\n\n(defun org-ml-timestamp-set-double-time (time1 time2 timestamp)\n  \"Return TIMESTAMP node with start/end times set to TIME1/TIME2 respectively.\nTIME1 and TIME2 are lists analogous to the TIME argument specified in\n`org-ml-build-timestamp!'.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (org-ml--check-timelist-from-list time1)\n  (org-ml--check-timelist-from-list time2)\n  (org-ml--timestamp-set-double-timelist time1 time2 (org-ml-copy timestamp)))\n\n(defun org-ml-timestamp-set-length (n unit timestamp)\n  \"Return TIMESTAMP node with length set to N UNITs.\nIf TIMESTAMP is ranged, keep start time the same and adjust the end\ntime. If not, make a new end time. The units for RANGE are in minutes\nif TIMESTAMP is in long format and days if TIMESTAMP is in short\nformat.\"\n  (org-ml--check-type 'timestamp timestamp)\n  ;; ASSUME unit will be checked internally\n  (org-ml--timestamp-set-length n unit (org-ml-copy timestamp)))\n\n(defun org-ml-timestamp-set-active (flag timestamp)\n  \"Return TIMESTAMP node with active type if FLAG is t.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (org-ml--timestamp-set-active flag (org-ml-copy timestamp)))\n\n(defun org-ml-timestamp-shift (n unit timestamp)\n  \"Return TIMESTAMP node with time shifted by N UNIT's.\n\nThis function will move the start and end times together; therefore\nranged inputs will always output ranged timestamps and same for\nnon-ranged. To move the start and end time independently, use\n`org-ml-timestamp-shift-start' or `org-ml-timestamp-shift-end'.\n\nN is a positive or negative integer and UNIT is one of `minute',\n`hour', `day', `month', or `year'. Overflows will wrap around\ntransparently; for instance, supplying `minute' for UNIT and 90 for N\nwill increase the hour property by 1 and the minute property by 30.\"\n  ;; ASSUME unit will be checked internally\n  ;;\n  ;; if not ranged, simply need to shift start and end (which are the same);\n  ;; otherwise need to shift both, set both, and update the timerange depending\n  ;; on if we straddle a day boundary after the shift\n  (org-ml--check-type 'timestamp timestamp)\n  (let ((rt (org-element-property-raw :range-type timestamp))\n        (timestamp* (org-ml-copy timestamp)))\n    (if (not rt)\n        (let ((t1 (->> (org-ml--timestamp-get-start-timelist timestamp)\n                       (org-ml-timelist-shift n unit))))\n          (->> (org-ml--timestamp-set-start-timelist t1 timestamp*)\n               (org-ml--timestamp-set-end-timelist t1)))\n      (-let* ((s1 (->> (org-ml--timestamp-get-start-timelist timestamp)\n                       (org-ml-timelist-shift n unit)))\n              (s2 (->> (org-ml--timestamp-get-end-timelist timestamp)\n                       (org-ml-timelist-shift n unit)))\n              ;; total micro-optimization...\n              ((d1 t1) (org-ml-timelist-split s1))\n              ((d2 t2) (org-ml-timelist-split s2))\n              (rt* (if (and (not (equal t1 t2)) (equal d1 d2))\n                       (or rt 'timerange)\n                     'daterange)))\n        (->> (org-ml--timestamp-set-start-timelist s1 timestamp*)\n             (org-ml--timestamp-set-end-timelist s2)\n             (org-ml--timestamp-set-range-type rt*))))))\n\n(defun org-ml-timestamp-shift-start (n unit timestamp)\n  \"Return TIMESTAMP node with start time shifted by N UNIT's.\n\nN and UNIT behave the same as those in `org-ml-timestamp-shift'.\n\nIf TIMESTAMP is not range, the output will be a ranged timestamp with\nthe shifted start time and the end time as that of TIMESTAMP. If this\nbehavior is not desired, use `org-ml-timestamp-shift'.\"\n  ;; ASSUME unit will be checked internally\n  (org-ml--check-type 'timestamp timestamp)\n  (let* ((t1 (->> (org-ml--timestamp-get-start-timelist timestamp)\n                  (org-ml-timelist-shift n unit)))\n         (t2 (org-ml--timestamp-get-end-timelist timestamp))\n         (rt (->> (org-element-property-raw :range-type timestamp)\n                  (org-ml--timelists-get-range-type t1 t2))))\n    (->> (org-ml-copy timestamp)\n         (org-ml--timestamp-set-start-timelist t1)\n         (org-ml--timestamp-set-range-type rt))))\n\n(defun org-ml-timestamp-shift-end (n unit timestamp)\n  \"Return TIMESTAMP node with end time shifted by N UNIT's.\n\nN and UNIT behave the same as those in `org-ml-timestamp-shift'.\n\nIf TIMESTAMP is not range, the output will be a ranged timestamp with\nthe shifted end time and the start time as that of TIMESTAMP. If this\nbehavior is not desired, use `org-ml-timestamp-shift'.\"\n  ;; ASSUME unit will be checked internally\n  (org-ml--check-type 'timestamp timestamp)\n  (let* ((t1 (org-ml--timestamp-get-start-timelist timestamp))\n         (t2 (->> (org-ml--timestamp-get-end-timelist timestamp)\n                  (org-ml-timelist-shift n unit)))\n         (rt (->> (org-element-property-raw :range-type timestamp)\n                  (org-ml--timelists-get-range-type t1 t2))))\n    (->> (org-ml-copy timestamp)\n         (org-ml--timestamp-set-end-timelist t2)\n         (org-ml--timestamp-set-range-type rt))))\n\n(defun org-ml-timestamp-toggle-active (timestamp)\n  \"Return TIMESTAMP node with its active/inactive type flipped.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (-> (org-ml--timestamp-is-active timestamp)\n      (not)\n      (org-ml--timestamp-set-active (org-ml-copy timestamp))))\n\n(defun org-ml-timestamp-truncate (timestamp)\n  \"Return TIMESTAMP node with start/end times forced to short format.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (let* ((t1 (->> (org-ml--timestamp-get-start-timelist timestamp)\n                  (org-ml--timelist-truncate)))\n         (t2 (->> (org-ml--timestamp-get-end-timelist timestamp)\n                  (org-ml--timelist-truncate)))\n         ;; NOTE it is impossible for range-type to be 'timerange since hours\n         ;; and minutes will be missing\n         (rt (if (equal t1 t2) nil 'daterange)))\n    (->> (org-ml-copy timestamp)\n         (org-ml--timestamp-set-start-timelist t1)\n         (org-ml--timestamp-set-end-timelist t2)\n         (org-ml--timestamp-set-range-type rt))))\n\n(defun org-ml-timestamp-truncate-start (timestamp)\n  \"Return TIMESTAMP node with start time forced to short format.\n\nCollapsed timestamps will become uncollapsed.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (let* ((t1 (->> (org-ml--timestamp-get-start-timelist timestamp)\n                  (org-ml--timelist-truncate)))\n         (t2 (->> (org-ml--timestamp-get-end-timelist timestamp)\n                  (org-ml--timelist-truncate)))\n         (rt (if (equal t1 t2) nil 'daterange)))\n    (->> (org-ml-copy timestamp)\n         (org-ml--timestamp-set-start-timelist t1)\n         (org-ml--timestamp-set-range-type rt))))\n\n(defun org-ml-timestamp-truncate-end (timestamp)\n  \"Return TIMESTAMP node with end time forced to short format.\n\nCollapsed timestamps will become uncollapsed.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (let* ((t1 (->> (org-ml--timestamp-get-start-timelist timestamp)\n                  (org-ml--timelist-truncate)))\n         (t2 (->> (org-ml--timestamp-get-end-timelist timestamp)\n                  (org-ml--timelist-truncate)))\n         (rt (if (equal t1 t2) nil 'daterange)))\n    (->> (org-ml-copy timestamp)\n         (org-ml--timestamp-set-end-timelist t2)\n         (org-ml--timestamp-set-range-type rt))))\n\n(defun org-ml-timestamp-set-collapsed (flag timestamp)\n  \"Return TIMESTAMP with collapsed set to FLAG.\n\nCollapsed timestamps are like [yyyy-mm-dd xxx hh:mm-hh:mm].\n\nUncollapsed timestamp are like\n[yyyy-mm-dd xxx hh:mm]--[yyyy-mm-dd xxx hh:mm].\n\nFLAG may be one of nil, t, or `force'.\n\nIf nil, uncollapse the timestamp if it is collapsed. The dates in\nthe uncollapsed timestamp will be the same. Has no effect if the\ntimestamp is not collapsed.\n\nIf t, collapse the timestamp from uncollapsed format if the following\nconditions are met:\n1. the dates are the same\n2. start and end hours/minutes are non-nil\n\nHas no effect if timestamp id not uncollapsed and these\nconditions are not met.\n\nIf `force', ignore condition 1 above. The date in the collapsed\ntimestamp will be taken from the start date and the end date will\nbe ignored.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (org-ml--timestamp-set-collapsed flag timestamp))\n\n(defun org-ml-timestamp-get-warning (timestamp)\n  \"Return the warning component of TIMESTAMP.\nReturn a list like (TYPE VALUE UNIT).\"\n  (org-ml--check-type 'timestamp timestamp)\n  (-let (((&plist :warning-type y\n                  :warning-value v\n                  :warning-unit u)\n          (org-ml--get-nonstandard-properties timestamp)))\n    (when (and y v u) `(,y ,v ,u))))\n\n(defun org-ml-timestamp-set-warning (warning timestamp)\n  \"Set the warning of TIMESTAMP to WARNING.\n\nWARNING is a list like (TYPE VALUE UNIT). TYPE is `all' or\n`first' VALUE and is an integer. UNIT is one of `year', `month',\n`week', or `day'.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (when warning\n    (org-ml--check-warning-from-list warning))\n  (->> (org-ml-copy timestamp)\n       (org-ml--timestamp-set-warning warning)))\n\n(org-ml--defun-anaphoric* org-ml-timestamp-map-warning (fun timestamp)\n  \"Apply FUN to the warning of TIMESTAMP.\nFUN is a function that takes a warning list like and returns a\nnew warning list. The same rules that apply to\n`org-ml-timestamp-set-warning' and `org-ml-timestamp-get-warning'\napply here.\"\n  ;; TODO this will check node type twice\n  (let ((w (org-ml-timestamp-get-warning timestamp)))\n    (org-ml-timestamp-set-warning (funcall fun w) timestamp)))\n\n(defun org-ml-timestamp-get-repeater (timestamp)\n  \"Return the repeater component of TIMESTAMP.\nReturn a list like (TYPE VALUE UNIT) or nil.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (org-ml--timestamp-get-repeater timestamp))\n\n(defun org-ml-timestamp-set-repeater (repeater timestamp)\n  \"Set the repeater of TIMESTAMP to REPEATER.\n\nREPEATER is a list like (TYPE VALUE UNIT); TYPE is one of\n`cumulate', `restart', or `catch-up'. VALUE is an integer. UNIT\nis one of `year', `month', `week', or `day'.\n\nSetting REPEATER to nil will remove the repeater and its deadline\nif present.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (when repeater\n    (org-ml--check-repeater-from-list repeater))\n  (->> (org-ml-copy timestamp)\n       (org-ml--timestamp-set-repeater repeater)))\n\n(org-ml--defun-anaphoric* org-ml-timestamp-map-repeater (fun timestamp)\n  \"Apply FUN to the warning of TIMESTAMP.\nFUN is a function that takes a repeater list like and returns a\nnew repeater list. The same rules that apply to\n`org-ml-timestamp-set-repeater' and\n`org-ml-timestamp-get-repeater' apply here.\"\n  (let ((r (org-ml-timestamp-get-repeater timestamp)))\n    (org-ml-timestamp-set-repeater (funcall fun r) timestamp)))\n\n(defun org-ml-timestamp-get-deadline (timestamp)\n  \"Return the repeater component of TIMESTAMP.\nReturn a list like (VALUE UNIT) or nil.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (-let (((&plist :repeater-deadline-value dv\n                  :repeater-deadline-unit du)\n          (org-ml--get-nonstandard-properties timestamp)))\n    (when (and dv du) (list dv du))))\n\n(defun org-ml-timestamp-set-deadline (deadline timestamp)\n  \"Set the repeater of TIMESTAMP to DEADLINE.\n\nDEADLINE is a list like (VALUE UNIT); VALUE is an integer. UNIT\nis one of `year', `month', `week', or `day'.\n\nSetting DEADLINE to nil will remove the deadline. Will have no effect\nif repeater is not present.\"\n  (org-ml--check-type 'timestamp timestamp)\n  (when deadline\n    (org-ml--check-deadline-from-list deadline))\n  (->> (org-ml-copy timestamp)\n       (org-ml--timestamp-set-deadline deadline)))\n\n(org-ml--defun-anaphoric* org-ml-timestamp-map-deadline (fun timestamp)\n  \"Apply FUN to the deadline of TIMESTAMP.\nFUN is a function that takes a repeater list like and returns a\nnew repeater list. The same rules that apply to\n`org-ml-timestamp-set-deadline' and\n`org-ml-timestamp-get-deadline' apply here.\"\n  (let ((d (org-ml-timestamp-get-deadline timestamp)))\n    (org-ml-timestamp-set-deadline (funcall fun d) timestamp)))\n\n;; timestamp (diary)\n\n(defun org-ml-timestamp-diary-set-value (form timestamp-diary)\n  \"Return TIMESTAMP-DIARY node with value set to FORM.\nThe node must have a type `eq' to `diary'. FORM is a quoted list.\"\n  (org-ml--check-type 'timestamp timestamp-diary)\n  (if (listp form)\n      (->> (org-ml-copy timestamp-diary)\n           (org-element-put-property-2 :raw-value (format \"<%%%%%S>\" form))\n           (org-element-put-property-2 :diary-sexp (format \"%S\" form)))\n    (org-ml--arg-error \"Timestamp-diary node value must be a form: Got %S\" form)))\n\n(defun org-ml-timestamp-diary-get-start-time (timestamp-diary)\n  \"Return start time for  TIMESTAMP-DIARY or nil.\"\n  (org-ml--check-type 'timestamp timestamp-diary)\n  (org-ml--timestamp-get-start-time timestamp-diary))\n\n(defun org-ml-timestamp-diary-get-end-time (timestamp-diary)\n  \"Return end time for  TIMESTAMP-DIARY or nil.\"\n  (org-ml--check-type 'timestamp timestamp-diary)\n  (org-ml--timestamp-get-end-time timestamp-diary))\n\n(defun org-ml-timestamp-diary-set-single-time (time timestamp-diary)\n  \"Return TIMESTAMP-DIARY node with start/end time set to TIME.\nThe node must have a type `eq' to `diary'. TIME is a list\nlike (hour min). If TIME is nil remove the time.\"\n  (org-ml--check-type 'timestamp timestamp-diary)\n  (when time\n    (org-ml--check-time-from-list time))\n  (->> (org-ml-copy timestamp-diary)\n       (org-ml--timestamp-set-start-time time)\n       (org-ml--timestamp-set-end-time time)\n       (org-ml--timestamp-update-type-ranged-timeonly)))\n\n(defun org-ml-timestamp-diary-set-start-time (time timestamp-diary)\n  \"Return TIMESTAMP-DIARY node with start time set to TIME.\nThe node must have a type `eq' to `diary'. TIME is a list\nlike (hour min). TIME may not be nil\"\n  (org-ml--check-type 'timestamp timestamp-diary)\n  (org-ml--check-time-from-list time)\n  (let* ((start (org-ml--timestamp-get-start-time timestamp-diary))\n         (end (or (org-ml--timestamp-get-end-time timestamp-diary) start time)))\n    (->> (org-ml-copy timestamp-diary)\n         (org-ml--timestamp-set-start-time time)\n         (org-ml--timestamp-set-end-time end)\n         (org-ml--timestamp-update-type-ranged-timeonly))))\n\n(defun org-ml-timestamp-diary-set-end-time (time timestamp-diary)\n  \"Return TIMESTAMP-DIARY node with end time set to TIME.\nThe node must have a type `eq' to `diary'. TIME is a list\nlike (hour min). If TIME is nil then remove the end time.\nIf start time is not set, return node unchanged.\"\n  (org-ml--check-type 'timestamp timestamp-diary)\n  (when time\n    (org-ml--check-time-from-list time))\n  (let ((start (org-ml--timestamp-get-start-time timestamp-diary)))\n    (if (not start) timestamp-diary\n      (->> (org-ml-copy timestamp-diary)\n           (org-ml--timestamp-set-end-time (or time start))\n           (org-ml--timestamp-update-type-ranged-timeonly)))))\n\n(defun org-ml-timestamp-diary-set-double-time (time1 time2 timestamp-diary)\n  \"Return TIMESTAMP-DIARY node with time set to TIME1 and TIME2.\nThe node must have a type `eq' to `diary'. TIME1 and TIME2 are\nlists like (hour min). Either time may be nil, but if TIME1 is nil\nthen TIME2 must also be nil.\"\n  (org-ml--check-type 'timestamp timestamp-diary)\n  (when (and (not time1) time2)\n    (org-ml--arg-error \"Time1 cannot be nil if Time2 is non-nil\"))\n  (when time1\n    (org-ml--check-time-from-list time1))\n  (when time2\n    (org-ml--check-time-from-list time2))\n  (->> (org-ml-copy timestamp-diary)\n       (org-ml--timestamp-set-start-time time1)\n       (org-ml--timestamp-set-end-time (or time2 time1))\n       (org-ml--timestamp-update-type-ranged-timeonly)))\n\n(defun org-ml-timestamp-diary-set-length (n unit timestamp-diary)\n  \"Return TIMESTAMP-DIARY node with range set to N UNITs.\nIf TIMESTAMP-DIARY is ranged, keep start time the same and adjust\nthe end time. If not, make a new end time.\"\n  (org-ml--check-type 'timestamp timestamp-diary)\n  (-if-let (start (org-ml--timestamp-get-start-time timestamp-diary))\n      (let ((s (org-ml--time-shift n unit start)))\n        (->> (org-ml-copy timestamp-diary)\n             (org-ml--timestamp-set-end-time s)\n             (org-ml--timestamp-update-type-ranged-timeonly)))\n    timestamp-diary))\n\n(defun org-ml-timestamp-diary-shift (n unit timestamp-diary)\n  \"Return TIMESTAMP-DIARY node with time shifted by N UNITs.\n\nThis function will move the start and end times together;\ntherefore ranged inputs will always output ranged timestamps and\nsame for non-ranged. To move the start and end time\nindependently, use `org-ml-timestamp-diary-shift-start' or\n`org-ml-timestamp-shift-end'.\n\nN is a positive or negative integer and UNIT is one of `minute',\n`hour', `day', `month', or `year'. Overflows will wrap around\ntransparently; for instance, supplying `minute' for UNIT and 90\nfor N will increase the hour property by 1 and the minute\nproperty by 30.\"\n  (org-ml--check-type 'timestamp timestamp-diary)\n  (-if-let (start (org-ml--timestamp-get-start-time timestamp-diary))\n      ;; 'or' to guard against nil end time when start is set, which is not\n      ;; supposed to happen (but might)\n      (let* ((end (or (org-ml--timestamp-get-end-time timestamp-diary) start))\n             (start* (org-ml--time-shift n unit start))\n             (end* (org-ml--time-shift n unit end)))\n        (->> (org-ml-copy timestamp-diary)\n             (org-ml--timestamp-set-start-time start*)\n             (org-ml--timestamp-set-end-time end*)\n             ;; update this in case range is in undefined state\n             (org-ml--timestamp-update-type-ranged-timeonly)))\n    timestamp-diary))\n\n(defun org-ml-timestamp-diary-shift-start (n unit timestamp-diary)\n  \"Return TIMESTAMP-DIARY node with start time shifted by N UNITs.\n\nN and UNIT behave the same as those in `org-ml-timestamp-diary-shift'.\n\nIf TIMESTAMP-DIARY is not range, the output will be a ranged timestamp with\nthe shifted start time and the end time as that of TIMESTAMP-DIARY. If this\nbehavior is not desired, use `org-ml-timestamp-diary-shift'.\"\n  (org-ml--check-type 'timestamp timestamp-diary)\n  (-if-let (start (org-ml--timestamp-get-start-time timestamp-diary))\n      (let ((end (or (org-ml--timestamp-get-end-time timestamp-diary) start))\n            (start* (org-ml--time-shift n unit start)))\n        (->> (org-ml-copy timestamp-diary)\n             (org-ml--timestamp-set-start-time start*)\n             (org-ml--timestamp-set-end-time end)\n             (org-ml--timestamp-update-type-ranged-timeonly)))\n    timestamp-diary))\n\n(defun org-ml-timestamp-diary-shift-end (n unit timestamp-diary)\n  \"Return TIMESTAMP-DIARY node with end time shifted by N UNITs.\n\nN and UNIT behave the same as those in `org-ml-timestamp-diary-shift'.\n\nIf TIMESTAMP-DIARY is not range, the output will be a ranged timestamp with\nthe shifted end time and the start time as that of TIMESTAMP-DIARY. If this\nbehavior is not desired, use `org-ml-timestamp-diary-shift'.\"\n  (org-ml--check-type 'timestamp timestamp-diary)\n  (-if-let (start (org-ml--timestamp-get-start-time timestamp-diary))\n      (let* ((end (or (org-ml--timestamp-get-end-time timestamp-diary) start)))\n        (-> (org-ml--time-shift n unit end)\n            (org-ml--timestamp-set-end-time (org-ml-copy timestamp-diary))\n            (org-ml--timestamp-update-type-ranged-timeonly)))\n    timestamp-diary))\n\n;;; element nodes\n;;\n;; clock\n\n(defun org-ml-clock-is-running (clock)\n  \"Return t if CLOCK element is running (eg is open).\"\n  (org-ml--check-type 'clock clock)\n  (org-ml--property-is-eq :status 'running clock))\n\n;; headline\n\n(defun org-ml-headline-get-statistics-cookie (headline)\n  \"Return the statistics cookie node from HEADLINE if it exists.\"\n  (org-ml--check-type 'headline headline)\n  (->> (org-element-property :title headline)\n       (-last-item)\n       (org-ml--filter-type 'statistics-cookie)))\n\n(defun org-ml-headline-is-done (headline)\n  \"Return t if HEADLINE node has a done todo-keyword.\"\n  (org-ml--check-type 'headline headline)\n  (-> (org-element-property :todo-keyword headline)\n      (member org-done-keywords)\n      (and t)))\n\n(defun org-ml-headline-has-tag (tag headline)\n  \"Return t if HEADLINE node is tagged with TAG.\"\n  (org-ml--check-type 'headline headline)\n  (if (member tag (org-element-property :tags headline)) t))\n\n(defun org-ml-headline-set-title! (title-text stats-cookie-value headline)\n  \"Return HEADLINE node with new title.\n\nTITLE-TEXT is a string to be parsed into object nodes for the title\nvia `org-ml-build-secondary-string!' (see that function for restrictions)\nand STATS-COOKIE-VALUE is a list described in\n`org-ml-build-statistics-cookie'.\"\n  (org-ml--check-type 'headline headline)\n  (let ((ss (org-ml-build-secondary-string! title-text)))\n    (if (not stats-cookie-value)\n        (org-ml-set-property :title ss headline)\n      (let ((ss* (org-ml--set-last-post-blank 1 ss))\n            (sc (org-ml-build-statistics-cookie stats-cookie-value)))\n        (org-ml-set-property :title (-snoc ss* sc) headline)))))\n\n;; item\n\n(defun org-ml-item-toggle-checkbox (item)\n  \"Return ITEM node with its checkbox state flipped.\nThis only affects item nodes with checkboxes in the `on' or `off'\nstates; return ITEM node unchanged if the checkbox property is `trans'\nor nil.\"\n  (org-ml--check-type 'item item)\n  (pcase (org-element-property-raw :checkbox item)\n    ('on (org-element-put-property-2 :checkbox 'off (org-ml-copy item)))\n    ('off (org-element-put-property-2 :checkbox 'on (org-ml-copy item)))\n    ((or `trans `nil) item)\n    (_ (error \"This should not happen\"))))\n\n;;; PUBLIC BRANCH/CHILD FUNCTIONS\n\n;;; polymorphic\n\n(defun org-ml-children-contain-point (point branch-node)\n  \"Return t if POINT is within the boundaries of BRANCH-NODE's children.\"\n  (org-ml--check-types org-ml-branch-nodes branch-node)\n  (-let ((b (org-element-contents-begin branch-node))\n         (e (org-element-contents-end branch-node)))\n    (<= b point e)))\n\n(defun org-ml-get-children (branch-node)\n  \"Return the children of BRANCH-NODE as a list.\"\n  (org-ml--check-types org-ml-branch-nodes branch-node)\n  (org-element-contents branch-node))\n\n(defun org-ml-set-children (children branch-node)\n  \"Return BRANCH-NODE with its children set to CHILDREN.\nCHILDREN is a list of nodes; the types permitted in this list depend\non the type of NODE.\"\n  (let ((type (org-ml-get-type branch-node)))\n    (-if-let (child-types (alist-get type org-ml--node-restrictions))\n        (-if-let (illegal (-difference (-map #'org-ml-get-type children)\n                                       child-types))\n            (org-ml--set-children-throw-error type child-types illegal)\n          (org-ml--set-children-nocheck children branch-node))\n      ;; this should not happen\n      (error \"Child type restrictions not found for %s\" type))))\n\n(org-ml--defun-anaphoric* org-ml-map-children (fun branch-node)\n  \"Return BRANCH-NODE with FUN applied to its children.\nFUN is a unary function that takes the current list of children and\nreturns a modified list of children.\"\n  (--> (org-ml-get-children branch-node)\n       (org-ml-set-children (funcall fun it) branch-node)))\n\n(defun org-ml-is-childless (branch-node)\n  \"Return t if BRANCH-NODE has no children.\"\n  (not (org-ml-get-children branch-node)))\n\n;;; objects\n\n(defun org-ml--normalize-secondary-string (secondary-string)\n  \"Return SECONDARY-STRING with all adjacent strings concatenated.\"\n  (cl-flet\n      ((concat-maybe\n        (acc node)\n        (let ((last (car acc)))\n          (if (and (org-ml--is-type 'plain-text last)\n                   (org-ml--is-type 'plain-text node))\n              (cons (concat last node) (cdr acc))\n            (cons node acc)))))\n    (reverse (-reduce-from #'concat-maybe nil secondary-string))))\n\n(eval-when-compile\n  (defmacro org-ml--mapcat-normalize (form secondary-string)\n    \"Return mapped, concatenated, and normalized SECONDARY-STRING.\nFORM is a form supplied to `--mapcat'.\"\n    (declare (debug (def-form form)))\n    `(->> (--map ,form ,secondary-string)\n          (apply #'append)\n          (org-ml--normalize-secondary-string))))\n\n(defun org-ml-unwrap (object-node)\n  \"Return the children of OBJECT-NODE as a secondary string.\nIf OBJECT-NODE is a plain-text node, wrap it in a list and return.\nElse add the post-blank property of OBJECT-NODE to the last member\nof its children and return children as a secondary string.\"\n  (org-ml--check-types org-ml-objects object-node)\n  (if (org-ml--is-type 'plain-text object-node)\n      (list object-node)\n    (let ((post-blank (org-ml--get-post-blank-textsafe object-node)))\n      (->> (org-ml-copy object-node t)\n           (org-element-contents)\n           (org-ml--map-last*\n            (org-ml--shift-post-blank-textsafe post-blank it))))))\n\n(defun org-ml-unwrap-types-deep (types object-node)\n  \"Return the children of OBJECT-NODE as a secondary string.\nIf OBJECT-NODE is a plain-text node, wrap it in a list and return.\nElse recursively descend into the children of OBJECT-NODE and splice\nthe children of nodes with type in TYPES in place of said node and\nreturn the result as a secondary string.\"\n  ;; TODO this will check for object nodes in nested levels which is redundant\n  (org-ml--check-types org-ml-objects object-node)\n  (cond\n   ((org-ml--is-type 'plain-text object-node)\n    (list object-node))\n   ((org-ml-is-any-type types object-node)\n    (let ((post-blank (org-ml--get-post-blank-textsafe object-node)))\n      (->> (org-element-contents object-node)\n           (org-ml--mapcat-normalize\n            (->> (org-ml-copy it)\n                 (org-ml-unwrap-types-deep types)))\n           (org-ml--map-last* (org-ml--shift-post-blank-textsafe post-blank it)))))\n   (t\n    (->> object-node\n         (org-ml-map-children*\n           (org-ml--mapcat-normalize (org-ml-unwrap-types-deep types it) it))\n         (list)))))\n\n(defun org-ml-unwrap-deep (object-node)\n  \"Return the children of OBJECT-NODE as plain-text wrapped in a list.\"\n  (org-ml-unwrap-types-deep org-ml-nodes object-node))\n\n;;; secondary strings\n\n(defun org-ml-flatten (secondary-string)\n  \"Return SECONDARY-STRING with its first level unwrapped.\nThe unwrap operation will be done with `org-ml-unwrap'.\"\n  (org-ml--mapcat-normalize (org-ml-unwrap it) secondary-string))\n\n(defun org-ml-flatten-types-deep (types secondary-string)\n  \"Return SECONDARY-STRING with object nodes in TYPES unwrapped.\nThe unwrap operation will be done with `org-ml-unwrap-types-deep'.\"\n  (org-ml--mapcat-normalize (org-ml-unwrap-types-deep types it) secondary-string))\n\n(defun org-ml-flatten-deep (secondary-string)\n  \"Return SECONDARY-STRING with all object nodes unwrapped to plain-text.\nThe unwrap operation will be done with `org-ml-unwrap-deep'.\"\n  (org-ml--mapcat-normalize (org-ml-unwrap-deep it) secondary-string))\n\n;;; item\n\n(defun org-ml--append-join-plain-lists (nodes1 nodes2)\n  \"Append NODES1 and NODES2 into one list.\nIf the last node in NODES1 and the first node in NODES2 are\nplain-lists, join the two lists together.\"\n  (let ((last (-last-item nodes1))\n        (first (car nodes2)))\n    (if (and (org-ml--is-type 'plain-list last)\n             (org-ml--is-type 'plain-list first))\n        (let ((pb (org-element-post-blank last)))\n          (--> (org-element-contents last)\n               (org-ml--set-last-post-blank pb it)\n               (append it (org-element-contents first))\n               (org-ml--set-children-nocheck it last)\n               (cons it (cdr nodes2))\n               (append (-drop-last 1 nodes1) it)))\n      (append nodes1 nodes2))))\n\n(defun org-ml-item-get-paragraph (item)\n  \"Return the first paragraph's children of ITEM or nil if none.\"\n  (org-ml--check-type 'item item)\n  (-when-let (first-child (car (org-element-contents item)))\n    (when (org-ml--is-type 'paragraph first-child)\n      (org-element-contents first-child))))\n\n(defun org-ml-item-set-paragraph (secondary-string item)\n  \"Set the first paragraph's children of ITEM to SECONDARY-STRING.\"\n  (org-ml--check-type 'item item)\n  (org-ml-map-children*\n    (if (org-ml--is-type 'paragraph (car it))\n        (if (not secondary-string) (cdr it)\n          (cons (org-ml-set-children secondary-string (car it)) (cdr it)))\n      (cons (apply #'org-ml-build-paragraph secondary-string) it))\n    item))\n\n(org-ml--defun-anaphoric* org-ml-item-map-paragraph (fun item)\n  \"Apply FUN to the first paragraph's children in ITEM.\nFUN is a UNARY function that takes the secondary-string of the\nfirst paragraph and returns modified secondary-string.\"\n  (--> (org-ml-item-get-paragraph item)\n       (org-ml-item-set-paragraph (funcall fun it) item)))\n\n;;; headline (supercontents)\n\n;; Everything under a headline in the \"section\" should follow a predictable\n;; structure. The planning and property-drawer nodes is always first and second\n;; respectively (if present) followed by a \"logbook\" and the \"contents\" The\n;; \"logbook\" contains two types of nodes, here called \"log items\" (or sometimes\n;; simply \"items\" if the context is obvious) and \"clocks.\" The former include\n;; any plain-list/item node as given by `org-log-note-headings' (except for\n;; 'clock-out' which applies only to clocks), and clocks includes clock nodes\n;; and optionally plain-list/item nodes that represent the clock-out notes.\n;; Anything that comes after the logbook is deemed \"contents.\" To make this even\n;; more complicated, the spacing after these nodes potentially interacts the\n;; encapsulating headline itself through the :pre-blank property. For instance,\n;; if a planning node has a non-zero :post-blank property, this value should be\n;; set to the :pre-blank property of the headline if the planning node is\n;; deleted (indicating that the space \"moves up\").\n\n;; To simplify this entire process, here we introduce an abstraction layer\n;; called the \"supercontents\" which will encompass the entire headline section\n;; and the :pre-blank property of the headline itself. This will represent all\n;; these components in a standardized way. A similar data structure for\n;; \"logbook\" also exists within the supercontents for similar reasons, as the\n;; logbook has many different complex representations.\n\n;; In haskell types, the supercontents and logbook are like this:\n\n;; type Blank = Natural\n;;\n;; type Property = (Text, Text)\n;;\n;; data LogBook = LogBook\n;;   { clocks :: [ClockNode]\n;;   , items :: [ItemNode]\n;;   , unknown :: [Node]\n;;   }\n;;\n;; data SuperContents = SuperContents\n;;   { planning :: (Maybe Planning)\n;;   , node-properties :: [Property]\n;;   , logbook :: LogBook\n;;   , blank :: Blank\n;;   , contents :: [Nodes]\n;;   }\n\n;; There is one *very important* assumption built into this data structure. If\n;; the planning, property-drawer, or logbook are present, there must not be any\n;; spaces between them the nor can there be a space between the headline and the\n;; first node. By extension this means :pre-blank (in the encapsulating\n;; headline) must be 0 if planning, property-drawer, or logbook are present. In\n;; this case, \"blank\" represents the first blank after any of these three\n;; components and the first node after (the start of the \"contents\"). If\n;; planning, property-drawer, or logbook are not present, \"blank\" is the same as\n;; :pre-blank. The advantage of this setup is that this annoying whitespace\n;; doesn't need to be transferred to different nodes as the headline section is\n;; edited.\n\n;; The \"logbook\" requires its own special attention, since it could be several\n;; different things depending on configuration. This is controlled by\n;; `org-log-into-drawer', `org-clock-into-drawer', and `org-log-note-clock-out'.\n;; These roughly control when the log items and/or clocks are in a named drawer\n;; or \"loose\" by themselves.\n;; \n;; Aside from these variables, the logbook will be defined as follows:\n;; - a continuous string of multi-line text after the headline metadata (in\n;;   other words, the logbook cannot contain consecutive newlines)\n;; - any items that are parsed as log items must conform to\n;;   `org-log-note-headines', which (for now) means they must end with a\n;;   timestamp on the first line (in the future, this will be extended since it\n;;   is possible to modify `org-log-note-headings', even if it is not the best\n;;   idea). Note this also implies that any log item must have a timestamp\n;;   regardless of `org-log-note-headings', which makes sense for a log note to\n;;   have...\n;; - any clock notes are defined as any item that is not a log item but after a\n;;   clock\n;;\n;; The first node that breaks any of the above conditions will be the dividing\n;; line between the logbook and contents. Note that the one loophole this\n;; creates is that it is theoretically possible (but unlikely) that an item\n;; immediately after a clock could be interpreted as a clock note even if it was\n;; not intended as one.\n\n;; Any operation involving the logbook or contents will either require\n;; separating the supercontents of the headline into the supercontents object,\n;; merging (the reverse), or both. All cases will require the user-specified\n;; config to determine how to perform the separation/merge.\n;;\n;; Steps for separating a headlines section nodes to a supercontents list are:\n;; 1. determine the logbook configuration\n;; 2. initialize a list of functions called the \"state\" which will be used to\n;;    identify and collect logbook nodes\n;; 3. use the state to \"walk\" down the nodes of the headline's section, sorting\n;;    them as items, clocks, or unknown; as the state iterates, the functions\n;;    in the state list will be modified to reflect valid logbook nodes that\n;;    can be subsequently parsed (hence the name)\n;; 4. Stop \"walking\" when the state node either runs out of functions or a node\n;;    is encountered that satisfies none of the state functions; all the\n;;    remaining nodes are deemed \"contents\"\n;; 5. return a supercontents list using the sorted items/clocks/unknown nodes\n;;    from the walk and the contents\n;;\n;; Steps for merging a supercontents list to nodes are:\n;; 1. determine the logbook configuration\n;; 2. sort the items and clocks by their timestamp (most recent at the top)\n;; 3. merge the items and clocks if required by the config\n;; 4. encapsulate items and clocks in drawers if required\n;; 5. append items and clocks (or drawers if applicable)\n;; 6. append the logbook nodes from above with the contents from the contents\n\n;; logbook struct\n\n(define-inline org-ml--logbook-init (items clocks unknown)\n  \"Create a new logbook alist.\nITEMS, CLOCKS, and UNKNOWN correspond to a list of item nodes,\nclock notes (which may contain item nodes for notes) and other\nnodes.\"\n  (declare (pure t) (side-effect-free t))\n  (inline-quote\n   (list :items ,items :clocks ,clocks :unknown ,unknown)))\n\n(define-inline org-ml-logbook-get-items (logbook)\n  \"Return the :items slot from LOGBOOK.\"\n  (declare (pure t) (side-effect-free t))\n  (inline-quote\n   (plist-get ,logbook :items)))\n\n(define-inline org-ml-logbook-get-clocks (logbook)\n  \"Return the :clocks slot from LOGBOOK.\"\n  (declare (pure t) (side-effect-free t))\n  (inline-quote\n   (plist-get ,logbook :clocks)))\n\n(define-inline org-ml-logbook-get-post-blank (logbook)\n  \"Return the :clocks slot from LOGBOOK.\"\n  (declare (pure t) (side-effect-free t))\n  (inline-quote\n   (plist-get ,logbook :post-blank)))\n\n(define-inline org-ml-logbook-set-items (items logbook)\n  \"Set the :items slot in LOGBOOK to ITEMS.\"\n  (declare (pure t) (side-effect-free t))\n  (inline-quote\n   (-let (((&plist :clocks :unknown) ,logbook))\n     (org-ml--logbook-init ,items clocks unknown))))\n\n(define-inline org-ml-logbook-set-clocks (clocks logbook)\n  \"Set the :clocks slot in LOGBOOK to CLOCKS.\"\n  (declare (pure t) (side-effect-free t))\n  (inline-quote\n   (-let (((&plist :items :unknown) ,logbook))\n     (org-ml--logbook-init items ,clocks unknown))))\n\n(org-ml--defun-anaphoric* org-ml-logbook-map-items (fun logbook)\n  \"Apply function to :item slot in LOGBOOK.\nFUN is a unary function that takes a list of items and returns a\nnew list of items.\"\n  (--> (org-ml-logbook-get-items logbook)\n       (org-ml-logbook-set-items (funcall fun it) logbook)))\n\n(org-ml--defun-anaphoric* org-ml-logbook-map-clocks (fun logbook)\n  \"Apply function to :clocks slot in LOGBOOK.\nFUN is a unary function that takes a list of clocks and returns a\nnew list of clocks.\"\n  (--> (org-ml-logbook-get-clocks logbook)\n       (org-ml-logbook-set-clocks (funcall fun it) logbook)))\n\n;; supercontents struct\n\n;; This is a structure that the user may interact with, so some of these\n;; functions are public\n\n(define-inline org-ml--supercontents-init-from-lb\n  (planning node-props logbook blank contents)\n  \"Create a supercontents plist.\n\nPLANNING is a planning node or nil.\n\nNODE-PROPS is a list of like (KEY VAL) for each node property.\n\nLOGBOOK is a logbook as given by `org-ml--logbook-init'.\n\nBLANK is the blank space above the contents.\n\nCONTENTS is a list of nodes corresponding to the headline\ncontents (the stuff after the logbook).\"\n  (declare (pure t) (side-effect-free t))\n  (inline-quote\n   (list :planning ,planning\n         :node-props ,node-props\n         :logbook ,logbook\n         :blank ,blank\n         :contents ,contents)))\n\n(define-inline org-ml--supercontents-init\n  (planning node-props items clocks unknown blank contents)\n  \"Create a supercontents alist.\nITEMS, CLOCKS, UNKNOWN, and POST-BLANK are lists corresponding to\nthe arguments in `org-ml--logbook-init' and CONTENTS has the same\nmeaning as `org-ml--supercontents-init-from-lb'.\"\n  (declare (pure t) (side-effect-free t))\n  (inline-quote\n   (let ((lb (org-ml--logbook-init ,items ,clocks ,unknown)))\n     (org-ml--supercontents-init-from-lb ,planning ,node-props lb ,blank ,contents))))\n\n(define-inline org-ml-supercontents-get-planning (supercontents)\n  \"Return the :planning slot of SUPERCONTENTS.\"\n  (declare (pure t) (side-effect-free t))\n  (inline-quote (plist-get ,supercontents :planning)))\n\n(define-inline org-ml-supercontents-set-planning (planning supercontents)\n  \"Set the :planning slot of SUPERCONTENTS to CONTENTS.\"\n  (declare (pure t) (side-effect-free t))\n  (inline-quote\n   (-let (((&plist :node-props n :logbook l :blank b :contents c) ,supercontents))\n     (org-ml--supercontents-init-from-lb ,planning n l b c))))\n\n(define-inline org-ml-supercontents-get-node-properties (supercontents)\n  \"Return the :node-props slot of SUPERCONTENTS.\"\n  (declare (pure t) (side-effect-free t))\n  (inline-quote (plist-get ,supercontents :node-props)))\n\n(define-inline org-ml-supercontents-set-node-properties (node-props supercontents)\n  \"Set the :node-props slot of SUPERCONTENTS to CONTENTS.\"\n  (declare (pure t) (side-effect-free t))\n  (inline-quote\n   (-let (((&plist :planning p :logbook l :blank b :contents c) ,supercontents))\n     (org-ml--supercontents-init-from-lb p ,node-props l b c))))\n\n(define-inline org-ml-supercontents-get-contents (supercontents)\n  \"Return the :contents slot of SUPERCONTENTS.\"\n  (declare (pure t) (side-effect-free t))\n  (inline-quote (plist-get ,supercontents :contents)))\n\n(define-inline org-ml-supercontents-set-contents (contents supercontents)\n  \"Set the :contents slot of SUPERCONTENTS to CONTENTS.\"\n  (declare (pure t) (side-effect-free t))\n  (inline-quote\n   (-let (((&plist :planning p :node-props n :logbook l :blank b) ,supercontents))\n     (org-ml--supercontents-init-from-lb p n l b ,contents))))\n\n(org-ml--defun-anaphoric* org-ml-supercontents-map-contents (fun supercontents)\n  \"Apply function to :contents slot in SUPERCONTENTS.\nFUN is a unary function that takes a list of nodes and returns a\nnew list of nodes.\"\n  (--> (org-ml-supercontents-get-contents supercontents)\n       (org-ml-supercontents-set-contents (funcall fun it) supercontents)))\n\n(define-inline org-ml-supercontents-get-logbook (supercontents)\n  \"Return the :logbook slot of SUPERCONTENTS.\"\n  (declare (pure t) (side-effect-free t))\n  (inline-quote\n   (plist-get ,supercontents :logbook)))\n\n(define-inline org-ml-supercontents-set-logbook (logbook supercontents)\n  \"Set the :logbook slot of SUPERCONTENTS to LOGBOOK.\"\n  (declare (pure t) (side-effect-free t))\n  (inline-quote\n   (-let (((&plist :planning p :node-props n :blank b :contents c) ,supercontents))\n     (org-ml--supercontents-init-from-lb p n ,logbook b c))))\n\n(org-ml--defun-anaphoric* org-ml-supercontents-map-logbook (fun supercontents)\n  \"Apply function to :logbook slot in SUPERCONTENTS.\nFUN is a unary function that takes a logbook and returns a new\nlogbook.\"\n  (--> (org-ml-supercontents-get-logbook supercontents)\n       (org-ml-supercontents-set-logbook (funcall fun it) supercontents)))\n\n;; supercontents config (scc) data structure\n\n;; Internal alist representing the user-specified config. The user form of the\n;; config is a plist with the keys :log-into-drawer, :clock-into-drawer, and\n;; :clock-notes which correspond to `org-log-into-drawer',\n;; `org-clock-into-drawer', and `org-log-note-clock-out' respectively.\n\n;; The options :log/clock-into-drawer control the \"drawer configuration\". Eight\n;; configurations are possible:\n;; \n;; | log    | clock   | result                                                                  |\n;; |----------+----------+----------------------------------------------------------------------|\n;; | nil    | nil     | items and clocks loose                                                  |\n;; | t      | nil     | items in a drawer called LOGBOOK, clocks loose                          |\n;; | nil    | t       | clocks in a drawer called LOGBOOK, items loose                          |\n;; | STR1   | STR2    | items and clocks in different drawers called STR1 and STR2              |\n;; | STR/t  | STR/t   | items and clocks in the same drawer called STRING (or LOGBOOK if t)     |\n;; | nil    | INTEGER | items loose, clocks in a drawer called LOGBOOK if > INTEGER             |\n;; | t      | INTEGER | items in drawer called LOGBOOK, and same for clocks if > INTEGER        |\n;; | STR    | INTEGER | items in drawer called STR clocks in drawer called LOGBOOK if > INTEGER |\n;;\n;; :clock-out-notes applies to all the above cases and is thus an independent\n;; consideration\n\n(defun org-ml-logbook-item-get-timestamp (item)\n  \"Return the log timestamp of ITEM if it exists.\"\n  (cl-flet\n      ((is-long-inactive-timestamp\n        (node)\n        (when (and (org-ml--is-type 'timestamp node)\n                   (org-ml--property-is-eq :type 'inactive node)\n                   (-some->> (org-ml--timestamp-get-start-timelist node)\n                     (org-ml-timelist-has-time)))\n          (org-ml--timestamp-get-start-unixtime node)))\n       (is-line-break\n        (node)\n        (or (org-ml--is-type 'line-break node)\n            (and (org-ml--is-type 'plain-text node)\n                 (equal \"\\n\" node))))\n       (get-paragraph-children\n        (item)\n        (-when-let (first-child (car (org-element-contents item)))\n          (when (org-ml--is-type 'paragraph first-child)\n            (org-element-contents first-child)))))\n    (when (org-ml--is-type 'item item)\n      (let ((pchildren (get-paragraph-children item)))\n        (-if-let (i (-find-index #'is-line-break pchildren))\n            (is-long-inactive-timestamp (nth (1- i) pchildren))\n          (is-long-inactive-timestamp (-last-item pchildren)))))))\n\n(defun org-ml--scc-encode (config)\n  \"Return a supercontents-config object from CONFIG.\"\n  (cl-flet*\n      ((select-name\n        (option)\n        (pcase option\n          (`t \"LOGBOOK\")\n          (`nil nil)\n          ((and (pred stringp) s) s)\n          (e (error \"Invalid option: %s\" e)))))\n    (-let* (((&plist :log-into-drawer lid\n                     :clock-into-drawer cid\n                     :clock-out-notes notes)\n             config)\n            (clock-limit (and (integerp cid) cid))\n            (id-name (select-name lid))\n            (cd-name (if clock-limit \"LOGBOOK\" (select-name cid)))\n            (single-drawer? (equal id-name cd-name)))\n      `((:drawers :items ,(and (not single-drawer?) id-name)\n                  :clocks ,(and (not single-drawer?) cd-name)\n                  :mixed ,(and single-drawer? id-name)\n                  :clock-limit ,clock-limit)\n        (:clock-notes . ,notes)\n        (:is-log-item-fun . ,#'org-ml-logbook-item-get-timestamp)))))\n\n(defun org-ml--scc-get-drawer-key (key scc)\n  \"Return the drawer from SCC in slot denoted by KEY.\"\n  (-let (((&alist :drawers) scc))\n    (plist-get drawers key)))\n\n(define-inline org-ml--scc-get-clock-notes (scc)\n  \"Return the :clock-notes slot from SCC.\"\n  (inline-quote\n   (alist-get :clock-notes ,scc)))\n\n(define-inline org-ml--scc-get-log-item-fun (scc)\n  \"Return the :is-log-item-fun slot from SCC.\"\n  (inline-quote\n   (alist-get :is-log-item-fun ,scc)))\n\n;; logbook separation (nodes -> logbook + contents)\n\n(defun org-ml--node-has-trailing-space (node)\n  \"Return t if NODE has at least one newline after it.\"\n  (and (< 0 (org-ml--get-post-blank-textsafe node)) t))\n\n(defun org-ml--node-is-drawer-with-name (drawer-name node)\n  \"Return t if NODE is a drawer with DRAWER-NAME.\"\n  (and (org-ml--is-type 'drawer node)\n       (equal drawer-name (org-element-property-raw :drawer-name node))))\n\n(defun org-ml--flatten-plain-lists (nodes)\n  \"Return NODES with unwrapped plain-list nodes.\n\\\"Unwrapping\\\" means replacing the plain-list with its top-level\nitems.\"\n  (cl-flet\n      ((flatten\n        (plain-list)\n        (let ((pb (org-element-post-blank plain-list)))\n          (->> (org-element-contents plain-list)\n               (org-ml--set-last-post-blank pb)))))\n    (--splice (org-ml--is-type 'plain-list it) (flatten it) nodes)))\n\n(defun org-ml--wrap-plain-lists (nodes)\n  \"Return NODES with all subsequent items wrapped as plain-lists.\nThis is the dual of `org-ml--flatten-plain-lists'.\"\n  (cl-flet\n      ((wrap\n        (acc node)\n        (cond\n         ((and (org-ml--is-type 'item node)\n               (org-ml--is-type 'plain-list (car acc)))\n          (cons (org-ml-map-children* (cons node it) (car acc))\n                (cdr acc)))\n         ((org-ml--is-type 'item node)\n          (let* ((pb (org-element-post-blank node))\n                 (pl (->> (org-ml--set-post-blank 0 node)\n                          (org-ml-build-plain-list :post-blank pb))))\n            (cons pl acc)))\n         (t\n          (cons node acc)))))\n    (-reduce-from #'wrap nil (reverse nodes))))\n\n(defun org-ml--separate-logbook (scc mode nodes)\n  \"Separate NODES into logbook components.\nSCC is the supercontents-config as given by `org-ml--scc-encode'.\nMODE is the mode by which to separate the nodes and is one of\n`mixed' (items and clocks are mixed together), `clocks', or\n`items'. The returned list will be like (ITEMS CLOCKS UNKNOWN).\"\n  (let ((n (org-ml--scc-get-clock-notes scc))\n        (f (org-ml--scc-get-log-item-fun scc)))\n    (cl-flet\n        ((split\n          (acc node)\n          (if (not node) acc\n            (cond\n             ((and (org-ml--is-type 'clock node)\n                   (memq mode '(:mixed :clocks)))\n              (cons (cons 'clocks node) acc))\n             ((and (org-ml--is-type 'item node)\n                   (memq mode '(:items :mixed))\n                   (funcall f node))\n              (cons (cons 'items node) acc))\n             ((and (org-ml--is-type 'item node)\n                   (memq mode '(:clocks :mixed))\n                   n\n                   (not (funcall f node))\n                   (org-ml--is-type 'clock (cdr (car acc))))\n              (cons (cons 'clocks node) acc))\n             (t\n              (cons (cons 'unknown node) acc))))))\n      (->> (org-ml--flatten-plain-lists nodes)\n           (-reduce-from #'split nil)))))\n\n(defmacro org-ml--state-slot (key limit eliminators next-fun)\n  \"Return a new slot for logbook separator state.\nKEY is the slot's key and LIMIT, ELIMINATORS, and NEXT-FUN are\nthe respectibe values for the plist part of the slot.\"\n  (declare (indent 3))\n  `(list ,key\n         :limit ,limit\n         :eliminators ,eliminators\n         :next ,next-fun))\n\n(defun org-ml--state-add-slot (slot state)\n  \"Add SLOT to STATE if SLOT's key is not already present.\"\n  (if (--any? (eq (car slot) (car it)) (cdr state)) state\n    (cons 'state (cons slot (cdr state)))))\n\n(defun org-ml--state-remove-slot (key state)\n  \"Remove slot from STATE given by KEY.\"\n  (cons 'state (--remove-first (eq key (car it)) (cdr state))))\n\n(defun org-ml--state-tick (key state)\n  \"Update STATE based on KEY.\n\nThe following will happen:\n1) the slot named KEY will have its limit decremented by 1\n2) all slots with limits of 0 will be removed\n3) all slots with eliminators containing KEY will be removed\"\n  (cl-flet\n      ((decrement\n        (slot)\n        (-let* (((key . (&plist :limit :eliminators :next)) slot)\n                (limit* (when limit (1- limit))))\n          (org-ml--state-slot key limit* eliminators next)))\n       (is-at-limit\n        (slot)\n        (let ((limit (plist-get (cdr slot) :limit)))\n          (when limit (= 0 limit))))\n       (can-eliminate\n        (key slot)\n        (let ((el (plist-get (cdr slot) :eliminators)))\n          (or (eq t el) (memq key el)))))\n    (->> (cdr state)\n         (--map-first (eq key (car it)) (decrement it))\n         (-remove #'is-at-limit)\n         (--remove (can-eliminate key it))\n         (cons 'state))))\n\n(defun org-ml--item-get-next-state (scc state node)\n  \"Return updated state for NODE if it is a valid log item.\nSCC is given by `org-ml--scc-encode' and STATE is given by\n`org-ml--state-init'. STATE will be updated by called\n`org-ml--state-tick'.\"\n  (let ((f (org-ml--scc-get-log-item-fun scc)))\n    (when (and (org-ml--is-type 'item node) (funcall f node))\n      (list (org-ml--state-tick :item state) (list (cons 'items node))))))\n\n(defun org-ml--clock-note-get-next-state (scc state node)\n  \"Return updated state for NODE if it is a valid clock note.\nSCC is given by `org-ml--scc-encode' and STATE is given by\n`org-ml--state-init'. STATE will be updated by called\n`org-ml--state-tick'.\"\n  (-let ((f (org-ml--scc-get-log-item-fun scc)))\n    (when (and (org-ml--is-type 'item node) (not (funcall f node)))\n      (list (org-ml--state-tick :clock-note state)\n            (list (cons 'clocks node))))))\n\n(defun org-ml--clock-get-next-state (scc state node)\n  \"Return updated state for NODE if it is a valid clock.\n\nThis will distinguish between clocks and clock notes as\nappropriate, and if a clock as found and clock notes are allowed,\nSTATE will be updated with a new slot to detect clock notes that\nwill exist for one pass.\n\nSCC is given by `org-ml--scc-encode' and STATE is given by\n`org-ml--state-init'. STATE will be updated by called\n`org-ml--state-tick'.\"\n  (when (org-ml--is-type 'clock node)\n    (let* ((slot (org-ml--state-slot :clock-notes 1 t\n                   #'org-ml--clock-note-get-next-state))\n           (next-state (--> (org-ml--state-tick :clock state)\n                            (if (org-ml--scc-get-clock-notes scc)\n                                (org-ml--state-add-slot slot it)\n                              it))))\n      (list next-state (list (cons 'clocks node))))))\n\n(defun org-ml--drawer-get-next-state (mode name scc state node)\n  \"Return updated state for NODE if it is a valid drawer.\n\nThe drawer will be separated using `org-ml--separate-logbook'\naccording to MODE but only if NAME matches.\n\nSCC is given by `org-ml--scc-encode' and STATE is given by\n`org-ml--state-init'. STATE will be updated by called\n`org-ml--state-tick'.\"\n  (when (org-ml--node-is-drawer-with-name name node)\n    (let ((drawer-nodes (->> (org-element-contents node)\n                             (org-ml--separate-logbook scc mode)))\n                             ;; (reverse)))\n          (key (cl-case mode\n                 (items :item-drawer)\n                 (clocks :clock-drawer)\n                 (mixed :mixed-drawer))))\n      (list (org-ml--state-tick key state) drawer-nodes))))\n\n(defun org-ml--clock-get-next-state* (scc state node)\n  \"Return updated state for NODE if it is a valid clock.\n\nUnlike `org-ml--clock-get-next-state' this will add a new slot to\nSTATE that detects item drawers (using the :item-drawer name from\nSCC) and removes the slot that detects mixed drawers. This is only\nintended to be used for the configuration option where clocks may\nor may not be in a drawer with items.\n\nSCC is given by `org-ml--scc-encode' and STATE is given by\n`org-ml--state-init'. STATE will be updated by called\n`org-ml--state-tick'.\"\n  (-let (((next-state log-nodes) (org-ml--clock-get-next-state scc state node)))\n    (when next-state\n      (let* ((name (org-ml--scc-get-drawer-key :mixed scc))\n             (slot (org-ml--state-slot :item-drawer 1 nil\n                     (-partial #'org-ml--drawer-get-next-state :items name)))\n             (next-state (->> (org-ml--state-add-slot slot state)\n                              (org-ml--state-remove-slot :mixed-drawer)\n                              (org-ml--state-tick :clock))))\n        (list next-state log-nodes)))))\n\n(defun org-ml--mixed-drawer-get-next-state** (mode name scc state node)\n  \"Return updated state for NODE if it is a valid mixed drawer.\n\nUnlike `org-ml--drawer-get-next-state' this will remove the slot\nthat detects clocks This is only intended to be used for the\nconfiguration option where clocks may or may not be in a drawer\nwith items. MODE and NAME carry the same meaning.\n\nSCC is given by `org-ml--scc-encode' and STATE is given by\n`org-ml--state-init'. STATE will be updated by called\n`org-ml--state-tick'.\"\n  (-let (((next-state log-nodes)\n          (org-ml--drawer-get-next-state mode name scc state node)))\n    (if (--any? (eq 'clocks (car it)) log-nodes)\n        (let ((next-state (->> (org-ml--state-remove-slot :clock next-state)\n                               (org-ml--state-tick :mixed-drawer))))\n          (list next-state log-nodes)))))\n\n(defun org-ml--init-state (scc)\n  \"Create a new state from SCC.\nThe state will be a list like (state SLOT1 SLOT2 ...) where SLOTX\nis given by `org-ml--state-slot'. The purpose of this data\nstructure is to represent valid logbook nodes for a particular\nconfiguration as well as their order and number. The reason it is\ncalled a state is because it will be used in an iterator as the\nlogbook is separated from the contents, and a `stateful' iterator\nis needed because most logbook configurations have sequential\ndependencies for valid nodes.\"\n  (-let ((funs\n          (pcase (alist-get :drawers scc)\n\n            ;; items not in drawer, clocks not in drawer\n            (`(:items nil :clocks nil :mixed nil :clock-limit nil)\n             (list (org-ml--state-slot :item nil nil\n                     #'org-ml--item-get-next-state)\n                   (org-ml--state-slot :clock nil nil\n                     #'org-ml--clock-get-next-state)))\n\n            ;; items and clocks in the same drawer\n            (`(:items nil :clocks nil :mixed ,m :clock-limit nil)\n             (list (org-ml--state-slot :mixed-drawer 1 nil\n                     (-partial #'org-ml--drawer-get-next-state :mixed m))))\n\n            ;; items not in drawer, clocks in drawer\n            (`(:items nil :clocks ,c :mixed nil :clock-limit nil)\n             (list (org-ml--state-slot :item nil nil\n                     #'org-ml--item-get-next-state)\n                   (org-ml--state-slot :clock-drawer 1 nil\n                     (-partial #'org-ml--drawer-get-next-state :clocks c))))\n\n            ;; items not in drawer, clocks might be in a drawer\n            (`(:items nil :clocks ,c :mixed nil :clock-limit ,L)\n             (list (org-ml--state-slot :item nil nil\n                     #'org-ml--item-get-next-state)\n                   (org-ml--state-slot :clock L '(:clock-drawer)\n                     #'org-ml--clock-get-next-state)\n                   (org-ml--state-slot :clock-drawer 1 '(:clock)\n                     (-partial #'org-ml--drawer-get-next-state :clocks c))))\n\n            ;; items in drawer, clocks not in drawer\n            (`(:items ,i :clocks nil :mixed nil :clock-limit nil)\n             (list (org-ml--state-slot :clock nil nil\n                     #'org-ml--clock-get-next-state)\n                   (org-ml--state-slot :item-drawer 1 nil\n                     (-partial #'org-ml--drawer-get-next-state :items i))))\n\n            ;; items in drawer, clocks in a different drawer\n            (`(:items ,i :clocks ,c :mixed nil :clock-limit nil)\n             (list (org-ml--state-slot :item-drawer 1 nil\n                     (-partial #'org-ml--drawer-get-next-state :items i))\n                   (org-ml--state-slot :clock-drawer 1 nil\n                     (-partial #'org-ml--drawer-get-next-state :clocks c))))\n\n            ;; items in drawer, clocks either loose or in a different drawer\n            (`(:items ,i :clocks ,c :mixed nil :clock-limit ,L)\n             (list (org-ml--state-slot :item-drawer 1 nil\n                     (-partial #'org-ml--drawer-get-next-state :items i))\n                   (org-ml--state-slot :clock L '(:clock-drawer)\n                     #'org-ml--clock-get-next-state)\n                   (org-ml--state-slot :clock-drawer 1 '(:clock)\n                     (-partial #'org-ml--drawer-get-next-state :clocks c))))\n\n            ;; items in drawer, clocks might be in the same drawer\n            (`(:items nil :clocks nil :mixed ,m :clock-limit ,L)\n             (list (org-ml--state-slot :clock L nil\n                     #'org-ml--clock-get-next-state*)\n                   (org-ml--state-slot :mixed-drawer 1 nil\n                     (-partial #'org-ml--mixed-drawer-get-next-state** :mixed m))))\n\n            (e (error \"This shouldn't happen: %s\" e)))))\n    (cons 'state funs)))\n\n(defmacro org-ml--reduce-state (initial-state form list)\n  \"Sort of like `--reduce' but with state.\nIt is only \\\"sort of\\\" like a standard reduce function for two\nreasons a) it keeps track of state and b) returns the remainder\nof LIST when the state is nil (which signifies termination of the\nloop). FORM is a form where `it' is bound to the current node,\n`acc' is bound to the accumulated nodes, and `it-state' is bound\nto the current state. FORM must return a list like\n\\(NEW-STATE NEW-ACC), where NEW-STATE and NEW-ACC become the state\nand accumulator on the next iteration. INITIAL-STATE is bound to\n`it-state' on the first iteration.\"\n  (declare (indent 1))\n  (let ((r (make-symbol \"--rest\")))\n    `(let ((it-state ,initial-state)\n           (,r ,list)\n           acc it)\n       (while (and it-state ,r)\n         (setq it (car ,r))\n         (-setq (it-state acc) ,form)\n         (when it-state\n           (setq ,r (cdr ,r))))\n       (list acc ,r))))\n\n(defun org-ml--split-logbook (config nodes)\n  \"Return NODES split to logbook components.\nCONFIG is a plist parsable by `org-ml--scc-encode'.\"\n  (if (not nodes) (list (org-ml--logbook-init nil nil nil) 0 nil)\n    (cl-flet\n        ((map-cdr\n           (list)\n           (-map #'cdr list))\n         (try-test-funs\n           (scc state node)\n           (-let ((test-funs (--map (plist-get (cdr it) :next) (cdr state))))\n             (--reduce-from (if acc acc (funcall it scc state node)) nil test-funs))))\n      (-let* ((scc (org-ml--scc-encode config))\n              (init-state (org-ml--init-state scc))\n              (flat (org-ml--flatten-plain-lists nodes))\n              (i (-some->> flat\n                   (-find-index #'org-ml--node-has-trailing-space)\n                   (1+)))\n              ((nodes-before-space nodes-after-space)\n               (if i (-split-at i flat) (list flat nil)))\n              (first-space-post-blank (->> (-last-item nodes-before-space)\n                                           (org-element-post-blank)))\n              ((logbook-nodes contents-nodes-before-space)\n               (org-ml--reduce-state init-state\n                 (-let (((next-state logbook-nodes) (try-test-funs scc it-state it)))\n                   (if logbook-nodes\n                       (list next-state (append logbook-nodes acc))\n                     (list nil acc)))\n                 nodes-before-space))\n              ((&alist 'items 'clocks 'unknown) (->> (reverse logbook-nodes)\n                                                     (-group-by #'car)))\n              (post-blank\n               (if logbook-nodes\n                   (if contents-nodes-before-space 0 first-space-post-blank)\n                 0))\n              (contents (->> nodes-after-space\n                             (append contents-nodes-before-space)\n                             (org-ml--wrap-plain-lists))))\n        (list (org-ml--logbook-init\n               (map-cdr items)\n               (map-cdr clocks)\n               (map-cdr unknown))\n              post-blank\n              contents)))))\n\n;; logbook merging (supercontents -> nodes)\n\n(defun org-ml--sort-logbook (mode scc nodes)\n  \"Sort NODES and return.\nNODES will be sorted according to their timestamps and are\nassumed to be valid log items or clocks/clock-notes (anything\nelse will trigger an error). SCC is a supercontents-config as\nreturned by `org-ml--scc-init'. MODE is one of :mixed, :items,\nor :clocks depending on what is intended to be sorted.\"\n  (-let (((&alist :clock-notes n :is-log-item-fun f) scc))\n    (cl-labels\n        ((get-ts\n          (node)\n          (cl-case (org-ml-get-type node)\n            (clock\n             (-some->> (org-element-property-raw :value node)\n               (org-ml--timestamp-get-start-unixtime)))\n            (item\n             (funcall f node))))\n         (prepare-node\n          (acc node)\n          ;; Return a list like (NODE . NOTE) where NODE is a clock or item and\n          ;; NOTE is a clock note, which is only added to NODE if the type of\n          ;; NODE is a clock and NOTE appears immediately after.\n          (cond\n           ((and n\n                 (memq mode '(:clocks :mixed))\n                 (org-ml--is-type 'item node)\n                 (org-ml--is-type 'clock (car (car acc)))\n                 (not (funcall f node)))\n            (cons (list (car (car acc)) node) (cdr acc)))\n           ((or (and (memq mode '(:items :mixed))\n                     (org-ml--is-type 'item node)\n                     (funcall f node))\n                (and (memq mode '(:clocks :mixed))\n                     (org-ml--is-type 'clock node)))\n            (cons (list node) acc))\n           (t\n            (let ((msg (cond\n                        ((and n (eq mode :clocks))\n                         \"Not a valid clock or note: %s\")\n                        ((and n (eq mode :mixed))\n                         \"Not a valid item, clock, or note: %s\")\n                        ((eq mode :clocks)\n                         \"Not a valid clock: %s\")\n                        ((eq mode :items)\n                         \"Not a valid item: %s\")\n                        (t\n                         \"Not a valid clock or item: %s\"))))\n              (error msg node)))))\n         ;; this should be imperative because the recursive version has O(n)\n         ;; calls to itself...byebye stack :(\n         (merge-nodes\n          (nodes-a nodes-b)\n          (let (merged)\n            (while (or nodes-a nodes-b)\n              (pcase (cons nodes-a nodes-b)\n                (`(,as . nil)\n                 (setq merged (append (nreverse as) merged)\n                       nodes-a nil))\n                (`(nil . ,bs)\n                 (setq merged (append (nreverse bs) merged)\n                       nodes-b nil))\n                (`((,a . ,as) . (,b . ,bs))\n                 (let ((ts-a (get-ts (car a)))\n                       (ts-b (get-ts (car b))))\n                   (cond\n                    ((not ts-a)\n                     (error \"Could not get timestamp for logbook node: %s\" a))\n                    ((not ts-b)\n                     (error \"Could not get timestamp for logbook node: %s\" b))\n                    ((<= ts-b ts-a)\n                     (setq merged (cons a merged)\n                           nodes-a as))\n                    ((< ts-a ts-b)\n                     (setq merged (cons b merged)\n                           nodes-b bs))\n                    (t\n                     (error \"Unknown merge error\")))))))\n            (nreverse merged)))\n         (merge-and-sort\n          (nodes)\n          (let ((L (length nodes)))\n            (if (<= L 1) nodes\n              (-let (((left right) (-split-at (/ L 2) nodes)))\n                (merge-nodes (merge-and-sort left) (merge-and-sort right)))))))\n      (->> (-reduce-from #'prepare-node nil nodes)\n           (merge-and-sort)\n           (-flatten-n 1)\n           (org-ml--wrap-plain-lists)))))\n\n(defun org-ml--merge-logbook (scc items clocks)\n  \"Merge ITEMS and CLOCKS.\nReturn these two inputs as a single sorted list (highest\ntimestamp first) according to `org-ml--sort-logbook'. SCC is a\nsupercontents-config as returned by `org-ml--scc-encode'.\"\n  (org-ml--sort-logbook :mixed scc (append items clocks)))\n\n(defun org-ml--logbook-items-to-nodes (scc logbook)\n  \"Return items in LOGBOOK as a sorted list of NODES.\nSCC is a supercontents-config as returned by\n`org-ml--scc-encode'.\"\n  (->> (org-ml-logbook-get-items logbook)\n       (org-ml--sort-logbook :items scc)))\n\n(defun org-ml--logbook-clocks-to-nodes (scc logbook)\n  \"Return clocks in LOGBOOK as a sorted list of NODES.\nSCC is a supercontents-config as returned by\n`org-ml--scc-encode'.\"\n  (->> (org-ml-logbook-get-clocks logbook)\n       (org-ml--sort-logbook :clocks scc)))\n\n(defun org-ml--logbook-to-nodes (config logbook)\n  \"Return LOGBOOK as a list of NODES.\nAnything in the UNKNOWN slot will be ignored. The exact\nnodes (drawers, loose items, etc) will be determined by the SCC.\nCONFIG is a config plist to be given to `org-ml--scc-encode'.\"\n  ;; TODO clean this up\n  (-let (((&plist :items :clocks) logbook))\n    (when (or items clocks)\n      (cl-flet*\n          ((build-drawer\n             (name children)\n             (apply #'org-ml-build-drawer name children))\n           (build-drawer-maybe\n             (name children)\n             (-some->> children (build-drawer name)))\n           (cons-drawer-maybe\n             (name drawer-nodes loose-nodes)\n             (let ((drawer (-some->> drawer-nodes (build-drawer name))))\n               (if drawer (cons drawer loose-nodes) loose-nodes)))\n           (below-limit\n             (limit lb)\n             (->> (org-ml-logbook-get-clocks lb)\n                  (--count (org-ml--is-type 'clock it))\n                  (>= limit)))\n           (merge-nodes\n             (enconf lb)\n             (-let (((&plist :items :clocks) lb))\n               (org-ml--merge-logbook enconf items clocks)))\n           (build-mixed-drawer-maybe\n             (enconf m lb)\n             (-some->> (merge-nodes enconf lb)\n               (build-drawer m)\n               (list)))\n           (to-item-clock-nodes\n             (enconf lb)\n             (list (org-ml--logbook-items-to-nodes enconf lb)\n                   (org-ml--logbook-clocks-to-nodes enconf lb))))\n        (-let (((enconf &as &alist :drawers d) (org-ml--scc-encode config)))\n          (pcase d\n\n            ;; items not in drawer, clocks not in drawer\n            (`(:items nil :clocks nil :mixed nil :clock-limit nil)\n             (merge-nodes enconf logbook))\n\n            ;; items and clocks in the same drawer\n            (`(:items nil :clocks nil :mixed ,m :clock-limit nil)\n             (build-mixed-drawer-maybe enconf m logbook))\n\n            ;; items in drawer, clocks not in drawer\n            (`(:items ,i :clocks nil :mixed nil :clock-limit nil)\n             (-let* (((items clocks) (to-item-clock-nodes enconf logbook)))\n               (cons-drawer-maybe i items clocks)))\n\n            ;; items not in drawer, clocks in drawer\n            (`(:items nil :clocks ,c :mixed nil :clock-limit nil)\n             (-let* (((items clocks) (to-item-clock-nodes enconf logbook)))\n               (cons-drawer-maybe c clocks items)))\n\n            ;; items in drawer, clocks might be in the same drawer\n            (`(:items nil :clocks nil :mixed ,m :clock-limit ,l)\n             (if (below-limit l logbook)\n                 (-let* (((items clocks) (to-item-clock-nodes enconf logbook)))\n                   (cons-drawer-maybe m items clocks))\n               (build-mixed-drawer-maybe enconf m logbook)))\n            \n            ;; items not in drawer, clocks might be in a drawer\n            (`(:items nil :clocks ,c :mixed nil :clock-limit ,l)\n             (if (below-limit l logbook) (merge-nodes enconf logbook)\n               (-let* (((items clocks) (to-item-clock-nodes enconf logbook)))\n                 (cons-drawer-maybe c clocks items))))\n\n            ;; items in drawer, clocks in a different drawer\n            (`(:items ,i :clocks ,c :mixed nil :clock-limit nil)\n             (-let* (((items clocks) (to-item-clock-nodes enconf logbook))\n                     (items-drawer (build-drawer-maybe i items))\n                     (clocks-drawer (build-drawer-maybe c clocks)))\n               (-non-nil (list items-drawer clocks-drawer))))\n\n            ;; items in drawer, clocks either loose or in a different drawer\n            (`(:items ,i :clocks ,c :mixed nil :clock-limit ,l)\n             (-let* (((items clocks) (to-item-clock-nodes enconf logbook))\n                     (items-drawer (build-drawer-maybe i items)))\n               (if (below-limit l logbook)\n                   (if items-drawer (cons items-drawer clocks) clocks)\n                 (->> (build-drawer-maybe c clocks)\n                      (list items-drawer)\n                      (-non-nil)))))\n\n            (e (error \"This shouldn't happen: %s\" e))))))))\n\n;; section -> supercontents\n\n;; Introduce another data abstraction here called the \"supersection\". This is\n;; much simpler than the supercontents, and consists only of the :pre-blank and\n;; section children of the encapsulating headline. The main purpose of this is\n;; to provide a means to update \"the stuff under the headline\" easily without\n;; triggering the headline itself to undefer most of its properties. The\n;; :pre-blank is part of the headline node even though it visually affects the\n;; stuff underneath it. This especially matters for functions like\n;; `org-ml-update-supersections' and `org-ml-update-supercontents' which can be\n;; massively sped up if they don't need to update the headline in the buffer.\n\n(define-inline org-ml--supersection-init (pre-blank section)\n  \"Create a supersection plist from PRE-BLANK and SECTION.\nSECTION is a list of nodes under the section node in a headline.\"\n  (inline-quote (list :pre-blank ,pre-blank :section ,section)))\n\n(defun org-ml--planning-split (planning)\n  \"Decompose PLANNING into lists.\"\n  (list :closed (-some->> (org-element-property-raw :closed planning)\n                  (org-ml--timestamp-get-start-timelist))\n        :scheduled (-some->> (org-element-property-raw :scheduled planning)\n                     (org-ml--timestamp-to-planning-list))\n        :deadline (-some->> (org-element-property-raw :deadline planning)\n                    (org-ml--timestamp-to-planning-list))))\n\n(defun org-ml--property-drawer-split (property-drawer)\n  \"Decompose PROPERTY-DRAWER into a list of node-property nodes.\"\n  (->> (org-element-contents property-drawer)\n       (--map (list (org-element-property-raw :key it)\n                    (org-element-property-raw :value it)))))\n\n(defun org-ml--from-first-second-rest\n    (config planning prop-drawer blank-node children)\n  \"Create a new supercontents node in various ways.\n\nCONFIG is a list corresponding to `org-ml--scc-encode'. PLANNING\nis a planning node. PROP-DRAWER is a property-drawer node.\nBLANK-NODE is a node that has a post-blank behind it. CHILDREN is\neverything after the planning and/or property-drawer.\"\n  (-let* ((pb (org-element-post-blank blank-node))\n          ((logbook blank contents) (if (< 0 pb)\n                                        `(nil ,pb ,children)\n                                      (org-ml--split-logbook config children))))\n    (org-ml--supercontents-init-from-lb\n     (and planning (org-ml--planning-split planning))\n     (and prop-drawer (org-ml--property-drawer-split prop-drawer))\n     logbook\n     blank\n     contents)))\n\n(defun org-ml--supersection-to-supercontents (config supersection)\n  \"Convert SUPERSECTION to supercontents.\nCONFIG is a list corresponding to `org-ml--scc-encode'.\"\n  (-let (((&plist :pre-blank pb :section children) supersection))\n    ;; If pre-blank is >0, by definition there is no planning,\n    ;; property-drawer, or logbook\n    (if (< 0 pb) (org-ml--supercontents-init nil nil nil nil nil pb children)\n      (-let* (((first . rest1) children)\n              ((second . rest2) rest1)\n              (t1 (org-ml-get-type first))\n              (t2 (org-ml-get-type second)))\n        (cond\n         ((and (eq t1 'planning) (eq t2 'property-drawer))\n          (org-ml--from-first-second-rest config first second second rest2))\n         ((eq t1 'planning)\n          (org-ml--from-first-second-rest config first nil first rest1))\n         ((eq t1 'property-drawer)\n          (org-ml--from-first-second-rest config nil first first rest1))\n         (t\n          (->> (org-ml--split-logbook config children)\n               (apply #'org-ml--supercontents-init-from-lb nil nil))))))))\n\n(defun org-ml--supercontents-to-supersection (config supercontents)\n  \"Convert SUPERCONTENTS to supersection.\nCONFIG is a list corresponding to `org-ml--scc-encode'.\"\n  (-let* (((&plist :planning p :node-props n :logbook lb :blank b :contents c)\n           supercontents)\n          (anyp (or (plist-get p :closed)\n                    (plist-get p :scheduled)\n                    (plist-get p :deadline)))\n          (lb-nodes (org-ml--logbook-to-nodes config lb)))\n    (cond\n     (lb-nodes\n      (org-ml--supersection-init\n       0 `(,@(when anyp (list (apply #'org-ml-build-planning! p)))\n           ,@(when n (list (apply #'org-ml-build-property-drawer! n)))\n           ,@(org-ml--set-last-post-blank b lb-nodes)\n           ,@c)))\n     (n\n      (org-ml--supersection-init\n       0 `(,@(when anyp (list (apply #'org-ml-build-planning! p)))\n           ,(apply #'org-ml-build-property-drawer! :post-blank b n)\n           ,@c)))\n     (anyp\n      (org-ml--supersection-init\n       0 (cons (apply #'org-ml-build-planning! :post-blank b p) c)))\n     (t\n      (org-ml--supersection-init b c)))))\n\n;; public supercontents functions\n\n(defun org-ml-headline-get-supercontents (config headline)\n  \"Return the supercontents of HEADLINE node.\n\nSupercontents will be a plist like:\n\n\\(\n  :planning PLANNING\n  :node-props PROPS\n  :logbook LB\n  :blank BLANK\n  :contents CONTENTS\n)\n\nPLANNING is a plist like the analogous argument of\n`org-ml-build-planning!' or nil if non-existent.\n\nPROPS is a list of node-property nodes.\n\nLB is the logbook, which is another plist (see below).\n\nBLANK is the value of any whitespace after the planning,\nproperty-drawer, or logbook (assuming any exist) or the\n:pre-blank value of the encapsulating headline (if they don't\nexist).\n\nCONTENTS is a list of nodes after all the other stuff above.\n\nThe logbook will be have keys :items, :clocks, and :unknown,\nwhere the first two will include the item and clock nodes of the\nlogbook respectively, and the third will contain anything that\ncould not be identified as a valid logbook entry. Note that items\nare actually stored under a plain-list node but will be returned\nhere as a flat list of items for convenience. Also note that the\n:clocks slot can also include item nodes if clock notes are\nreturned.\n\nCONFIG is a plist representing the logbook configuration to\ntarget and will contain the following keys;\n- :log-into-drawer - corresponds to the value of\n  symbol `org-log-into-drawer' and carriers the same meaning\n- :clock-into-drawer - corresponds to the value of\n  symbol `org-clock-into-drawer' and carriers the same meaning\n- :clock-out-notes - corresponds to the value of\n  `org-log-note-clock-out'\n\nAny values not given will default to nil. Note that there is no\nway to infer what the logbook configuration should be, and thus\nthis controls how the logbook will be parsed; this means it also\ndetermines which nodes will be returned in the :items/:clocks\nslots and which will be deemed :unknown (see above) so be sure\nthis plist is set according to your desired target configuration.\"\n  (->> (org-ml-headline-get-supersection headline)\n       (org-ml--supersection-to-supercontents config)))\n     \n(defun org-ml-headline-set-supercontents (config supercontents headline)\n  \"Set logbook and contents of HEADLINE according to SUPERCONTENTS.\nSee `org-ml-headline-get-supercontents' for the meaning of CONFIG\nand the structure of the SUPERCONTENTS list.\"\n  (-> (org-ml--supercontents-to-supersection config supercontents)\n      (org-ml-headline-set-supersection headline)))\n\n(org-ml--defun-anaphoric* org-ml-headline-map-supercontents (config fun headline)\n  \"Map a function over the supercontents of HEADLINE.\nFUN is a unary function that takes a supercontents list and\nreturns a modified supercontents list. See\n`org-ml-headline-get-supercontents' for the meaning of CONFIG and\nthe structure of the supercontents list.\"\n  (--> (org-ml-headline-get-supercontents config headline)\n       (org-ml-headline-set-supercontents config (funcall fun it) headline)))\n\n(defun org-ml-headline-get-supersection (headline)\n  \"Return supersection list for the section in HEADLINE.\"\n  (org-ml--check-type 'headline headline)\n  (org-ml--supersection-init\n   (org-element-property-raw :pre-blank headline)\n   (org-ml-headline-get-section headline)))\n\n(defun org-ml-headline-set-supersection (supersection headline)\n  \"Return SUPERSECTION for the section in HEADLINE.\"\n  (org-ml--check-type 'headline headline)\n  (-let* (((&plist :pre-blank p :section m) supersection)\n          (pb (org-element-property-raw :pre-blank headline))\n          (headline* (if (/= p pb)\n                         (->> (org-ml-copy headline)\n                              (org-element-put-property-2 :pre-blank p))\n                       headline)))\n    (org-ml-headline-set-section m headline*)))\n\n(org-ml--defun-anaphoric* org-ml-headline-map-supersection (fun headline)\n  \"Apply FUN to HEADLINE supersection.\"\n  (let ((it (org-ml-headline-get-supersection headline)))\n    (org-ml-headline-set-supersection (funcall fun it) headline)))\n\n;; planning\n\n(defun org-ml-headline-get-planning (headline)\n  \"Return the planning node in HEADLINE or nil if none.\"\n  (org-ml--check-type 'headline headline)\n  ;; TODO it seems silly that we need to \"convert\" the logbook when I'm not\n  ;; modifying it. Lazy eval?\n  (->> (org-ml-headline-get-supercontents nil headline)\n       (org-ml-supercontents-get-planning)))\n\n(defun org-ml-headline-set-planning (planning headline)\n  \"Return HEADLINE node with planning components set to PLANNING node.\"\n  (org-ml--check-type 'headline headline)\n  (org-ml-headline-map-supercontents* nil\n    (org-ml-supercontents-set-planning planning it)\n    headline))\n\n(org-ml--defun-anaphoric* org-ml-headline-map-planning (fun headline)\n  \"Return HEADLINE node with planning node modified by FUN.\n\nFUN is a unary function that takes a planning node and returns a\nmodified planning node.\"\n   (--> (org-ml-headline-get-planning headline)\n        (org-ml-headline-set-planning (funcall fun it) headline)))\n\n;; node-properties (eg the entire property drawer)\n\n(defun org-ml-headline-get-node-properties (headline)\n  \"Return a list of node-properties nodes in HEADLINE or nil if none.\"\n  (org-ml--check-type 'headline headline)\n  ;; TODO it seems silly that we need to \"convert\" the logbook when I'm not\n  ;; modifying it. Lazy eval?\n  (->> (org-ml-headline-get-supercontents nil headline)\n       (org-ml-supercontents-get-node-properties)))\n\n(defun org-ml-headline-set-node-properties (node-properties headline)\n  \"Return HEADLINE node with property drawer containing NODE-PROPERTIES.\nNODE-PROPERTIES is a list of (key . value) pairs (both strings).\"\n  (org-ml--check-type 'headline headline)\n  (org-ml-headline-map-supercontents* nil\n    (org-ml-supercontents-set-node-properties node-properties it)\n    headline))\n\n(org-ml--defun-anaphoric* org-ml-headline-map-node-properties (fun headline)\n  \"Return HEADLINE node with property-drawer node modified by FUN.\n\nFUN is a unary function that takes a property-drawer node and returns\na modified property-drawer node.\"\n   (--> (org-ml-headline-get-node-properties headline)\n        (org-ml-headline-set-node-properties (funcall fun it) headline)))\n\n;; node-property\n\n(defun org-ml-headline-get-node-property (key headline)\n  \"Return value of property with KEY in HEADLINE or nil if not found.\nIf multiple properties with KEY are present, only return the first.\"\n  (->> (org-ml-headline-get-node-properties headline)\n       (--first (equal key (car it)))\n       (cadr)))\n\n(defun org-ml-headline-set-node-property (key value headline)\n  \"Return HEADLINE with node property matching KEY set to VALUE.\nIf a property matching KEY is present, set it to VALUE. If multiple\nproperties matching KEY are present, only set the first.\"\n  (org-ml-headline-map-node-properties*\n    (-if-let (np (-some->> value (list key)))\n        (-if-let (i (--find-index (equal key (car it)) it))\n            (-replace-at i np it)\n          (cons np it))\n      (--remove-first (equal key (car it)) it))\n    headline))\n\n(org-ml--defun-anaphoric* org-ml-headline-map-node-property (key fun headline)\n  \"Return HEADLINE node with property value matching KEY modified by FUN.\n\nFUN is a unary function that takes a node-property value and returns\na modified node-property value.\"\n   (--> (org-ml-headline-get-node-property key headline)\n        (org-ml-headline-set-node-property key (funcall fun it) headline)))\n\n;; public logbook/contents getters/setters/mappers\n\n(defun org-ml-headline-get-logbook-items (config headline)\n  \"Return the logbook items of HEADLINE.\nSee `org-ml-headline-get-supercontents' for the meaning of\nCONFIG. The returned items will be a flat list of item nodes,\nnot a plain-list node.\"\n  (->> (org-ml-headline-get-supercontents config headline)\n       (org-ml-supercontents-get-logbook)\n       (org-ml-logbook-get-items)))\n\n(defun org-ml-headline-set-logbook-items (config items headline)\n  \"Set the logbook items of HEADLINE to ITEMS.\nSee `org-ml-headline-get-supercontents' for the meaning of\nCONFIG. ITEMS must be supplied as a flat list of valid logbook\nitem nodes, not as a plain-list node.\"\n  (org-ml-headline-map-supercontents* config\n    (org-ml-supercontents-map-logbook* (org-ml-logbook-set-items items it) it)\n    headline))\n\n(org-ml--defun-anaphoric* org-ml-headline-map-logbook-items (config fun headline)\n  \"Map a function over the logbook items of HEADLINE.\nFUN is a unary function that takes a list of item nodes and\nreturns a modified list of item nodes. See\n`org-ml-headline-get-supercontents' for the meaning of CONFIG.\"\n  (--> (org-ml-headline-get-logbook-items config headline)\n       (org-ml-headline-set-logbook-items config (funcall fun it) headline)))\n\n(defun org-ml-headline-get-logbook-clocks (config headline)\n  \"Return the logbook clocks of HEADLINE.\nSee `org-ml-headline-get-supercontents' for the meaning of\nCONFIG. The returned list will include clock nodes and maybe item\nnodes if :clock-out-notes is t in CONFIG.\"\n  (->> (org-ml-headline-get-supercontents config headline)\n       (org-ml-supercontents-get-logbook)\n       (org-ml-logbook-get-clocks)))\n\n(defun org-ml-headline-set-logbook-clocks (config clocks headline)\n  \"Set the logbook clocks of HEADLINE to CLOCKS.\nSee `org-ml-headline-get-supercontents' for the meaning of\nCONFIG. CLOCKS must be supplied as a flat list of valid clock\nnodes and optionally item nodes if :clock-out-notes is t in\nCONFIG.\"\n  (org-ml-headline-map-supercontents* config\n    (org-ml-supercontents-map-logbook* (org-ml-logbook-set-clocks clocks it) it)\n    headline))\n\n(org-ml--defun-anaphoric* org-ml-headline-map-logbook-clocks (config fun headline)\n  \"Map a function over the logbook clocks of HEADLINE.\nFUN is a unary function that takes a list of clock nodes and\noptionally item nodes to represent the clock notes and returns a\nmodified list of said nodes. `org-ml-headline-get-supercontents'\nfor the meaning of CONFIG.\"\n  (--> (org-ml-headline-get-logbook-clocks config headline)\n       (org-ml-headline-set-logbook-clocks config (funcall fun it) headline)))\n\n(defun org-ml-headline-get-contents (config headline)\n  \"Return the contents of HEADLINE.\nContents is everything in the headline after the logbook and will\nbe returned as a flat list of nodes. See\n`org-ml-headline-get-supercontents' for the meaning of CONFIG.\"\n  (->> (org-ml-headline-get-supercontents config headline)\n       (org-ml-supercontents-get-contents)))\n\n(defun org-ml-headline-set-contents (config contents headline)\n  \"Set the contents of HEADLINE to CONTENTS.\nContents is everything in the headline after the logbook, and\nCONTENTS must be a flat list of nodes. See\n`org-ml-headline-get-supercontents' for the meaning of CONFIG.\"\n  (org-ml-headline-map-supercontents* config\n    (org-ml-supercontents-set-contents contents it)\n    headline))\n\n(org-ml--defun-anaphoric* org-ml-headline-map-contents (config fun headline)\n  \"Map a function over the contents of HEADLINE.\nContents is everything in the headline after the logbook. FUN is\na unary function that takes a list of nodes representing the\ncontents and returns a modified list of nodes. See\n`org-ml-headline-get-supercontents' for the meaning of CONFIG.\"\n  (--> (org-ml-headline-get-contents config headline)\n       (org-ml-headline-set-contents config (funcall fun it) headline)))\n\n;; public high-level logbook operations\n\n(defun org-ml-headline-logbook-append-item (config item headline)\n  \"Append ITEM to the logbook of HEADLINE.\nSee `org-ml-headline-get-supercontents' for the meaning of\nCONFIG. ITEM must be a valid logbook item. The logbook will be\nstarted if it does not already exist, else ITEM will be added in\nchronological order.\"\n  (org-ml-headline-map-logbook-items* config (cons item it) headline))\n\n(defun org-ml-headline-logbook-append-open-clock (config unixtime headline)\n  \"Append an open clock to the logbook of HEADLINE.\nSee `org-ml-headline-get-supercontents' for the meaning of\nCONFIG. UNIXTIME will set the start time of the clock. The\nlogbook will be started if it does not already exist, else the\nnew clock will be added in chronological order.\"\n  (let ((clock (-> (org-ml-unixtime-to-datetime unixtime)\n                   (org-ml-build-clock!))))\n    (org-ml-headline-map-logbook-clocks* config (cons clock it) headline)))\n\n(defun org-ml-headline-logbook-close-open-clock (config unixtime note headline)\n  \"Close an open clock to the logbook of HEADLINE.\nSee `org-ml-headline-get-supercontents' for the meaning of\nCONFIG. UNIXTIME will set the end time of the clock. This will\nonly close an open clock if it is the most recent clock; else it\nwill do nothing. NOTE is a string representing the clock-out\nnote (or nil if not desired). Note that supplying a non-nil\nclock-note when it is not allowed by CONFIG will trigger an\nerror.\"\n  (org-ml-headline-map-logbook-clocks* config\n    (-let (((first . rest) it))\n      (if (not (and first (org-ml-clock-is-running first))) it\n        (let* ((time (org-ml-unixtime-to-datetime unixtime))\n               (closed (->> first\n                            ;; NOTE making copies here is necessary\n                            (org-ml-map-property* :value\n                              (org-ml-timestamp-set-end-time time it))))\n               (note* (-some->> note\n                        (org-ml-build-paragraph)\n                        (org-ml-build-item))))\n          (if note* `(,closed ,note* ,@rest) (cons closed rest)))))\n    headline))\n\n(defun org-ml-headline-logbook-convert-config (config1 config2 headline)\n  \"Convert the logbook of HEADLINE to a new configuration.\nCONFIG1 is the current config and CONFIG2 is the target config.\nNote that any logbook nodes that are invalid under CONFIG1 will\nbe silently dropped, and nodes which do not conform to CONFIG2\nwill trigger an error. See `org-ml-headline-get-supercontents'\nfor the structure of both config lists.\"\n  (--> (org-ml-headline-get-supercontents config1 headline)\n       (org-ml-headline-set-supercontents config2 it headline)))\n\n;; misc\n\n(defun org-ml-headline-get-path (headline)\n  \"Return tree path of HEADLINE node.\n\nThe return value is a list of headline titles (including that from\nHEADLINE) leading to the root node.\"\n  (org-ml--check-type 'headline headline)\n  (->> (org-ml-get-parents headline)\n       (--map (org-element-property :raw-value it))))\n\n(defun org-ml-headline-update-item-statistics (headline)\n  \"Return HEADLINE node with updated statistics cookie via items.\n\nThe percent/fraction will be computed as the number of checked items\nover the number of items with checkboxes (non-checkbox items will\nnot be considered).\"\n  (let* ((items\n          (->> (org-ml-headline-get-section headline)\n               (org-element-contents)\n               (--filter (org-ml--is-type 'plain-list it))\n               (-mapcat #'org-element-contents)\n               (--filter (org-element-property-raw :checkbox it))))\n         (done (length (--filter (org-ml--property-is-eq :checkbox 'on it)\n                                 items)))\n         (total (length items)))\n    (->> (org-ml-copy headline)\n         (org-ml--headline-set-statistics-cookie-fraction done total))))\n\n(defun org-ml-headline-update-todo-statistics (headline)\n  \"Return HEADLINE node with updated statistics cookie via subheadlines.\n\nThe percent/fraction will be computed as the number of done\nsubheadlines over the number of todo subheadlines (eg non-todo\nsubheadlines will not be counted).\"\n  (let* ((subtodo (->> (org-ml-headline-get-subheadlines headline)\n                       (--filter (org-element-property :todo-keyword it))))\n         (done (length (-filter #'org-ml-headline-is-done subtodo)))\n         (total (length subtodo)))\n    (->> (org-ml-copy headline)\n         (org-ml--headline-set-statistics-cookie-fraction done total))))\n\n;;; plain-list\n\n;; TODO there seems to be a bug in the interpreter that prevents \"+\" bullets from\n;; being recognized (as of org-9.1.9 they are simply read as \"-\")\n(defun org-ml-plain-list-set-type (type plain-list)\n  \"Return PLAIN-LIST node with type property set to TYPE.\nTYPE is one of the symbols `unordered' or `ordered'.\"\n  (org-ml--check-type 'plain-list plain-list)\n  (cond\n   ((eq type 'unordered)\n    (org-ml--map-children-nocheck*\n      (--map (org-ml-set-property :bullet '- it) it)\n      plain-list))\n   ((eq type 'ordered)\n    ;; NOTE the org-interpreter seems to use the correct, ordered numbers if any\n    ;; number is set here. This behavior may not be reliable.\n    (org-ml--map-children-nocheck*\n      (--map (org-ml-set-property :bullet 1 it) it)\n      plain-list))\n   (t (org-ml--arg-error \"Invalid type: %s\" type))))\n\n;;; table\n\n(defun org-ml-table-get-cell (row-index column-index table)\n  \"Return table-cell node at ROW-INDEX and COLUMN-INDEX in TABLE node.\nRule-type rows do not count toward row indices.\"\n  (org-ml--check-type 'table table)\n  (->> (org-ml--table-get-row row-index table)\n       (org-element-contents)\n       (org-ml--nth column-index)))\n\n(defun org-ml-table-delete-row (row-index table)\n  \"Return TABLE node with row at ROW-INDEX deleted.\"\n  (org-ml--check-type 'table table)\n  (org-ml--map-children-nocheck* (org-ml--remove-at row-index it) table))\n\n(defun org-ml-table-delete-column (column-index table)\n  \"Return TABLE node with column at COLUMN-INDEX deleted.\"\n  (org-ml--check-type 'table table)\n  (org-ml--map-children-nocheck*\n   (--map\n    (if (org-ml--property-is-eq :type 'rule it) it\n      (org-ml--map-children-nocheck* (org-ml--remove-at column-index it) it))\n    it)\n   table))\n\n(defun org-ml-table-insert-column! (column-index column-text table)\n  \"Return TABLE node with COLUMN-TEXT inserted at COLUMN-INDEX.\n\nCOLUMN-INDEX is the index of the column and COLUMN-TEXT is a list of\nstrings to be made into table-cells to be inserted following the same\nsyntax as `org-ml-build-table-cell!'.\"\n  (org-ml--check-type 'table table)\n  (let ((column (-map #'org-ml-build-table-cell! column-text)))\n    (org-ml--column-map-down-rows\n     (lambda (new-cell cells) (org-ml--insert-at column-index new-cell cells))\n     column\n     table)))\n\n(defun org-ml-table-insert-row! (row-index row-text table)\n  \"Return TABLE node with ROW-TEXT inserted at ROW-INDEX.\n\nROW-INDEX is the index of the column and ROW-TEXT is a list of strings\nto be made into table-cells to be inserted following the same syntax\nas `org-ml-build-table-row!'.\"\n  (org-ml--check-type 'table table)\n  (if (not row-text) (org-ml--table-clear-row row-index table)\n    (let ((row (->> (org-ml-build-table-row! row-text)\n                    (org-ml--table-row-pad-maybe table))))\n      (org-ml--map-children-nocheck*\n        (org-ml--insert-at row-index row it)\n        table))))\n\n(defun org-ml-table-replace-cell! (row-index column-index cell-text table)\n  \"Return TABLE node with a table-cell node replaced by CELL-TEXT.\n\nIf CELL-TEXT is a string, it will replace the children of the\ntable-cell at ROW-INDEX and COLUMN-INDEX in TABLE. CELL-TEXT will be\nprocessed the same as the argument given to `org-ml-build-table-cell!'.\n\nIf CELL-TEXT is nil, it will set the cell to an empty string.\"\n  (org-ml--check-type 'table table)\n  (let* ((cell (if cell-text (org-ml-build-table-cell! cell-text)\n                 (org-ml-build-table-cell \"\")))\n         (row (->> (org-ml--table-get-row row-index table)\n                   (org-ml--map-children-nocheck*\n                     (org-ml--replace-at column-index cell it)))))\n    (org-ml--table-replace-row row-index row table)))\n\n(defun org-ml-table-replace-column! (column-index column-text table)\n  \"Return TABLE node with the column at COLUMN-INDEX replaced by COLUMN-TEXT.\n\nIf COLUMN-TEXT is a list of strings, it will replace the table-cells\nat COLUMN-INDEX. Each member of COLUMN-TEXT will be processed the\nsame as the argument given to `org-ml-build-table-cell!'.\n\nIf COLUMN-TEXT is nil, it will clear all cells at COLUMN-INDEX.\"\n  (org-ml--check-type 'table table)\n  (if (not column-text) (org-ml--table-clear-column column-index table)\n    (let ((column-cells (-map #'org-ml-build-table-cell! column-text)))\n      (org-ml--table-replace-column column-index column-cells table))))\n\n(defun org-ml-table-replace-row! (row-index row-text table)\n  \"Return TABLE node with the row at ROW-INDEX replaced by ROW-TEXT.\n\nIf ROW-TEXT is a list of strings, it will replace the cells at\nROW-INDEX. Each member of ROW-TEXT will be processed the same as\nthe argument given to `org-ml-build-table-row!'.\n\nIf ROW-TEXT is nil, it will clear all cells at ROW-INDEX.\"\n  (org-ml--check-type 'table table)\n  (let ((row-cells (org-ml-build-table-row! row-text)))\n    (org-ml--table-replace-row row-index row-cells table)))\n\n;;; INDENTATION FUNCTIONS\n\n;; NOTE: for headlines, promote = outdent, and demote = indent\n\n;;; indentation (single and tree)\n\n;; high level steps to indent\n;;\n;; Assume an abstract tree thing like this:\n;; - 0.\n;; - 1.\n;;   - 1.0\n;; - 2.\n;;\n;; We wish to indent 1. There are two cases:\n;; 1. indent only 1.\n;; 2. indent 1. and 1.0 along with it\n;;\n;; In both cases, make 1.0 a child of 0. Remove 1.0 from the\n;; top-level list and leave 1.0 and 2. untouched\n;;\n;; In case 2, this is all that is needed since 1.0 is already a child of 1. and\n;; will \"autoindent\" as 1. itself is moved.\n;;\n;; To make it \"stay in place,\" as in case 1, remove 1.0 as a child of\n;; 1., append it to the end of the list containing 2., and set this list (with\n;; both 1. and 1.0.) as the child of 0.\n;;\n;; parameters for indenting:\n;; - index of target to indent (1 in above example)\n\n;; TODO these mostly work except for whitespace edgecases, and those are really\n;; ugly to work around\n\n(defmacro org-ml--tree-set-child* (index form tree)\n  \"Return TREE with node at INDEX set as child of the node before it.\nFORM is a Lisp form that takes the last member of TREE\nimmediately before INDEX (called \\\"parent\\\", bound to `it') and\nthe item at INDEX to be set as its child (bound to `it-target')\nand returns a new \\\"parent\\\" node.\"\n  (declare (indent 1) (debug (form form form)))\n  (let ((i (make-symbol \"index\"))\n        (h (make-symbol \"head\"))\n        (T (make-symbol \"tail\")))\n    `(let ((,i ,index))\n       (unless (and (integerp ,i) (< 0 ,index))\n         (error \"Cannot indent topmost item at this level\"))\n       (-let (((,h ,T) (-split-at ,i ,tree)))\n         (if (not ,T) (error \"Index over range: %s\" ,i)\n           (let ((it-target (car ,T)))\n             (append (org-ml--map-last* ,form ,h) (cdr ,T))))))))\n\n(defun org-ml--headline-move-post-blank (headline)\n  \"Move :post-blank to :pre-blank if HEADLINE is totally empty.\"\n  (if (org-element-contents headline) headline\n    (let ((pre (org-element-property :pre-blank headline))\n          (post (org-element-post-blank headline)))\n      (org-ml--set-properties-raw (org-ml-copy headline)\n        :pre-blank (+ pre post)\n        :post-blank 0))))\n\n;; headline\n\n(defun org-ml-headline-demote-subtree (index headline)\n  \"Return HEADLINE node with child headline at INDEX demoted.\nUnlike `org-ml-headline-demote-subheadline' this will also demote the\ndemoted headline node's children.\"\n  (org-ml-headline-map-subheadlines*\n    (org-ml--tree-set-child* index\n      (org-ml--map-children-nocheck*\n       (-snoc it (org-ml--headline-subtree-shift-level 1 it-target))\n       (org-ml--headline-move-post-blank it))\n      it)\n    headline))\n\n(defun org-ml-headline-demote-subheadline (index headline)\n  \"Return HEADLINE node with child headline at INDEX demoted.\nUnlike `org-ml-headline-demote-subtree' this will not demote the\ndemoted headline node's children.\"\n  (org-ml-headline-map-subheadlines*\n    (org-ml--tree-set-child* index\n      (let* ((headlines-in-target (org-ml-headline-get-subheadlines it-target))\n             (tgt-children (org-element-contents it-target))\n             (tgt-pb (if (org-ml--is-type 'section (car tgt-children))\n                         (org-ml--get-post-blank-textsafe (car tgt-children))\n                       (org-element-property-raw :pre-blank it-target)))\n             (tgt-headline* (->> (org-ml-copy it-target)\n                                 (org-ml-headline-set-subheadlines nil)\n                                 (org-ml--headline-shift-level 1)\n                                 (org-ml--set-post-blank tgt-pb))))\n        (org-ml--map-children-nocheck*\n         (append it (list tgt-headline*) headlines-in-target)\n         (org-ml--headline-move-post-blank it)))\n      it)\n    headline))\n\n;; plain-list\n\n(defun org-ml-plain-list-indent-item-tree (index plain-list)\n  \"Return PLAIN-LIST node with child item at INDEX indented.\nUnlike `org-ml-item-indent-item' this will also indent the indented item\nnode's children.\"\n  (org-ml--check-type 'plain-list plain-list)\n  (org-ml--map-children-nocheck*\n   (org-ml--tree-set-child* index\n     (let ((parent-pb (org-element-post-blank it))\n           (indented-target (org-ml--set-post-blank 0 it-target)))\n       (org-ml--item-map-subcomponents-cond*\n           ;; If neither subitems nor rest present, add indented tree as new\n           ;; subitem under parent. Put the parent post-blank at the end of\n           ;; the header material.\n           (list (org-ml--shift-last-post-blank parent-pb it-head)\n                 `(,indented-target) 0 nil)\n           ;; If rest not present but subitems present, append indented tree\n           ;; to the end of these subitems. Put the parent post-blank at the\n           ;; end of the list of subitems.\n           (list (-snoc\n                  (org-ml--shift-last-post-blank parent-pb it-subitems)\n                  indented-target)\n                 0 nil)\n           ;; If rest present, append indented item tree to the end of rest.\n           ;; Add the post-blank from parent to the end the last node in rest\n             (cons\n              (org-ml--shift-last-post-blank parent-pb it-rest)\n              (org-ml-build-plain-list indented-target))\n        it))\n     it)\n   plain-list))\n\n(defun org-ml-plain-list-indent-item (index plain-list)\n  \"Return PLAIN-LIST node with child item at INDEX indented.\nUnlike `org-ml-item-indent-item-tree' this will not indent the indented\nitem node's children.\"\n  (org-ml--check-type 'plain-list plain-list)\n  (org-ml--map-children-nocheck*\n   (org-ml--tree-set-child* index\n     ;; Get the target item (the one to be indented) and set its children\n     ;; to nil. Assume that its subitems and anything after it (including the\n     ;; post-blank) will not change.\n     (-let* (((tgt-head tgt-subitems tgt-rest-pb tgt-rest)\n              (org-ml--item-get-subcomponents it-target))\n             (indented-target (org-ml--item-set-subcomponents\n                               `(,tgt-head nil nil nil) it-target))\n             ;; NOTE: this is the post-blank of the *topmost* item in front of\n             ;; the one to be indented. Any space after the the last subitem (if\n             ;; any) are reflected in this (more to come below).\n             (parent-pb (org-element-post-blank it)))\n       (org-ml--item-map-subcomponents-cond*\n           (list\n            (org-ml--shift-last-post-blank parent-pb it-head)\n            (cons indented-target tgt-subitems)\n            0 nil)\n           ;; Otherwise, add the indented target and its children to the\n           ;; subitems under the parent, and set the rest to be that of the\n           ;; target (if anything). The only tricky part here is to most the\n           ;; post-blank of the toplevel parent into the last subitem of the\n           ;; parent (otherwise the post blank would move to the end of the\n           ;; entire new list after indentation)\n           (let ((psub (org-ml--shift-last-post-blank parent-pb it-subitems)))\n             `((,@psub ,indented-target ,@tgt-subitems) 0 nil))\n           ;; If the parent has \"extra stuff\" underneath its subitems (ie\n           ;; \"rest\") then we need to append the indented item after this \"extra\n           ;; stuff.\" Make a new list with the indented item and its children\n           ;; (which will be at the same level after the target is indented)\n           (let ((rest*\n                  (->> (cons indented-target tgt-subitems)\n                       (apply #'org-ml-build-plain-list :post-blank tgt-rest-pb)\n                       (cons it-rest))))\n             `(,@rest* ,@tgt-rest))\n         it))\n     it)\n   plain-list))\n\n;;; unindentation (tree)\n\n;; high level steps to unindent a tree\n;;\n;; Assume an abstract tree thing like this:\n;; - 0.\n;; - 1.\n;;   - 1.0.\n;;   - 1.1.\n;;     - 1.1.0.\n;;   - 1.2.\n;; - 2.\n;;\n;; We want to unindent everything under 1. So just take all children of 1. and\n;; splice them into the top-level list between 1. and 2. In this case 1.1.0 will\n;; remain a child of 1.1 but it will be unindented as well because its parent is\n;; being unindented\n;;\n;; parameters for unindenting a tree:\n;; - the index whose children are to be unindented\n\n(defmacro org-ml--split-children-at-index* (index form tree)\n  \"Return TREE with node at INDEX split according to FORM.\nThe node at INDEX will be bound to the symbol `it' which is to be\nreferenced in FORM, and FORM is to return a list like (PARENT\nCHILDREN) where PARENT is the modified node at INDEX and CHILDREN\nis a list of nodes that were children under PARENT but are to be\nspliced after parent. The new TREE will effectively splice the\nCHILDREN nodes after PARENT at the same level as PARENT.\"\n  (declare (indent 1) (debug (form form form)))\n  (let ((head (make-symbol \"head\"))\n        (tail (make-symbol \"tail\")))\n    `(-let* (((,head ,tail) (-split-at ,index ,tree))\n             (it (car ,tail))\n             ((parent children) ,form))\n       `(,@,head ,parent ,@children ,@(cdr ,tail)))))\n\n;; headline\n\n(defun org-ml-headline-promote-all-subheadlines (index headline)\n  \"Return HEADLINE node with all child headlines under INDEX promoted.\"\n  (org-ml-headline-map-subheadlines*\n    (org-ml--split-children-at-index* index\n      (let* ((children (->> (org-element-contents it)\n                            (--map (org-ml--headline-subtree-shift-level -1 it))))\n             (parent (org-ml--set-children-nocheck nil it))\n             (parent* (org-ml--headline-move-post-blank parent)))\n        (list parent* children))\n      it)\n    headline))\n\n;; plain-list\n\n(defun org-ml-plain-list-outdent-all-items (index plain-list)\n  \"Return PLAIN-LIST node with all child items under INDEX outdented.\"\n  (org-ml--check-type 'plain-list plain-list)\n  (org-ml--map-children-nocheck*\n   (org-ml--split-children-at-index* index\n     (-let* (((tgt-head tgt-subitems tgt-rest-pb tgt-rest)\n              (org-ml--item-get-subcomponents it))\n             (parent-pb (or (org-element-post-blank (-last-item tgt-head)) 0))\n             (parent (->> (org-ml--item-set-subcomponents `(,tgt-head nil nil nil) it)\n                          (org-ml--set-post-blank parent-pb)))\n             (outdent-pb (org-element-post-blank it))\n             (outdented\n              (cond\n               (tgt-rest\n                (append\n                 (org-ml--shift-last-post-blank tgt-rest-pb tgt-subitems)\n                 (org-ml--shift-last-post-blank outdent-pb tgt-rest)))\n               (tgt-subitems\n                (org-ml--shift-last-post-blank outdent-pb tgt-subitems))\n               (t\n                nil))))\n       `(,parent ,outdented))\n     it)\n   plain-list))\n\n;;; unindentation (single target)\n\n;; high level steps to unindent a single item\n;;\n;; Assume an abstract tree thing like this:\n;; - 0.\n;; - 1.\n;;   - 1.0.\n;;   - 1.1.\n;;     - 1.1.0.\n;;   - 1.2.\n;; - 2.\n;; \n;; We want to unindent 1.1. First, indent everything after 1.1 (in this case\n;; only 1.2, which will be appended to the list starting with 1.1.0). Then move\n;; 1.1 (with 1.1.0 and 1.2 as children) between 1 and 2 in the toplevel list.\n;;\n;; parameters for unindenting:\n;; - parent index (in this case 1 for 1.)\n;; - child index (in this case 1 for 1.1)\n\n;; headline\n\n;; TODO trigger error when child-index is out of range\n\n(defun org-ml-headline-promote-subheadline (index child-index headline)\n  \"Return HEADLINE node with a child headline under INDEX promoted.\nThe specific child headline to promote is selected by CHILD-INDEX.\"\n  (org-ml-headline-map-subheadlines*\n    (org-ml--split-children-at-index* index\n      (-let* (((head tail) (-split-at child-index (org-element-contents it)))\n              (target (->> (car tail)\n                           (org-ml-copy)\n                           (org-ml--headline-subtree-shift-level -1)\n                           (org-ml-headline-map-subheadlines*\n                             (append it (cdr tail)))))\n              (parent (org-ml--set-children-nocheck head it)))\n        (list parent (list target)))\n      it)\n    headline))\n\n;; plain-list\n\n(defun org-ml-plain-list-outdent-item (index child-index plain-list)\n  \"Return PLAIN-LIST node with a child item under INDEX outdented.\nThe specific child item to outdent is selected by CHILD-INDEX.\"\n  (org-ml--check-type 'plain-list plain-list)\n  (org-ml--map-children-nocheck*\n   (org-ml--split-children-at-index* index\n     (-let* (((parent-head parent-subitems parent-rest-pb parent-rest)\n              (org-ml--item-get-subcomponents it))\n             ((above-outdent (to-outdent . outdent-subitems))\n              (-split-at child-index parent-subitems))\n             (parent-pb (or (org-element-post-blank (-last-item above-outdent)) 0))\n             ;; Make new parent with the subitems that are above the item to\n             ;; be outdented (if any) and remove its rest component since this\n             ;; will become part of the outdented item's children\n             (parent (->> (org-ml--set-post-blank parent-pb it)\n                          (org-ml--item-set-subcomponents\n                           `(,parent-head ,above-outdent 0 nil))))\n             (tgt-pb (org-element-post-blank to-outdent)))\n       (if (not to-outdent) `(,parent nil)\n         (let ((outdented\n                (org-ml--item-map-subcomponents-cond*\n                    (list (org-ml--set-last-post-blank tgt-pb it-head)\n                          outdent-subitems\n                          0 nil)\n                    (-> (org-ml--shift-last-post-blank tgt-pb it-subitems)\n                        (append outdent-subitems)\n                        (list 0 nil))\n                    (let ((psub (apply #'org-ml-build-plain-list\n                                       :post-blank parent-rest-pb\n                                       outdent-subitems)))\n                      `(,@it-rest ,psub ,@parent-rest))\n                 to-outdent)))\n           `(,parent (,outdented)))))\n     it)\n   plain-list))\n\n;;; PRINTING FUNCTIONS\n\n;; For the most part, printing a node only involves\n;; `org-element-interpret-data', except this function has several limitations to\n;; work around\n;; - printing the string 'nil' where there should be a blank string\n;; - printing the node when it should not be printed at all\n;; - throwing an error when blank\n\n;;; printing workaround functions\n\n(defun org-ml--set-blank-children (node)\n  \"Set the children of NODE to a blank string (\\\"\\\").\"\n  (org-ml--set-children-nocheck '(\"\") node))\n\n;; Some objects and greater elements should be removed if blank. Table and plain\n;; list will error, and the others make no sense if they are empty.\n(defconst org-ml--rm-if-empty\n  '(table plain-list bold italic radio-target strike-through\n          superscript subscript table-cell underline)\n  \"Nodes that will be blank if printed and empty.\nThis is a workaround for a bug\")\n\n;; Some greater elements will print \"nil\" in their children if they are empty.\n;; The workaround for this is to set the children to a single blank string if\n;; empty\n(defconst org-ml--blank-if-empty\n  '(center-block drawer dynamic-block property-drawer quote-block\n                 special-block table-cell verse-block)\n  \"Branch element nodes that require \\\"\\\" to correctly print empty.\nThis is a workaround for a bug.\")\n\n;; TODO do I still need this in 9.7?\n(defun org-ml--blank (node)\n  \"Return NODE with empty child nodes `org-ml--blank-if-empty' set to contain \\\"\\\".\"\n  (if (not (org-element-contents node))\n      (cond\n       ((org-ml--is-any-type org-ml--blank-if-empty node)\n        (org-ml--set-blank-children node))\n       (t\n        (unless (or (org-ml--is-any-type org-ml--rm-if-empty node)\n                    (org-ml--is-table-row node))\n          node)))\n    (org-ml--map-children-nocheck*\n     (remove nil (-map #'org-ml--blank it))\n     node)))\n\n;;; print functions\n\n(defun org-ml-to-string (node)\n  \"Return NODE as an interpreted string without text properties.\"\n  (cond\n   ((null node)\n    \"\")\n   ((org-ml--is-node node)\n    (let ((s (->> (org-ml--blank node)\n                  (org-element-interpret-data)\n                  (substring-no-properties))))\n      (if (not (org-ml--is-type 'section node)) s\n        ;; TODO this is a bug in 9.7; sections now don't carry a post-blank\n        ;; property, and instead assume that the post-blank is encoded in the\n        ;; underlying contents. Unfortunately, `org-element-interpret-data' will\n        ;; normalize the underlying contents (as a string) to only have one\n        ;; newline regardless of post-blank. This workaround will manually add\n        ;; the newlines back in the case of section nodes\n        (let ((pb (->> (org-element-contents node)\n                       (-last-item)\n                       (org-element-post-blank))))\n          (concat s (make-string pb ?\\n))))))\n   (t\n    (org-ml--arg-error \"Can only stringify node or nil, got %s\" node))))\n\n(defun org-ml-to-trimmed-string (node)\n  \"Like `org-ml-to-string' but strip whitespace when returning NODE.\"\n  (-some->> (org-ml-to-string node) (s-trim)))\n\n;;; inverse printing functions\n\n(defun org-ml-from-string (type string)\n  \"Convert STRING to a node.\nTYPE is the node type intended by STRING; if STRING cannot be\nparsed into TYPE this function will return nil.\"\n  (cl-flet*\n      ((string-to-post-blank\n        (s)\n        (-let (((b . e) (car (s-matched-positions-all \"\\n+$\" s))))\n          (if (not (and b e)) 0\n            (let ((d (- e b)))\n              (if (= 1 d) 0 d)))))\n       (shift-property\n        (prop n node)\n        (org-ml--map-property-raw* prop (+ n it) node))\n       (shift-property-maybe\n        (prop n node)\n        (org-ml--map-property-raw* prop (when it (+ n it)) node))\n       (shift-object-node\n        (n node)\n        (->> (shift-property :begin n node)\n             (shift-property :end n)))\n       (shift-branch-object-node\n        (n node)\n        (->> (shift-object-node n node)\n             (shift-property-maybe :contents-begin n)\n             (shift-property-maybe :contents-end n)))\n       (shift-element-node\n        (n node)\n        (->> (shift-object-node n node)\n             (shift-property :post-affiliated n)))\n       (shift-branch-element-node\n        (n node)\n        (->> (shift-element-node n node)\n             (shift-property-maybe :contents-begin n)\n             (shift-property-maybe :contents-end n)))\n       (shift-property-node\n        (prop n node)\n        (org-ml--map-property-raw* prop\n          (-some->> it (shift-object-node n))\n          node))\n       (decrement-object-node\n        (node)\n        (if (org-ml-is-branch-node node) (shift-branch-object-node -1 node)\n          (shift-object-node -1 node)))\n       (decrement-node\n        (node)\n        (if (org-ml-is-element node)\n            (if (org-ml-is-branch-node node) (shift-branch-element-node -1 node)\n              (shift-element-node -1 node))\n          (decrement-object-node node)))\n       (remove-leading-space-maybe\n        (node)\n        (org-ml--map-children-nocheck*\n         (org-ml--map-first*\n          (if (org-ml--is-type 'paragraph it)\n              (org-ml--map-children-nocheck*\n               (if (equal (car it) \" \") (cdr it)\n                 (org-ml--map-first*\n                  (substring it 1)\n                  it))\n               it))\n          it)\n         node))\n       (from-prefixed-string\n        (prefix level string)\n        (-if-let (x (->> (concat prefix string)\n                         (org-ml--from-string)\n                         (org-ml--get-descendent level)))\n            (->> (shift-branch-object-node -1 x)\n                 (org-ml-match-map '((:not plain-text) *) #'decrement-object-node)))))\n    (-some->> (cond\n               ((eq type 'paragraph)\n                (let* ((pb (string-to-post-blank string))\n                       (e (1+ (length string)))\n                       (ce (- e pb)))\n                  (-> (org-ml-build-paragraph! string :post-blank pb)\n                    (org-ml--set-properties-raw\n                        :begin 1\n                        :contents-begin 1\n                        :end e\n                        :contents-end ce))))\n               ((and (eq type 'section) (s-matches-p \"^\\\\*\" string))\n                (->> (concat \" \" string)\n                     (org-ml--from-string)\n                     (remove-leading-space-maybe)\n                     (shift-property :end -1)\n                     (shift-property-maybe :contents-end -1)\n                     (org-ml-match-map '((:and 0 (:not plain-text)) *)\n                       (lambda (node)\n                         (->> (if (not (org-ml-is-branch-node node)) node\n                                (shift-property :contents-end -1 node))\n                              (shift-property :end -1))))\n                     (org-ml-match-map '((:and (> 0) (:not plain-text)) *)\n                       #'decrement-node)))\n               ((eq type 'node-property)\n                (let* ((pb (string-to-post-blank string))\n                       (e (1+ (- (length string) pb))))\n                  (-if-let (x (->> (format \"* dummy\\n:PROPERTIES:\\n%s\\n:END:\" string)\n                                   (org-ml--from-string)\n                                   (org-ml--get-descendent '(0 0 0))))\n                      (org-ml--set-properties-raw x\n                        :post-affiliated 1\n                        :begin 1\n                        :post-blank pb\n                        :end e))))\n               ((eq type 'property-drawer)\n                (-if-let (x (->> (concat \"* dummy\\n\" string)\n                                 (org-ml--from-string)\n                                 (org-ml--get-descendent '(0 0))))\n                    (->> (shift-branch-element-node -8 x)\n                         (org-ml--map-children-nocheck*\n                          (--map (shift-element-node -8 it) it)))))\n               ((eq type 'planning)\n                (-if-let (x (->> (concat \"* dummy\\n\" string)\n                                 (org-ml--from-string)\n                                 (org-ml--get-descendent '(0 0))))\n                    (->> (shift-element-node -8 x)\n                         (shift-property-node :scheduled -8)\n                         (shift-property-node :deadline -8)\n                         (shift-property-node :closed -8))))\n               ((eq type 'bold)\n                (from-prefixed-string \" \" '(0 1) string))\n               ((memq type '(superscript subscript))\n                (from-prefixed-string \"s\" '(0 1) string))\n               ((eq type 'table-cell)\n                (from-prefixed-string \"|\" '(0 0 0) string))\n               (t (let ((level (cond\n                                ((eq type 'headline) nil)\n                                ((eq type 'section) nil)\n                                ((eq type 'item) '(0 0))\n                                ((eq type 'table-row) '(0 0))\n                                ((memq type org-ml-objects) '(0 0))\n                                (t '(0)))))\n                    (->> (org-ml--from-string string)\n                         (org-ml--get-descendent level)))))\n      (org-element-put-property-2 :parent nil))))\n\n;;; PATTERN MATCHING\n\n;; This is a framework for applying \"pattern matching\" on node trees. All these\n;; functions search through the node tree and return (and sometimes operate on)\n;; a list of matches much like the UNIX find function for searching filesystems.\n\n;; Patterns are composed of the following parts:\n;; conditions - match a node based on its type, properties, and index\n;; wildcards - keywords that match one of more nodes regardless of type,\n;;   properties, and index\n;; slicers - keywords with arguments that limit the returned match list to a\n;;   subset all matches (such as first match or 2nd - 5th matches)\n;;\n;; Of the above, only conditions are required in the pattern\n\n;; When a pattern is fed into any of the match functions, it will first be\n;; 'compiled' into a lambda function that will walk through the node tree and\n;; accumulate/return the results the pattern requests. All possible functions\n;; operate on the same data structure which is a list of children in the tree at\n;; each level with the indices cons'ed to them like ((L . R) . CHILD) where L is\n;; the left index and R is the right index (starting from -1 and counting down\n;; to the left given a list of children in a node). The right index is necessary\n;; for negative index matching.\n\n;; This data structure ensures that any child has the information necessary for\n;; a condition form to determine if a match is successful (if this data\n;; structure wasn't used, index matching would fail as it would require\n;; knowledge of the entire list when the match is made)\n\n;; For slicers, two tricks are used to ensure that work is minimized. The first\n;; is that searches are limited to the maximum number of matches needed. If only\n;; the first match is needed, the search will stop after one match. If the 2nd\n;; to 5th matches are needed, the search will stop after 5 matches and return\n;; this with the first match dropped. The second trick is that the search is\n;; reversed if the slicer requests negatively indexed results. If the last match\n;; is needed, reverse the tree and return the first result. These tricks are\n;; possible/easier with the indexed-children data structure described above as\n;; it ensures that indexing information is preserved even when the children are\n;; reversed, and ensures that matching can be made on one child node at a time,\n;; which guarantees the limit will never be overshot.\n\n(defmacro org-ml--map-indexed (reverse? form list)\n  \"Like `--map-indexed' but can be told to reverse the result.\nIf REVERSE? is t, the final results are reversed (which actually\nmeans not reversed since the results are made in reverse order).\nFORM and LIST carry the same meaning.\"\n  (declare (indent 1))\n  (let* ((r (make-symbol \"result\"))\n         (return (if reverse? r `(nreverse ,r))))\n    `(let (,r)\n       (--each ,list (!cons ,form ,r))\n       ,return)))\n\n(defmacro org-ml--get-children-indexed (reverse? node)\n  \"Return list of children from NODE (if any) with index annotations.\nIf REVERSE is t, reverse the final result.\"\n  `(let* ((children (org-element-contents ,node))\n          (len (- (length children))))\n     (org-ml--map-indexed ,reverse?\n       (cons `(,it-index . ,(+ len it-index)) it) children)))\n\n(defmacro org-ml--reduce-from-while (pred form initial-value list)\n  \"Like `--reduce-from' but only reduce LIST while PRED is t.\nFORM and INITIAL-VALUE work the same way, and the exposed symbols `it'\nand `acc' carry the same meaning.\"\n  (declare (debug (form form form form)))\n  `(let ((acc ,initial-value))\n     (--each-while ,list ,pred (setq acc ,form))\n     acc))\n\n(defun org-ml--match-make-condition-form (condition)\n  \"Return a Lisp form equivalent to CONDITION.\nAssume that `it' is a symbol bound to a list of the form\n\\((INDEX RINDEX) . NODE) where NODE is the node being matched to\nCONDITION, INDEX is the INDEX of NODE, and RINDEX is the right-index\nof NODE (starting at -1 on the rightmost side of the children list).\"\n  ;; initialize some 'accessor' forms\n  (let ((it-node '(cdr it))\n        (it-lindex '(caar it))\n        (it-rindex '(cdar it)))\n    (pcase condition\n      ;;\n      ;; condition should not be nil\n      (`nil\n       (org-ml--arg-error \"Condition cannot be nil\"))\n      ;;\n      ;; quote is invalid (may be accidentally in condition)\n      (`(quote . ,_)\n       (org-ml--arg-error \"'quote' not allowed in condition\"))\n      ;;\n      ;; function is invalid (may be accidentally in condition)\n      (`(function . ,_)\n       (org-ml--arg-error \"'function' not allowed in condition\"))\n      ;;\n      ;; literal node\n      ((and (pred org-ml--is-node) pattern-node)\n       `(equal ,it-node ',pattern-node))\n      ;;\n      ;; type\n      ((and (pred (lambda (y) (memq y org-ml-nodes))) type)\n       `(org-ml-is-type ',type ,it-node))\n      ;; \n      ;; index\n      ((and (pred integerp) index)\n       `(= ,index ,(if (< index 0) it-rindex it-lindex)))\n      ;; \n      ;; relative index\n      (`(,(and (or '< '<= '> '>=) op) ,(and (pred integerp) index))\n       `(funcall #',op ,(if (< index 0) it-rindex it-lindex) ,index))\n      ;; \n      ;; predicate\n      (`(:pred . (,pred . nil))\n       `(funcall #',pred ,it-node))\n      ;; \n      ;; not\n      (`(:not . (,p . nil))\n       `(not ,(org-ml--match-make-condition-form p)))\n      ;; \n      ;; and\n      (`(:and . ,(and (pred and) p))\n       `(and ,@(-map #'org-ml--match-make-condition-form p)))\n      ;; \n      ;; or\n      (`(:or . ,(and (pred and) p))\n       `(or ,@(-map #'org-ml--match-make-condition-form p)))\n      ;;\n      ;; property\n      ;; NOTE: this must go last if we don't want :pred/:and/:or/:not\n      ;; to be interpreted as a property\n      (`(,(and (pred keywordp) prop) . (,val . nil))\n       `(equal (org-ml-get-property ,prop ,it-node) ,val))\n      ;;\n      ;; :any\n      (:any t)\n      ;;\n      (p (org-ml--arg-error \"Invalid condition: %s\" p)))))\n\n(defun org-ml--match-pattern-make-inner-form (end? limit pattern)\n  \"Return matching form for PATTERN.\nEND? is a boolean describing if the search should be made in reverse.\nIf t, reverse all children when obtaining from any given node. LIMIT\nis an integer or nil describing the number of matches at which the\nsearch should terminate. If nil, don't perform any checks and\nterminate only when the entire tree is searched within PATTERN.\"\n  (let* ((accum '(cons (cdr it) acc))\n         (get-children `(org-ml--get-children-indexed ,end? (cdr it)))\n         (reduce (if (not limit) '(--reduce-from)\n                   `(org-ml--reduce-from-while (< (length acc) ,limit)))))\n    (pcase pattern\n      ;; slicers should not be here\n      (`(,(or :first :last :nth :slice) . ,_)\n       (org-ml--arg-error \"Slicers can only appear at the front of pattern\"))\n      ;; empty pattern - add current node to accumulator as-is\n      ('nil\n       accum)\n      ;; * - if condition0 and condition1 match, add node to accumulator and\n      ;;   descend into child to repeat, if only condition0 matches just descend\n      ;;   into child and continue\n      (`(,condition . (* . nil))\n       (let* ((pred (org-ml--match-make-condition-form condition))\n              ;; need to explicitly check limit here because not in reduce form\n              ;; where limit is build in, this doesn't conform to the pattern of\n              ;; the rest of this function\n              (add-maybe\n               (if limit `(if (< (length acc) ,limit) ,accum acc) accum))\n              (add-descend\n               (if (not end?) `(get-many ,add-maybe ,get-children)\n                 `(let ((acc (get-many acc ,get-children)))\n                    ,add-maybe))))\n         `(cl-labels\n              ((get-many\n                (acc children)\n                (,@reduce (if ,pred ,add-descend acc) acc children)))\n            (let ((acc ,accum))\n              (get-many acc ,get-children)))))\n      (`(,condition0 . (* . ,ps))\n       (let* ((condition1 (car ps))\n              (ps (cdr ps))\n              (pred0 (org-ml--match-make-condition-form condition0))\n              (pred1 (org-ml--match-make-condition-form condition1))\n              (inner\n               (if (not ps) accum\n                 (org-ml--match-pattern-make-inner-form end? limit ps)))\n              ;; need to explicitly check limit here because not in reduce form\n              ;; where limit is build in, this doesn't conform to the pattern of\n              ;; the rest of this function\n              (add-maybe\n               (if (not limit) `(if ,pred1 ,inner acc)\n                 `(if (and (< (length acc) ,limit) ,pred1) ,inner acc)))\n              (add-descend\n               (if end? `(let ((acc (get-many acc ,get-children)))\n                           ,add-maybe)\n                 `(get-many ,add-maybe ,get-children))))\n         `(cl-labels\n              ((get-many\n                (acc children)\n                (,@reduce (cond (,pred0 ,add-descend)\n                                (,pred1 ,inner)\n                                (t acc))\n                          acc children)))\n            (get-many acc ,get-children))))\n      ;; condition - descend into the children of matching nodes and either\n      ;;   continue searching or add to accumulator if no more conditions to\n      ;;   match\n      (`(,condition . ,ps)\n       (let ((pred (org-ml--match-make-condition-form condition))\n             (inner\n              (if (not ps) accum\n                (org-ml--match-pattern-make-inner-form end? limit ps))))\n         `(,@reduce (if ,pred ,inner acc) acc ,get-children)))\n      ;;\n      (ps (org-ml--arg-error \"Invalid pattern: %s\" ps)))))\n\n(defun org-ml--match-is-alternate-form (form)\n  \"Return t if FORM is an alternative pattern form (eg has `|`s).\"\n  (and (listp form) (memq '| form)))\n\n(defun org-ml--match-pattern-expand-alternations (pattern)\n  \"Convert PATTERN with alternations to a list of patterns.\nEg given (a (b | c)), return ((a b) (a c)). This will act\nrecursively on nested alternations. The returned list will\nbe deduplicated.\"\n  (cl-flet\n      ((add-subpattern\n        (acc p)\n        (if (org-ml--match-is-alternate-form p)\n            (let ((p* (->>\n                       (-split-on '| p)\n                       (-replace '(nil) nil)\n                       (-mapcat #'org-ml--match-pattern-expand-alternations))))\n              (-mapcat (lambda (a) (--map (append a it) p*)) acc))\n          (--map (append it (list p)) acc))))\n    (-uniq (-reduce-from #'add-subpattern '(()) pattern))))\n\n(defun org-ml--match-pattern-process-alternations (end? limit alt-patterns)\n  \"Convert ALT-PATTERNS to a matching form.\nALT-PATTERNS is a list of patterns created from expanded\nalternations in the original pattern.\n\nSee `org-ml--match-pattern-make-inner-form' for the meaning of\nEND? and LIMIT.\"\n  (->> (if end? alt-patterns (reverse alt-patterns))\n       (--map (org-ml--match-pattern-make-inner-form end? limit it))\n       ;; use nested let statements to keep track of accumulator\n       ;; note the comma usage to make this extra confusing :)\n       (--reduce `(let ((acc ,it)) ,acc))))\n\n(defun org-ml--match-pattern-simplify-wildcards (pattern)\n  \"Return PATTERN with wildcards replaced by simpler syntax.\nSpecifically, this means brackets, `\\\\?`, `+` wildcards will be put in\nterms of explicit conditions, alternative branches, and `*` wildcards.\"\n  (cl-flet*\n      ((append-n\n        (acc n)\n        (append (-repeat (1- n) (car acc)) acc))\n       (append-m-n\n        (acc m n)\n        (--> (-repeat n (car acc))\n             (-reductions-from (lambda (a b) (cons b a)) nil it)\n             (-drop m it)\n             (-interpose '(|) it)\n             (if (= m 0) (cons '(nil) it) it)\n             (-flatten-n 1 it)\n             (cons it (cdr acc))))\n       (expand\n        (acc sym)\n        (pcase sym\n          ;; match X at least once\n          ;; (X +) -> (X X *)\n          ('+\n           (append (list '* (car acc)) acc))\n          ;; match X 0 or 1 times\n          ;; (X \\?) -> ((nil | X))\n          ('\\? (cons (list nil '| (car acc)) (cdr acc)))\n          ;; match X N times\n          ;; (X [N]) -> (X1 X2 ... XN)\n          (`[,(and (pred integerp) n)]\n           (if (< 0 n) (append-n acc n)\n             (org-ml--arg-error \"In [N], N must be > 0: got %s\" n)))\n          ;; match X at least M times\n          ;; (X [M nil]) -> (X1 X2 ... XN X *)\n          ;; (X [M !]) -> (X1 X2 ... XN X *!)\n          (`[,(and (pred integerp) m)\n             ,(and (pred (lambda (x) (or (null x) (eq '! x)))) n)]\n           (let ((wc (if (eq n '!) '*! '*)))\n             (if (< 0 m) (append (cons wc (-repeat m (car acc))) acc)\n               (org-ml--arg-error \"In [M nil] M must be positive; got %s\" m))))\n          ;; match X M to N times (inclusive)\n          ;; (X [M N]) -> (XM XM+1 ... XN-1 XN)\n          (`[,(and (pred integerp) m) ,(and (pred integerp) n)]\n           (cond\n            ;; if they are equal and greater than 0, same as [N]\n            ((= 0 m n)\n             (org-ml--arg-error \"Both in [M N] cannot be zero\"))\n            ((and (< 0 m) (< 0 n) (= m n))\n             (append-n acc n))\n            ((or (< m 0) (< n 0))\n             (org-ml--arg-error \"Both in [M N] must be positive: got %s and %s\" m n))\n            ((< n m)\n             (org-ml--arg-error \"In [M N], M must be <= N: got %s and %s\" m n))\n            (t\n             (append-m-n acc m n))))\n          ;; all else\n          (s (cons s acc)))))\n    (reverse (-reduce-from #'expand nil pattern))))\n\n(defun org-ml--match-make-pattern-form (end? limit pattern)\n  \"Return non-slicer matching form for PATTERN.\nSee `org-ml--match-pattern-make-inner-form' for meaning of END?\nand LIMIT which are passed directly through this function. NODE\nis the target node to be matched\"\n  (let ((body (->> (org-ml--match-pattern-simplify-wildcards pattern)\n                   (org-ml--match-pattern-expand-alternations)\n                   (org-ml--match-pattern-process-alternations end? limit))))\n    ;; NOTE: the accumulator is assembled in reverse due to the nature of linked\n    ;; lists. Consing to the front is a linear operation, while appending to the\n    ;; back is a quadratic operation since the list needs to be fully traversed\n    ;; with each append and the list is growing. This means that the list is\n    ;; reversed here if `END?' is nil (which means we want the list in\n    ;; forward-order) and left in reverse order if `END?' is t (meaning backward\n    ;; order)\n    (if end? body `(nreverse ,body))))\n\n(defun org-ml--match-make-slicer-form (pattern)\n  \"Return matching form with slicer operations for PATTERN.\nNODE is the node to be matched.\"\n  (pcase pattern\n    ;; :first - search until one match found and return that\n    (`(:first . ,ps)\n     (org-ml--match-make-pattern-form nil 1 ps))\n    ;;\n    ;; :last - search backwards until one match found and return that\n    (`(:last . ,ps)\n     (org-ml--match-make-pattern-form t 1 ps))\n    ;;\n    ;; :nth - search until N matches found and return Nth; note that nil will be\n    ;;   returned if N refers to anything outside the results list\n    (`(:nth . (,n . ,ps))\n     (unless (integerp n)\n       (org-ml--arg-error \":nth argument must be an integer\"))\n     (if (<= 0 n)\n         `(-drop ,n ,(org-ml--match-make-pattern-form nil (1+ n) ps))\n       `(-drop-last ,(1- (- n)) ,(org-ml--match-make-pattern-form t (- n) ps))))\n    ;;\n    ;; :sub - search until B matches found, drop A+1, and return; note that if B\n    ;;   is longer than the results then all results will be dropped and nil\n    ;;   will be ultimately returned\n    (`(:sub . (,a . (,b . ,ps)))\n     (cond\n      ((not (and (integerp a) (integerp b)))\n       (org-ml--arg-error \":sub arguments must be an integers\"))\n      ((> a b)\n       (org-ml--arg-error \":sub left index must be less than right index\"))\n      ((and (<= 0 a) (<= 0 b))\n       `(-drop ,a ,(org-ml--match-make-pattern-form nil (1+ b) ps)))\n      ((and (< a 0) (< b 0))\n       `(-drop-last ,(1- (- b)) ,(org-ml--match-make-pattern-form t (- a) ps)))\n      (t\n       (org-ml--arg-error \"Both indices must be on the same side of zero\"))))\n    ;;\n    ;; no slicer - search without limit and return all\n    (ps (org-ml--match-make-pattern-form nil nil ps))))\n\n(defvar org-ml--match-form-cache (make-hash-table :test #'equal)\n  \"Cache of previously generated lambda forms.\")\n\n(defun org-ml-clear-match-cache ()\n  \"Clear the pattern cache for `org-ml-match' and friends.\nSee `org-ml-memoize-match-patterns' for details.\"\n  (interactive)\n  (clrhash org-ml--match-form-cache))\n\n(defun org-ml--match-make-lambda-form-nocache (pattern)\n  \"Return callable lambda form for PATTERN.\nNODE is the node to be matched.\"\n  (let ((body (org-ml--match-make-slicer-form pattern)))\n    `(lambda (it) (let ((it (cons nil it)) (acc)) ,body))))\n\n(defun org-ml--match-make-lambda-form (pattern)\n  \"Run memoized version of `org-ml--match-make-lambda-form-nocache'.\nPATTERN has the same meaning.\"\n  (if org-ml-memoize-match-patterns\n      (or (gethash pattern org-ml--match-form-cache)\n          (let ((form (--> (org-ml--match-make-lambda-form-nocache pattern)\n                           (if (eq 'compiled org-ml-memoize-match-patterns)\n                               (byte-compile it)\n                             it))))\n            (puthash pattern form org-ml--match-form-cache)\n            form))\n    (org-ml--match-make-lambda-form-nocache pattern)))\n\n;;; match\n\n(defun org-ml-match (pattern node)\n  \"Return a list of child nodes matching PATTERN in NODE.\n\nPATTERN is a list like ([SLICER [X] [Y]] [SUB1 ...]).\n\nSLICER is an optional prefix to the pattern describing how many\nand which matches to return. If not given, all matches are\nreturned. Possible values are:\n\n- `:first' - return the first match\n- `:last' - return the last match\n- `:nth' X - return the nth match where X is an integer denoting\n  the index to return (starting at 0). X may be a negative number\n  to start counting at the end of the match list, in which case\n  -1 is the last index. Using 0 and -1 for X is equivalent to\n  using `:first' and `:last' respectively\n- `:sub' X Y - return a sublist between indices X and Y. X may\n  not be greater than Y, and both must either be non-negative\n  integers or negative integers. In the case of negative\n  integers, the indices refer to the same counterparts as\n  described in `:nth'. If X and Y are equal, this slicer has the\n  same behavior as `:nth'.\n\nSUBX denotes subpatterns that that match nodes in the parse tree.\nSubpatterns may either be wildcards or conditions.\n\nConditions match exactly one level of the node tree being\nsearched based on the node's type (the symbol returned by\n`org-ml-get-type'), properties (the value returned by\n`org-ml-get-property' for a valid property keyword), and\nindex (the position of the node in the list returned by\n`org-ml-get-children'). For index, both left indices (where zero\nrefers to the left end of the list) and right indices (where -1\nrefers to the right end of the list) are understood. Conditions\nmay either be atomic or compound, where compound conditions are\nthemselves composed of atomic or compound conditions.\n\nThe types of atomic conditions are:\n\n- TYPE - match when the node's type is `eq' to TYPE (a symbol)\n- INDEX - match when the node's index is `=' to INDEX (an\n  integer)\n- (OP INDEX) - match when (OP NODE-INDEX INDEX) returns t. OP is\n  one of `<', `>', `<=', or `>=' and NODE-INDEX is the index of\n  the node being evaluated\n- (PROP VAL) - match nodes whose property PROP (a keyword) is\n  `equal' to VAL; VAL is obtained by evaluating\n  `org-ml-get-property' with PROP and the current node; if PROP\n  is invalid, an error will be thrown\n- (:pred PRED) - match when PRED evaluates to t; PRED is a symbol\n  for a unary function that takes the current node as its\n  argument\n\nCompound conditions start with an operator followed by their\ncomponent conditions. The types of compound conditions are:\n\n- (:and C1 C2 [C3 ...]) - match when all `C' are true\n- (:or C1 C2 [C3 ...]) - match when at least one `C' is true\n- (:not C) - match when `C' is not true\n\nIn addition, SUBX may be a wildcard keyword or symbol. These are\nanalogous to the special characters found in POSIX extended\nregular expression syntax. Specifically, `[' and `]' correspond\nto `{' and `}' respectively and `:any' corresponds to the `.'\noperator. All other characters have the same meaning between this\nfunction and POSIX extended regular expressions.:\n\n- `:any' - always match exactly one node\n- SUB `?' - match SUB zero or once\n- SUB `*' - match SUB zero or more times\n- SUB `+' - match SUB one or more times\n- SUB [N] - match SUB N times\n- SUB [M N] - match SUB M to N times (inclusive); if M or N is\n  nil, this will match \\\"at most N times\\\" or \\\"at least M times\\\"\n  respectively\n- (ALT-A1 [ALT-A2 ...] | ALT-B1 [ALT-B2 ...] [| ...]) - match\n  any of the ALT expressions separated by `|' where ALT is a list\n  of subpatterns as described above or nil to match nothing;\n  these expressions may be nested\n\nIf PATTERN is nil, return NODE. Likewise, if any wildcard\npatterns match the nil pattern, also return NODE along with\nanything else the wildcard matches. Examples of this would\nbe (SUB *), (SUB ?), and ((nil | SUB)).\n\nFor increased performance, this function (and all others that\nconsume a PATTERN parameter) can be memoized using\n`org-ml-memoize-match-patterns'. If nil, PATTERN is processed\ninto a lambda form for every function call. If t, the resulting\nlambda forms are cached for each unique PATTERN, running\ngeneration step only once if multiple instances of the same\nPATTERN are used. Note that `org-ml-memoize-match-patterns' is\nshared between all functions that consume a PATTERN parameter.\"\n  (let ((match-fun (org-ml--match-make-lambda-form pattern)))\n    (funcall match-fun node)))\n\n;;; generalized tree modification\n\n;; this macro provides the means of using a list of matches returned from\n;; `org-ml--match' for other operations that use the match list as targets for\n;; modifying the original tree\n\n(eval-when-compile\n  (defmacro org-ml--modify-children (node form)\n    \"Recursively modify the children of NODE using FORM.\nFORM returns a list of element or object nodes as the new children,\nand the variable `it' is bound to the original children.\"\n    (declare (debug (form def-form)) (indent 1))\n    ;; TODO this makes a closure\n    `(cl-labels\n         ((rec\n           (node)\n           (if (not (org-ml-is-branch-node node)) node\n             (org-ml-map-children*\n               (let ((it (--map (rec it) it)))\n                 ,form)\n               node))))\n       (rec ,node))))\n\n;;; delete\n\n(defun org-ml--delete-targets (node targets)\n  \"Return NODE without children in TARGETS (a list of nodes).\"\n  (org-ml--modify-children node\n    (--remove (member it targets) it)))\n\n(defun org-ml-match-delete (pattern node)\n  \"Return NODE without children matching PATTERN.\n\nPATTERN follows the same rules as `org-ml-match'.\"\n  (-if-let (targets (org-ml-match pattern node))\n      (org-ml--delete-targets node targets)\n    node))\n\n;;; extract\n\n(defun org-ml-match-extract (pattern node)\n  \"Remove nodes matching PATTERN from NODE.\nReturn cons cell where the car is a list of all removed nodes and\nthe cdr is the modified NODE.\n\nPATTERN follows the same rules as `org-ml-match'.\"\n  (-if-let (targets (org-ml-match pattern node))\n      (cons targets (org-ml--delete-targets node targets))\n    node))\n\n;;; map\n\n(org-ml--defun-anaphoric* org-ml-match-map (pattern fun node)\n  \"Return NODE with FUN applied to children matching PATTERN.\nFUN is a unary function that takes a node and returns a new node\nwhich will replace the original.\n\nPATTERN follows the same rules as `org-ml-match'.\"\n  (-if-let (targets (org-ml-match pattern node))\n      (org-ml--modify-children node\n        (--map-when (member it targets) (funcall fun it) it))\n    node))\n\n;;; mapcat\n\n(org-ml--defun-anaphoric* org-ml-match-mapcat (pattern fun node)\n  \"Return NODE with FUN applied to children matching PATTERN.\nFUN is a unary function that takes a node and returns a list of new\nnodes which will be spliced in place of the original node.\n\nPATTERN follows the same rules as `org-ml-match'.\"\n  (-if-let (targets (org-ml-match pattern node))\n      (org-ml--modify-children node\n        (--mapcat (if (member it targets) (funcall fun it) (list it)) it))\n    node))\n\n;;; replace\n\n(defun org-ml-match-replace (pattern node* node)\n  \"Return NODE with NODE* in place of children matching PATTERN.\n\nPATTERN follows the same rules as `org-ml-match'.\"\n  (declare (indent 1))\n  (-if-let (targets (org-ml-match pattern node))\n      (org-ml--modify-children node\n        (--map-when (member it targets) node* it))\n    node))\n\n;;; insert-before\n\n(defun org-ml-match-insert-before (pattern node* node)\n  \"Return NODE with NODE* inserted before children matching PATTERN.\n\nPATTERN follows the same rules as `org-ml-match'.\"\n  (declare (indent 1))\n  (-if-let (targets (org-ml-match pattern node))\n      (org-ml--modify-children node\n        (--mapcat (if (member it targets) (list node* it) (list it)) it))\n    node))\n\n;;; insert-after\n\n(defun org-ml-match-insert-after (pattern node* node)\n  \"Return NODE with NODE* inserted after children matching PATTERN.\n\nPATTERN follows the same rules as `org-ml-match'.\"\n  (declare (indent 1))\n  (-if-let (targets (org-ml-match pattern node))\n      (org-ml--modify-children node\n        (--mapcat (if (member it targets) (list it node*) (list it)) it))\n    node))\n\n;;; insert-within\n\n(defun org-ml-match-insert-within (pattern index node* node)\n  \"Return NODE with NODE* inserted at INDEX in children matching PATTERN.\n\nPATTERN follows the same rules as `org-ml-match' with the exception\nthat PATTERN may be nil. In this case NODE* will be inserted at INDEX\nin the immediate, top level children of NODE.\"\n  (declare (indent 2))\n  (if pattern\n      (-if-let (targets (org-ml-match pattern node))\n          (org-ml--modify-children node\n            (--map-when\n             (member it targets)\n             (org-ml-map-children*\n               (org-ml--insert-at index node* it t)\n               it)\n             it))\n        node)\n    (org-ml-map-children* (org-ml--insert-at index node* it t) node)))\n\n;;; splice\n\n(defun org-ml-match-splice (pattern nodes* node)\n  \"Return NODE with NODES* spliced in place of children matching PATTERN.\nNODES* is a list of nodes.\n\nPATTERN follows the same rules as `org-ml-match'.\"\n  (declare (indent 1))\n  (-if-let (targets (org-ml-match pattern node))\n      (org-ml--modify-children node\n        (--mapcat (if (member it targets) nodes* (list it)) it))\n    node))\n\n;;; splice-before\n\n(defun org-ml-match-splice-before (pattern nodes* node)\n  \"Return NODE with NODES* spliced before children matching PATTERN.\nNODES* is a list of nodes.\n\nPATTERN follows the same rules as `org-ml-match'.\"\n  (declare (indent 1))\n  (-if-let (targets (org-ml-match pattern node))\n      (org-ml--modify-children node\n        (--mapcat (if (member it targets)\n                      (append nodes* (list it))\n                    (list it))\n                  it))\n    node))\n\n;;; splice-after\n\n(defun org-ml-match-splice-after (pattern nodes* node)\n  \"Return NODE with NODES* spliced after children matching PATTERN.\nNODES* is a list of nodes.\n\nPATTERN follows the same rules as `org-ml-match'.\"\n  (declare (indent 1))\n  (-if-let (targets (org-ml-match pattern node))\n      (org-ml--modify-children node\n        (--mapcat (if (member it targets) (cons it nodes*) (list it)) it))\n    node))\n\n;;; splice-within\n\n(defun org-ml-match-splice-within (pattern index nodes* node)\n  \"Return NODE with NODES* spliced at INDEX in children matching PATTERN.\nNODES* is a list of nodes.\n\nPATTERN follows the same rules as `org-ml-match' with the exception\nthat PATTERN may be nil. In this case NODES* will be inserted at INDEX\nin the immediate, top level children of NODE.\"\n  (declare (indent 2))\n  (if pattern\n      (-if-let (targets (org-ml-match pattern node))\n          (org-ml--modify-children node\n            (--map-when\n             (member it targets)\n             (org-ml-map-children*\n               (org-ml--splice-at index nodes* it t)\n               it)\n             it))\n        node)\n    (org-ml-map-children* (org-ml--splice-at index nodes* it t) node)))\n\n;;; side-effects\n\n(defun org-ml-match-do (pattern fun node)\n  \"Like `org-ml-match-map' but for side effects only.\nFUN is a unary function that has side effects and is applied to the\nmatches from NODE using PATTERN. This function itself returns nil.\n\nPATTERN follows the same rules as `org-ml-match'.\"\n  (-when-let (targets (org-ml-match pattern node))\n      (--each targets (funcall fun it))))\n\n;; anaphoric form doesn't work here for some reason\n(defmacro org-ml-match-do* (pattern form node)\n  \"Anaphoric form of `org-ml-match-do'.\n\nLike `org-ml-match-map' but for side effects only.\nFORM is a unary form that has side effects and is applied to the\nmatches from NODE using PATTERN. This form itself returns nil.\n\nPATTERN follows the same rules as `org-ml-match'.\"\n  (let ((n (make-symbol \"--n\")))\n    `(let ((,n ,node))\n       (-when-let (targets (org-ml-match ,pattern ,n))\n         (--each targets ,form)))))\n\n;;; BUFFER PARSING\n\n;;; org-element--parse-elements wrapper\n\n(defun org-ml--parse-elements (beg end mode)\n  \"Call `org-element--parse-elements' and unpack the result.\nBEG, END and MODE are passed to `org-element--parse-elements'.\"\n  ;; NOTE: A subject to review if something breaks eventually with another Org\n  ;; update.\n  ;;\n  ;; HACK: In future versions of Org as of commit fc80d052d, the last\n  ;; argument to `org-element--parse-elemnts' may not be nil.  We create a\n  ;; dummy list, pass it to the function and unpack the result.\n  (cddr\n   (org-element--parse-elements beg end mode nil nil nil (list 'org-data nil))))\n\n;;; parse at specific point\n\n(defun org-ml--parse-objects (type begin end)\n  \"Return a parsed object defined in the buffer by BEGIN and END.\nTYPE is the type of the node to be parsed.\"\n  (if (eq type 'link)\n      ;; NOTE these two variables will change the parsed link representation in\n      ;; an irreversible and non-obvious way, thus set them to nil (which means\n      ;; that parsing and then printing will compose to the identity)\n      (let ((org-link-abbrev-alist nil)\n            (org-link-translation-function nil))\n        (org-ml--parse-elements begin end 'first-section))\n    (org-ml--parse-elements begin end 'first-section)))\n\n;; TODO add test for plain-text parsing\n(defun org-ml-parse-object-at (point)\n  \"Return object node under POINT or nil if not on an object.\"\n  (save-excursion\n    (goto-char point)\n    (-let* ((context (org-element-context))\n            (type (org-ml-get-type context))\n            ((offset nesting) (pcase type\n                                ((or `superscript `subscript) '(-1 (0 1)))\n                                (`table-cell '(-1 (0 0 0)))\n                                (_ '(0 (0 0)))))\n            (begin (org-element-begin context))\n            (end (org-element-end context))\n            (tree (org-ml--parse-objects type (+ begin offset) end)))\n      (->> (car tree)\n           (org-ml--get-descendent nesting)\n           (org-ml--filter-types org-ml-objects)))))\n\n;; TODO this seems really inefficient; essentially we are parsing twice and\n;; there is probably a better way to do this with the new API\n(defun org-ml--parse-element-at (point type)\n  \"Return element node immediately under POINT.\nFor a list of all possible return types refer to `org-ml-elements'; this\nwill return everything in this list except `section' which is\nambiguous when referring to a single point.\n\\(see `org-ml-parse-section-at').\n\nIf TYPE is non-nil, only return nil if the object under point is not\nof that type. TYPE is a symbol from `org-ml-elements'. Furthermore,\nsetting TYPE to `table-row' will prefer table-row elements over table\nelements and likewise when setting TYPE to `item' for plain-list\nelements vs item elements.\"\n  (save-excursion\n    (goto-char point)\n    (let* ((node (org-element-at-point))\n           (node-type (org-ml-get-type node)))\n      ;; NOTE this will not filter by type if it is a leaf node\n      (if (not (memq node-type org-ml-branch-nodes)) node\n        ;; need to parse again if branch-node since\n        ;; `org-element-at-point' does not parse children\n        (-let* ((begin (org-element-begin node))\n                (end (org-element-end node))\n                (contents-end (org-element-contents-end node))\n                (tree (car (org-ml--parse-elements begin end 'first-section)))\n                (nesting (pcase node-type\n                           (`headline nil)\n                           ;; `org-element-at-point' will return a table if on\n                           ;; the first row of a table, and a table-row\n                           ;; otherwise\n                           (`table-row '(0 0))\n                           (`table (if (eq type 'table-row) '(0 0) '(0)))\n                           (`plain-list (if (eq type 'item) '(0 0) '(0)))\n                           (`item '(0 0))\n                           (_ '(0))))\n                (node* (->> (org-ml--get-descendent nesting tree)\n                            ;; set ending boundaries according to what we get\n                            ;; from `org-element-at-point'\n                            (org-element-put-property-2 :end end)\n                            (org-element-put-property-2 :contents-end contents-end))))\n          ;; some elements will always have post-blank set to 0, so no need to\n          ;; update it\n          (--> (if (memq node-type '(section table-row)) node*\n                 (let ((pb (org-element-post-blank node)))\n                   (org-ml--set-post-blank pb node*)))\n               (if type (org-ml--filter-type type it) it)))))))\n\n(defun org-ml-parse-element-at (point)\n  \"Return element node under POINT or nil if not on an element.\n\nThis function will return every element available in `org-ml-elements'\nwith the exception of `section', `item', and `table-row'. To\nspecifically parse these, use the functions `org-ml-parse-section-at',\n`org-ml-parse-item-at', and `org-ml-parse-table-row-at'.\"\n  (org-ml--parse-element-at point nil))\n\n(defun org-ml-parse-table-row-at (point)\n  \"Return table-row node under POINT or nil if not on a table-row.\"\n  (save-excursion\n    (goto-char point)\n    (beginning-of-line)\n    (org-ml--parse-element-at (point) 'table-row)))\n\n(defun org-ml-parse-item-at (point)\n  \"Return item node under POINT or nil if not on an item.\nThis will return the item node even if POINT is not at the beginning\nof the line.\"\n  ;; TODO this doesn't work if not on the first item\n  (save-excursion\n    (goto-char point)\n    (beginning-of-line)\n    (org-ml--parse-element-at (point) 'item)))\n\n(defun org-ml--parse-headline-subtree-at (point subtree)\n  \"Return headline node under POINT in the current buffer.\nPOINT may be anywhere between the points given by\n`org-back-to-heading' and `org-end-of-subtree'; it does not matter\nif the node immediately under POINT is not a headline. If SUBTREE is\nt, parse the entire subtree, else just parse the top headline.\"\n  (save-excursion\n    (goto-char point)\n    (when (ignore-errors (org-back-to-heading t))\n      (let* ((b (point))\n             (e (if subtree\n                   (progn\n                     (org-end-of-subtree)\n                     ;; skip ahead to the next headline because\n                     ;; `org-end-of-subtree' does not by default, which misses\n                     ;; any spacing after headlines\n                     (or (outline-next-heading)\n                         (point-max)))\n                  (or (outline-next-heading) (point-max))))\n             (tree (car (org-ml--parse-elements b e 'first-section))))\n        ;; TODO this is a hack; since org 9.6 setting the boundaries at the next\n        ;; headline will not stop the parser from parsing the entire subtree,\n        ;; even if we don't want it. Workaround is to parse the entire subtree\n        ;; and possibly throw away most of it\n        (if subtree tree\n          (let ((cs (org-element-contents tree)))\n            (if (< 1 (length cs))\n                (org-ml-set-children (list (car cs)) tree)\n              tree)))))))\n\n(defun org-ml-parse-headline-at (point)\n  \"Return headline node under POINT or nil if not on a headline.\nPOINT does not need to be on the headline itself. Only the headline\nand its section will be returned. To include subheadlines, use\n`org-ml-parse-subtree-at'.\"\n  (org-ml--parse-headline-subtree-at point nil))\n\n(defun org-ml-parse-subtree-at (point)\n  \"Return headline node under POINT or nil if not on a headline.\nPOINT does not need to be on the headline itself. Unlike\n`org-ml-parse-headline-at', the returned node will include\nchild headlines.\"\n  (org-ml--parse-headline-subtree-at point t))\n\n(defun org-ml-parse-section-at (point)\n  \"Return section node under POINT or nil if not on a section.\nIf POINT is on or within a headline, return the section under that\nheadline. If POINT is before the first headline (if any), return\nthe section at the top of the org buffer.\"\n  (save-excursion\n    (goto-char point)\n    (->> (condition-case nil\n             (progn\n               (org-back-to-heading)\n               ;; TODO this suffers from the same problem as the headline parser\n               ;; (parses entire subtree and probably wastes most of it)\n               (org-ml--parse-headline-subtree-at point nil))\n           (error\n            (org-ml--parse-elements\n             (point-min) (or (outline-next-heading) (point-max)) 'first-section)))\n         (org-ml--get-descendent '(0))\n         (org-ml--filter-type 'section))))\n\n;;; parse at current point\n\n(eval-when-compile\n  (defun org-ml--autodef-parse-node-form (name)\n    \"Return defun form for NAME.\"\n    (let* ((fun-name (intern (format \"org-ml-parse-this-%s\" name)))\n           (call (intern (format \"org-ml-parse-%s-at\" name)))\n           (doc (format \"Call `%s' with the current point.\" call))\n           (body `(,call (point))))\n      `(defun ,fun-name () ,doc ,body)))\n\n  (defmacro org-ml--autodef-parse-node-functions ()\n    \"Define all parse functions.\"\n    (let ((forms (->> '(object element table-row item headline subtree section)\n                      (-map #'org-ml--autodef-parse-node-form))))\n      `(progn ,@forms))))\n\n(org-ml--autodef-parse-node-functions)\n\n(defun org-ml-parse-this-toplevel-section ()\n  \"Return section node corresponding to the top of the current buffer.\nIf there is no such section, return nil.\"\n  (save-excursion\n    (goto-char (point-min))\n    (unless (= ?* (char-after))\n      (org-ml-parse-this-section))))\n\n(defun org-ml-parse-this-buffer ()\n  \"Return org-data document tree for the current buffer.\nContrary to the org-element specification, the org-data element\nreturned from this function will have :begin and :end properties.\"\n  (org-element-parse-buffer))\n\n(defun org-ml-this-buffer-has-headlines ()\n  \"Return t if the current buffer has headlines, else return nil.\"\n  (save-excursion\n    (goto-char (point-min))\n    (and (re-search-forward \"^\\\\*\" nil t) t)))\n\n;;; BUFFER SIDE EFFECTS\n\n;;; insert\n\n(defun org-ml--nodes-to-string-maybe (nodes)\n  \"Return NODES as a string.\nNODES may either be a single node or a list of nodes.\"\n  (cond\n   ((org-ml--is-node nodes) (org-ml-to-string nodes))\n   ((listp nodes) (mapconcat #'org-ml-to-string nodes \"\"))\n   (t (error \"Must a node or a list of nodes\"))))\n\n(defun org-ml--insert (point node)\n  \"Convert NODE to a string and insert at POINT in the current buffer.\nNODE may be a node or a list of nodes. Return NODE.\nDoes not save point.\"\n  (goto-char point)\n  (insert (org-ml--nodes-to-string-maybe node)))\n\n(defun org-ml-insert (point node)\n  \"Convert NODE to a string and insert at POINT in the current buffer.\nNODE may be a node or a list of nodes. Return NODE.\"\n  (save-excursion (org-ml--insert point node))\n  node)\n\n(defun org-ml-insert-tail (point node)\n  \"Like `org-ml-insert' but insert NODE at POINT and move to end of insertion.\"\n  (let ((s (org-ml--nodes-to-string-maybe node)))\n    (save-excursion\n      (goto-char point)\n      (insert s))\n    (goto-char (+ point (length s))))\n  node)\n\n;;; update\n\n(defun org-ml--apply-overlays (os)\n  \"Apply overlays OS to the current buffer.\"\n  (cl-flet\n      ((apply-overlays\n        (o)\n        (let* ((beg (plist-get o :start))\n               (end (plist-get o :end))\n               (props (plist-get o :props))\n               (o* (make-overlay beg end)))\n          (--each (-partition 2 props) (apply #'overlay-put o* it)))))\n    (-each os #'apply-overlays)))\n\n;; Myers diff algorithm\n;; Myers, E.W. AnO(ND) difference algorithm and its variations. Algorithmica 1,\n;; 251–266 (1986). https://doi.org/10.1007/BF01840446\n\n;; TODO There is a 99.99999% chance I can do way better than this. If the goal\n;; is to figure out what stringy bits to put into the buffer based on what has\n;; been modified, it makes more sense to make a crazy tree-based diff algorithm\n;; that is specialized for org-element nodes (which almost assuredly does not\n;; exist so I can't just steal a paper like I did with Myers) and only convert\n;; the things that have changes to strings and put those in the buffer. At least\n;; that seems to make sense, I haven't done complexity analysis yet.\n\n;; this is a souped-up version of the linear-space diff algorithm as presented\n;; from Myers; adapted from the python implementation listed here:\n;; https://blog.robertelder.org/diff-algorithm/\n\n(defun org-ml--diff-find-middle (str-eq M N)\n  \"Return the coordinates for the middle snake.\n\nSTR-EQ is a ternary function that takes a direction (0 = forward, 1\n= backward) and X/Y coordinates; it will return t if the two\nstrings at the given coordinates in the indicated direction\nmatch.\n\nM and N are the length of the current substrings.\"\n  (cl-flet*\n      ((init-V\n        (len)\n        (make-vector len 0))\n       (init-k\n        (D len)\n        (- D (* 2 (max 0 (- D len)))))\n       (get-x\n        (len V k)\n        (elt V (mod k len))))\n    (let* ((D-max (+ M N))\n           (D-mid (ceiling D-max 2))\n           (delta (- M N))\n           (V-len (+ 2 (* 2 (min M N))))\n           (V+ (init-V V-len))\n           (V- (init-V V-len))\n           ;; if D-max is odd, only check for overlaps in the forward direction\n           ;; (and vice versa); note that the directions are coded 0 for forward\n           ;; and 1 for backward (see below)\n           (check-dir-p (mod (1+ D-max) 2))\n           (D 0)\n           ret\n           dir fwd-p kstart kend z-lim x-vert x-horz x0 y0 x y z Va Vb offset k)\n      ;; iterate through D-paths for all D\n      (while (and (not ret) (<= D D-mid))\n        (setq kstart (- (init-k D N))\n              kend (init-k D M)\n              dir 0)\n        ;; this loop runs 2x for each direction (0 = forward and 1 = backward)\n        (while (and (not ret) (<= dir 1))\n          (setq fwd-p (= dir 0)\n                Va (if fwd-p V+ V-)\n                Vb (if fwd-p V- V+)\n                offset (if fwd-p 1 0)\n                z-lim (- D offset)\n                k kstart)\n          ;; iterate across all diagonals (k) to find furthest reaching paths\n          (while (and (not ret) (<= k kend))\n            (setq x-vert (get-x V-len Va (1+ k))\n                  x-horz (get-x V-len Va (1- k))\n                  x0 (if (or (= k (- D)) (and (/= k D) (< x-horz x-vert)))\n                         x-vert\n                       (1+ x-horz))\n                  y0 (- x0 k)\n                  x x0\n                  y y0\n                  z (- delta k))\n            (while (and (< x M) (< y N) (funcall str-eq fwd-p x y))\n              (setq x (1+ x)\n                    y (1+ y)))\n            (aset Va (mod k V-len) x)\n            (when (and (= dir check-dir-p)\n                       (<= (- z-lim) z z-lim)\n                       (<= M (+ (get-x V-len Va k) (get-x V-len Vb z))))\n              (setq ret (->> (if fwd-p `(,x0 ,y0 ,x ,y)\n                               `(,(- M x) ,(- N y) ,(- M x0) ,(- N y0)))\n                             (cons (- (* 2 D) offset)))))\n            (setq k (+ 2 k)))\n          (setq dir (1+ dir)))\n        (setq D (1+ D)))\n      ret)))\n\n(defun org-ml--diff (str-a str-b)\n  \"Return the edit commands to make STR-A `equal' STR-B.\n\nThis is the linear-space version of the Myers diff algorithm with\nseveral other enhancements, including tighter diagonal bounds to\nprevent running off the edit grid (useless CPU cycles) and only\nallocating memory for each V-array according to the minimum\nstring length and using them as circular buffers. After these\nedits, the time complexity should be O(min(A, B)*D) and the space\ncomplexity should be O(min(A, B).\n\nReturn value will be a list of either `(ins I M N)' or `(del I\nJ)'. For `ins' commands M and N are the indices from STR-B to\ninsert at I in STR-A, and for `del' commands I and J are the\nindices between which will be deleted in STR-A. Note that\nconsecutive edits will be consolidated so the length of the\nreturn list will not necessarily be the length of the LCS\ncomputed by the Myers diff algorithm.\"\n  (cl-labels\n      ((diff\n        (a0 a1 b0 b1 i j)\n        (let* ((M (- a1 a0))\n               (N (- b1 b0))\n               (str=\n                (lambda (fwd-p x y)\n                  (if fwd-p\n                      (= (elt str-a (+ a0 x)) (elt str-b (+ b0 y)))\n                    (= (elt str-a (+ a0 (- M 1 x)))\n                       (elt str-b (+ b0 (- N 1 y))))))))\n          (cond\n           ((and (< 0 M) (< 0 N))\n            (-let (((D-tot x y u v) (org-ml--diff-find-middle str= M N)))\n              (cond\n               ((and (or (< 1 D-tot) (and (/= x u) (/= y v))))\n                (append\n                 (diff a0 (+ a0 x) b0 (+ b0 y) i j)\n                 (diff (+ a0 u) (+ a0 M) (+ b0 v) (+ b0 N) (+ i u) (+ j v))))\n               ((< M N)\n                (diff 0 0 (+ b0 M) (+ b0 N) (+ i M) (+ j M)))\n               ((< N M)\n                (diff (+ a0 N) (+ a0 M) 0 0 (+ i N) (+ j N)))\n               (t\n                nil))))\n           ((< 0 M)\n            `((del ,i ,(+ i M))))\n           ((< 0 N)\n            `((ins ,i ,j ,(+ j N)))))))\n       (consolidate\n        (acc next)\n        (-let (((last . rest) acc))\n          (pcase `(,last ,next)\n            (`((ins ,i0 ,m0 ,n0) (ins ,i1 ,m1 ,n1))\n             (if (and (= i0 i1) (= n0 m1))\n                 (cons `(ins ,i0 ,m0 ,n1) rest)\n               (cons next acc)))\n            (`((del ,i0 ,j0) (del ,i1 ,j1))\n             (if (= j0 i1)\n                 (cons `(del ,i0 ,j1) rest)\n               (cons next acc)))\n            (_\n             (cons next acc))))))\n    (let ((a1 (length str-a))\n          (b1 (length str-b)))\n      (->> (diff 0 a1 0 b1 0 0)\n           (-reduce-from #'consolidate nil)))))\n\n(defun org-ml--diff-region (start end new-str)\n  \"Use Myers Diff algorithm to update the current buffer.\nThe region to be updated will be between START and END and will\nbe made to look like NEW-STR. Only differences as given by the Myers\ndiff algorithm (eg insertions and deletions) will actually be\napplied to the buffer.\"\n  (-let* ((old-str (buffer-substring-no-properties start end))\n          (edits (org-ml--diff old-str new-str)))\n    (save-excursion\n      (while edits\n        (pcase (car edits)\n          (`(ins ,i ,m ,n)\n           (goto-char (+ start i))\n           (insert (substring new-str m n)))\n          (`(del ,i ,j)\n           (delete-region (+ start i) (+ start j))))\n        (!cdr edits)))))\n\n;; (defun org-ml--properties-equal (type prop value1 value2)\n;;   \"Return t if VALUE1 and VALUE2 are 'the same'.\n;; If TYPE and PROP are 'headline/:title or 'item/:tag respectively,\n;; compare using `org-ml--equal' on all their members (as these are\n;; secondary strings). Otherwise use `equal'. This function is meant\n;; to avoid infinite loops which may be caused by comparing the\n;; parent nodes in secondary strings.\"\n;;   (if (or (and (eq type 'headline) (eq prop :title))\n;;           (and (eq type 'item) (eq prop :tag)))\n;;       (let ((matches t))\n;;         (while (and value1 matches)\n;;           (setq matches (org-ml--equal (car value1) (car value2))\n;;                 value1 (cdr value1)\n;;                 value2 (cdr value2)))\n;;         (and (not value1) matches))\n;;     (equal value1 value2)))\n\n;; (defun org-ml--equal (node1 node2)\n;;   \"Test of NODE1 is 'the same' as NODE2.\n;; 'The same' means that both nodes have the same type, children,\n;; and properties, where children are assessed recursively.\n\n;; For properties, order and presence matters, and all properties\n;; except for parent will be tested for equality using `equal' when\n;; comparing their values (if :parent is present in one, it will\n;; still be expected in the other but their values are ignored).\n;; This may be contradictory to the more general definition of 'the\n;; same' because a plist is unordered, but this function is only\n;; intended to test for equality in cases where NODE2 is a modified\n;; version of NODE1 and thus their plists should have the same\n;; order.\"\n;;   (let* ((is-str-1 (stringp node1))\n;;          (is-str-2 (stringp node2)))\n;;     (cond\n;;      ((and is-str-1 is-str-2)\n;;       (equal node1 node2))\n;;      ((not (and is-str-1 is-str-2))\n;;       (-let (((t1 . (p1 . c1)) node1)\n;;              ((t2 . (p2 . c2)) node2))\n;;         ;; first test if types match\n;;         (and (eq t1 t2)\n;;              ;; then test children (which will test their types first)\n;;              (let ((children-match t))\n;;                (while (and c1 children-match)\n;;                  (setq children-match (org-ml--equal (car c1) (car c2))\n;;                        c1 (cdr c1)\n;;                        c2 (cdr c2)))\n;;                (and (not c2) children-match))\n;;              ;; then test the plist, which will likely be slower than testing\n;;              ;; types so do it last so the average run time is shorter\n;;              (let ((plist-matches t))\n;;                (while (and p1 plist-matches)\n;;                  ;; skip over parents since these could make circular lists\n;;                  (setq plist-matches (and (eq (car p1) (car p2))\n;;                                           (or (eq p1 :parent)\n;;                                               (org-ml--properties-equal\n;;                                                t1 (car p1) (cadr p1) (cadr p2))))\n;;                        p1 (cdr (cdr p1))\n;;                        p2 (cdr (cdr p2))))\n;;                (and (not p2) plist-matches))))))))\n\n(defun org-ml--replace-region (begin end text)\n  \"Replace text between BEGIN and END with TEXT.\"\n  (delete-region begin end)\n  (goto-char begin)\n  (insert text))\n\n(defun org-ml--replace-bounds (diff-mode begin end node)\n  \"Replace text between BEGIN and END with NODE1 in current buffer.\nSee `org-ml~update' for meaning of DIFF-MODE.\"\n  (let ((ov-cmd (-if-let (x (->> (overlays-in begin end)\n                                 (--filter (eq 'outline (overlay-get it 'invisible)))\n                                 (--map (list :start (overlay-start it)\n                                              :end (overlay-end it)\n                                              :props (overlay-properties it)))))\n                    (list 'apply 'org-ml--apply-overlays x))))\n    ;; hacky way to add overlays to undo tree\n    (when ov-cmd\n      (setq-local buffer-undo-list (cons ov-cmd buffer-undo-list)))\n    (if diff-mode\n        (org-ml--diff-region begin end (org-ml-to-string node))\n      ;; convert node to string before deleting so deferred properties can get\n      ;; what they need from the buffer\n      (org-ml--replace-region begin end (org-ml-to-string node)))\n    nil))\n\n(org-ml--defun-anaphoric* org-ml--update (diff-mode fun node)\n  \"Internal version of `org-ml~update'.\nDIFF-MODE, FUN, and NODE have the same meaning. The only\ndifference is this function does not save the point's position\"\n  ;; do all computation before modifying buffer\n  ;;\n  ;; NOTE force resolution so that we can convert back to string after\n  ;; deleting the node from the buffer\n  (let* ((begin (org-element-begin node))\n         (end (org-element-end node)))\n    (->> (funcall fun node)\n         (org-ml--replace-bounds diff-mode begin end))))\n\n(org-ml--defun* org-ml~update (diff-mode fun node)\n  \"Replace NODE in the current buffer with a new one.\nFUN is a unary function that takes NODE and returns a modified node\nor list of nodes.\n\nDIFF-MODE describes how the buffer will be updated and can be one of\nthe following:\n- t: use the Myers diff algorithm to compare the old buffer\n  string with the new string from the modified NODE, and only\n  edit the the regions that are different\n- nil: use no diff algorithm; just replace the old buffer string\n  entirely with the new one.\"\n  (save-excursion\n    (org-ml--update diff-mode fun node)))\n\n(org-ml--defun* org-ml-update (fun node)\n  \"Replace NODE in the current buffer with a new one.\nFUN is a unary function that takes NODE and returns a modified node\nor list of nodes.\n\nThe modified NODE will be converted to a string and then compared\nto the old buffer string using the Myers diff algorithm. This has\nan average time complexity of O(M+N+D^2) where M and N are the\nlengths of the old and new strings respectively and D is the\nnumber of inserts or deletes required to change one into the\nother. At the cost of performance, only the parts of the buffer\nthat need to be modified will actually be changed, which is less\nlikely to disturb overlays and move the cursor (and is also more\nlike how org-mode's build-in imperative functions behave).\n\nIf one does not need this level of precision, use the function\n`org-ml~update' and supply nil for the DIFF-MODE argument. This\nwill simply replace the old node's string representation with the\nmodified node's string in its entirety. This will likely be\nfaster but could destroy overlays (eg folding) and will\nreposition the cursor to the beginning of NODE if it is in the\nmiddle of NODE.\"\n  (org-ml~update t fun node))\n\n;; generate all update functions for corresponding parse functions\n;; since all take function args, also generate anaphoric forms\n(eval-when-compile\n  (defun org-ml--autodef-update-node-forms (name)\n    \"Return defun and defmacro forms for NAME.\"\n    (cl-flet\n        ((format-doc\n          (name doclist)\n          (--> (s-join \"\\n\" doclist)\n               (format it name))))\n      (let* ((update-at (intern (format \"org-ml-update-%s-at\" name)))\n             (update-this (intern (format \"org-ml-update-this-%s\" name)))\n             (update-at~ (intern (format \"org-ml~update-%s-at\" name)))\n             (update-this~ (intern (format \"org-ml~update-this-%s\" name)))\n             (myers-doc (list \"This function uses the Myers diff algorithm.\"\n                              \"See `org-ml-update' for what this means.\"))\n             (diff-doc (list \"See `org-ml~update' for the meaning of DIFF-MODE\"))\n             (update-at-doc-header\n              (list \"Update %1$s under POINT using FUN.\"\n                    \"FUN takes an %1$s and returns a modified %1$s\"))\n             (update-this-doc-header\n              (list \"Update %1$s under current point using FUN.\"\n                    \"FUN takes an %1$s and returns a modified %1$s\"))\n             (update-at-doc~\n              (->> (append update-at-doc-header '(\"\") diff-doc)\n                   (format-doc name)))\n             (update-this-doc~\n              (->> (append update-this-doc-header '(\"\") diff-doc)\n                   (format-doc name)))\n             (update-at-doc\n              (->> (append update-at-doc-header '(\"\") myers-doc)\n                   (format-doc name)))\n             (update-this-doc\n              (->> (append update-this-doc-header '(\"\") myers-doc)\n                   (format-doc name)))\n             (call (intern (format \"org-ml-parse-%s-at\" name)))\n             (update-at-body~ `(org-ml~update diff-mode fun (,call point)))\n             (update-this-body~ `(,update-at~ diff-mode (point) fun))\n             (update-at-body `(,update-at~ t point fun))\n             (update-this-body `(,update-this~ t fun)))\n        (list `(org-ml--defun* ,update-at~ (diff-mode point fun)\n                 ,update-at-doc~\n                 ,update-at-body~)\n              `(org-ml--defun* ,update-this~ (diff-mode fun)\n                 ,update-this-doc~\n                 ,update-this-body~)\n              `(org-ml--defun* ,update-at (point fun)\n                 ,update-at-doc\n                 ,update-at-body)\n              `(org-ml--defun* ,update-this (fun)\n                 ,update-this-doc\n                 ,update-this-body)))))\n\n  (defmacro org-ml--autodef-update-node-functions ()\n    \"Define all update-node functions and macros.\"\n    (let ((forms (->> '(object element table-row item headline subtree section)\n                      (-mapcat #'org-ml--autodef-update-node-forms))))\n      `(progn ,@forms))))\n\n(org-ml--autodef-update-node-functions)\n\n(org-ml--defun* org-ml-update-this-buffer (fun)\n  \"Apply FUN to the contents of the current buffer.\nFUN is a unary function that takes a node of type `org-data' and\nreturns a modified node.\"\n  (org-ml-update fun (org-ml-parse-this-buffer)))\n\n;;; fold\n\n(defun org-ml--fold-get-contents-begin-maybe (node)\n  \"Return :contents-begin minus one or nil if not found for NODE.\"\n  (-some-> (org-element-contents-begin node) (1-)))\n\n(eval-when-compile\n  (defmacro org-ml--fold-get-contents-begin-offset (node offset)\n    \"Return the fold beginning boundary of NODE.\nTry `org-ml--fold-get-contents-begin-maybe' first, and if this returns nil,\nuse OFFSET to calculated the beginning fold boundary beginning.\nOFFSET can either be an integer or a form that evaluates to an\ninteger.\"\n    (declare (indent 1) (debug (form form)))\n    (let ((n (make-symbol \"--node\")))\n      `(let ((,n ,node))\n         (or (org-ml--fold-get-contents-begin-maybe ,n)\n             (+ ,offset (org-element-begin ,n)))))))\n\n(defun org-ml--fold-get-begin-boundary (node)\n  \"Return integer for point at the beginning of fold region for NODE.\"\n  (cl-case (org-ml-get-type node)\n    ;; Blocks must be folded regardless of if they have children\n    (center-block\n     (org-ml--fold-get-contents-begin-offset node 14))\n    (dynamic-block\n     (org-ml--fold-get-contents-begin-offset node\n       (+ 9 (length (org-element-property-raw :block-name node)))))\n    (drawer\n     (org-ml--fold-get-contents-begin-offset node\n       (+ 2 (length (org-element-property-raw :drawer-name node)))))\n    (property-drawer\n     (org-ml--fold-get-contents-begin-offset node 12))\n    ((quote-block verse-block)\n     (org-ml--fold-get-contents-begin-offset node 13))\n    (special-block\n     (org-ml--fold-get-contents-begin-offset node\n       (+ 9 (length (org-element-property-raw :type node)))))\n    ;; Headlines should only be folded if they have children\n    (headline\n     (org-ml--fold-get-contents-begin-maybe node))\n    ;; Items are tricky since everything after the \"first line\" is folded. If\n    ;; the first child is a paragraph, need to figure out how long its first\n    ;; line is and add that to :contents-begin. Do nothing if there are no\n    ;; children\n    (item\n     (-when-let (first (-first-item (org-element-contents node)))\n       (let ((offset (if (not (org-ml--is-type 'paragraph first)) -1\n                       (->> (org-ml-to-string first)\n                            (s-split \"\\n\")\n                            (-first-item)\n                            (length)))))\n         (+ offset (org-element-contents-begin node)))))\n    ;; These elements are not branch types and thus don't have child boundaries,\n    ;; so will need to manually calculated where the boundaries should be\n    ((comment-block example-block)\n     (+ 15 (org-element-begin node)))\n    (export-block\n     (+ (org-element-begin node)\n        (-if-let (type (org-element-property-raw :type node))\n            (1+ (length type)) 0)\n        14))\n    (src-block\n     (+ (org-element-begin node)\n        (-if-let (meta (->\n                        (list\n                         (org-element-property-raw :language node)\n                         (org-element-property-raw :switches node)\n                         (org-element-property-raw :parameters node))\n                        (-non-nil)))\n            (1+ (length (s-join \" \" meta))) 0)\n        11))))\n\n(defun org-ml--fold-flag-region-block (begin end flag)\n  \"Set invisibility for region denoted by BEGIN and END.\nFLAG is a boolean (t for invisible). The overlays applied should\nonly be used for block elements.\"\n  ;; Code ripped off from `org-flag-region' with overlay properties set to\n  ;; match those created in `org-hide-block-toggle'\n  (remove-overlays begin end 'invisible)\n  (when flag\n    (let ((o (make-overlay begin end nil 'front-advance)))\n      (overlay-put o 'evaporate t)\n      (overlay-put o 'invisible 'org-hide-block)\n      (overlay-put o 'isearch-open-invisible #'delete-overlay))))\n\n(defun org-ml--fold-flag-node (flag node)\n  \"Set folding of buffer contents in NODE to FLAG.\"\n  (-when-let (begin (org-ml--fold-get-begin-boundary node))\n    (let ((end (- (org-element-end node) (org-element-post-blank node) 1)))\n      (cl-case (org-ml-get-type node)\n        ((drawer headline item property-drawer)\n         (outline-flag-region begin end flag))\n        ((center-block comment-block dynamic-block example-block\n                       export-block quote-block special-block\n                       src-block verse-block)\n         (org-ml--fold-flag-region-block begin end flag))))))\n\n(defun org-ml-fold (node)\n  \"Fold the children of NODE if they exist.\"\n  (org-ml--fold-flag-node t node))\n\n(defun org-ml-unfold (node)\n  \"Unfold the children of NODE if they exist.\"\n  (org-ml--fold-flag-node nil node))\n\n(defun org-ml-subtree-set-fold (fold-state headline)\n  \"Set the fold state of HEADLINE node.\nThis function will do nothing unless HEADLINE has children.\nFOLD-STATE may be one of:\n- `none`: hide everything\n- `children`: show section and 1st-level subheadlines only\n- `subtree`: show section and subheadline contents (but not drawers)\n- `all`: show everything\"\n  (cl-flet\n      ((fold-drawers\n        (headline)\n        (-when-let (section (org-ml-headline-get-section headline))\n          (-some->> (--first (org-ml--is-type 'property-drawer it) section)\n            (org-ml-fold))\n          (let ((drawers (->> (org-element-contents section)\n                              (--filter (org-ml--is-type 'drawer it)))))\n            (--each drawers (org-ml-fold it))))))\n    (cl-case fold-state\n      (none\n       (org-ml-fold headline))\n      (children\n       (org-ml-unfold headline)\n       (fold-drawers headline)\n       (--each (org-ml-headline-get-subheadlines headline) (org-ml-fold it)))\n      (subtree\n       (org-ml-unfold headline)\n       (fold-drawers headline)\n       (--each (org-ml-headline-get-subheadlines headline)\n         (org-ml-subtree-set-fold fold-state it)))\n      (all\n       (org-ml-unfold headline)))))\n\n;;; headline batch processing\n\n(defun org-ml--get-forward-bounds (m n re)\n  \"Return the boundaries of headlines to parse.\nM is the minimum number of headlines and N is the maximum number\nof headlines. RE is a regular expression that will be used to\nsearch for a headline. The return value will be a list\nlike (BEGIN END) where BEGIN is the start of the Mth headline and\nEND is the end of the Nth headline.\"\n  (save-excursion\n    (save-match-data\n      (goto-char (point-min))\n      (let (begin end)\n        (when (re-search-forward re nil t)\n          (let ((i 0)\n                (next t))\n            (while (and next (<= i n))\n              (when (= m i)\n                (setq begin (match-beginning 0)))\n              (setq i (1+ i)\n                    next (re-search-forward re nil t)))\n            (setq end (if next (match-beginning 0) (point-max)))))\n        (list begin end)))))\n\n(defun org-ml--get-backward-bounds (m n re)\n  \"Return the boundaries of headlines to parse.\nThis is like `org-ml--get-forward-bounds' except it searches\nbackwards. M is the minimum number of headlines and N is the\nmaximum number of headlines. RE is a regular expression that will\nbe used to search for a headline. The return value will be a list\nlike (BEGIN END) where BEGIN is the start of the Nth headline and\nEND is the end of the Mth headline.\"\n  (save-excursion\n    (save-match-data\n      (goto-char (point-max))\n      (let ((i 0)\n            (prev-point (point-max))\n            begin end)\n        (while (and (<= i n) (re-search-backward re nil t))\n          (when (= m i)\n            (setq end prev-point))\n          (setq i (1+ i)\n                prev-point (point)))\n        (when end\n          (setq begin (point)))\n        (list begin end)))))\n\n(defun org-ml--get-region-bounds (begin end re)\n  \"Return the boundaries of headlines to parse.\nRE is a regular expression that will be used to search for a\nheadline. The return value will be a list like (PBEGIN PEND)\nwhere PBEGIN is the start of the headline immediately after BEGIN\nand PEND is the end of the headline immediately before END.\"\n  (save-match-data\n    (save-excursion\n      (let ((b (progn\n                 (goto-char begin)\n                 (if (looking-at re) begin\n                   (when (re-search-forward re nil t)\n                     (match-beginning 0)))))\n            (e (or (progn\n                     (goto-char end)\n                     (if (looking-at re) end\n                       (when (re-search-forward re nil t)\n                         (match-beginning 0))))\n                   (point-max))))\n        (list b e)))))\n\n(defun org-ml--parse-patterns-where (which re)\n  \"Return the parse boundaries of a headline based on WHICH.\nSee `org-ml-get-some-headlines' for the meaning of WHICH. RE is a\nregular expression used to search for the next headline.\"\n  (declare (indent 1))\n  (cl-flet\n      ((int-or-nil-p\n        (x)\n        (or (null x) (integerp x))))\n    (-let (((b e)\n            (pcase which\n              ;; parse N\n              (`all\n               (org-ml--get-region-bounds (point-min) (point-max) re))\n              ((and (pred integerp) n)\n               (if (<= 0 n) (org-ml--get-forward-bounds 0 n re)\n                 (org-ml--get-backward-bounds 0 n re)))\n              ;; parse M-N\n              (`(,(and (pred integerp) m) ,(and (pred integerp) n))\n               (cond\n                ((<= 0 m n) (org-ml--get-forward-bounds m n re))\n                ((<= m n -1) (org-ml--get-backward-bounds (1- (- n)) (1- (- m)) re))\n                ((< n m) (org-ml--arg-error \"M must be less than or equal to N\"))\n                (t (org-ml--arg-error \"M and N must be the same sign\"))))\n              ;; parse region between A and B\n              (`[,(and (pred int-or-nil-p) a) ,(and (pred int-or-nil-p) b)]\n               (let ((a (or a (point-min)))\n                     (b (or b (point-max))))\n                 (org-ml--get-region-bounds a b re)))\n              (e (org-ml--arg-error \"Invalid 'which' specification: Got %S\" e)))))\n      (when (and b e)\n        (org-ml--parse-elements b e 'first-section)))))\n\n(defun org-ml-parse-headlines (which)\n  \"Return list of headline nodes from current buffer.\n\nWHICH describes the location of headlines to be parsed and is one\nof the following:\n- N: parse up to index N headlines (which 0 is the first); if negative\n  start counting from the last headline (which -1 refers to the last)\n- (M N): like N but parse after index M headlines; M and N may both\n  be similarly negative\n- [A B]: parse all headlines whose first point falls between points\n  A and B in the buffer; if A and B are nil, use `point-min' and\n  `point-max' respectively.\n- `all': parse all headlines (equivalent to [nil nil])\n\nEach headline is obtained with `org-ml-parse-headline-at'.\"\n  (cl-labels\n      ((get-subheadlines\n        (headline)\n        (->> (org-ml-headline-get-subheadlines headline)\n             (-mapcat #'get-subheadlines)\n             (cons headline))))\n    (->> (org-ml--parse-patterns-where which \"^\\\\*+ \")\n         (-mapcat #'get-subheadlines))))\n\n(defun org-ml-parse-subtrees (which)\n  \"Return list of subtree nodes from current buffer.\n\nWHICH has analogous meaning to that in `org-ml-parse-headlines'\nexcept applied to subtrees not individual headlines.\"\n  (org-ml--parse-patterns-where which \"^\\\\* \"))\n\n(org-ml--defun* org-ml-update-headlines (which fun)\n  \"Update some headlines in the current using FUN.\n\nSee `org-ml-parse-headlines' for the meaning of WHICH.\n\nHeadlines are updated using `org-ml~update' with DIFF-ARG set to\nnil (see this for use and meaning of FUN).\"\n  ;; don't use the myers diff algorithm here, since these functions are meant\n  ;; for batch processing.\n  (save-excursion\n    (cl-labels\n        ((map-to-subheadlines\n          (headline)\n          (org-ml-headline-map-subheadlines*\n            (-map #'map-to-subheadlines it)\n            (funcall fun headline))))\n      ;; NOTE there two main ways to do this. We can either flatten the output\n      ;; of `org-ml--parse-patterns-where' into individual headlines (eg no\n      ;; headline would have a subheadline and all subheadlines would be in the\n      ;; top level of the list) and update each of them in the buffer\n      ;; individually using `FUN'. Or we can do we we do here, which is to apply\n      ;; `FUN' recursively to each subtree and then update the entire subtree in\n      ;; place in the buffer. This has the advantage of not requiring the\n      ;; subtrees to be broken apart which could introduce whitespace errors\n      ;; between headlines, section, etc. It has the disadvantage of requiring\n      ;; more text to be modified in the buffer at once, which could be\n      ;; disruptive.\n      (--> (org-ml--parse-patterns-where which \"^\\\\*+ \")\n           (nreverse it)\n           (--each it (org-ml~update nil #'map-to-subheadlines it))))))\n\n(org-ml--defun* org-ml-update-subtrees (which fun)\n  \"Update some toplevel subtrees in the current buffer using FUN.\n\nSee `org-ml-parse-subtrees' for the meaning of WHICH.\n\nSubtrees are updated using `org-ml~update' with DIFF-ARG set to\nnil (see this for use and meaning of FUN).\"\n  (save-excursion\n    (--> (org-ml--parse-patterns-where which \"^\\\\* \")\n         (nreverse it)\n         (--each it (org-ml~update nil fun it)))))\n\n(org-ml--defun* org-ml-update-supersections (which fun)\n  \"Update some headline supersections in the current using FUN.\n\nSee `org-ml-parse-headlines' for the meaning of WHICH.\n\nHeadlines are updated using `org-ml~update' with DIFF-ARG set to\nnil (see this for use and meaning of FUN).\"\n  ;; don't use the myers diff algorithm here, since these functions are meant\n  ;; for batch processing.\n  (save-excursion\n    (cl-labels\n        ((map-to-subheadlines\n           (headline)\n           (-each (nreverse (org-ml-headline-get-subheadlines headline))\n             #'map-to-subheadlines)\n           (-let* (((ss0 &as &plist :pre-blank pb0 :section nodes0)\n                    (org-ml-headline-get-supersection headline))\n                   ((ss1 &as &plist :pre-blank pb1 :section nodes1) (funcall fun ss0)))\n             (if (or (not pb1) (= pb0 pb1))\n                 (let ((s (->> (-map #'org-ml-to-string nodes1)\n                               (apply #'concat (make-string pb0 ?\\n)))))\n                   (if nodes0\n                       (let ((begin (org-element-begin (-first-item nodes0)))\n                             (end (org-element-end (-last-item nodes0))))\n                         (org-ml--replace-region begin end s))\n                     (let* ((begin (or (org-element-contents-begin headline)\n                                       ;; If there are no contents, go to\n                                       ;; headline start, try to go to next\n                                       ;; line, and insert new line if we can't\n                                       ;; (which means we are at the end)\n                                       (progn\n                                         (goto-char (org-element-begin headline))\n                                         (forward-line)\n                                         (if (bolp) (point)\n                                           ;; use this since this plays nice\n                                           ;; with evil mode\n                                           (when (re-search-forward \"$\" nil t)\n                                             (replace-match \"\\n\" nil nil))\n                                           (forward-line)\n                                           (point))))))\n                       (goto-char begin)\n                       (insert s))))\n               (let ((headline* (->> (org-ml-copy headline)\n                                     (org-ml-headline-set-subheadlines nil)\n                                     (org-ml-headline-set-supersection ss1)))\n                     (begin (org-element-begin headline))\n                     (end (or (outline-next-heading) (point-max))))\n                 (org-ml--replace-bounds nil begin end headline*))))))\n      (-each (nreverse (org-ml--parse-patterns-where which \"^\\\\* \"))\n        #'map-to-subheadlines))))\n\n(org-ml--defun* org-ml-update-supercontents (config which fun)\n  \"Update some headline supercontents in the current using FUN.\n\nSee `org-ml-parse-headlines' for the meaning of WHICH.\n\nHeadlines are updated using `org-ml~update' with DIFF-ARG set to\nnil (see this for use and meaning of FUN).\"\n  (org-ml-update-supersections* which\n    (->> (org-ml--supersection-to-supercontents config it)\n         (funcall fun)\n         (org-ml--supercontents-to-supersection config))))\n\n;;; deprecated functions\n\n(define-obsolete-function-alias 'org-ml-timestamp-get-range 'org-ml-timestamp-get-length \"6.0.0\")\n\n(defun org-ml-timestamp-set-range (n timestamp)\n  \"Return TIMESTAMP node with range set to N seconds.\n\nIf TIMESTAMP is ranged, keep start time the same and adjust the end\ntime. If not, make a new end time. The units for RANGE are in minutes\nif TIMESTAMP is in long format and days if TIMESTAMP is in short\nformat.\n\nThis function is deprecated. Use `org-ml-timestamp-set-length'\ninstead.\"\n  (if (->> (org-ml-timestamp-get-start-time timestamp)\n           (org-ml-timelist-has-time))\n      (org-ml-timestamp-set-length (* 60 n) 'minute timestamp)\n    (org-ml-timestamp-set-length (* 86400 n) 'day timestamp)))\n\n(define-obsolete-function-alias `org-ml-time-is-long 'org-ml-timelist-has-time \"6.0.0\")\n\n(define-obsolete-function-alias `org-ml-time-to-unixtime 'org-ml-timelist-to-unixtime \"6.0.0\")\n\n(defun org-ml-unixtime-to-time-short (unixtime)\n  \"Convert UNIXTIME to list like (YEAR MONTH DAY).\n\nThis function is deprecated.\"\n  (-take 3 (org-ml-unixtime-to-timelist nil unixtime)))\n\n(defun org-ml-unixtime-to-time-long (unixtime)\n  \"Convert UNIXTIME to list like (YEAR MONTH DAY HOUR MINUTE).\n\nThis function is deprecated.\"\n  (org-ml-unixtime-to-timelist t unixtime))\n\n(provide 'org-ml)\n;;; org-ml.el ends here\n"
  }
]