197 Commits

Author SHA1 Message Date
Tom Alexander
4b54f95087 Publish version 0.1.10.
Some checks failed
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-test Build rust-test has succeeded
2023-10-14 19:31:41 -04:00
Tom Alexander
17c2e9fefe Add a build of compare and foreign document test without tracing. 2023-10-14 19:31:41 -04:00
Tom Alexander
d2d8c1ffcf Fix build for compare with tracing. 2023-10-14 19:20:49 -04:00
Tom Alexander
b9c638c280 Fix tracing.
Some checks failed
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-test Build rust-test has succeeded
2023-10-14 19:02:35 -04:00
Tom Alexander
8ac8f9fe6e Merge branch 'foreign_document_test'
Some checks failed
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-test Build rust-test has succeeded
2023-10-14 18:53:52 -04:00
Tom Alexander
ddb3144e66 Fix the tests.
Some checks failed
rust-build Build rust-build has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-test Build rust-test has succeeded
2023-10-14 18:25:26 -04:00
Tom Alexander
ad5efc4b0f Only require sync on FileAccessInterface when compiling for compare utilities.
Some checks failed
rust-test Build rust-test has failed
rust-build Build rust-build has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
Otherwise async compatibility would impact sync users of the plain library.
2023-10-14 18:09:50 -04:00
Tom Alexander
2de33b8150 Enable release-lto profile on foreign document test. 2023-10-14 18:00:20 -04:00
Tom Alexander
e1fde88a60 Switch to using tokio too invoke emacs async. 2023-10-14 17:58:50 -04:00
Tom Alexander
123da9cca3 Make the compare functions async. 2023-10-14 17:51:12 -04:00
Tom Alexander
c20e7b5f2f Make compare an always-async program.
This program is used as a development tool, so it is more valuable that we make it simple by only supporting one mode of operation (async) than making it widely compatible by supporting both.
2023-10-14 17:50:53 -04:00
Tom Alexander
74a3512038 Report whether all the tests passed. 2023-10-14 17:50:53 -04:00
Tom Alexander
ff04c4a131 Sort files in a compare_all_org_document. 2023-10-14 17:50:53 -04:00
Tom Alexander
00611e05c2 Remove old bash script. 2023-10-14 15:32:36 -04:00
Tom Alexander
3e7e54a1bd Add the foreign_document_test to the build tests. 2023-10-14 15:26:04 -04:00
Tom Alexander
aa35d1dc03 Copy over the rest of the foreign document test config. 2023-10-14 15:18:57 -04:00
Tom Alexander
92afdc0ea6 Strip prefix from file path. 2023-10-14 15:18:57 -04:00
Tom Alexander
f43920fc7c Add a silent mode for running the diff. 2023-10-14 15:18:57 -04:00
Tom Alexander
dde4bc7920 Add code for structured printing of test results. 2023-10-14 14:30:24 -04:00
Tom Alexander
3d68e1fd00 Switch the docker container over to invoking the rust-based foreign document test. 2023-10-13 13:28:24 -04:00
Tom Alexander
8271f6b44a Start invoking the tests. 2023-10-13 13:20:21 -04:00
Tom Alexander
8a26965e14 Write a function to compare all org-mode files in a directory. 2023-10-13 13:02:17 -04:00
Tom Alexander
3927889e66 Change test status to pass/fail. 2023-10-13 12:14:46 -04:00
Tom Alexander
5ecd7b8bef Launch tests in parallel. 2023-10-13 12:07:52 -04:00
Tom Alexander
b0b795d13b Limit concurrency of running tests. 2023-10-13 11:41:49 -04:00
Tom Alexander
182c2737cd Add futures. 2023-10-13 11:27:43 -04:00
Tom Alexander
5f93cabff5 Hit recursive async. 2023-10-13 11:27:42 -04:00
Tom Alexander
a1f8cbe079 Working on invoking the tests. 2023-10-13 11:27:42 -04:00
Tom Alexander
d7e870cba1 Starting to make result structs. 2023-10-13 11:27:42 -04:00
Tom Alexander
591b5ed382 Starting to define a TestConfig enum. 2023-10-13 11:27:42 -04:00
Tom Alexander
fd141762f0 Start a rust-based foreign document test.
Instead of using a simple bash script, this will use async rust to run multiple tests in parallel.
2023-10-13 11:27:42 -04:00
Tom Alexander
d59bbfa7d2 Update org-mode.
Some checks failed
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has failed
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has failed
2023-10-13 11:26:41 -04:00
Tom Alexander
1d9f91cdd2 Merge branch 'paragraph_without_affiliated_keyword'
Some checks failed
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has failed
rust-test Build rust-test has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
2023-10-12 17:46:56 -04:00
Tom Alexander
68f8e04ee8 Add tests for affiliated keywords before elements that cannot have affiliated keywords. 2023-10-12 17:46:25 -04:00
Tom Alexander
c039e0d62c Keywords without affiliated keywords should be lower priority than paragraphs with affiliated keywords. 2023-10-12 17:32:29 -04:00
Tom Alexander
e6e3783ec6 Support parsing affiliated keywords as keywords. 2023-10-12 17:23:54 -04:00
Tom Alexander
5ae19e455d Remove unused imports. 2023-10-12 17:14:18 -04:00
Tom Alexander
f475754a71 Port the rest of the element parsers over. 2023-10-12 17:12:55 -04:00
Tom Alexander
767f44f94d Port comment. 2023-10-12 16:52:49 -04:00
Tom Alexander
1411aca7b5 Port footnote definition. 2023-10-12 16:48:50 -04:00
Tom Alexander
9ccdcaac24 Port dynamic block. 2023-10-12 16:46:15 -04:00
Tom Alexander
d1223dcdb7 Port paragraph parser to use ak_element. 2023-10-12 16:37:31 -04:00
Tom Alexander
59448a4f2c Port greater blocker over to ak_element!(). 2023-10-12 16:27:57 -04:00
Tom Alexander
5136880532 Fix tests. 2023-10-12 16:14:23 -04:00
Tom Alexander
8654cf5507 Fully port over the first parser. 2023-10-12 16:09:35 -04:00
Tom Alexander
6ca4dc8ffc Add a macro for calling parsers that take in the affiliated keywords. 2023-10-12 16:06:59 -04:00
Tom Alexander
a6f36ba679 Create a template for new element functions that will take in the affiliated keywords instead of re-parsing them multiple times. 2023-10-12 15:47:06 -04:00
Tom Alexander
176e37874e Remove the parser to parse affiliated keywords as regular keywords. 2023-10-12 15:25:26 -04:00
Tom Alexander
3208a04f7a Update project status in the README.
Some checks failed
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
2023-10-12 14:13:54 -04:00
Tom Alexander
0d579263cb Remove affiliated keyword non-exit for paragraph. 2023-10-12 14:07:57 -04:00
Tom Alexander
c022b30110 Update org-mode. 2023-10-12 13:38:11 -04:00
Tom Alexander
7da4e4a29b Merge branch 'optional_value'
Some checks failed
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
2023-10-11 19:14:46 -04:00
Tom Alexander
8bc942a26f Create artificial scopes for the optional value and mandatory value. 2023-10-11 19:14:32 -04:00
Tom Alexander
dff7550038 Remove prints. 2023-10-11 19:09:51 -04:00
Tom Alexander
c4edcb8c24 Fix parsing the optional value. 2023-10-11 19:07:47 -04:00
Tom Alexander
4a44d88461 Better error messages. 2023-10-11 19:05:39 -04:00
Tom Alexander
efc6bd11d9 Do not exit on first loop. 2023-10-11 18:59:13 -04:00
Tom Alexander
51429e3155 Handle optval when translating names. 2023-10-11 18:50:22 -04:00
Tom Alexander
b1a0fa4acf Compare optional value. 2023-10-11 18:44:21 -04:00
Tom Alexander
aeb2b6fe68 Parse out the optional value objects. 2023-10-11 18:29:07 -04:00
Tom Alexander
6679db98a8 Add comments. 2023-10-11 17:53:32 -04:00
Tom Alexander
5363324bbf Add a test showing we are not handling optional values properly. 2023-10-11 17:37:39 -04:00
Tom Alexander
dc9188dffc Better error logging when emacs errors out.
Some checks failed
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
2023-10-11 17:22:01 -04:00
Tom Alexander
bd620ccd0d Format. 2023-10-11 16:32:08 -04:00
Tom Alexander
1947ae9f22 Merge branch 'document_properties'
Some checks failed
rustfmt Build rustfmt has succeeded
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-build Build rust-build has succeeded
rust-test Build rust-test has failed
2023-10-11 16:28:53 -04:00
Tom Alexander
3fcf1b3864 Compare Document additional properties. 2023-10-11 16:27:25 -04:00
Tom Alexander
d965dd6fd1 Implement get_additional_properties for Document. 2023-10-11 16:14:03 -04:00
Tom Alexander
42dcd41e48 Add tests for document properties. 2023-10-11 16:02:44 -04:00
Tom Alexander
68fac7cfe8 Merge branch 'affiliated_keywords_properties'
Some checks failed
rustfmt Build rustfmt has failed
rust-build Build rust-build has failed
rust-test Build rust-test has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
2023-10-11 15:31:23 -04:00
Tom Alexander
a9f067e25a Update name tests to use all the affiliated keywords. 2023-10-11 15:25:09 -04:00
Tom Alexander
41b4b1015e Update all comparisons to handle affiliated keywords. 2023-10-11 14:57:32 -04:00
Tom Alexander
9523365090 Update all elements to the new AffiliatedKeywords. 2023-10-11 14:44:25 -04:00
Tom Alexander
aa33fe42a8 Update compare_properties to handle affiliated keywords. 2023-10-11 14:14:33 -04:00
Tom Alexander
78a9b93f22 Nevermind, it did work. 2023-10-11 14:03:42 -04:00
Tom Alexander
a89339e472 Using owned string did not solve it. 2023-10-11 14:00:18 -04:00
Tom Alexander
f3307a8159 Lifetime issue. 2023-10-11 13:55:43 -04:00
Tom Alexander
4bfa25dcb3 Comparing lists of quoted strings. 2023-10-11 13:04:33 -04:00
Tom Alexander
441a240c33 Comparing single string values. 2023-10-11 13:00:21 -04:00
Tom Alexander
e767892dd5 Add capitalization. 2023-10-11 12:42:42 -04:00
Tom Alexander
8e0a7dea0f A very simple GetAffiliatedKeywords trait. 2023-10-11 12:19:27 -04:00
Tom Alexander
c4cc40f5e1 Parsing the affiliated keywords. 2023-10-11 12:16:35 -04:00
Tom Alexander
a5129199c7 I need to parse the affiliated keywords during parsing because it relies on the global settings. 2023-10-11 12:16:35 -04:00
Tom Alexander
c0e462944d Comment out the broken stuff. 2023-10-11 12:16:35 -04:00
Tom Alexander
63614841e8 Trait lifetime issues. 2023-10-11 12:16:35 -04:00
Tom Alexander
bc4c09c546 Add constants for affiliated keyword settings. 2023-10-11 12:16:35 -04:00
Tom Alexander
0d1ab0e75a Add a debug assert for org entities being sorted. 2023-10-11 12:16:35 -04:00
Tom Alexander
6e39029f91 Store the affiliated keywords on the plainlist. 2023-10-10 18:10:34 -04:00
Tom Alexander
d550966516 Add a test with all the affiliated keyword types. 2023-10-10 18:02:23 -04:00
Tom Alexander
32d6b3d1ee Merge branch 'compare_additional_properties'
Some checks failed
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has failed
2023-10-10 17:40:40 -04:00
Tom Alexander
7cb71a5a0a Compare the additional properties on headlines. 2023-10-10 17:40:27 -04:00
Tom Alexander
6b90a9bfa8 Add TODO. 2023-10-10 17:15:51 -04:00
Tom Alexander
83d939bdc0 Update org-mode. 2023-10-10 17:00:13 -04:00
Tom Alexander
0e1ace78f4 Merge branch 'headline_extra_properties'
Some checks failed
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
2023-10-10 16:57:14 -04:00
Tom Alexander
6d4db60688 Support ("") as being equal to None in compare_property_list_of_ast_nodes. 2023-10-10 16:35:16 -04:00
Tom Alexander
3aca01891d Remove the old implementation of comparing headlines. 2023-10-10 16:12:29 -04:00
Tom Alexander
275ed87c3f Add a compare_properties variant with support for additional properties. 2023-10-10 16:10:12 -04:00
Tom Alexander
b385270d7b Implement a function to get the additional properties on a headline. 2023-10-10 15:54:44 -04:00
Tom Alexander
fb412bc2cd Merge branch 'compare_properties_document'
Some checks failed
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
2023-10-10 15:31:21 -04:00
Tom Alexander
585b1d2b74 compare_properties for document. 2023-10-10 15:30:27 -04:00
Tom Alexander
c578bb45af Implement ExactSizeIterator for the other node types. 2023-10-10 15:27:03 -04:00
Tom Alexander
b7f7876706 Impl ExactSizeIterator for multi_field_iter. 2023-10-10 15:23:04 -04:00
Tom Alexander
ee5ed17c20 Implement ExactSizeIterator for DocumentIter. 2023-10-10 15:19:42 -04:00
Tom Alexander
a873794068 Starting a compare properties document function. 2023-10-10 14:58:18 -04:00
Tom Alexander
360b2d963d Merge branch 'compare_properties_the_other_nodes'
Some checks failed
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
2023-10-10 02:10:09 -04:00
Tom Alexander
534c5ded3c Fix table type is required. 2023-10-10 02:07:36 -04:00
Tom Alexander
bdaf90af03 compare_properties timestamp. 2023-10-10 02:05:31 -04:00
Tom Alexander
1a67aac502 compare_properties latex environment. 2023-10-10 01:39:47 -04:00
Tom Alexander
9f166278f4 compare_properties babel call. 2023-10-10 01:36:48 -04:00
Tom Alexander
c2222c9102 compare_properties keyword. 2023-10-10 01:32:36 -04:00
Tom Alexander
fc104680eb compare_properties horizontal rule. 2023-10-10 01:29:00 -04:00
Tom Alexander
e40e3ff553 compare_properties fixed width area. 2023-10-10 01:27:37 -04:00
Tom Alexander
0b465fe290 compare_properties planning. 2023-10-10 01:24:44 -04:00
Tom Alexander
d5396e311b compare_properties diary sexp. 2023-10-10 01:22:16 -04:00
Tom Alexander
384242af87 compare_properties clock. 2023-10-10 01:19:14 -04:00
Tom Alexander
ec755bae8b compare_properties src block. 2023-10-10 01:13:09 -04:00
Tom Alexander
0b1e06f0d5 compare_properties export block. 2023-10-10 01:07:41 -04:00
Tom Alexander
bdfa050ee3 compare_properties example block. 2023-10-10 01:02:45 -04:00
Tom Alexander
ec98e1c3c5 compare_properties comment block. 2023-10-10 00:15:04 -04:00
Tom Alexander
0d0b1b2051 compare_properties verse block. 2023-10-10 00:12:40 -04:00
Tom Alexander
2215c32e57 compare_properties table cell. 2023-10-10 00:11:05 -04:00
Tom Alexander
cf257443b0 compare_properties table row. 2023-10-10 00:07:34 -04:00
Tom Alexander
62815621e4 compare_properties table. 2023-10-10 00:05:34 -04:00
Tom Alexander
7af5359e00 compare_properties node property. 2023-10-09 22:45:32 -04:00
Tom Alexander
d1184fa1d0 compare_properties property drawer. 2023-10-09 22:42:02 -04:00
Tom Alexander
5b146d7c07 compare_properties drawer. 2023-10-09 22:37:53 -04:00
Tom Alexander
926682d513 compare_properties comment. 2023-10-09 22:36:15 -04:00
Tom Alexander
4c89d6c813 compare_properties footnote definition. 2023-10-09 22:32:25 -04:00
Tom Alexander
62926bb91d compare_properties dynamic block. 2023-10-09 22:28:32 -04:00
Tom Alexander
9ab649ebd4 compare_properties special block. 2023-10-09 22:20:20 -04:00
Tom Alexander
ca1b633a9f compare_properties quote block. 2023-10-09 21:57:11 -04:00
Tom Alexander
f543caee00 compare_properties plain list item. 2023-10-09 21:55:29 -04:00
Tom Alexander
409a92333e compare_properties plain list. 2023-10-09 21:55:28 -04:00
Tom Alexander
7a38d1ead3 compare_properties paragraph. 2023-10-09 21:55:28 -04:00
Tom Alexander
33c53a14ab Switch back to the old compare heading function until we support additional properties. 2023-10-09 21:32:29 -04:00
Tom Alexander
65615c64d2 Implement a new compare_properties implementation of the heading comparison. 2023-10-09 21:27:18 -04:00
Tom Alexander
166e59b922 Implement a compare_property_numeric. 2023-10-09 21:04:41 -04:00
Tom Alexander
8e357ed3b6 compare_properties section. 2023-10-09 20:54:31 -04:00
Tom Alexander
45074e3be3 Add a test. 2023-10-09 20:50:19 -04:00
Tom Alexander
ee1d8ca321 Text markup uses confine context. 2023-10-09 20:36:38 -04:00
Tom Alexander
25531cc443 Accept eof as a disallowed character for plain links. 2023-10-09 20:24:43 -04:00
Tom Alexander
47a440147f Fix formatting.
Some checks failed
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
2023-10-09 19:52:32 -04:00
Tom Alexander
8a91b9a074 Merge branch 'subscript_and_superscript_properties' 2023-10-09 19:52:05 -04:00
Tom Alexander
d1ef83afca Compare properties of subscript and superscript. 2023-10-09 19:51:31 -04:00
Tom Alexander
86dd526dde Merge branch 'statistics_cookie_properties' 2023-10-09 19:40:39 -04:00
Tom Alexander
4d88256394 Compare statistics cookies properties. 2023-10-09 19:40:13 -04:00
Tom Alexander
c49455e9ce Merge branch 'target_properties' 2023-10-09 19:37:54 -04:00
Tom Alexander
a588da2c4a Compare target properties. 2023-10-09 19:37:47 -04:00
Tom Alexander
869c98d8b2 Compare line break properties. 2023-10-09 19:33:51 -04:00
Tom Alexander
0374743cad Merge branch 'inline_source_block_properties' 2023-10-09 19:32:04 -04:00
Tom Alexander
4bc2f48738 Compare inline source block properties. 2023-10-09 19:31:57 -04:00
Tom Alexander
69500837f2 Merge branch 'inline_babel_properties' 2023-10-09 19:25:33 -04:00
Tom Alexander
0d0b9863c3 Populate inline babel call fields. 2023-10-09 19:25:00 -04:00
Tom Alexander
6bc6fdc87b Compare properties for inline babel calls. 2023-10-09 19:21:58 -04:00
Tom Alexander
53b9deff10 Merge branch 'citation_properties'
Some checks failed
rustfmt Build rustfmt has succeeded
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-build Build rust-build has succeeded
rust-test Build rust-test has failed
2023-10-09 19:14:05 -04:00
Tom Alexander
627f16e3a0 Update text markup tests with failure cases. 2023-10-09 19:10:55 -04:00
Tom Alexander
bf1a281c29 Footnote reference description is in a confined context. 2023-10-09 18:23:13 -04:00
Tom Alexander
df4daa0e16 Do not include context in tracing. 2023-10-09 18:02:36 -04:00
Tom Alexander
5cd34ba3a2 Create a new context tree when calling into confine_context.
The parent exit matchers were causing an issue.
2023-10-09 18:02:36 -04:00
Tom Alexander
13b95cd0a1 Fix handling of text markup at the start/end of regular link descriptions and radio targets. 2023-10-09 18:02:36 -04:00
Tom Alexander
876d042c37 Fix tests. 2023-10-09 15:54:30 -04:00
Tom Alexander
81c0b7079f Do not include leading slash in citation style.
Some checks failed
rust-test Build rust-test has failed
rust-build Build rust-build has succeeded
rust-foreign-document-test Build rust-foreign-document-test has failed
2023-10-09 15:48:43 -04:00
Tom Alexander
4a367dd7e0 Include closing semicolon in citation reference. 2023-10-09 15:45:10 -04:00
Tom Alexander
8a0f9d4540 Fix comparing key and mark prefix/suffix as optional. 2023-10-09 15:41:21 -04:00
Tom Alexander
f6155ecf93 Switch to returning ComparePropertiesResult.
This is to support returning lists of child results for properties that contain lists of ast nodes.
2023-10-09 15:33:33 -04:00
Tom Alexander
c077d34933 Populate citation reference properties. 2023-10-09 15:33:33 -04:00
Tom Alexander
1ecc3ecf9d Fill citation fields. 2023-10-09 15:33:33 -04:00
Tom Alexander
ced35e1694 Merge branch 'footnote_reference_properties' 2023-10-09 15:33:13 -04:00
Tom Alexander
840dc0a750 Support text markup at the start of a regular link description.
Some checks failed
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has failed
2023-10-09 14:02:27 -04:00
Tom Alexander
adc5a383c3 Allow text markup at the start of a radio target. 2023-10-09 13:47:36 -04:00
Tom Alexander
5ac12229f4 Fix footnote reference type. 2023-10-09 13:23:08 -04:00
Tom Alexander
9565435526 Compare footnote reference properties. 2023-10-09 13:14:35 -04:00
Tom Alexander
e39562c85d Roll out the new children functions to all compare functions using the new compare_properties macro.
We will roll it out to the rest of them when we move them over to the new compare_properties macro.
2023-10-09 13:08:45 -04:00
Tom Alexander
c49556bd5d Merge branch 'export_snippet_properties'
Some checks failed
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has failed
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-10-09 13:02:07 -04:00
Tom Alexander
84ec2f2023 Introduce functions to compare children and assert there are no children. 2023-10-09 13:01:55 -04:00
Tom Alexander
00ed63dcaa Compare properties for export snippets. 2023-10-09 13:01:55 -04:00
Tom Alexander
8dde8ce4e1 Merge branch 'latex_fragment_properties' 2023-10-09 13:01:29 -04:00
Tom Alexander
4e551e6d7e Compare latex fragment properties.
All checks were successful
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-10-09 12:30:59 -04:00
Tom Alexander
ea29ad8667 Merge branch 'entity_properties'
All checks were successful
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-10-09 10:33:20 -04:00
Tom Alexander
085e728c34 Add an all entities test without escaping braces. 2023-10-09 10:33:07 -04:00
Tom Alexander
99318f39e3 Do not accept escaping {} for entities ending in whitespace. 2023-10-09 10:33:07 -04:00
Tom Alexander
c679a85e77 Fix sorting of entity names. 2023-10-09 10:33:07 -04:00
Tom Alexander
fc17fc224c Update definitions to the version of entities used in the org-mode in the docker container. 2023-10-09 10:33:07 -04:00
Tom Alexander
7fe7e7a6ea Add use_brackets. 2023-10-09 10:33:07 -04:00
Tom Alexander
ef591556fe Populate entity fields. 2023-10-08 18:06:56 -04:00
Tom Alexander
c150aa4dea Add support for an org-entities global setting. 2023-10-08 18:01:42 -04:00
Tom Alexander
1df6777b0b Starting to compare entity properties. 2023-10-08 17:24:03 -04:00
Tom Alexander
8a109276df Merge branch 'macro_properties' 2023-10-08 17:17:51 -04:00
Tom Alexander
f07d041eb9 Fix handling capitalization in macro names.
All checks were successful
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-10-08 16:51:44 -04:00
Tom Alexander
9bdec391f1 Do not capture trailing whitespace on the final macro arg.
Some checks failed
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has failed
2023-10-08 16:39:22 -04:00
Tom Alexander
b9ead09dde Add test showing we are not handling trailing whitespace in macro args properly. 2023-10-08 16:34:28 -04:00
Tom Alexander
dd009498dd Switch to using coalesce_whitespace_escaped for macro args.
Some checks failed
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has failed
2023-10-08 16:19:25 -04:00
Tom Alexander
17c745ee71 Improve coalesce_whitespace_escaped to borrow when single spaces are used. 2023-10-08 16:15:49 -04:00
Tom Alexander
41aa0349a0 Add tests for coalesce_whitespace_escaped. 2023-10-08 16:06:52 -04:00
Tom Alexander
a6adeee40b Handle escaping the characters in org macro arguments. 2023-10-08 15:54:56 -04:00
Tom Alexander
a32cea8139 Coalesce whitespace in macro args. 2023-10-08 15:08:21 -04:00
Tom Alexander
37bc5ef712 Do not include whitespace at the end of value. 2023-10-08 14:48:29 -04:00
Tom Alexander
1a2f0856da Compare macro properties. 2023-10-08 14:40:01 -04:00
150 changed files with 8765 additions and 3469 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
/target /target
Cargo.lock Cargo.lock
TODO.org

View File

@@ -137,7 +137,7 @@ spec:
value: [] value: []
- name: docker-image - name: docker-image
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)" value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
- name: run-image-all - name: run-image-tracing-compare
taskRef: taskRef:
name: run-docker-image name: run-docker-image
workspaces: workspaces:
@@ -152,6 +152,46 @@ spec:
value: ["--no-default-features", "--features", "tracing,compare"] value: ["--no-default-features", "--features", "tracing,compare"]
- name: docker-image - name: docker-image
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)" value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
- name: run-image-compare-foreign
taskRef:
name: run-docker-image
workspaces:
- name: source
workspace: git-source
- name: cargo-cache
workspace: cargo-cache
runAfter:
- run-image-tracing-compare
params:
- name: args
value:
[
"--no-default-features",
"--features",
"compare,foreign_document_test",
]
- name: docker-image
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
- name: run-image-all
taskRef:
name: run-docker-image
workspaces:
- name: source
workspace: git-source
- name: cargo-cache
workspace: cargo-cache
runAfter:
- run-image-compare-foreign
params:
- name: args
value:
[
"--no-default-features",
"--features",
"tracing,compare,foreign_document_test",
]
- name: docker-image
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
finally: finally:
- name: report-success - name: report-success
when: when:

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "organic" name = "organic"
version = "0.1.9" version = "0.1.10"
authors = ["Tom Alexander <tom@fizz.buzz>"] authors = ["Tom Alexander <tom@fizz.buzz>"]
description = "An org-mode parser." description = "An org-mode parser."
edition = "2021" edition = "2021"
@@ -31,7 +31,14 @@ path = "src/lib.rs"
path = "src/bin_compare.rs" path = "src/bin_compare.rs"
required-features = ["compare"] required-features = ["compare"]
[[bin]]
# This bin exists for development purposes only. The real target of this crate is the library.
name = "foreign_document_test"
path = "src/bin_foreign_document_test.rs"
required-features = ["foreign_document_test"]
[dependencies] [dependencies]
futures = { version = "0.3.28", optional = true }
nom = "7.1.1" nom = "7.1.1"
opentelemetry = { version = "0.20.0", optional = true, default-features = false, features = ["trace", "rt-tokio"] } opentelemetry = { version = "0.20.0", optional = true, default-features = false, features = ["trace", "rt-tokio"] }
opentelemetry-otlp = { version = "0.13.0", optional = true } opentelemetry-otlp = { version = "0.13.0", optional = true }
@@ -40,13 +47,15 @@ tokio = { version = "1.30.0", optional = true, default-features = false, feature
tracing = { version = "0.1.37", optional = true } tracing = { version = "0.1.37", optional = true }
tracing-opentelemetry = { version = "0.20.0", optional = true } tracing-opentelemetry = { version = "0.20.0", optional = true }
tracing-subscriber = { version = "0.3.17", optional = true, features = ["env-filter"] } tracing-subscriber = { version = "0.3.17", optional = true, features = ["env-filter"] }
walkdir = { version = "2.3.3", optional = true }
[build-dependencies] [build-dependencies]
walkdir = "2.3.3" walkdir = "2.3.3"
[features] [features]
default = [] default = []
compare = [] compare = ["tokio/process", "tokio/macros"]
foreign_document_test = ["compare", "dep:futures", "tokio/sync", "dep:walkdir", "tokio/process"]
tracing = ["dep:opentelemetry", "dep:opentelemetry-otlp", "dep:opentelemetry-semantic-conventions", "dep:tokio", "dep:tracing", "dep:tracing-opentelemetry", "dep:tracing-subscriber"] tracing = ["dep:opentelemetry", "dep:opentelemetry-otlp", "dep:opentelemetry-semantic-conventions", "dep:tokio", "dep:tracing", "dep:tracing-opentelemetry", "dep:tracing-subscriber"]
# Optimized build for any sort of release. # Optimized build for any sort of release.

View File

@@ -52,6 +52,8 @@ buildtest:
> cargo build --no-default-features --features compare > cargo build --no-default-features --features compare
> cargo build --no-default-features --features tracing > cargo build --no-default-features --features tracing
> cargo build --no-default-features --features compare,tracing > cargo build --no-default-features --features compare,tracing
> cargo build --no-default-features --features compare,foreign_document_test
> cargo build --no-default-features --features compare,tracing,foreign_document_test
.PHONY: foreign_document_test .PHONY: foreign_document_test
foreign_document_test: foreign_document_test:

View File

@@ -6,14 +6,13 @@ Organic is an emacs-less implementation of an [org-mode](https://orgmode.org/) p
This project is still under HEAVY development. While the version remains v0.1.x the API will be changing often. Once we hit v0.2.x we will start following semver. This project is still under HEAVY development. While the version remains v0.1.x the API will be changing often. Once we hit v0.2.x we will start following semver.
Currently, the parser is able to correctly identify the start/end bounds of all the org-mode objects and elements (except table.el tables, org-mode tables are supported) but many of the interior properties are not yet populated. Currently, Organic parses most documents the same as the official org-mode parser. Most of the development right now is finding documents where the parsers differ and fixing those issues.
### Project Goals ### Project Goals
- We aim to provide perfect parity with the emacs org-mode parser. In that regard, any document that parses differently between Emacs and Organic is considered a bug. - We aim to provide perfect parity with the emacs org-mode parser. In that regard, any document that parses differently between Emacs and Organic is considered a bug.
- The parser should be fast. We're not doing anything special, but since this is written in Rust and natively compiled we should be able to beat the existing parsers.
- The parser should have minimal dependencies. This should reduce effort w.r.t.: security audits, legal compliance, portability. - The parser should have minimal dependencies. This should reduce effort w.r.t.: security audits, legal compliance, portability.
- The parser should be usable everywhere. In the interest of getting org-mode used in as many places as possible, this parser should be usable by everyone everywhere. This means: - The parser should be usable everywhere. In the interest of getting org-mode used in as many places as possible, this parser should be usable by everyone everywhere. This means:
- It must have a permissive license for use in proprietary code bases. - It must have a permissive license.
- We will investigate compiling to WASM. This is an important goal of the project and will definitely happen, but only after the parser has a more stable API. - We will investigate compiling to WASM. This is an important goal of the project and will definitely happen, but only after the parser has a more stable API.
- We will investigate compiling to a C library for native linking to other code. This is more of a maybe-goal for the project. - We will investigate compiling to a C library for native linking to other code. This is more of a maybe-goal for the project.
### Project Non-Goals ### Project Non-Goals

View File

@@ -14,7 +14,7 @@ RUN make DESTDIR="/root/dist" install
FROM build AS build-org-mode FROM build AS build-org-mode
ARG ORG_VERSION=c703541ffcc14965e3567f928de1683a1c1e33f6 ARG ORG_VERSION=abf5156096c06ee5aa05795c3dc5a065f76ada97
COPY --from=build-emacs /root/dist/ / COPY --from=build-emacs /root/dist/ /
RUN mkdir /root/dist RUN mkdir /root/dist
# Savannah does not allow fetching specific revisions, so we're going to have to put unnecessary load on their server by cloning main and then checking out the revision we want. # Savannah does not allow fetching specific revisions, so we're going to have to put unnecessary load on their server by cloning main and then checking out the revision we want.
@@ -102,6 +102,4 @@ COPY --from=foreign-document-gather /foreign_documents/doomemacs /foreign_docume
COPY --from=foreign-document-gather /foreign_documents/worg /foreign_documents/worg COPY --from=foreign-document-gather /foreign_documents/worg /foreign_documents/worg
COPY --from=build-org-mode /root/org-mode /foreign_documents/org-mode COPY --from=build-org-mode /root/org-mode /foreign_documents/org-mode
COPY --from=build-emacs /root/emacs /foreign_documents/emacs COPY --from=build-emacs /root/emacs /foreign_documents/emacs
COPY foreign_document_test_entrypoint.sh /entrypoint.sh ENTRYPOINT ["cargo", "run", "--bin", "foreign_document_test", "--features", "compare,foreign_document_test", "--profile", "release-lto"]
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

View File

@@ -1,149 +0,0 @@
#!/usr/bin/env bash
#
# Run the Organic compare script against a series of documents sourced from exterior places.
set -euo pipefail
IFS=$'\n\t'
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
REALPATH=$(command -v uu-realpath || command -v realpath)
function log {
(>&2 echo "${@}")
}
function die {
local status_code="$1"
shift
(>&2 echo "${@}")
exit "$status_code"
}
function main {
cargo build --no-default-features --features compare --profile release-lto
if [ "${CARGO_TARGET_DIR:-}" = "" ]; then
CARGO_TARGET_DIR=$(realpath target/)
fi
PARSE="${CARGO_TARGET_DIR}/release-lto/compare"
local all_status=0
set +e
(run_compare_function "org-mode" compare_all_org_document "/foreign_documents/org-mode")
if [ "$?" -ne 0 ]; then all_status=1; fi
(run_compare_function "emacs" compare_all_org_document "/foreign_documents/emacs")
if [ "$?" -ne 0 ]; then all_status=1; fi
(run_compare_function "worg" compare_all_org_document "/foreign_documents/worg")
if [ "$?" -ne 0 ]; then all_status=1; fi
(run_compare_function "howard_abrams" compare_howard_abrams)
if [ "$?" -ne 0 ]; then all_status=1; fi
(run_compare_function "doomemacs" compare_all_org_document "/foreign_documents/doomemacs")
if [ "$?" -ne 0 ]; then all_status=1; fi
set -e
if [ "$all_status" -ne 0 ]; then
red_text "Some tests failed."
else
green_text "All tests passed."
fi
return "$all_status"
}
function green_text {
(IFS=' '; printf '\x1b[38;2;0;255;0m%s\x1b[0m' "${*}")
}
function red_text {
(IFS=' '; printf '\x1b[38;2;255;0;0m%s\x1b[0m' "${*}")
}
function yellow_text {
(IFS=' '; printf '\x1b[38;2;255;255;0m%s\x1b[0m' "${*}")
}
function indent {
local depth="$1"
local scaled_depth=$((depth * 2))
shift 1
local prefix
prefix=$(printf -- "%${scaled_depth}s")
while read -r l; do
(IFS=' '; printf -- '%s%s\n' "$prefix" "$l")
done
}
function run_compare_function {
local name="$1"
local stdoutput
shift 1
set +e
stdoutput=$("${@}")
local status=$?
set -e
if [ "$status" -eq 0 ]; then
echo "$(green_text "GOOD") $name"
indent 1 <<<"$stdoutput"
else
echo "$(red_text "FAIL") $name"
indent 1 <<<"$stdoutput"
return 1
fi
}
function compare_all_org_document {
local root_dir="$1"
local target_document
local all_status=0
while read target_document; do
local relative_path
relative_path=$($REALPATH --relative-to "$root_dir" "$target_document")
set +e
(run_compare "$relative_path" "$target_document")
if [ "$?" -ne 0 ]; then all_status=1; fi
set -e
done<<<"$(find "$root_dir" -type f -iname '*.org' | sort)"
return "$all_status"
}
function run_compare {
local name="$1"
local target_document="$2"
set +e
($PARSE "$target_document" &> /dev/null)
local status=$?
set -e
if [ "$status" -eq 0 ]; then
echo "$(green_text "GOOD") $name"
else
echo "$(red_text "FAIL") $name"
return 1
fi
}
function compare_howard_abrams {
local all_status=0
set +e
(run_compare_function "dot-files" compare_all_org_document "/foreign_documents/howardabrams/dot-files")
if [ "$?" -ne 0 ]; then all_status=1; fi
(run_compare_function "hamacs" compare_all_org_document "/foreign_documents/howardabrams/hamacs")
if [ "$?" -ne 0 ]; then all_status=1; fi
(run_compare_function "demo-it" compare_all_org_document "/foreign_documents/howardabrams/demo-it")
if [ "$?" -ne 0 ]; then all_status=1; fi
(run_compare_function "magit-demo" compare_all_org_document "/foreign_documents/howardabrams/magit-demo")
if [ "$?" -ne 0 ]; then all_status=1; fi
(run_compare_function "pdx-emacs-hackers" compare_all_org_document "/foreign_documents/howardabrams/pdx-emacs-hackers")
if [ "$?" -ne 0 ]; then all_status=1; fi
(run_compare_function "flora-simulator" compare_all_org_document "/foreign_documents/howardabrams/flora-simulator")
if [ "$?" -ne 0 ]; then all_status=1; fi
(run_compare_function "literate-devops-demo" compare_all_org_document "/foreign_documents/howardabrams/literate-devops-demo")
if [ "$?" -ne 0 ]; then all_status=1; fi
(run_compare_function "clojure-yesql-xp" compare_all_org_document "/foreign_documents/howardabrams/clojure-yesql-xp")
if [ "$?" -ne 0 ]; then all_status=1; fi
(run_compare_function "veep" compare_all_org_document "/foreign_documents/howardabrams/veep")
if [ "$?" -ne 0 ]; then all_status=1; fi
set -e
return "$all_status"
}
main "${@}"

View File

@@ -1,5 +1,11 @@
(dolist (var org-entities) (dolist (var (sort (seq-filter 'listp org-entities)
(when (listp var) (lambda (x y) (> (length (nth 0 x)) (length (nth 0 y))))))
(message "\"%s\"," (nth 0 var)) (message "EntityDefinition {name: %s, latex_math_mode: %s, latex: %s, html: %s, ascii: %s, utf8: %s},"
) (prin1-to-string (nth 0 var))
(if (nth 2 var) "true" "false")
(prin1-to-string (nth 1 var))
(prin1-to-string (nth 3 var))
(prin1-to-string (nth 4 var))
(prin1-to-string (nth 6 var))
)
) )

View File

@@ -0,0 +1,8 @@
#+CAPTION[foo]: *bar*
#+CAPTION[*lorem* ipsum]: dolar
1. baz
#+CAPTION[foo]: *bar*
#+CAPTION[*lorem* ipsum]: dolar
# Comments cannot have affiliated keywords so those become regular keywords.

View File

@@ -0,0 +1,74 @@
* Headline
before
#+NAME: a
#+caption: b *lorem* ipsum
#+results: c
#+headers: d
#+header: e
#+label: f
#+plot: g
#+resname: h
#+result: i
#+source: j
#+srcname: k
#+tblname: l
#+attr_latex: m
#+attr_html: n
#+NAME: aa
#+caption: bb *lorem* ipsum
#+results: cc
#+headers: dd
#+header: ee
#+label: ff
#+plot: gg
#+resname: hh
#+result: ii
#+source: jj
#+srcname: kk
#+tblname: ll
#+attr_latex: mm
#+attr_html: nn
:candle:
inside
the drawer
:end:
#+NAME: A
#+CAPTION: B *LOREM* IPSUM
#+RESULTS: C
#+HEADERS: D
#+HEADER: E
#+LABEL: F
#+PLOT: G
#+RESNAME: H
#+RESULT: I
#+SOURCE: J
#+SRCNAME: K
#+TBLNAME: L
#+ATTR_LATEX: M
#+ATTR_HTML: N
#+NAME: AA
#+CAPTION: BB *LOREM* IPSUM
#+RESULTS: CC
#+HEADERS: DD
#+HEADER: EE
#+LABEL: FF
#+PLOT: GG
#+RESNAME: HH
#+RESULT: II
#+SOURCE: JJ
#+SRCNAME: KK
#+TBLNAME: LL
#+ATTR_LATEX: MM
#+ATTR_HTML: NN
:candle:
inside
the drawer
:end:
after

View File

@@ -1,10 +0,0 @@
* Headline
before
#+NAME: foo
:candle:
inside
the drawer
:end:
after

View File

@@ -0,0 +1,72 @@
#+NAME: a
#+caption: b *lorem* ipsum
#+results: c
#+headers: d
#+header: e
#+label: f
#+plot: g
#+resname: h
#+result: i
#+source: j
#+srcname: k
#+tblname: l
#+attr_latex: m
#+attr_html: n
#+NAME: aa
#+caption: bb *lorem* ipsum
#+results: cc
#+headers: dd
#+header: ee
#+label: ff
#+plot: gg
#+resname: hh
#+result: ii
#+source: jj
#+srcname: kk
#+tblname: ll
#+attr_latex: mm
#+attr_html: nn
#+BEGIN: clocktable :scope file :maxlevel 2
#+CAPTION: Clock summary at [2023-08-25 Fri 05:34]
| Headline | Time |
|--------------+--------|
| *Total time* | *0:00* |
#+END:
#+NAME: A
#+CAPTION: B *LOREM* IPSUM
#+RESULTS: C
#+HEADERS: D
#+HEADER: E
#+LABEL: F
#+PLOT: G
#+RESNAME: H
#+RESULT: I
#+SOURCE: J
#+SRCNAME: K
#+TBLNAME: L
#+ATTR_LATEX: M
#+ATTR_HTML: N
#+NAME: AA
#+CAPTION: BB *LOREM* IPSUM
#+RESULTS: CC
#+HEADERS: DD
#+HEADER: EE
#+LABEL: FF
#+PLOT: GG
#+RESNAME: HH
#+RESULT: II
#+SOURCE: JJ
#+SRCNAME: KK
#+TBLNAME: LL
#+ATTR_LATEX: MM
#+ATTR_HTML: NN
#+BEGIN: clocktable :scope file :maxlevel 2
#+CAPTION: Clock summary at [2023-08-25 Fri 05:34]
| Headline | Time |
|--------------+--------|
| *Total time* | *0:00* |
#+END:

View File

@@ -1,7 +0,0 @@
#+NAME: foo
#+BEGIN: clocktable :scope file :maxlevel 2
#+CAPTION: Clock summary at [2023-08-25 Fri 05:34]
| Headline | Time |
|--------------+--------|
| *Total time* | *0:00* |
#+END:

View File

@@ -0,0 +1,63 @@
#+NAME: a
#+caption: b *lorem* ipsum
#+results: c
#+headers: d
#+header: e
#+label: f
#+plot: g
#+resname: h
#+result: i
#+source: j
#+srcname: k
#+tblname: l
#+attr_latex: m
#+attr_html: n
#+NAME: aa
#+caption: bb *lorem* ipsum
#+results: cc
#+headers: dd
#+header: ee
#+label: ff
#+plot: gg
#+resname: hh
#+result: ii
#+source: jj
#+srcname: kk
#+tblname: ll
#+attr_latex: mm
#+attr_html: nn
[fn:1] A footnote.
#+NAME: A
#+CAPTION: B *LOREM* IPSUM
#+RESULTS: C
#+HEADERS: D
#+HEADER: E
#+LABEL: F
#+PLOT: G
#+RESNAME: H
#+RESULT: I
#+SOURCE: J
#+SRCNAME: K
#+TBLNAME: L
#+ATTR_LATEX: M
#+ATTR_HTML: N
#+NAME: AA
#+CAPTION: BB *LOREM* IPSUM
#+RESULTS: CC
#+HEADERS: DD
#+HEADER: EE
#+LABEL: FF
#+PLOT: GG
#+RESNAME: HH
#+RESULT: II
#+SOURCE: JJ
#+SRCNAME: KK
#+TBLNAME: LL
#+ATTR_LATEX: MM
#+ATTR_HTML: NN
[fn:1] A footnote.

View File

@@ -0,0 +1,67 @@
#+NAME: a
#+caption: b *lorem* ipsum
#+results: c
#+headers: d
#+header: e
#+label: f
#+plot: g
#+resname: h
#+result: i
#+source: j
#+srcname: k
#+tblname: l
#+attr_latex: m
#+attr_html: n
#+NAME: aa
#+caption: bb *lorem* ipsum
#+results: cc
#+headers: dd
#+header: ee
#+label: ff
#+plot: gg
#+resname: hh
#+result: ii
#+source: jj
#+srcname: kk
#+tblname: ll
#+attr_latex: mm
#+attr_html: nn
#+begin_center
#+end_center
#+NAME: A
#+CAPTION: B *LOREM* IPSUM
#+RESULTS: C
#+HEADERS: D
#+HEADER: E
#+LABEL: F
#+PLOT: G
#+RESNAME: H
#+RESULT: I
#+SOURCE: J
#+SRCNAME: K
#+TBLNAME: L
#+ATTR_LATEX: M
#+ATTR_HTML: N
#+NAME: AA
#+CAPTION: BB *LOREM* IPSUM
#+RESULTS: CC
#+HEADERS: DD
#+HEADER: EE
#+LABEL: FF
#+PLOT: GG
#+RESNAME: HH
#+RESULT: II
#+SOURCE: JJ
#+SRCNAME: KK
#+TBLNAME: LL
#+ATTR_LATEX: MM
#+ATTR_HTML: NN
#+begin_center
#+end_center

View File

@@ -1,4 +0,0 @@
#+NAME: foo
#+begin_center
#+end_center

View File

@@ -0,0 +1,67 @@
#+NAME: a
#+caption: b *lorem* ipsum
#+results: c
#+headers: d
#+header: e
#+label: f
#+plot: g
#+resname: h
#+result: i
#+source: j
#+srcname: k
#+tblname: l
#+attr_latex: m
#+attr_html: n
#+NAME: aa
#+caption: bb *lorem* ipsum
#+results: cc
#+headers: dd
#+header: ee
#+label: ff
#+plot: gg
#+resname: hh
#+result: ii
#+source: jj
#+srcname: kk
#+tblname: ll
#+attr_latex: mm
#+attr_html: nn
#+begin_quote
#+end_quote
#+NAME: A
#+CAPTION: B *LOREM* IPSUM
#+RESULTS: C
#+HEADERS: D
#+HEADER: E
#+LABEL: F
#+PLOT: G
#+RESNAME: H
#+RESULT: I
#+SOURCE: J
#+SRCNAME: K
#+TBLNAME: L
#+ATTR_LATEX: M
#+ATTR_HTML: N
#+NAME: AA
#+CAPTION: BB *LOREM* IPSUM
#+RESULTS: CC
#+HEADERS: DD
#+HEADER: EE
#+LABEL: FF
#+PLOT: GG
#+RESNAME: HH
#+RESULT: II
#+SOURCE: JJ
#+SRCNAME: KK
#+TBLNAME: LL
#+ATTR_LATEX: MM
#+ATTR_HTML: NN
#+begin_quote
#+end_quote

View File

@@ -1,4 +0,0 @@
#+NAME: foo
#+begin_quote
#+end_quote

View File

@@ -0,0 +1,71 @@
#+NAME: a
#+caption: b *lorem* ipsum
#+results: c
#+headers: d
#+header: e
#+label: f
#+plot: g
#+resname: h
#+result: i
#+source: j
#+srcname: k
#+tblname: l
#+attr_latex: m
#+attr_html: n
#+NAME: aa
#+caption: bb *lorem* ipsum
#+results: cc
#+headers: dd
#+header: ee
#+label: ff
#+plot: gg
#+resname: hh
#+result: ii
#+source: jj
#+srcname: kk
#+tblname: ll
#+attr_latex: mm
#+attr_html: nn
#+begin_defun
foo
{{{bar(baz)}}}
#+end_defun
#+NAME: A
#+CAPTION: B *LOREM* IPSUM
#+RESULTS: C
#+HEADERS: D
#+HEADER: E
#+LABEL: F
#+PLOT: G
#+RESNAME: H
#+RESULT: I
#+SOURCE: J
#+SRCNAME: K
#+TBLNAME: L
#+ATTR_LATEX: M
#+ATTR_HTML: N
#+NAME: AA
#+CAPTION: BB *LOREM* IPSUM
#+RESULTS: CC
#+HEADERS: DD
#+HEADER: EE
#+LABEL: FF
#+PLOT: GG
#+RESNAME: HH
#+RESULT: II
#+SOURCE: JJ
#+SRCNAME: KK
#+TBLNAME: LL
#+ATTR_LATEX: MM
#+ATTR_HTML: NN
#+begin_defun
foo
{{{bar(baz)}}}
#+end_defun

View File

@@ -1,6 +0,0 @@
#+NAME: foo
#+begin_defun
foo
{{{bar(baz)}}}
#+end_defun

View File

@@ -0,0 +1,63 @@
#+NAME: a
#+caption: b *lorem* ipsum
#+results: c
#+headers: d
#+header: e
#+label: f
#+plot: g
#+resname: h
#+result: i
#+source: j
#+srcname: k
#+tblname: l
#+attr_latex: m
#+attr_html: n
#+NAME: aa
#+caption: bb *lorem* ipsum
#+results: cc
#+headers: dd
#+header: ee
#+label: ff
#+plot: gg
#+resname: hh
#+result: ii
#+source: jj
#+srcname: kk
#+tblname: ll
#+attr_latex: mm
#+attr_html: nn
1. bar
#+NAME: A
#+CAPTION: B *LOREM* IPSUM
#+RESULTS: C
#+HEADERS: D
#+HEADER: E
#+LABEL: F
#+PLOT: G
#+RESNAME: H
#+RESULT: I
#+SOURCE: J
#+SRCNAME: K
#+TBLNAME: L
#+ATTR_LATEX: M
#+ATTR_HTML: N
#+NAME: AA
#+CAPTION: BB *LOREM* IPSUM
#+RESULTS: CC
#+HEADERS: DD
#+HEADER: EE
#+LABEL: FF
#+PLOT: GG
#+RESNAME: HH
#+RESULT: II
#+SOURCE: JJ
#+SRCNAME: KK
#+TBLNAME: LL
#+ATTR_LATEX: MM
#+ATTR_HTML: NN
1. BAR

View File

@@ -0,0 +1,3 @@
- [ ] Foo
- [-] Bar
- [X] Baz

View File

@@ -0,0 +1,3 @@
1. foo
#+NAME: bar
2. baz

View File

@@ -1,2 +0,0 @@
#+NAME: foo
1. bar

View File

@@ -0,0 +1,2 @@
# This test causes problems with regular links if we do not create a new ContextTree when calling into confine_context.
- foo [[info:bar][baz]] lorem

View File

@@ -0,0 +1,5 @@
* baz
# lorem
:PROPERTIES:
:FOO: bar
:END:

View File

@@ -0,0 +1,6 @@
* baz
# lorem
:PROPERTIES:
:FOO: bar
:END:

View File

@@ -0,0 +1,4 @@
* baz
:PROPERTIES:
:FOO: bar
:END:

View File

@@ -0,0 +1,5 @@
* baz
:PROPERTIES:
:FOO: bar
:END:

View File

@@ -0,0 +1,13 @@
* baz
# lorem
:PROPERTIES:
:FOO: bar
:END:

View File

@@ -0,0 +1,19 @@
:PROPERTIES:
:foo: bar
:foo+: baz
:cat: dog
:END:
# Even though these are inheriting the properties and overwriting and/or appending to them, this is not represented in the parser AST so Organic does not do any special handling of this.
* Overwrite
:PROPERTIES:
:foo: lorem
:bat: car
:END:
* Append
:PROPERTIES:
:foo+: ipsum
:cake: lie
:END:

View File

@@ -0,0 +1,5 @@
* Overwrite
#+NAME: foo
:PROPERTIES:
:header-args: :var foo="lorem"
:END:

View File

@@ -1,25 +0,0 @@
# Blank lines and comments can come before property drawers in the zeroth section
:PROPERTIES:
:FOO: bar
:END:
* Spaces turn property drawers into regular drawers
:PROPERTIES:
:FOO: bar
:END:
* Comments turn property drawers into regular drawers
# Comment
:PROPERTIES:
:FOO: bar
:END:
* Baseline
:PROPERTIES:
:FOO: bar
:END:

View File

@@ -0,0 +1,4 @@
# lorem
:PROPERTIES:
:FOO: bar
:END:

View File

@@ -0,0 +1,5 @@
# lorem
:PROPERTIES:
:FOO: bar
:END:

View File

@@ -0,0 +1,3 @@
:PROPERTIES:
:FOO: bar
:END:

View File

@@ -0,0 +1,4 @@
:PROPERTIES:
:FOO: bar
:END:

View File

@@ -0,0 +1,12 @@
# lorem
:PROPERTIES:
:FOO: bar
:END:

View File

@@ -1,12 +0,0 @@
# Blank lines and comments can come before property drawers in the zeroth section
:PROPERTIES:
:FOO: bar
:END:

View File

@@ -0,0 +1,63 @@
#+NAME: a
#+caption: b *lorem* ipsum
#+results: c
#+headers: d
#+header: e
#+label: f
#+plot: g
#+resname: h
#+result: i
#+source: j
#+srcname: k
#+tblname: l
#+attr_latex: m
#+attr_html: n
#+NAME: aa
#+caption: bb *lorem* ipsum
#+results: cc
#+headers: dd
#+header: ee
#+label: ff
#+plot: gg
#+resname: hh
#+result: ii
#+source: jj
#+srcname: kk
#+tblname: ll
#+attr_latex: mm
#+attr_html: nn
| foo | bar |
#+NAME: A
#+CAPTION: B *LOREM* IPSUM
#+RESULTS: C
#+HEADERS: D
#+HEADER: E
#+LABEL: F
#+PLOT: G
#+RESNAME: H
#+RESULT: I
#+SOURCE: J
#+SRCNAME: K
#+TBLNAME: L
#+ATTR_LATEX: M
#+ATTR_HTML: N
#+NAME: AA
#+CAPTION: BB *LOREM* IPSUM
#+RESULTS: CC
#+HEADERS: DD
#+HEADER: EE
#+LABEL: FF
#+PLOT: GG
#+RESNAME: HH
#+RESULT: II
#+SOURCE: JJ
#+SRCNAME: KK
#+TBLNAME: LL
#+ATTR_LATEX: MM
#+ATTR_HTML: NN
| foo | bar |

View File

@@ -1,2 +0,0 @@
#+NAME: foo
| foo | bar |

View File

@@ -0,0 +1,3 @@
|foo|
#+NAME: bar
|baz |

View File

@@ -0,0 +1,63 @@
#+NAME: a
#+caption: b *lorem* ipsum
#+results: c
#+headers: d
#+header: e
#+label: f
#+plot: g
#+resname: h
#+result: i
#+source: j
#+srcname: k
#+tblname: l
#+attr_latex: m
#+attr_html: n
#+NAME: aa
#+caption: bb *lorem* ipsum
#+results: cc
#+headers: dd
#+header: ee
#+label: ff
#+plot: gg
#+resname: hh
#+result: ii
#+source: jj
#+srcname: kk
#+tblname: ll
#+attr_latex: mm
#+attr_html: nn
#+call: foo(bar="baz")
#+NAME: A
#+CAPTION: B *LOREM* IPSUM
#+RESULTS: C
#+HEADERS: D
#+HEADER: E
#+LABEL: F
#+PLOT: G
#+RESNAME: H
#+RESULT: I
#+SOURCE: J
#+SRCNAME: K
#+TBLNAME: L
#+ATTR_LATEX: M
#+ATTR_HTML: N
#+NAME: AA
#+CAPTION: BB *LOREM* IPSUM
#+RESULTS: CC
#+HEADERS: DD
#+HEADER: EE
#+LABEL: FF
#+PLOT: GG
#+RESNAME: HH
#+RESULT: II
#+SOURCE: JJ
#+SRCNAME: KK
#+TBLNAME: LL
#+ATTR_LATEX: MM
#+ATTR_HTML: NN
#+call: foo(bar="baz")

View File

@@ -1,2 +0,0 @@
#+NAME: foo
#+call: foo(bar="baz")

View File

@@ -0,0 +1,63 @@
#+NAME: a
#+caption: b *lorem* ipsum
#+results: c
#+headers: d
#+header: e
#+label: f
#+plot: g
#+resname: h
#+result: i
#+source: j
#+srcname: k
#+tblname: l
#+attr_latex: m
#+attr_html: n
#+NAME: aa
#+caption: bb *lorem* ipsum
#+results: cc
#+headers: dd
#+header: ee
#+label: ff
#+plot: gg
#+resname: hh
#+result: ii
#+source: jj
#+srcname: kk
#+tblname: ll
#+attr_latex: mm
#+attr_html: nn
%%(foo)
#+NAME: A
#+CAPTION: B *LOREM* IPSUM
#+RESULTS: C
#+HEADERS: D
#+HEADER: E
#+LABEL: F
#+PLOT: G
#+RESNAME: H
#+RESULT: I
#+SOURCE: J
#+SRCNAME: K
#+TBLNAME: L
#+ATTR_LATEX: M
#+ATTR_HTML: N
#+NAME: AA
#+CAPTION: BB *LOREM* IPSUM
#+RESULTS: CC
#+HEADERS: DD
#+HEADER: EE
#+LABEL: FF
#+PLOT: GG
#+RESNAME: HH
#+RESULT: II
#+SOURCE: JJ
#+SRCNAME: KK
#+TBLNAME: LL
#+ATTR_LATEX: MM
#+ATTR_HTML: NN
%%(foo)

View File

@@ -1,2 +0,0 @@
#+NAME: foo
%%(foo)

View File

@@ -0,0 +1,63 @@
#+NAME: a
#+caption: b *lorem* ipsum
#+results: c
#+headers: d
#+header: e
#+label: f
#+plot: g
#+resname: h
#+result: i
#+source: j
#+srcname: k
#+tblname: l
#+attr_latex: m
#+attr_html: n
#+NAME: aa
#+caption: bb *lorem* ipsum
#+results: cc
#+headers: dd
#+header: ee
#+label: ff
#+plot: gg
#+resname: hh
#+result: ii
#+source: jj
#+srcname: kk
#+tblname: ll
#+attr_latex: mm
#+attr_html: nn
: bar
#+NAME: A
#+CAPTION: B *LOREM* IPSUM
#+RESULTS: C
#+HEADERS: D
#+HEADER: E
#+LABEL: F
#+PLOT: G
#+RESNAME: H
#+RESULT: I
#+SOURCE: J
#+SRCNAME: K
#+TBLNAME: L
#+ATTR_LATEX: M
#+ATTR_HTML: N
#+NAME: AA
#+CAPTION: BB *LOREM* IPSUM
#+RESULTS: CC
#+HEADERS: DD
#+HEADER: EE
#+LABEL: FF
#+PLOT: GG
#+RESNAME: HH
#+RESULT: II
#+SOURCE: JJ
#+SRCNAME: KK
#+TBLNAME: LL
#+ATTR_LATEX: MM
#+ATTR_HTML: NN
: bar

View File

@@ -1,2 +0,0 @@
#+NAME: foo
: bar

View File

@@ -0,0 +1,63 @@
#+NAME: a
#+caption: b *lorem* ipsum
#+results: c
#+headers: d
#+header: e
#+label: f
#+plot: g
#+resname: h
#+result: i
#+source: j
#+srcname: k
#+tblname: l
#+attr_latex: m
#+attr_html: n
#+NAME: aa
#+caption: bb *lorem* ipsum
#+results: cc
#+headers: dd
#+header: ee
#+label: ff
#+plot: gg
#+resname: hh
#+result: ii
#+source: jj
#+srcname: kk
#+tblname: ll
#+attr_latex: mm
#+attr_html: nn
-----
#+NAME: A
#+CAPTION: B *LOREM* IPSUM
#+RESULTS: C
#+HEADERS: D
#+HEADER: E
#+LABEL: F
#+PLOT: G
#+RESNAME: H
#+RESULT: I
#+SOURCE: J
#+SRCNAME: K
#+TBLNAME: L
#+ATTR_LATEX: M
#+ATTR_HTML: N
#+NAME: AA
#+CAPTION: BB *LOREM* IPSUM
#+RESULTS: CC
#+HEADERS: DD
#+HEADER: EE
#+LABEL: FF
#+PLOT: GG
#+RESNAME: HH
#+RESULT: II
#+SOURCE: JJ
#+SRCNAME: KK
#+TBLNAME: LL
#+ATTR_LATEX: MM
#+ATTR_HTML: NN
-----

View File

@@ -1,2 +0,0 @@
#+NAME: foo
-----

View File

@@ -1,8 +1,63 @@
#+name: foo #+NAME: a
#+caption: bar #+caption: b *lorem* ipsum
#+caption: baz #+results: c
[[file:lorem/ipsum.png]] #+headers: d
#+header: e
#+label: f
#+plot: g
#+resname: h
#+result: i
#+source: j
#+srcname: k
#+tblname: l
#+attr_latex: m
#+attr_html: n
#+NAME: aa
#+caption: bb *lorem* ipsum
#+results: cc
#+headers: dd
#+header: ee
#+label: ff
#+plot: gg
#+resname: hh
#+result: ii
#+source: jj
#+srcname: kk
#+tblname: ll
#+attr_latex: mm
#+attr_html: nn
#+foo: bar
#+name: cat
#+caption: dog
[[file:lorem/ipsum.png]]
#+NAME: A
#+CAPTION: B *LOREM* IPSUM
#+RESULTS: C
#+HEADERS: D
#+HEADER: E
#+LABEL: F
#+PLOT: G
#+RESNAME: H
#+RESULT: I
#+SOURCE: J
#+SRCNAME: K
#+TBLNAME: L
#+ATTR_LATEX: M
#+ATTR_HTML: N
#+NAME: AA
#+CAPTION: BB *LOREM* IPSUM
#+RESULTS: CC
#+HEADERS: DD
#+HEADER: EE
#+LABEL: FF
#+PLOT: GG
#+RESNAME: HH
#+RESULT: II
#+SOURCE: JJ
#+SRCNAME: KK
#+TBLNAME: LL
#+ATTR_LATEX: MM
#+ATTR_HTML: NN
#+foo: bar

View File

@@ -1,7 +0,0 @@
# This test is to prove that the parser works with affiliated keywords that have both a shorter and longer version.
#+results:
#+result:
#+begin_latex
\foo
#+end_latex

View File

@@ -1,2 +0,0 @@
#+NAME: foo
#+FOO: BAR

View File

@@ -0,0 +1,67 @@
#+NAME: a
#+caption: b *lorem* ipsum
#+results: c
#+headers: d
#+header: e
#+label: f
#+plot: g
#+resname: h
#+result: i
#+source: j
#+srcname: k
#+tblname: l
#+attr_latex: m
#+attr_html: n
#+NAME: aa
#+caption: bb *lorem* ipsum
#+results: cc
#+headers: dd
#+header: ee
#+label: ff
#+plot: gg
#+resname: hh
#+result: ii
#+source: jj
#+srcname: kk
#+tblname: ll
#+attr_latex: mm
#+attr_html: nn
\begin{foo}
bar
\end{foo}
#+NAME: A
#+CAPTION: B *LOREM* IPSUM
#+RESULTS: C
#+HEADERS: D
#+HEADER: E
#+LABEL: F
#+PLOT: G
#+RESNAME: H
#+RESULT: I
#+SOURCE: J
#+SRCNAME: K
#+TBLNAME: L
#+ATTR_LATEX: M
#+ATTR_HTML: N
#+NAME: AA
#+CAPTION: BB *LOREM* IPSUM
#+RESULTS: CC
#+HEADERS: DD
#+HEADER: EE
#+LABEL: FF
#+PLOT: GG
#+RESNAME: HH
#+RESULT: II
#+SOURCE: JJ
#+SRCNAME: KK
#+TBLNAME: LL
#+ATTR_LATEX: MM
#+ATTR_HTML: NN
\begin{foo}
bar
\end{foo}

View File

@@ -1,4 +0,0 @@
#+NAME: foo
\begin{foo}
bar
\end{foo}

View File

@@ -0,0 +1,67 @@
#+NAME: a
#+caption: b *lorem* ipsum
#+results: c
#+headers: d
#+header: e
#+label: f
#+plot: g
#+resname: h
#+result: i
#+source: j
#+srcname: k
#+tblname: l
#+attr_latex: m
#+attr_html: n
#+NAME: aa
#+caption: bb *lorem* ipsum
#+results: cc
#+headers: dd
#+header: ee
#+label: ff
#+plot: gg
#+resname: hh
#+result: ii
#+source: jj
#+srcname: kk
#+tblname: ll
#+attr_latex: mm
#+attr_html: nn
#+begin_comment text
bar
#+end_comment
#+NAME: A
#+CAPTION: B *LOREM* IPSUM
#+RESULTS: C
#+HEADERS: D
#+HEADER: E
#+LABEL: F
#+PLOT: G
#+RESNAME: H
#+RESULT: I
#+SOURCE: J
#+SRCNAME: K
#+TBLNAME: L
#+ATTR_LATEX: M
#+ATTR_HTML: N
#+NAME: AA
#+CAPTION: BB *LOREM* IPSUM
#+RESULTS: CC
#+HEADERS: DD
#+HEADER: EE
#+LABEL: FF
#+PLOT: GG
#+RESNAME: HH
#+RESULT: II
#+SOURCE: JJ
#+SRCNAME: KK
#+TBLNAME: LL
#+ATTR_LATEX: MM
#+ATTR_HTML: NN
#+begin_comment text
bar
#+end_comment

View File

@@ -1,4 +0,0 @@
#+NAME: foo
#+begin_comment text
bar
#+end_comment

View File

@@ -0,0 +1,67 @@
#+NAME: a
#+caption: b *lorem* ipsum
#+results: c
#+headers: d
#+header: e
#+label: f
#+plot: g
#+resname: h
#+result: i
#+source: j
#+srcname: k
#+tblname: l
#+attr_latex: m
#+attr_html: n
#+NAME: aa
#+caption: bb *lorem* ipsum
#+results: cc
#+headers: dd
#+header: ee
#+label: ff
#+plot: gg
#+resname: hh
#+result: ii
#+source: jj
#+srcname: kk
#+tblname: ll
#+attr_latex: mm
#+attr_html: nn
#+begin_example text
bar
#+end_example
#+NAME: A
#+CAPTION: B *LOREM* IPSUM
#+RESULTS: C
#+HEADERS: D
#+HEADER: E
#+LABEL: F
#+PLOT: G
#+RESNAME: H
#+RESULT: I
#+SOURCE: J
#+SRCNAME: K
#+TBLNAME: L
#+ATTR_LATEX: M
#+ATTR_HTML: N
#+NAME: AA
#+CAPTION: BB *LOREM* IPSUM
#+RESULTS: CC
#+HEADERS: DD
#+HEADER: EE
#+LABEL: FF
#+PLOT: GG
#+RESNAME: HH
#+RESULT: II
#+SOURCE: JJ
#+SRCNAME: KK
#+TBLNAME: LL
#+ATTR_LATEX: MM
#+ATTR_HTML: NN
#+begin_example text
bar
#+end_example

View File

@@ -1,4 +0,0 @@
#+NAME: foo
#+begin_example text
bar
#+end_example

View File

@@ -0,0 +1,67 @@
#+NAME: a
#+caption: b *lorem* ipsum
#+results: c
#+headers: d
#+header: e
#+label: f
#+plot: g
#+resname: h
#+result: i
#+source: j
#+srcname: k
#+tblname: l
#+attr_latex: m
#+attr_html: n
#+NAME: aa
#+caption: bb *lorem* ipsum
#+results: cc
#+headers: dd
#+header: ee
#+label: ff
#+plot: gg
#+resname: hh
#+result: ii
#+source: jj
#+srcname: kk
#+tblname: ll
#+attr_latex: mm
#+attr_html: nn
#+begin_export text
bar
#+end_export
#+NAME: A
#+CAPTION: B *LOREM* IPSUM
#+RESULTS: C
#+HEADERS: D
#+HEADER: E
#+LABEL: F
#+PLOT: G
#+RESNAME: H
#+RESULT: I
#+SOURCE: J
#+SRCNAME: K
#+TBLNAME: L
#+ATTR_LATEX: M
#+ATTR_HTML: N
#+NAME: AA
#+CAPTION: BB *LOREM* IPSUM
#+RESULTS: CC
#+HEADERS: DD
#+HEADER: EE
#+LABEL: FF
#+PLOT: GG
#+RESNAME: HH
#+RESULT: II
#+SOURCE: JJ
#+SRCNAME: KK
#+TBLNAME: LL
#+ATTR_LATEX: MM
#+ATTR_HTML: NN
#+begin_export text
bar
#+end_export

View File

@@ -1,4 +0,0 @@
#+NAME: foo
#+begin_export text
bar
#+end_export

View File

@@ -0,0 +1,67 @@
#+NAME: a
#+caption: b *lorem* ipsum
#+results: c
#+headers: d
#+header: e
#+label: f
#+plot: g
#+resname: h
#+result: i
#+source: j
#+srcname: k
#+tblname: l
#+attr_latex: m
#+attr_html: n
#+NAME: aa
#+caption: bb *lorem* ipsum
#+results: cc
#+headers: dd
#+header: ee
#+label: ff
#+plot: gg
#+resname: hh
#+result: ii
#+source: jj
#+srcname: kk
#+tblname: ll
#+attr_latex: mm
#+attr_html: nn
#+begin_src text
bar
#+end_src
#+NAME: A
#+CAPTION: B *LOREM* IPSUM
#+RESULTS: C
#+HEADERS: D
#+HEADER: E
#+LABEL: F
#+PLOT: G
#+RESNAME: H
#+RESULT: I
#+SOURCE: J
#+SRCNAME: K
#+TBLNAME: L
#+ATTR_LATEX: M
#+ATTR_HTML: N
#+NAME: AA
#+CAPTION: BB *LOREM* IPSUM
#+RESULTS: CC
#+HEADERS: DD
#+HEADER: EE
#+LABEL: FF
#+PLOT: GG
#+RESNAME: HH
#+RESULT: II
#+SOURCE: JJ
#+SRCNAME: KK
#+TBLNAME: LL
#+ATTR_LATEX: MM
#+ATTR_HTML: NN
#+begin_src text
bar
#+end_src

View File

@@ -1,4 +0,0 @@
#+NAME: foo
#+begin_src text
bar
#+end_src

View File

@@ -0,0 +1,67 @@
#+NAME: a
#+caption: b *lorem* ipsum
#+results: c
#+headers: d
#+header: e
#+label: f
#+plot: g
#+resname: h
#+result: i
#+source: j
#+srcname: k
#+tblname: l
#+attr_latex: m
#+attr_html: n
#+NAME: aa
#+caption: bb *lorem* ipsum
#+results: cc
#+headers: dd
#+header: ee
#+label: ff
#+plot: gg
#+resname: hh
#+result: ii
#+source: jj
#+srcname: kk
#+tblname: ll
#+attr_latex: mm
#+attr_html: nn
#+begin_verse text
bar
#+end_verse
#+NAME: A
#+CAPTION: B *LOREM* IPSUM
#+RESULTS: C
#+HEADERS: D
#+HEADER: E
#+LABEL: F
#+PLOT: G
#+RESNAME: H
#+RESULT: I
#+SOURCE: J
#+SRCNAME: K
#+TBLNAME: L
#+ATTR_LATEX: M
#+ATTR_HTML: N
#+NAME: AA
#+CAPTION: BB *LOREM* IPSUM
#+RESULTS: CC
#+HEADERS: DD
#+HEADER: EE
#+LABEL: FF
#+PLOT: GG
#+RESNAME: HH
#+RESULT: II
#+SOURCE: JJ
#+SRCNAME: KK
#+TBLNAME: LL
#+ATTR_LATEX: MM
#+ATTR_HTML: NN
#+begin_verse text
bar
#+end_verse

View File

@@ -1,4 +0,0 @@
#+NAME: foo
#+begin_verse text
bar
#+end_verse

View File

@@ -0,0 +1,63 @@
#+NAME: a
#+caption: b *lorem* ipsum
#+results: c
#+headers: d
#+header: e
#+label: f
#+plot: g
#+resname: h
#+result: i
#+source: j
#+srcname: k
#+tblname: l
#+attr_latex: m
#+attr_html: n
#+NAME: aa
#+caption: bb *lorem* ipsum
#+results: cc
#+headers: dd
#+header: ee
#+label: ff
#+plot: gg
#+resname: hh
#+result: ii
#+source: jj
#+srcname: kk
#+tblname: ll
#+attr_latex: mm
#+attr_html: nn
bar
#+NAME: A
#+CAPTION: B *LOREM* IPSUM
#+RESULTS: C
#+HEADERS: D
#+HEADER: E
#+LABEL: F
#+PLOT: G
#+RESNAME: H
#+RESULT: I
#+SOURCE: J
#+SRCNAME: K
#+TBLNAME: L
#+ATTR_LATEX: M
#+ATTR_HTML: N
#+NAME: AA
#+CAPTION: BB *LOREM* IPSUM
#+RESULTS: CC
#+HEADERS: DD
#+HEADER: EE
#+LABEL: FF
#+PLOT: GG
#+RESNAME: HH
#+RESULT: II
#+SOURCE: JJ
#+SRCNAME: KK
#+TBLNAME: LL
#+ATTR_LATEX: MM
#+ATTR_HTML: NN
bar

View File

@@ -1,2 +0,0 @@
#+NAME: foo
bar

View File

@@ -0,0 +1,414 @@
\Agrave{}
\agrave{}
\Aacute{}
\aacute{}
\Acirc{}
\acirc{}
\Amacr{}
\amacr{}
\Atilde{}
\atilde{}
\Auml{}
\auml{}
\Aring{}
\AA{}
\aring{}
\AElig{}
\aelig{}
\Ccedil{}
\ccedil{}
\Egrave{}
\egrave{}
\Eacute{}
\eacute{}
\Ecirc{}
\ecirc{}
\Euml{}
\euml{}
\Igrave{}
\igrave{}
\Iacute{}
\iacute{}
\Idot{}
\inodot{}
\Icirc{}
\icirc{}
\Iuml{}
\iuml{}
\Ntilde{}
\ntilde{}
\Ograve{}
\ograve{}
\Oacute{}
\oacute{}
\Ocirc{}
\ocirc{}
\Otilde{}
\otilde{}
\Ouml{}
\ouml{}
\Oslash{}
\oslash{}
\OElig{}
\oelig{}
\Scaron{}
\scaron{}
\szlig{}
\Ugrave{}
\ugrave{}
\Uacute{}
\uacute{}
\Ucirc{}
\ucirc{}
\Uuml{}
\uuml{}
\Yacute{}
\yacute{}
\Yuml{}
\yuml{}
\fnof{}
\real{}
\image{}
\weierp{}
\ell{}
\imath{}
\jmath{}
\Alpha{}
\alpha{}
\Beta{}
\beta{}
\Gamma{}
\gamma{}
\Delta{}
\delta{}
\Epsilon{}
\epsilon{}
\varepsilon{}
\Zeta{}
\zeta{}
\Eta{}
\eta{}
\Theta{}
\theta{}
\thetasym{}
\vartheta{}
\Iota{}
\iota{}
\Kappa{}
\kappa{}
\Lambda{}
\lambda{}
\Mu{}
\mu{}
\nu{}
\Nu{}
\Xi{}
\xi{}
\Omicron{}
\omicron{}
\Pi{}
\pi{}
\Rho{}
\rho{}
\Sigma{}
\sigma{}
\sigmaf{}
\varsigma{}
\Tau{}
\Upsilon{}
\upsih{}
\upsilon{}
\Phi{}
\phi{}
\varphi{}
\Chi{}
\chi{}
\acutex{}
\Psi{}
\psi{}
\tau{}
\Omega{}
\omega{}
\piv{}
\varpi{}
\partial{}
\alefsym{}
\aleph{}
\gimel{}
\beth{}
\dalet{}
\ETH{}
\eth{}
\THORN{}
\thorn{}
\dots{}
\cdots{}
\hellip{}
\middot{}
\iexcl{}
\iquest{}
\shy{}
\ndash{}
\mdash{}
\quot{}
\acute{}
\ldquo{}
\rdquo{}
\bdquo{}
\lsquo{}
\rsquo{}
\sbquo{}
\laquo{}
\raquo{}
\lsaquo{}
\rsaquo{}
\circ{}
\vert{}
\vbar{}
\brvbar{}
\S{}
\sect{}
\P{}
\para{}
\amp{}
\lt{}
\gt{}
\tilde{}
\slash{}
\plus{}
\under{}
\equal{}
\asciicirc{}
\dagger{}
\dag{}
\Dagger{}
\ddag{}
\nbsp{}
\ensp{}
\emsp{}
\thinsp{}
\curren{}
\cent{}
\pound{}
\yen{}
\euro{}
\EUR{}
\dollar{}
\USD{}
\copy{}
\reg{}
\trade{}
\minus{}
\pm{}
\plusmn{}
\times{}
\frasl{}
\colon{}
\div{}
\frac12{}
\frac14{}
\frac34{}
\permil{}
\sup1{}
\sup2{}
\sup3{}
\radic{}
\sum{}
\prod{}
\micro{}
\macr{}
\deg{}
\prime{}
\Prime{}
\infin{}
\infty{}
\prop{}
\propto{}
\not{}
\neg{}
\land{}
\wedge{}
\lor{}
\vee{}
\cap{}
\cup{}
\smile{}
\frown{}
\int{}
\therefore{}
\there4{}
\because{}
\sim{}
\cong{}
\simeq{}
\asymp{}
\approx{}
\ne{}
\neq{}
\equiv{}
\triangleq{}
\le{}
\leq{}
\ge{}
\geq{}
\lessgtr{}
\lesseqgtr{}
\ll{}
\Ll{}
\lll{}
\gg{}
\Gg{}
\ggg{}
\prec{}
\preceq{}
\preccurlyeq{}
\succ{}
\succeq{}
\succcurlyeq{}
\sub{}
\subset{}
\sup{}
\supset{}
\nsub{}
\sube{}
\nsup{}
\supe{}
\setminus{}
\forall{}
\exist{}
\exists{}
\nexist{}
\nexists{}
\empty{}
\emptyset{}
\isin{}
\in{}
\notin{}
\ni{}
\nabla{}
\ang{}
\angle{}
\perp{}
\parallel{}
\sdot{}
\cdot{}
\lceil{}
\rceil{}
\lfloor{}
\rfloor{}
\lang{}
\rang{}
\langle{}
\rangle{}
\hbar{}
\mho{}
\larr{}
\leftarrow{}
\gets{}
\lArr{}
\Leftarrow{}
\uarr{}
\uparrow{}
\uArr{}
\Uparrow{}
\rarr{}
\to{}
\rightarrow{}
\rArr{}
\Rightarrow{}
\darr{}
\downarrow{}
\dArr{}
\Downarrow{}
\harr{}
\leftrightarrow{}
\hArr{}
\Leftrightarrow{}
\crarr{}
\hookleftarrow{}
\arccos{}
\arcsin{}
\arctan{}
\arg{}
\cos{}
\cosh{}
\cot{}
\coth{}
\csc{}
\deg{}
\det{}
\dim{}
\exp{}
\gcd{}
\hom{}
\inf{}
\ker{}
\lg{}
\lim{}
\liminf{}
\limsup{}
\ln{}
\log{}
\max{}
\min{}
\Pr{}
\sec{}
\sin{}
\sinh{}
\sup{}
\tan{}
\tanh{}
\bull{}
\bullet{}
\star{}
\lowast{}
\ast{}
\odot{}
\oplus{}
\otimes{}
\check{}
\checkmark{}
\ordf{}
\ordm{}
\cedil{}
\oline{}
\uml{}
\zwnj{}
\zwj{}
\lrm{}
\rlm{}
\smiley{}
\blacksmile{}
\sad{}
\frowny{}
\clubs{}
\clubsuit{}
\spades{}
\spadesuit{}
\hearts{}
\heartsuit{}
\diams{}
\diamondsuit{}
\diamond{}
\Diamond{}
\loz{}
\_ {}
\_ {}
\_ {}
\_ {}
\_ {}
\_ {}
\_ {}
\_ {}
\_ {}
\_ {}
\_ {}
\_ {}
\_ {}
\_ {}
\_ {}
\_ {}
\_ {}
\_ {}
\_ {}
\_ {}

View File

@@ -0,0 +1,414 @@
\Agrave
\agrave
\Aacute
\aacute
\Acirc
\acirc
\Amacr
\amacr
\Atilde
\atilde
\Auml
\auml
\Aring
\AA
\aring
\AElig
\aelig
\Ccedil
\ccedil
\Egrave
\egrave
\Eacute
\eacute
\Ecirc
\ecirc
\Euml
\euml
\Igrave
\igrave
\Iacute
\iacute
\Idot
\inodot
\Icirc
\icirc
\Iuml
\iuml
\Ntilde
\ntilde
\Ograve
\ograve
\Oacute
\oacute
\Ocirc
\ocirc
\Otilde
\otilde
\Ouml
\ouml
\Oslash
\oslash
\OElig
\oelig
\Scaron
\scaron
\szlig
\Ugrave
\ugrave
\Uacute
\uacute
\Ucirc
\ucirc
\Uuml
\uuml
\Yacute
\yacute
\Yuml
\yuml
\fnof
\real
\image
\weierp
\ell
\imath
\jmath
\Alpha
\alpha
\Beta
\beta
\Gamma
\gamma
\Delta
\delta
\Epsilon
\epsilon
\varepsilon
\Zeta
\zeta
\Eta
\eta
\Theta
\theta
\thetasym
\vartheta
\Iota
\iota
\Kappa
\kappa
\Lambda
\lambda
\Mu
\mu
\nu
\Nu
\Xi
\xi
\Omicron
\omicron
\Pi
\pi
\Rho
\rho
\Sigma
\sigma
\sigmaf
\varsigma
\Tau
\Upsilon
\upsih
\upsilon
\Phi
\phi
\varphi
\Chi
\chi
\acutex
\Psi
\psi
\tau
\Omega
\omega
\piv
\varpi
\partial
\alefsym
\aleph
\gimel
\beth
\dalet
\ETH
\eth
\THORN
\thorn
\dots
\cdots
\hellip
\middot
\iexcl
\iquest
\shy
\ndash
\mdash
\quot
\acute
\ldquo
\rdquo
\bdquo
\lsquo
\rsquo
\sbquo
\laquo
\raquo
\lsaquo
\rsaquo
\circ
\vert
\vbar
\brvbar
\S
\sect
\P
\para
\amp
\lt
\gt
\tilde
\slash
\plus
\under
\equal
\asciicirc
\dagger
\dag
\Dagger
\ddag
\nbsp
\ensp
\emsp
\thinsp
\curren
\cent
\pound
\yen
\euro
\EUR
\dollar
\USD
\copy
\reg
\trade
\minus
\pm
\plusmn
\times
\frasl
\colon
\div
\frac12
\frac14
\frac34
\permil
\sup1
\sup2
\sup3
\radic
\sum
\prod
\micro
\macr
\deg
\prime
\Prime
\infin
\infty
\prop
\propto
\not
\neg
\land
\wedge
\lor
\vee
\cap
\cup
\smile
\frown
\int
\therefore
\there4
\because
\sim
\cong
\simeq
\asymp
\approx
\ne
\neq
\equiv
\triangleq
\le
\leq
\ge
\geq
\lessgtr
\lesseqgtr
\ll
\Ll
\lll
\gg
\Gg
\ggg
\prec
\preceq
\preccurlyeq
\succ
\succeq
\succcurlyeq
\sub
\subset
\sup
\supset
\nsub
\sube
\nsup
\supe
\setminus
\forall
\exist
\exists
\nexist
\nexists
\empty
\emptyset
\isin
\in
\notin
\ni
\nabla
\ang
\angle
\perp
\parallel
\sdot
\cdot
\lceil
\rceil
\lfloor
\rfloor
\lang
\rang
\langle
\rangle
\hbar
\mho
\larr
\leftarrow
\gets
\lArr
\Leftarrow
\uarr
\uparrow
\uArr
\Uparrow
\rarr
\to
\rightarrow
\rArr
\Rightarrow
\darr
\downarrow
\dArr
\Downarrow
\harr
\leftrightarrow
\hArr
\Leftrightarrow
\crarr
\hookleftarrow
\arccos
\arcsin
\arctan
\arg
\cos
\cosh
\cot
\coth
\csc
\deg
\det
\dim
\exp
\gcd
\hom
\inf
\ker
\lg
\lim
\liminf
\limsup
\ln
\log
\max
\min
\Pr
\sec
\sin
\sinh
\sup
\tan
\tanh
\bull
\bullet
\star
\lowast
\ast
\odot
\oplus
\otimes
\check
\checkmark
\ordf
\ordm
\cedil
\oline
\uml
\zwnj
\zwj
\lrm
\rlm
\smiley
\blacksmile
\sad
\frowny
\clubs
\clubsuit
\spades
\spadesuit
\hearts
\heartsuit
\diams
\diamondsuit
\diamond
\Diamond
\loz
\_
\_
\_
\_
\_
\_
\_
\_
\_
\_
\_
\_
\_
\_
\_
\_
\_
\_
\_
\_

View File

@@ -1,3 +1,4 @@
call_foo()
call_foo(arguments) call_foo(arguments)
call_bar[header](arguments) call_bar[header](arguments)
call_baz(arguments)[header] call_baz(arguments)[header]

View File

@@ -1,2 +1,3 @@
before src_foo{ipsum} after src_foo{}
src_bar[lorem]{ipsum} before src_bar{lorem} after
src_baz[ipsum]{dolar}

View File

@@ -0,0 +1 @@
{{{Foo(Bar,Baz)}}}

View File

@@ -0,0 +1,7 @@
{{{foo}}}
{{{fo\o}}}
{{{foo(b\ar)}}}
{{{foo(b\,r)}}}

View File

@@ -0,0 +1,10 @@
{{{foo(bar baz)}}}
{{{foo(bar
baz)}}}
{{{foo(foo )}}}
{{{foo(foo , bar )}}}
{{{foo(foo , bar , baz )}}}

View File

@@ -1 +1,3 @@
**foo**
foo ** bar ** baz foo ** bar ** baz

View File

@@ -0,0 +1 @@
__foo_bar_baz__

View File

@@ -0,0 +1 @@
/*foo*/

View File

@@ -0,0 +1,3 @@
#+NAME: foo
* bar
#+NAME: baz

View File

@@ -14,7 +14,12 @@ mod init_tracing;
#[cfg(not(feature = "tracing"))] #[cfg(not(feature = "tracing"))]
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
main_body() let rt = tokio::runtime::Runtime::new()?;
let result = rt.block_on(async {
let main_body_result = main_body().await;
main_body_result
});
result
} }
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
@@ -22,7 +27,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let rt = tokio::runtime::Runtime::new()?; let rt = tokio::runtime::Runtime::new()?;
let result = rt.block_on(async { let result = rt.block_on(async {
init_telemetry()?; init_telemetry()?;
let main_body_result = main_body(); let main_body_result = main_body().await;
shutdown_telemetry()?; shutdown_telemetry()?;
main_body_result main_body_result
}); });
@@ -30,14 +35,21 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn main_body() -> Result<(), Box<dyn std::error::Error>> { async fn main_body() -> Result<(), Box<dyn std::error::Error>> {
let args = std::env::args().skip(1); let args = std::env::args().skip(1);
if args.is_empty() { if args.is_empty() {
let org_contents = read_stdin_to_string()?; let org_contents = read_stdin_to_string()?;
run_anonymous_compare(org_contents) if run_anonymous_compare(org_contents).await? {
} else {
Err("Diff results do not match.")?;
}
Ok(())
} else { } else {
for arg in args { for arg in args {
run_compare_on_file(arg)? if run_compare_on_file(arg).await? {
} else {
Err("Diff results do not match.")?;
}
} }
Ok(()) Ok(())
} }

View File

@@ -0,0 +1,399 @@
#![feature(round_char_boundary)]
#![feature(exact_size_is_empty)]
use std::path::Path;
use std::path::PathBuf;
use std::process::ExitCode;
use futures::future::BoxFuture;
use futures::future::FutureExt;
use organic::compare::silent_compare_on_file;
use tokio::sync::Semaphore;
use tokio::task::JoinError;
use walkdir::WalkDir;
#[cfg(feature = "tracing")]
use crate::init_tracing::init_telemetry;
#[cfg(feature = "tracing")]
use crate::init_tracing::shutdown_telemetry;
#[cfg(feature = "tracing")]
mod init_tracing;
#[cfg(not(feature = "tracing"))]
fn main() -> Result<ExitCode, Box<dyn std::error::Error>> {
let rt = tokio::runtime::Runtime::new()?;
let result = rt.block_on(async {
let main_body_result = main_body().await;
main_body_result
});
result
}
#[cfg(feature = "tracing")]
fn main() -> Result<ExitCode, Box<dyn std::error::Error>> {
let rt = tokio::runtime::Runtime::new()?;
let result = rt.block_on(async {
init_telemetry()?;
let main_body_result = main_body().await;
shutdown_telemetry()?;
main_body_result
});
result
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
async fn main_body() -> Result<ExitCode, Box<dyn std::error::Error>> {
let layer = compare_group("org-mode", || {
compare_all_org_document("/foreign_documents/org-mode")
});
let layer = layer.chain(compare_group("emacs", || {
compare_all_org_document("/foreign_documents/emacs")
}));
let layer = layer.chain(compare_group("worg", || {
compare_all_org_document("/foreign_documents/worg")
}));
let layer = layer.chain(compare_group("howard_abrams", compare_howard_abrams));
let layer = layer.chain(compare_group("doomemacs", || {
compare_all_org_document("/foreign_documents/doomemacs")
}));
let running_tests: Vec<_> = layer.map(|c| tokio::spawn(c.run_test())).collect();
let mut any_failed = false;
for test in running_tests.into_iter() {
let test_result = test.await??;
if test_result.is_immediately_bad() || test_result.has_bad_children() {
any_failed = true;
}
test_result.print();
}
if any_failed {
println!(
"{color}Some tests failed.{reset}",
color = TestResult::foreground_color(255, 0, 0),
reset = TestResult::reset_color(),
);
Ok(ExitCode::FAILURE)
} else {
println!(
"{color}All tests passed.{reset}",
color = TestResult::foreground_color(0, 255, 0),
reset = TestResult::reset_color(),
);
Ok(ExitCode::SUCCESS)
}
}
fn compare_howard_abrams() -> impl Iterator<Item = TestConfig> {
let layer = compare_group("dot-files", || {
compare_all_org_document("/foreign_documents/howardabrams/dot-files")
});
let layer = layer.chain(compare_group("hamacs", || {
compare_all_org_document("/foreign_documents/howardabrams/hamacs")
}));
let layer = layer.chain(compare_group("demo-it", || {
compare_all_org_document("/foreign_documents/howardabrams/demo-it")
}));
let layer = layer.chain(compare_group("magit-demo", || {
compare_all_org_document("/foreign_documents/howardabrams/magit-demo")
}));
let layer = layer.chain(compare_group("pdx-emacs-hackers", || {
compare_all_org_document("/foreign_documents/howardabrams/pdx-emacs-hackers")
}));
let layer = layer.chain(compare_group("flora-simulator", || {
compare_all_org_document("/foreign_documents/howardabrams/flora-simulator")
}));
let layer = layer.chain(compare_group("literate-devops-demo", || {
compare_all_org_document("/foreign_documents/howardabrams/literate-devops-demo")
}));
let layer = layer.chain(compare_group("clojure-yesql-xp", || {
compare_all_org_document("/foreign_documents/howardabrams/clojure-yesql-xp")
}));
let layer = layer.chain(compare_group("veep", || {
compare_all_org_document("/foreign_documents/howardabrams/veep")
}));
layer
}
fn compare_group<N: Into<String>, F: Fn() -> I, I: Iterator<Item = TestConfig>>(
name: N,
inner: F,
) -> impl Iterator<Item = TestConfig> {
std::iter::once(TestConfig::TestLayer(TestLayer {
name: name.into(),
children: inner().collect(),
}))
}
fn compare_all_org_document<P: AsRef<Path>>(root_dir: P) -> impl Iterator<Item = TestConfig> {
let root_dir = root_dir.as_ref();
let mut test_files = WalkDir::new(root_dir)
.into_iter()
.filter(|e| match e {
Ok(dir_entry) => {
dir_entry.file_type().is_file()
&& Path::new(dir_entry.file_name())
.extension()
.map(|ext| ext.to_ascii_lowercase() == "org")
.unwrap_or(false)
}
Err(_) => true,
})
.collect::<Result<Vec<_>, _>>()
.unwrap();
test_files.sort_by_cached_key(|test_file| PathBuf::from(test_file.path()));
let test_configs: Vec<_> = test_files
.into_iter()
.map(|test_file| {
let name = test_file
.path()
.strip_prefix(root_dir)
.expect("Result is from walkdir so it must be below the root directory.")
.as_os_str()
.to_string_lossy()
.into_owned();
TestConfig::SingleFile(SingleFile {
name,
file_path: test_file.into_path(),
})
})
.collect();
test_configs.into_iter()
}
static TEST_PERMITS: Semaphore = Semaphore::const_new(8);
#[derive(Debug)]
enum TestConfig {
TestLayer(TestLayer),
SingleFile(SingleFile),
}
#[derive(Debug)]
struct TestLayer {
name: String,
children: Vec<TestConfig>,
}
#[derive(Debug)]
struct SingleFile {
name: String,
file_path: PathBuf,
}
#[derive(Debug)]
enum TestResult {
ResultLayer(ResultLayer),
SingleFileResult(SingleFileResult),
}
#[derive(Debug)]
struct ResultLayer {
name: String,
children: Vec<TestResult>,
}
#[derive(Debug)]
struct SingleFileResult {
name: String,
file_path: PathBuf,
status: TestStatus,
}
#[derive(Debug)]
pub(crate) enum TestStatus {
Pass,
Fail,
}
impl TestConfig {
fn run_test(self) -> BoxFuture<'static, Result<TestResult, JoinError>> {
async move {
match self {
TestConfig::TestLayer(test) => Ok(TestResult::ResultLayer(test.run_test().await?)),
TestConfig::SingleFile(test) => {
Ok(TestResult::SingleFileResult(test.run_test().await?))
}
}
}
.boxed()
}
}
impl SingleFile {
async fn run_test(self) -> Result<SingleFileResult, JoinError> {
let _permit = TEST_PERMITS.acquire().await.unwrap();
let result = silent_compare_on_file(&self.file_path).await;
Ok(SingleFileResult {
name: self.name,
file_path: self.file_path,
status: if let Ok(true) = result {
TestStatus::Pass
} else {
TestStatus::Fail
},
})
}
}
impl TestLayer {
async fn run_test(self) -> Result<ResultLayer, JoinError> {
let running_children: Vec<_> = self
.children
.into_iter()
.map(|c| tokio::spawn(c.run_test()))
.collect();
let mut children = Vec::with_capacity(running_children.len());
for c in running_children {
children.push(c.await??);
}
Ok(ResultLayer {
name: self.name,
children,
})
}
}
impl TestResult {
pub fn print(&self) {
self.print_indented(0);
}
fn print_indented(&self, indentation: usize) {
match self {
TestResult::ResultLayer(result) => result.print_indented(indentation),
TestResult::SingleFileResult(result) => result.print_indented(indentation),
}
}
fn has_bad_children(&self) -> bool {
match self {
TestResult::ResultLayer(result) => result.has_bad_children(),
TestResult::SingleFileResult(result) => result.has_bad_children(),
}
}
fn is_immediately_bad(&self) -> bool {
match self {
TestResult::ResultLayer(result) => result.is_immediately_bad(),
TestResult::SingleFileResult(result) => result.is_immediately_bad(),
}
}
pub(crate) fn foreground_color(red: u8, green: u8, blue: u8) -> String {
if TestResult::should_use_color() {
format!(
"\x1b[38;2;{red};{green};{blue}m",
red = red,
green = green,
blue = blue
)
} else {
String::new()
}
}
#[allow(dead_code)]
pub(crate) fn background_color(red: u8, green: u8, blue: u8) -> String {
if TestResult::should_use_color() {
format!(
"\x1b[48;2;{red};{green};{blue}m",
red = red,
green = green,
blue = blue
)
} else {
String::new()
}
}
pub(crate) fn reset_color() -> &'static str {
if TestResult::should_use_color() {
"\x1b[0m"
} else {
""
}
}
fn should_use_color() -> bool {
!std::env::var("NO_COLOR").is_ok_and(|val| !val.is_empty())
}
}
impl SingleFileResult {
fn print_indented(&self, indentation: usize) {
match self.status {
TestStatus::Pass => {
println!(
"{indentation}{color}PASS{reset} {name}",
indentation = " ".repeat(indentation),
color = TestResult::foreground_color(0, 255, 0),
reset = TestResult::reset_color(),
name = self.name
);
}
TestStatus::Fail => {
println!(
"{indentation}{color}FAIL{reset} {name}",
indentation = " ".repeat(indentation),
color = TestResult::foreground_color(255, 0, 0),
reset = TestResult::reset_color(),
name = self.name
);
}
}
}
fn has_bad_children(&self) -> bool {
false
}
fn is_immediately_bad(&self) -> bool {
match self.status {
TestStatus::Pass => false,
TestStatus::Fail => true,
}
}
}
impl ResultLayer {
fn print_indented(&self, indentation: usize) {
if self.is_immediately_bad() {
println!(
"{indentation}{color}FAIL{reset} {name}",
indentation = " ".repeat(indentation),
color = TestResult::foreground_color(255, 0, 0),
reset = TestResult::reset_color(),
name = self.name
);
} else if self.has_bad_children() {
println!(
"{indentation}{color}BADCHILD{reset} {name}",
indentation = " ".repeat(indentation),
color = TestResult::foreground_color(255, 255, 0),
reset = TestResult::reset_color(),
name = self.name
);
} else {
println!(
"{indentation}{color}PASS{reset} {name}",
indentation = " ".repeat(indentation),
color = TestResult::foreground_color(0, 255, 0),
reset = TestResult::reset_color(),
name = self.name
);
}
self.children
.iter()
.for_each(|result| result.print_indented(indentation + 1));
}
fn has_bad_children(&self) -> bool {
self.children
.iter()
.any(|result| result.is_immediately_bad() || result.has_bad_children())
}
fn is_immediately_bad(&self) -> bool {
false
}
}

View File

@@ -12,39 +12,60 @@ use crate::context::LocalFileAccessInterface;
use crate::parser::parse_file_with_settings; use crate::parser::parse_file_with_settings;
use crate::parser::parse_with_settings; use crate::parser::parse_with_settings;
pub fn run_anonymous_compare<P: AsRef<str>>( pub async fn run_anonymous_compare<P: AsRef<str>>(
org_contents: P, org_contents: P,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<bool, Box<dyn std::error::Error>> {
run_anonymous_compare_with_settings(org_contents, &GlobalSettings::default()) run_anonymous_compare_with_settings(org_contents, &GlobalSettings::default(), false).await
} }
pub fn run_compare_on_file<P: AsRef<Path>>(org_path: P) -> Result<(), Box<dyn std::error::Error>> { pub async fn run_compare_on_file<P: AsRef<Path>>(
run_compare_on_file_with_settings(org_path, &GlobalSettings::default()) org_path: P,
) -> Result<bool, Box<dyn std::error::Error>> {
run_compare_on_file_with_settings(org_path, &GlobalSettings::default(), false).await
} }
pub fn run_anonymous_compare_with_settings<P: AsRef<str>>( pub async fn silent_anonymous_compare<P: AsRef<str>>(
org_contents: P, org_contents: P,
global_settings: &GlobalSettings, ) -> Result<bool, Box<dyn std::error::Error>> {
) -> Result<(), Box<dyn std::error::Error>> { run_anonymous_compare_with_settings(org_contents, &GlobalSettings::default(), true).await
}
pub async fn silent_compare_on_file<P: AsRef<Path>>(
org_path: P,
) -> Result<bool, Box<dyn std::error::Error>> {
run_compare_on_file_with_settings(org_path, &GlobalSettings::default(), true).await
}
pub async fn run_anonymous_compare_with_settings<'g, 's, P: AsRef<str>>(
org_contents: P,
global_settings: &GlobalSettings<'g, 's>,
silent: bool,
) -> Result<bool, Box<dyn std::error::Error>> {
// TODO: This is a work-around to pretend that dos line endings do not exist. It would be better to handle the difference in line endings. // TODO: This is a work-around to pretend that dos line endings do not exist. It would be better to handle the difference in line endings.
let org_contents = org_contents.as_ref().replace("\r\n", "\n"); let org_contents = org_contents.as_ref().replace("\r\n", "\n");
let org_contents = org_contents.as_str(); let org_contents = org_contents.as_str();
print_versions()?; if !silent {
print_versions().await?;
}
let rust_parsed = parse_with_settings(org_contents, global_settings)?; let rust_parsed = parse_with_settings(org_contents, global_settings)?;
let org_sexp = emacs_parse_anonymous_org_document(org_contents, global_settings)?; let org_sexp = emacs_parse_anonymous_org_document(org_contents, global_settings).await?;
let (_remaining, parsed_sexp) = sexp(org_sexp.as_str()).map_err(|e| e.to_string())?; let (_remaining, parsed_sexp) = sexp(org_sexp.as_str()).map_err(|e| e.to_string())?;
println!("{}\n\n\n", org_contents); if !silent {
println!("{}", org_sexp); println!("{}\n\n\n", org_contents);
println!("{:#?}", rust_parsed); println!("{}", org_sexp);
println!("{:#?}", rust_parsed);
}
// We do the diffing after printing out both parsed forms in case the diffing panics // We do the diffing after printing out both parsed forms in case the diffing panics
let diff_result = compare_document(&parsed_sexp, &rust_parsed)?; let diff_result = compare_document(&parsed_sexp, &rust_parsed)?;
diff_result.print(org_contents)?; if !silent {
diff_result.print(org_contents)?;
}
if diff_result.is_bad() { if diff_result.is_bad() {
Err("Diff results do not match.")?; return Ok(false);
} else { } else if !silent {
println!( println!(
"{color}Entire document passes.{reset}", "{color}Entire document passes.{reset}",
color = DiffResult::foreground_color(0, 255, 0), color = DiffResult::foreground_color(0, 255, 0),
@@ -52,15 +73,18 @@ pub fn run_anonymous_compare_with_settings<P: AsRef<str>>(
); );
} }
Ok(()) Ok(true)
} }
pub fn run_compare_on_file_with_settings<P: AsRef<Path>>( pub async fn run_compare_on_file_with_settings<'g, 's, P: AsRef<Path>>(
org_path: P, org_path: P,
global_settings: &GlobalSettings, global_settings: &GlobalSettings<'g, 's>,
) -> Result<(), Box<dyn std::error::Error>> { silent: bool,
) -> Result<bool, Box<dyn std::error::Error>> {
let org_path = org_path.as_ref(); let org_path = org_path.as_ref();
print_versions()?; if !silent {
print_versions().await?;
}
let parent_directory = org_path let parent_directory = org_path
.parent() .parent()
.ok_or("Should be contained inside a directory.")?; .ok_or("Should be contained inside a directory.")?;
@@ -77,20 +101,24 @@ pub fn run_compare_on_file_with_settings<P: AsRef<Path>>(
global_settings global_settings
}; };
let rust_parsed = parse_file_with_settings(org_contents, &global_settings, Some(org_path))?; let rust_parsed = parse_file_with_settings(org_contents, &global_settings, Some(org_path))?;
let org_sexp = emacs_parse_file_org_document(org_path, &global_settings)?; let org_sexp = emacs_parse_file_org_document(org_path, &global_settings).await?;
let (_remaining, parsed_sexp) = sexp(org_sexp.as_str()).map_err(|e| e.to_string())?; let (_remaining, parsed_sexp) = sexp(org_sexp.as_str()).map_err(|e| e.to_string())?;
println!("{}\n\n\n", org_contents); if !silent {
println!("{}", org_sexp); println!("{}\n\n\n", org_contents);
println!("{:#?}", rust_parsed); println!("{}", org_sexp);
println!("{:#?}", rust_parsed);
}
// We do the diffing after printing out both parsed forms in case the diffing panics // We do the diffing after printing out both parsed forms in case the diffing panics
let diff_result = compare_document(&parsed_sexp, &rust_parsed)?; let diff_result = compare_document(&parsed_sexp, &rust_parsed)?;
diff_result.print(org_contents)?; if !silent {
diff_result.print(org_contents)?;
}
if diff_result.is_bad() { if diff_result.is_bad() {
Err("Diff results do not match.")?; return Ok(false);
} else { } else if !silent {
println!( println!(
"{color}Entire document passes.{reset}", "{color}Entire document passes.{reset}",
color = DiffResult::foreground_color(0, 255, 0), color = DiffResult::foreground_color(0, 255, 0),
@@ -98,11 +126,14 @@ pub fn run_compare_on_file_with_settings<P: AsRef<Path>>(
); );
} }
Ok(()) Ok(true)
} }
fn print_versions() -> Result<(), Box<dyn std::error::Error>> { async fn print_versions() -> Result<(), Box<dyn std::error::Error>> {
eprintln!("Using emacs version: {}", get_emacs_version()?.trim()); eprintln!("Using emacs version: {}", get_emacs_version().await?.trim());
eprintln!("Using org-mode version: {}", get_org_mode_version()?.trim()); eprintln!(
"Using org-mode version: {}",
get_org_mode_version().await?.trim()
);
Ok(()) Ok(())
} }

View File

@@ -1,35 +1,75 @@
use std::collections::BTreeSet;
use std::fmt::Debug; use std::fmt::Debug;
use std::str::FromStr;
use super::diff::artificial_diff_scope;
use super::diff::artificial_owned_diff_scope;
use super::diff::compare_ast_node;
use super::diff::DiffEntry;
use super::diff::DiffStatus; use super::diff::DiffStatus;
use super::sexp::unquote;
use super::sexp::Token; use super::sexp::Token;
use super::util::get_property; use super::util::get_property;
use super::util::get_property_numeric;
use super::util::get_property_quoted_string; use super::util::get_property_quoted_string;
use super::util::get_property_unquoted_atom; use super::util::get_property_unquoted_atom;
use crate::types::AstNode;
use crate::types::CharOffsetInLine;
use crate::types::LineNumber;
use crate::types::Object;
use crate::types::RetainLabels;
use crate::types::SwitchNumberLines;
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum EmacsField<'s> { pub(crate) enum EmacsField<'s> {
Required(&'s str), Required(&'s str),
#[allow(dead_code)]
Optional(&'s str), Optional(&'s str),
} }
#[derive(Debug)]
pub(crate) enum ComparePropertiesResult<'b, 's> {
NoChange,
/// Return when you want the status for "this" node to change (as opposed to collecting child status).
SelfChange(DiffStatus, Option<String>),
DiffEntry(DiffEntry<'b, 's>),
}
impl<'b, 's> ComparePropertiesResult<'b, 's> {
pub(crate) fn apply(
self,
child_status: &mut Vec<DiffEntry<'b, 's>>,
this_status: &mut DiffStatus,
message: &mut Option<String>,
) {
match self {
ComparePropertiesResult::NoChange => {}
ComparePropertiesResult::SelfChange(new_status, new_message) => {
*this_status = new_status;
*message = new_message
}
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
}
// foo
}
}
/// Do no comparison. /// Do no comparison.
/// ///
/// This is for when you want to acknowledge that a field exists in the emacs token, but you do not have any validation for it when using the compare_properties!() macro. Ideally, this should be kept to a minimum since this represents untested values. /// This is for when you want to acknowledge that a field exists in the emacs token, but you do not have any validation for it when using the compare_properties!() macro. Ideally, this should be kept to a minimum since this represents untested values.
#[allow(dead_code)]
pub(crate) fn compare_noop<'b, 's, 'x, R, RG>( pub(crate) fn compare_noop<'b, 's, 'x, R, RG>(
_source: &'s str,
_emacs: &'b Token<'s>, _emacs: &'b Token<'s>,
_rust_node: R, _rust_node: R,
_emacs_field: &'x str, _emacs_field: &'x str,
_rust_value_getter: RG, _rust_value_getter: RG,
) -> Result<Option<(DiffStatus, Option<String>)>, Box<dyn std::error::Error>> { ) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>> {
Ok(None) Ok(ComparePropertiesResult::NoChange)
} }
/// Do no comparison. /// Do no comparison.
/// ///
/// This is for when you want to acknowledge that a field exists in the emacs token, but you do not have any validation for it when using the compare_properties!() macro. Ideally, this should be kept to a minimum since this represents untested values. /// This is for when you want to acknowledge that a field exists in the emacs token, but you do not have any validation for it when using the compare_properties!() macro. Ideally, this should be kept to a minimum since this represents untested values.
#[allow(dead_code)]
pub(crate) fn compare_identity() -> () { pub(crate) fn compare_identity() -> () {
() ()
} }
@@ -38,11 +78,12 @@ pub(crate) fn compare_identity() -> () {
/// ///
/// This is usually used for fields which, in my testing, are always nil. Using this compare function instead of simply doing a compare_noop will enable us to be alerted when we finally come across an org-mode document that has a value other than nil for the property. /// This is usually used for fields which, in my testing, are always nil. Using this compare function instead of simply doing a compare_noop will enable us to be alerted when we finally come across an org-mode document that has a value other than nil for the property.
pub(crate) fn compare_property_always_nil<'b, 's, 'x, R, RG>( pub(crate) fn compare_property_always_nil<'b, 's, 'x, R, RG>(
_source: &'s str,
emacs: &'b Token<'s>, emacs: &'b Token<'s>,
_rust_node: R, _rust_node: R,
emacs_field: &'x str, emacs_field: &'x str,
_rust_value_getter: RG, _rust_value_getter: RG,
) -> Result<Option<(DiffStatus, Option<String>)>, Box<dyn std::error::Error>> { ) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>> {
let value = get_property(emacs, emacs_field)?; let value = get_property(emacs, emacs_field)?;
if value.is_some() { if value.is_some() {
let this_status = DiffStatus::Bad; let this_status = DiffStatus::Bad;
@@ -50,9 +91,9 @@ pub(crate) fn compare_property_always_nil<'b, 's, 'x, R, RG>(
"{} was expected to always be nil: {:?}", "{} was expected to always be nil: {:?}",
emacs_field, value emacs_field, value
)); ));
Ok(Some((this_status, message))) Ok(ComparePropertiesResult::SelfChange(this_status, message))
} else { } else {
Ok(None) Ok(ComparePropertiesResult::NoChange)
} }
} }
@@ -64,11 +105,12 @@ pub(crate) fn compare_property_quoted_string<
RV: AsRef<str> + std::fmt::Debug, RV: AsRef<str> + std::fmt::Debug,
RG: Fn(R) -> Option<RV>, RG: Fn(R) -> Option<RV>,
>( >(
_source: &'s str,
emacs: &'b Token<'s>, emacs: &'b Token<'s>,
rust_node: R, rust_node: R,
emacs_field: &'x str, emacs_field: &'x str,
rust_value_getter: RG, rust_value_getter: RG,
) -> Result<Option<(DiffStatus, Option<String>)>, Box<dyn std::error::Error>> { ) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>> {
let value = get_property_quoted_string(emacs, emacs_field)?; let value = get_property_quoted_string(emacs, emacs_field)?;
let rust_value = rust_value_getter(rust_node); let rust_value = rust_value_getter(rust_node);
if rust_value.as_ref().map(|s| s.as_ref()) != value.as_ref().map(String::as_str) { if rust_value.as_ref().map(|s| s.as_ref()) != value.as_ref().map(String::as_str) {
@@ -77,18 +119,19 @@ pub(crate) fn compare_property_quoted_string<
"{} mismatch (emacs != rust) {:?} != {:?}", "{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rust_value emacs_field, value, rust_value
)); ));
Ok(Some((this_status, message))) Ok(ComparePropertiesResult::SelfChange(this_status, message))
} else { } else {
Ok(None) Ok(ComparePropertiesResult::NoChange)
} }
} }
pub(crate) fn compare_property_unquoted_atom<'b, 's, 'x, R, RG: Fn(R) -> Option<&'s str>>( pub(crate) fn compare_property_unquoted_atom<'b, 's, 'x, R, RG: Fn(R) -> Option<&'s str>>(
_source: &'s str,
emacs: &'b Token<'s>, emacs: &'b Token<'s>,
rust_node: R, rust_node: R,
emacs_field: &'x str, emacs_field: &'x str,
rust_value_getter: RG, rust_value_getter: RG,
) -> Result<Option<(DiffStatus, Option<String>)>, Box<dyn std::error::Error>> { ) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>> {
let value = get_property_unquoted_atom(emacs, emacs_field)?; let value = get_property_unquoted_atom(emacs, emacs_field)?;
let rust_value = rust_value_getter(rust_node); let rust_value = rust_value_getter(rust_node);
if rust_value != value { if rust_value != value {
@@ -97,8 +140,506 @@ pub(crate) fn compare_property_unquoted_atom<'b, 's, 'x, R, RG: Fn(R) -> Option<
"{} mismatch (emacs != rust) {:?} != {:?}", "{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rust_value emacs_field, value, rust_value
)); ));
Ok(Some((this_status, message))) Ok(ComparePropertiesResult::SelfChange(this_status, message))
} else { } else {
Ok(None) Ok(ComparePropertiesResult::NoChange)
} }
} }
pub(crate) fn compare_property_numeric<
'b,
's,
'x,
R,
RV: FromStr + PartialEq + Debug,
RG: Fn(R) -> Option<RV>,
>(
_source: &'s str,
emacs: &'b Token<'s>,
rust_node: R,
emacs_field: &'x str,
rust_value_getter: RG,
) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error + 's>>
where
<RV as FromStr>::Err: std::error::Error,
<RV as FromStr>::Err: 's,
{
let value = get_property_numeric::<RV>(emacs, emacs_field)?;
let rust_value = rust_value_getter(rust_node);
if rust_value != value {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rust_value
));
Ok(ComparePropertiesResult::SelfChange(this_status, message))
} else {
Ok(ComparePropertiesResult::NoChange)
}
}
pub(crate) fn compare_property_list_of_quoted_string<
'b,
's,
'x,
R,
RV: AsRef<str> + std::fmt::Debug,
RI: Iterator<Item = RV>,
RG: Fn(R) -> Option<RI>,
>(
_source: &'s str,
emacs: &'b Token<'s>,
rust_node: R,
emacs_field: &'x str,
rust_value_getter: RG,
) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>> {
let value = get_property(emacs, emacs_field)?
.map(Token::as_list)
.map_or(Ok(None), |r| r.map(Some))?;
let rust_value = rust_value_getter(rust_node);
// TODO: Seems we are needlessly coverting to a vec here.
let rust_value: Option<Vec<RV>> = rust_value.map(|it| it.collect());
match (value, &rust_value) {
(None, None) => {}
(None, Some(_)) | (Some(_), None) => {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rust_value
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
(Some(el), Some(rl)) if el.len() != rl.len() => {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rust_value
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
(Some(el), Some(rl)) => {
for (e, r) in el.iter().zip(rl) {
let e = unquote(e.as_atom()?)?;
let r = r.as_ref();
if e != r {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}. Full list: {:?} != {:?}",
emacs_field, e, r, value, rust_value
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
}
}
}
Ok(ComparePropertiesResult::NoChange)
}
pub(crate) fn compare_property_set_of_quoted_string<
'a,
'b,
's,
'x,
R,
RV: AsRef<str> + std::fmt::Debug + Ord + 'a + ?Sized,
RI: Iterator<Item = &'a RV>,
RG: Fn(R) -> Option<RI>,
>(
_source: &'s str,
emacs: &'b Token<'s>,
rust_node: R,
emacs_field: &'x str,
rust_value_getter: RG,
) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>> {
let value = get_property(emacs, emacs_field)?
.map(Token::as_list)
.map_or(Ok(None), |r| r.map(Some))?;
let empty = Vec::new();
let value = value.unwrap_or(&empty);
let rust_value = rust_value_getter(rust_node);
let rust_value = if let Some(rust_value) = rust_value {
let slices: BTreeSet<&str> = rust_value.map(|rv| rv.as_ref()).collect();
slices
} else {
BTreeSet::new()
};
let value: Vec<&str> = value
.iter()
.map(|e| e.as_atom())
.collect::<Result<Vec<_>, _>>()?;
let value: Vec<String> = value
.into_iter()
.map(unquote)
.collect::<Result<Vec<_>, _>>()?;
let value: BTreeSet<&str> = value.iter().map(|e| e.as_str()).collect();
let mismatched: Vec<_> = value
.symmetric_difference(&rust_value)
.map(|val| *val)
.collect();
if !mismatched.is_empty() {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch. Mismatched values: {}",
emacs_field,
mismatched.join(", ")
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
Ok(ComparePropertiesResult::NoChange)
}
pub(crate) fn compare_property_boolean<'b, 's, 'x, R, RG: Fn(R) -> bool>(
_source: &'s str,
emacs: &'b Token<'s>,
rust_node: R,
emacs_field: &'x str,
rust_value_getter: RG,
) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>> {
// get_property already converts nil to None.
let value = get_property(emacs, emacs_field)?.is_some();
let rust_value = rust_value_getter(rust_node);
if rust_value != value {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rust_value
));
Ok(ComparePropertiesResult::SelfChange(this_status, message))
} else {
Ok(ComparePropertiesResult::NoChange)
}
}
pub(crate) fn compare_property_single_ast_node<
'b,
's,
'x: 'b + 's,
R,
RV: std::fmt::Debug,
RG: Fn(R) -> Option<RV>,
>(
source: &'s str,
emacs: &'b Token<'s>,
rust_node: R,
emacs_field: &'x str,
rust_value_getter: RG,
) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>>
where
AstNode<'b, 's>: From<RV>,
{
let value = get_property(emacs, emacs_field)?;
let rust_value = rust_value_getter(rust_node);
match (value, rust_value) {
(None, None) => {}
(None, rv @ Some(_)) | (Some(_), rv @ None) => {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rv
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
(Some(ev), Some(rv)) => {
let child_status: Vec<DiffEntry<'b, 's>> =
vec![compare_ast_node(source, ev, rv.into())?];
let diff_scope = artificial_diff_scope(emacs_field, child_status)?;
return Ok(ComparePropertiesResult::DiffEntry(diff_scope));
}
}
Ok(ComparePropertiesResult::NoChange)
}
pub(crate) fn compare_property_list_of_ast_nodes<
'b,
's,
'x: 'b + 's,
R,
RV: std::fmt::Debug,
RI: Iterator<Item = RV>,
RG: Fn(R) -> Option<RI>,
>(
source: &'s str,
emacs: &'b Token<'s>,
rust_node: R,
emacs_field: &'x str,
rust_value_getter: RG,
) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>>
where
AstNode<'b, 's>: From<RV>,
{
let value = get_property(emacs, emacs_field)?
.map(Token::as_list)
.map_or(Ok(None), |r| r.map(Some))?;
let rust_value = rust_value_getter(rust_node);
// TODO: Seems we are needlessly coverting to a vec here.
let rust_value: Option<Vec<RV>> = rust_value.map(|it| it.collect());
match (value, rust_value) {
(None, None) => {}
(Some(el), None)
if el.len() == 1
&& el.into_iter().all(|t| {
if let Ok(r#""""#) = t.as_atom() {
true
} else {
false
}
}) => {}
(None, rv @ Some(_)) | (Some(_), rv @ None) => {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rv
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
(Some(el), Some(rl)) if el.len() != rl.len() => {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, el, rl
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
(Some(el), Some(rl)) => {
let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(rl.len());
for (e, r) in el.iter().zip(rl) {
child_status.push(compare_ast_node(source, e, r.into())?);
}
let diff_scope = artificial_diff_scope(emacs_field, child_status)?;
return Ok(ComparePropertiesResult::DiffEntry(diff_scope));
}
}
Ok(ComparePropertiesResult::NoChange)
}
/// Special compare used for affiliate keywords that are parsed as objects.
///
/// Org-mode seems to store these as a 3-deep list:
/// - Outer list with 1 element per #+caption keyword (or other parsed keyword).
/// - Middle list which has:
/// - first element is a list of objects representing the value after the colon.
/// - every additional element is a list of objects from inside the square brackets (the optional value).
pub(crate) fn compare_property_list_of_list_of_list_of_ast_nodes<
'b,
's,
'x,
R,
RG: Fn(R) -> Option<&'b Vec<(Option<Vec<Object<'s>>>, Vec<Object<'s>>)>>,
>(
source: &'s str,
emacs: &'b Token<'s>,
rust_node: R,
emacs_field: &'x str,
rust_value_getter: RG,
) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>> {
// TODO: Replace Object<'s> with generics. I hard-coded Object in to make lifetimes easier.
let rust_value = rust_value_getter(rust_node);
let value = get_property(emacs, emacs_field)?
.map(Token::as_list)
.map_or(Ok(None), |r| r.map(Some))?;
let (value, rust_value) = match (value, rust_value) {
(None, None) => {
return Ok(ComparePropertiesResult::NoChange);
}
(None, Some(_)) | (Some(_), None) => {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rust_value
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
(Some(value), Some(rust_value)) if value.len() != rust_value.len() => {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rust_value
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
(Some(value), Some(rust_value)) => (value, rust_value),
};
let mut full_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(rust_value.len());
// Iterate the outer lists
for (value, (rust_optional, rust_value)) in value.iter().zip(rust_value.iter()) {
let mut middle_value = value.as_list()?.iter();
// First element of middle list is the mandatory value (the value past the colon).
let mandatory_value = middle_value.next();
let mandatory_value = match mandatory_value {
Some(mandatory_value) => mandatory_value,
None => {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rust_value
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
};
// Compare optional value
if let Some(rust_optional) = rust_optional {
let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(rust_value.len());
if rust_optional.len() != middle_value.len() {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} optional value length mismatch (emacs != rust) {} != {} | {:?}",
emacs_field,
middle_value.len(),
rust_optional.len(),
rust_optional
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
for (e, r) in middle_value.zip(rust_optional) {
child_status.push(compare_ast_node(source, e, r.into())?);
}
if !child_status.is_empty() {
let diff_scope = artificial_diff_scope("optional value", child_status)?;
full_status.push(diff_scope);
}
}
// Compare mandatory value
let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(rust_value.len());
let mandatory_value = mandatory_value.as_list()?;
if rust_value.len() != mandatory_value.len() {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mandatory value length mismatch (emacs != rust) {} != {} | {:?}",
emacs_field,
mandatory_value.len(),
rust_value.len(),
rust_value
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
for (e, r) in mandatory_value.iter().zip(rust_value) {
child_status.push(compare_ast_node(source, e, r.into())?);
}
if !child_status.is_empty() {
let diff_scope = artificial_diff_scope("mandatory value", child_status)?;
full_status.push(diff_scope);
}
}
if full_status.is_empty() {
Ok(ComparePropertiesResult::NoChange)
} else {
let diff_scope = artificial_owned_diff_scope(emacs_field, full_status)?;
Ok(ComparePropertiesResult::DiffEntry(diff_scope))
}
}
pub(crate) fn compare_property_number_lines<
'b,
's,
'x,
'y,
R,
RG: Fn(R) -> Option<&'y SwitchNumberLines>,
>(
_source: &'s str,
emacs: &'b Token<'s>,
rust_node: R,
emacs_field: &'x str,
rust_value_getter: RG,
) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>> {
let number_lines = get_property(emacs, emacs_field)?;
let rust_value = rust_value_getter(rust_node);
match (number_lines, &rust_value) {
(None, None) => {}
(Some(number_lines), Some(rust_number_lines)) => {
let token_list = number_lines.as_list()?;
let number_type = token_list
.get(0)
.map(Token::as_atom)
.map_or(Ok(None), |r| r.map(Some))?
.ok_or(":number-lines should have a type.")?;
let number_value = token_list
.get(2)
.map(Token::as_atom)
.map_or(Ok(None), |r| r.map(Some))?
.map(|val| val.parse::<LineNumber>())
.map_or(Ok(None), |r| r.map(Some))?
.ok_or(":number-lines should have a value.")?;
match (number_type, number_value, rust_number_lines) {
("new", emacs_val, SwitchNumberLines::New(rust_val)) if emacs_val == *rust_val => {}
("continued", emacs_val, SwitchNumberLines::Continued(rust_val))
if emacs_val == *rust_val => {}
_ => {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, number_lines, rust_value
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
}
}
_ => {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, number_lines, rust_value
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
};
Ok(ComparePropertiesResult::NoChange)
}
pub(crate) fn compare_property_retain_labels<'b, 's, 'x, 'y, R, RG: Fn(R) -> &'y RetainLabels>(
_source: &'s str,
emacs: &'b Token<'s>,
rust_node: R,
emacs_field: &'x str,
rust_value_getter: RG,
) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error + 's>> {
let rust_value = rust_value_getter(rust_node);
let retain_labels = get_property_unquoted_atom(emacs, ":retain-labels")?;
if let Some(retain_labels) = retain_labels {
if retain_labels == "t" {
match rust_value {
RetainLabels::Yes => {}
_ => {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, retain_labels, rust_value
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
}
} else {
let retain_labels: CharOffsetInLine = get_property_numeric(emacs, ":retain-labels")?.expect("Cannot be None or else the earlier get_property_unquoted_atom would have been None.");
match (retain_labels, rust_value) {
(e, RetainLabels::Keep(r)) if e == *r => {}
_ => {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, retain_labels, rust_value
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
}
}
} else {
match rust_value {
RetainLabels::No => {}
_ => {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, retain_labels, rust_value
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
}
}
Ok(ComparePropertiesResult::NoChange)
}

File diff suppressed because it is too large Load Diff

View File

@@ -30,10 +30,9 @@
/// } /// }
/// ``` /// ```
macro_rules! compare_properties { macro_rules! compare_properties {
($emacs:expr, $rust:expr, $(($emacs_field:expr, $rust_value_getter:expr, $compare_fn: expr)),+) => { ($source:expr, $emacs:expr, $rust:expr, $(($emacs_field:expr, $rust_value_getter:expr, $compare_fn: expr)),*) => {
{ {
let mut this_status = DiffStatus::Good; let mut new_status = Vec::new();
let mut message: Option<String> = None;
let children = $emacs.as_list()?; let children = $emacs.as_list()?;
let attributes_child = children let attributes_child = children
.iter() .iter()
@@ -44,10 +43,9 @@ macro_rules! compare_properties {
if emacs_keys.contains(":standard-properties") { if emacs_keys.contains(":standard-properties") {
emacs_keys.remove(":standard-properties"); emacs_keys.remove(":standard-properties");
} else { } else {
this_status = DiffStatus::Bad; new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
message = Some(format!(
"Emacs token lacks :standard-properties field.", "Emacs token lacks :standard-properties field.",
)); ))));
} }
$( $(
match $emacs_field { match $emacs_field {
@@ -58,24 +56,22 @@ macro_rules! compare_properties {
emacs_keys.remove(name); emacs_keys.remove(name);
}, },
EmacsField::Required(name) => { EmacsField::Required(name) => {
this_status = DiffStatus::Bad; new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
message = Some(format!(
"Emacs token lacks required field: {}", "Emacs token lacks required field: {}",
name name
)); ))));
}, },
EmacsField::Optional(_name) => {}, EmacsField::Optional(_name) => {},
} }
)+ )*
if !emacs_keys.is_empty() { if !emacs_keys.is_empty() {
let unexpected_keys: Vec<&str> = emacs_keys.into_iter().collect(); let unexpected_keys: Vec<&str> = emacs_keys.into_iter().collect();
let unexpected_keys = unexpected_keys.join(", "); let unexpected_keys = unexpected_keys.join(", ");
this_status = DiffStatus::Bad; new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
message = Some(format!(
"Emacs token had extra field(s): {}", "Emacs token had extra field(s): {}",
unexpected_keys unexpected_keys
)); ))));
} }
$( $(
@@ -87,33 +83,23 @@ macro_rules! compare_properties {
name name
}, },
}; };
let result = $compare_fn($emacs, $rust, emacs_name, $rust_value_getter)?; let result = $compare_fn($source, $emacs, $rust, emacs_name, $rust_value_getter)?;
match result { match result {
Some((DiffStatus::Good, _)) => unreachable!("No comparison functions should return Some() when DiffStatus is good."), ComparePropertiesResult::SelfChange(DiffStatus::Good, _) => unreachable!("No comparison functions should return SelfChange() when DiffStatus is good."),
Some((status, msg)) => { ComparePropertiesResult::NoChange => {},
this_status = status; result => {
message = msg; new_status.push(result);
},
_ => {}
}
)+
match this_status {
DiffStatus::Good => {
let result: Result<_, Box<dyn std::error::Error>> = Ok(None);
result
},
_ => {
Ok(Some((this_status, message)))
} }
} }
)*
new_status
} }
}; };
// Default case for when there are no expected properties except for :standard-properties // For elements with affiliated keywords
($emacs:expr) => { ($source:expr, $emacs:expr, $rust:expr, [], $(($emacs_field:expr, $rust_value_getter:expr, $compare_fn: expr)),*) => {
{ {
let mut this_status = DiffStatus::Good; let mut new_status = Vec::new();
let mut message: Option<String> = None;
let children = $emacs.as_list()?; let children = $emacs.as_list()?;
let attributes_child = children let attributes_child = children
.iter() .iter()
@@ -124,30 +110,193 @@ macro_rules! compare_properties {
if emacs_keys.contains(":standard-properties") { if emacs_keys.contains(":standard-properties") {
emacs_keys.remove(":standard-properties"); emacs_keys.remove(":standard-properties");
} else { } else {
this_status = DiffStatus::Bad; new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
message = Some(format!(
"Emacs token lacks :standard-properties field.", "Emacs token lacks :standard-properties field.",
)); ))));
}
let affiliated_keywords_names: Vec<String> = affiliated_keywords_names($rust).collect();
for additional_field in affiliated_keywords_names.iter().map(String::as_str).map(EmacsField::Required) {
match additional_field {
EmacsField::Required(name) if emacs_keys.contains(name) => {
emacs_keys.remove(name);
},
EmacsField::Optional(name) if emacs_keys.contains(name) => {
emacs_keys.remove(name);
},
EmacsField::Required(name) => {
new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
"Emacs token lacks required field: {}",
name
))));
},
EmacsField::Optional(_name) => {},
}
}
$(
match $emacs_field {
EmacsField::Required(name) if emacs_keys.contains(name) => {
emacs_keys.remove(name);
},
EmacsField::Optional(name) if emacs_keys.contains(name) => {
emacs_keys.remove(name);
},
EmacsField::Required(name) => {
new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
"Emacs token lacks required field: {}",
name
))));
},
EmacsField::Optional(_name) => {},
}
)*
if !emacs_keys.is_empty() {
let unexpected_keys: Vec<&str> = emacs_keys.into_iter().collect();
let unexpected_keys = unexpected_keys.join(", ");
new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
"Emacs token had extra field(s): {}",
unexpected_keys
))));
}
for diff in compare_affiliated_keywords($source, $emacs, $rust)? {
new_status.push(diff);
}
$(
let emacs_name = match $emacs_field {
EmacsField::Required(name) => {
name
},
EmacsField::Optional(name) => {
name
},
};
let result = $compare_fn($source, $emacs, $rust, emacs_name, $rust_value_getter)?;
match result {
ComparePropertiesResult::SelfChange(DiffStatus::Good, _) => unreachable!("No comparison functions should return SelfChange() when DiffStatus is good."),
ComparePropertiesResult::NoChange => {},
result => {
new_status.push(result);
}
}
)*
new_status
}
};
// Specifies additional properties
($source:expr, $emacs:expr, $rust:expr, $additionalproperties: expr, $(($emacs_field:expr, $rust_value_getter:expr, $compare_fn: expr)),*) => {
{
let mut new_status = Vec::new();
let children = $emacs.as_list()?;
let attributes_child = children
.iter()
.nth(1)
.ok_or("Should have an attributes child.")?;
let attributes_map = attributes_child.as_map()?;
let mut emacs_keys: BTreeSet<&str> = attributes_map.keys().map(|s| *s).collect();
if emacs_keys.contains(":standard-properties") {
emacs_keys.remove(":standard-properties");
} else {
new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
"Emacs token lacks :standard-properties field.",
))));
}
for additional_field in $additionalproperties {
match additional_field {
EmacsField::Required(name) if emacs_keys.contains(name) => {
emacs_keys.remove(name);
},
EmacsField::Optional(name) if emacs_keys.contains(name) => {
emacs_keys.remove(name);
},
EmacsField::Required(name) => {
new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
"Emacs token lacks required field: {}",
name
))));
},
EmacsField::Optional(_name) => {},
}
}
$(
match $emacs_field {
EmacsField::Required(name) if emacs_keys.contains(name) => {
emacs_keys.remove(name);
},
EmacsField::Optional(name) if emacs_keys.contains(name) => {
emacs_keys.remove(name);
},
EmacsField::Required(name) => {
new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
"Emacs token lacks required field: {}",
name
))));
},
EmacsField::Optional(_name) => {},
}
)*
if !emacs_keys.is_empty() {
let unexpected_keys: Vec<&str> = emacs_keys.into_iter().collect();
let unexpected_keys = unexpected_keys.join(", ");
new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
"Emacs token had extra field(s): {}",
unexpected_keys
))));
}
$(
let emacs_name = match $emacs_field {
EmacsField::Required(name) => {
name
},
EmacsField::Optional(name) => {
name
},
};
let result = $compare_fn($source, $emacs, $rust, emacs_name, $rust_value_getter)?;
match result {
ComparePropertiesResult::SelfChange(DiffStatus::Good, _) => unreachable!("No comparison functions should return SelfChange() when DiffStatus is good."),
ComparePropertiesResult::NoChange => {},
result => {
new_status.push(result);
}
}
)*
new_status
}
};
// Default case for when there are no expected properties except for :standard-properties
($emacs:expr) => {
{
let mut new_status = Vec::new();
let children = $emacs.as_list()?;
let attributes_child = children
.iter()
.nth(1)
.ok_or("Should have an attributes child.")?;
let attributes_map = attributes_child.as_map()?;
let mut emacs_keys: BTreeSet<&str> = attributes_map.keys().map(|s| *s).collect();
if emacs_keys.contains(":standard-properties") {
emacs_keys.remove(":standard-properties");
} else {
new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
"Emacs token lacks :standard-properties field.",
))));
} }
if !emacs_keys.is_empty() { if !emacs_keys.is_empty() {
let unexpected_keys: Vec<&str> = emacs_keys.into_iter().collect(); let unexpected_keys: Vec<&str> = emacs_keys.into_iter().collect();
let unexpected_keys = unexpected_keys.join(", "); let unexpected_keys = unexpected_keys.join(", ");
this_status = DiffStatus::Bad; new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
message = Some(format!(
"Emacs token had extra field(s): {}", "Emacs token had extra field(s): {}",
unexpected_keys unexpected_keys
)); ))));
}
match this_status {
DiffStatus::Good => {
let result: Result<_, Box<dyn std::error::Error>> = Ok(None);
result
},
_ => {
Ok(Some((this_status, message)))
}
} }
new_status
} }
}; };
} }

View File

@@ -10,3 +10,5 @@ pub use compare::run_anonymous_compare;
pub use compare::run_anonymous_compare_with_settings; pub use compare::run_anonymous_compare_with_settings;
pub use compare::run_compare_on_file; pub use compare::run_compare_on_file;
pub use compare::run_compare_on_file_with_settings; pub use compare::run_compare_on_file_with_settings;
pub use compare::silent_anonymous_compare;
pub use compare::silent_compare_on_file;

View File

@@ -1,5 +1,6 @@
use std::path::Path; use std::path::Path;
use std::process::Command;
use tokio::process::Command;
use crate::context::HeadlineLevelFilter; use crate::context::HeadlineLevelFilter;
use crate::settings::GlobalSettings; use crate::settings::GlobalSettings;
@@ -25,9 +26,9 @@ fn global_settings_elisp(global_settings: &GlobalSettings) -> String {
ret ret
} }
pub(crate) fn emacs_parse_anonymous_org_document<C>( pub(crate) async fn emacs_parse_anonymous_org_document<'g, 's, C>(
file_contents: C, file_contents: C,
global_settings: &GlobalSettings, global_settings: &GlobalSettings<'g, 's>,
) -> Result<String, Box<dyn std::error::Error>> ) -> Result<String, Box<dyn std::error::Error>>
where where
C: AsRef<str>, C: AsRef<str>,
@@ -54,15 +55,24 @@ where
.arg("--batch") .arg("--batch")
.arg("--eval") .arg("--eval")
.arg(elisp_script); .arg(elisp_script);
let out = cmd.output()?; let out = cmd.output().await?;
out.status.exit_ok()?; let status = out.status.exit_ok();
if status.is_err() {
eprintln!(
"Emacs errored out: {}\n{}",
String::from_utf8(out.stdout)?,
String::from_utf8(out.stderr)?
);
status?;
unreachable!();
}
let org_sexp = out.stderr; let org_sexp = out.stderr;
Ok(String::from_utf8(org_sexp)?) Ok(String::from_utf8(org_sexp)?)
} }
pub(crate) fn emacs_parse_file_org_document<P>( pub(crate) async fn emacs_parse_file_org_document<'g, 's, P>(
file_path: P, file_path: P,
global_settings: &GlobalSettings, global_settings: &GlobalSettings<'g, 's>,
) -> Result<String, Box<dyn std::error::Error>> ) -> Result<String, Box<dyn std::error::Error>>
where where
P: AsRef<Path>, P: AsRef<Path>,
@@ -97,8 +107,17 @@ where
.arg("--batch") .arg("--batch")
.arg("--eval") .arg("--eval")
.arg(elisp_script); .arg(elisp_script);
let out = cmd.output()?; let out = cmd.output().await?;
out.status.exit_ok()?; let status = out.status.exit_ok();
if status.is_err() {
eprintln!(
"Emacs errored out: {}\n{}",
String::from_utf8(out.stdout)?,
String::from_utf8(out.stderr)?
);
status?;
unreachable!();
}
let org_sexp = out.stderr; let org_sexp = out.stderr;
Ok(String::from_utf8(org_sexp)?) Ok(String::from_utf8(org_sexp)?)
} }
@@ -125,7 +144,7 @@ where
output output
} }
pub fn get_emacs_version() -> Result<String, Box<dyn std::error::Error>> { pub async fn get_emacs_version() -> Result<String, Box<dyn std::error::Error>> {
let elisp_script = r#"(progn let elisp_script = r#"(progn
(message "%s" (version)) (message "%s" (version))
)"#; )"#;
@@ -138,12 +157,12 @@ pub fn get_emacs_version() -> Result<String, Box<dyn std::error::Error>> {
.arg("--eval") .arg("--eval")
.arg(elisp_script); .arg(elisp_script);
let out = cmd.output()?; let out = cmd.output().await?;
out.status.exit_ok()?; out.status.exit_ok()?;
Ok(String::from_utf8(out.stderr)?) Ok(String::from_utf8(out.stderr)?)
} }
pub fn get_org_mode_version() -> Result<String, Box<dyn std::error::Error>> { pub async fn get_org_mode_version() -> Result<String, Box<dyn std::error::Error>> {
let elisp_script = r#"(progn let elisp_script = r#"(progn
(org-mode) (org-mode)
(message "%s" (org-version nil t nil)) (message "%s" (org-version nil t nil))
@@ -157,7 +176,7 @@ pub fn get_org_mode_version() -> Result<String, Box<dyn std::error::Error>> {
.arg("--eval") .arg("--eval")
.arg(elisp_script); .arg(elisp_script);
let out = cmd.output()?; let out = cmd.output().await?;
out.status.exit_ok()?; out.status.exit_ok()?;
Ok(String::from_utf8(out.stderr)?) Ok(String::from_utf8(out.stderr)?)
} }

View File

@@ -1,8 +1,18 @@
use std::str::FromStr; use std::str::FromStr;
use super::compare_field::compare_property_list_of_list_of_list_of_ast_nodes;
use super::compare_field::compare_property_list_of_quoted_string;
use super::compare_field::compare_property_quoted_string;
use super::compare_field::ComparePropertiesResult;
use super::diff::DiffEntry;
use super::diff::DiffStatus;
use super::elisp_fact::GetElispFact; use super::elisp_fact::GetElispFact;
use super::sexp::Token; use super::sexp::Token;
use crate::compare::diff::compare_ast_node;
use crate::compare::sexp::unquote; use crate::compare::sexp::unquote;
use crate::types::AffiliatedKeywordValue;
use crate::types::AstNode;
use crate::types::GetAffiliatedKeywords;
use crate::types::GetStandardProperties; use crate::types::GetStandardProperties;
use crate::types::StandardProperties; use crate::types::StandardProperties;
@@ -217,22 +227,6 @@ pub(crate) fn get_property_quoted_string<'b, 's, 'x>(
.map_or(Ok(None), |r| r.map(Some))?) .map_or(Ok(None), |r| r.map(Some))?)
} }
/// Get a named property containing a boolean value.
///
/// This uses the elisp convention of nil == false, non-nil == true.
///
/// Returns false if key is not found.
pub(crate) fn get_property_boolean<'b, 's, 'x>(
emacs: &'b Token<'s>,
key: &'x str,
) -> Result<bool, Box<dyn std::error::Error>> {
Ok(get_property(emacs, key)?
.map(Token::as_atom)
.map_or(Ok(None), |r| r.map(Some))?
.unwrap_or("nil")
!= "nil")
}
/// Get a named property containing an unquoted numeric value. /// Get a named property containing an unquoted numeric value.
/// ///
/// Returns None if key is not found. /// Returns None if key is not found.
@@ -252,3 +246,157 @@ where
.map_or(Ok(None), |r| r.map(Some))?; .map_or(Ok(None), |r| r.map(Some))?;
Ok(parsed_number) Ok(parsed_number)
} }
pub(crate) fn compare_children<'b, 's, 'x, RC>(
source: &'s str,
emacs: &'b Token<'s>,
rust_children: &'x Vec<RC>,
child_status: &mut Vec<DiffEntry<'b, 's>>,
this_status: &mut DiffStatus,
message: &mut Option<String>,
) -> Result<(), Box<dyn std::error::Error>>
where
AstNode<'b, 's>: From<&'x RC>,
{
let emacs_children = emacs.as_list()?;
let emacs_children_length = emacs_children.len() - 2;
if emacs_children_length != rust_children.len() {
*this_status = DiffStatus::Bad;
*message = Some(format!(
"Child length mismatch (emacs != rust) {:?} != {:?}",
emacs_children_length,
rust_children.len()
));
}
for (emacs_child, rust_child) in emacs_children.iter().skip(2).zip(rust_children.iter()) {
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
}
Ok(())
}
pub(crate) fn compare_children_iter<'b, 's, RC, RI: Iterator<Item = RC> + ExactSizeIterator>(
source: &'s str,
emacs: &'b Token<'s>,
rust_children: RI,
child_status: &mut Vec<DiffEntry<'b, 's>>,
this_status: &mut DiffStatus,
message: &mut Option<String>,
) -> Result<(), Box<dyn std::error::Error>>
where
AstNode<'b, 's>: From<RC>,
{
let emacs_children = emacs.as_list()?;
let emacs_children_length = emacs_children.len() - 2;
if emacs_children_length != rust_children.len() {
*this_status = DiffStatus::Bad;
*message = Some(format!(
"Child length mismatch (emacs != rust) {:?} != {:?}",
emacs_children_length,
rust_children.len()
));
}
for (emacs_child, rust_child) in emacs_children.iter().skip(2).zip(rust_children) {
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
}
Ok(())
}
pub(crate) fn assert_no_children<'b, 's>(
emacs: &'b Token<'s>,
this_status: &mut DiffStatus,
message: &mut Option<String>,
) -> Result<(), Box<dyn std::error::Error>> {
let emacs_children_length = emacs.as_list()?.len();
// 2, one for the name of the node and one for the properties. Children would come after that.
if emacs_children_length != 2 {
*this_status = DiffStatus::Bad;
*message = Some(format!(
"Should have no children but emacs has {:?} children.",
emacs_children_length - 2,
));
}
Ok(())
}
pub(crate) fn compare_additional_properties<'b, 's, RK, RV, RI>(
emacs: &'b Token<'s>,
rust_children: RI,
) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>>
where
RK: AsRef<str>,
RV: AsRef<str>,
RI: Iterator<Item = (RK, RV)> + ExactSizeIterator,
{
for (rust_key, rust_value) in rust_children {
let rust_key = rust_key.as_ref();
let rust_value = rust_value.as_ref();
let emacs_value = get_property_quoted_string(emacs, rust_key)?;
if Some(rust_value) != emacs_value.as_ref().map(String::as_str) {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
rust_key, emacs_value, rust_value
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
}
Ok(ComparePropertiesResult::NoChange)
}
pub(crate) fn compare_affiliated_keywords<'b, 's, GAK>(
source: &'s str,
emacs: &'b Token<'s>,
rust: &'b GAK,
) -> Result<Vec<ComparePropertiesResult<'b, 's>>, Box<dyn std::error::Error>>
where
GAK: GetAffiliatedKeywords<'s>,
{
let mut ret = Vec::new();
let affiliated_keywords = rust.get_affiliated_keywords();
for (rust_name, rust_value) in affiliated_keywords.keywords.iter() {
let emacs_property_name = format!(":{}", rust_name);
match rust_value {
AffiliatedKeywordValue::SingleString(rust_value) => {
let diff = compare_property_quoted_string(
source,
emacs,
rust,
emacs_property_name.as_str(),
|_| Some(*rust_value),
)?;
ret.push(diff);
}
AffiliatedKeywordValue::ListOfStrings(rust_value) => {
let diff = compare_property_list_of_quoted_string(
source,
emacs,
rust,
emacs_property_name.as_str(),
|_| Some(rust_value.iter()),
)?;
ret.push(diff);
}
AffiliatedKeywordValue::ListOfListsOfObjects(rust_value) => {
let diff = compare_property_list_of_list_of_list_of_ast_nodes(
source,
emacs,
rust,
emacs_property_name.as_str(),
|_| Some(rust_value),
)?;
ret.push(diff);
}
};
}
Ok(ret)
}
pub(crate) fn affiliated_keywords_names<'s, GAK>(rust: &'s GAK) -> impl Iterator<Item = String> + 's
where
GAK: GetAffiliatedKeywords<'s>,
{
rust.get_affiliated_keywords()
.keywords
.keys()
.map(|k| format!(":{}", k))
}

464
src/context/constants.rs Normal file
View File

@@ -0,0 +1,464 @@
use super::global_settings::EntityDefinition;
pub(crate) const DEFAULT_ORG_ELEMENT_PARSED_KEYWORDS: [&'static str; 1] = ["CAPTION"];
pub(crate) const DEFAULT_ORG_ELEMENT_DUAL_KEYWORDS: [&'static str; 2] = ["CAPTION", "RESULTS"];
pub(crate) const DEFAULT_ORG_ELEMENT_AFFILIATED_KEYWORDS: [&'static str; 13] = [
"CAPTION", "DATA", "HEADER", "HEADERS", "LABEL", "NAME", "PLOT", "RESNAME", "RESULT",
"RESULTS", "SOURCE", "SRCNAME", "TBLNAME",
];
pub(crate) const DEFAULT_ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST: [(&'static str, &'static str); 8] = [
("DATA", "NAME"),
("LABEL", "NAME"),
("RESNAME", "NAME"),
("SOURCE", "NAME"),
("SRCNAME", "NAME"),
("TBLNAME", "NAME"),
("RESULT", "RESULTS"),
("HEADERS", "HEADER"),
];
pub(crate) const DEFAULT_ORG_LINK_PARAMETERS: [&'static str; 23] = [
"id",
"eww",
"rmail",
"mhe",
"irc",
"info",
"gnus",
"docview",
"bibtex",
"bbdb",
"w3m",
"doi",
"file+sys",
"file+emacs",
"shell",
"news",
"mailto",
"https",
"http",
"ftp",
"help",
"file",
"elisp",
];
pub(crate) const DEFAULT_ORG_ENTITIES: [EntityDefinition<'static>; 414] = [
EntityDefinition {name: "_ ", latex_math_mode: false, latex: "\\hspace*{10.0em}", html: "&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "_ ", latex_math_mode: false, latex: "\\hspace*{9.5em}", html: "&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "_ ", latex_math_mode: false, latex: "\\hspace*{9.0em}", html: "&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "_ ", latex_math_mode: false, latex: "\\hspace*{8.5em}", html: "&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "_ ", latex_math_mode: false, latex: "\\hspace*{8.0em}", html: "&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "_ ", latex_math_mode: false, latex: "\\hspace*{7.5em}", html: "&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "_ ", latex_math_mode: false, latex: "\\hspace*{7.0em}", html: "&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "leftrightarrow", latex_math_mode: true, latex: "\\leftrightarrow", html: "&harr;", ascii: "<->", utf8: ""},
EntityDefinition {name: "Leftrightarrow", latex_math_mode: true, latex: "\\Leftrightarrow", html: "&hArr;", ascii: "<=>", utf8: ""},
EntityDefinition {name: "_ ", latex_math_mode: false, latex: "\\hspace*{6.5em}", html: "&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "hookleftarrow", latex_math_mode: true, latex: "\\hookleftarrow", html: "&crarr;", ascii: "<-'", utf8: ""},
EntityDefinition {name: "_ ", latex_math_mode: false, latex: "\\hspace*{6.0em}", html: "&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "_ ", latex_math_mode: false, latex: "\\hspace*{5.5em}", html: "&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "preccurlyeq", latex_math_mode: true, latex: "\\preccurlyeq", html: "&prcue;", ascii: "[precedes or equal]", utf8: ""},
EntityDefinition {name: "succcurlyeq", latex_math_mode: true, latex: "\\succcurlyeq", html: "&sccue;", ascii: "[succeeds or equal]", utf8: ""},
EntityDefinition {name: "diamondsuit", latex_math_mode: true, latex: "\\diamondsuit", html: "&diams;", ascii: "[diamonds]", utf8: ""},
EntityDefinition {name: "_ ", latex_math_mode: false, latex: "\\hspace*{5.0em}", html: "&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "varepsilon", latex_math_mode: true, latex: "\\varepsilon", html: "&epsilon;", ascii: "varepsilon", utf8: "ε"},
EntityDefinition {name: "rightarrow", latex_math_mode: true, latex: "\\rightarrow", html: "&rarr;", ascii: "->", utf8: ""},
EntityDefinition {name: "Rightarrow", latex_math_mode: true, latex: "\\Rightarrow", html: "&rArr;", ascii: "=>", utf8: ""},
EntityDefinition {name: "blacksmile", latex_math_mode: true, latex: "\\ddot\\smile", html: "&#9787;", ascii: ":-)", utf8: ""},
EntityDefinition {name: "_ ", latex_math_mode: false, latex: "\\hspace*{4.5em}", html: "&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "asciicirc", latex_math_mode: false, latex: "\\textasciicircum{}", html: "^", ascii: "^", utf8: "^"},
EntityDefinition {name: "therefore", latex_math_mode: true, latex: "\\therefore", html: "&there4;", ascii: "[therefore]", utf8: ""},
EntityDefinition {name: "triangleq", latex_math_mode: true, latex: "\\triangleq", html: "&triangleq;", ascii: "[defined to]", utf8: ""},
EntityDefinition {name: "lesseqgtr", latex_math_mode: true, latex: "\\lesseqgtr", html: "&lesseqgtr;", ascii: "[less than or equal or greater than or equal]", utf8: ""},
EntityDefinition {name: "leftarrow", latex_math_mode: true, latex: "\\leftarrow", html: "&larr;", ascii: "<-", utf8: ""},
EntityDefinition {name: "Leftarrow", latex_math_mode: true, latex: "\\Leftarrow", html: "&lArr;", ascii: "<=", utf8: ""},
EntityDefinition {name: "downarrow", latex_math_mode: true, latex: "\\downarrow", html: "&darr;", ascii: "[downarrow]", utf8: ""},
EntityDefinition {name: "Downarrow", latex_math_mode: true, latex: "\\Downarrow", html: "&dArr;", ascii: "[dbldownarrow]", utf8: ""},
EntityDefinition {name: "checkmark", latex_math_mode: true, latex: "\\checkmark", html: "&check;", ascii: "[checkmark]", utf8: ""},
EntityDefinition {name: "spadesuit", latex_math_mode: true, latex: "\\spadesuit", html: "&spades;", ascii: "[spades]", utf8: ""},
EntityDefinition {name: "heartsuit", latex_math_mode: true, latex: "\\heartsuit", html: "&heartsuit;", ascii: "[hearts]", utf8: ""},
EntityDefinition {name: "_ ", latex_math_mode: false, latex: "\\hspace*{4.0em}", html: "&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "thetasym", latex_math_mode: true, latex: "\\vartheta", html: "&thetasym;", ascii: "theta", utf8: "ϑ"},
EntityDefinition {name: "vartheta", latex_math_mode: true, latex: "\\vartheta", html: "&thetasym;", ascii: "theta", utf8: "ϑ"},
EntityDefinition {name: "varsigma", latex_math_mode: true, latex: "\\varsigma", html: "&sigmaf;", ascii: "varsigma", utf8: "ς"},
EntityDefinition {name: "setminus", latex_math_mode: true, latex: "\\setminus", html: "&setminus;", ascii: "\" ", utf8: ""},
EntityDefinition {name: "emptyset", latex_math_mode: true, latex: "\\emptyset", html: "&empty;", ascii: "[empty set]", utf8: ""},
EntityDefinition {name: "parallel", latex_math_mode: true, latex: "\\parallel", html: "&parallel;", ascii: "||", utf8: ""},
EntityDefinition {name: "clubsuit", latex_math_mode: true, latex: "\\clubsuit", html: "&clubs;", ascii: "[clubs]", utf8: ""},
EntityDefinition {name: "_ ", latex_math_mode: false, latex: "\\hspace*{3.5em}", html: "&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "Epsilon", latex_math_mode: false, latex: "E", html: "&Epsilon;", ascii: "Epsilon", utf8: "Ε"},
EntityDefinition {name: "epsilon", latex_math_mode: true, latex: "\\epsilon", html: "&epsilon;", ascii: "epsilon", utf8: "ε"},
EntityDefinition {name: "Omicron", latex_math_mode: false, latex: "O", html: "&Omicron;", ascii: "Omicron", utf8: "Ο"},
EntityDefinition {name: "omicron", latex_math_mode: false, latex: "\\textit{o}", html: "&omicron;", ascii: "omicron", utf8: "ο"},
EntityDefinition {name: "Upsilon", latex_math_mode: true, latex: "\\Upsilon", html: "&Upsilon;", ascii: "Upsilon", utf8: "Υ"},
EntityDefinition {name: "upsilon", latex_math_mode: true, latex: "\\upsilon", html: "&upsilon;", ascii: "upsilon", utf8: "υ"},
EntityDefinition {name: "partial", latex_math_mode: true, latex: "\\partial", html: "&part;", ascii: "[partial differential]", utf8: ""},
EntityDefinition {name: "alefsym", latex_math_mode: true, latex: "\\aleph", html: "&alefsym;", ascii: "aleph", utf8: ""},
EntityDefinition {name: "because", latex_math_mode: true, latex: "\\because", html: "&because;", ascii: "[because]", utf8: ""},
EntityDefinition {name: "lessgtr", latex_math_mode: true, latex: "\\lessgtr", html: "&lessgtr;", ascii: "[less than or greater than]", utf8: ""},
EntityDefinition {name: "nexists", latex_math_mode: true, latex: "\\nexists", html: "&exist;", ascii: "[there does not exists]", utf8: ""},
EntityDefinition {name: "uparrow", latex_math_mode: true, latex: "\\uparrow", html: "&uarr;", ascii: "[uparrow]", utf8: ""},
EntityDefinition {name: "Uparrow", latex_math_mode: true, latex: "\\Uparrow", html: "&uArr;", ascii: "[dbluparrow]", utf8: ""},
EntityDefinition {name: "diamond", latex_math_mode: true, latex: "\\diamondsuit", html: "&diamond;", ascii: "[diamond]", utf8: ""},
EntityDefinition {name: "Diamond", latex_math_mode: true, latex: "\\diamondsuit", html: "&diamond;", ascii: "[diamond]", utf8: ""},
EntityDefinition {name: "_ ", latex_math_mode: false, latex: "\\hspace*{3.0em}", html: "&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "Agrave", latex_math_mode: false, latex: "\\`{A}", html: "&Agrave;", ascii: "A", utf8: "À"},
EntityDefinition {name: "agrave", latex_math_mode: false, latex: "\\`{a}", html: "&agrave;", ascii: "a", utf8: "à"},
EntityDefinition {name: "Aacute", latex_math_mode: false, latex: "\\'{A}", html: "&Aacute;", ascii: "A", utf8: "Á"},
EntityDefinition {name: "aacute", latex_math_mode: false, latex: "\\'{a}", html: "&aacute;", ascii: "a", utf8: "á"},
EntityDefinition {name: "Atilde", latex_math_mode: false, latex: "\\~{A}", html: "&Atilde;", ascii: "A", utf8: "Ã"},
EntityDefinition {name: "atilde", latex_math_mode: false, latex: "\\~{a}", html: "&atilde;", ascii: "a", utf8: "ã"},
EntityDefinition {name: "Ccedil", latex_math_mode: false, latex: "\\c{C}", html: "&Ccedil;", ascii: "C", utf8: "Ç"},
EntityDefinition {name: "ccedil", latex_math_mode: false, latex: "\\c{c}", html: "&ccedil;", ascii: "c", utf8: "ç"},
EntityDefinition {name: "Egrave", latex_math_mode: false, latex: "\\`{E}", html: "&Egrave;", ascii: "E", utf8: "È"},
EntityDefinition {name: "egrave", latex_math_mode: false, latex: "\\`{e}", html: "&egrave;", ascii: "e", utf8: "è"},
EntityDefinition {name: "Eacute", latex_math_mode: false, latex: "\\'{E}", html: "&Eacute;", ascii: "E", utf8: "É"},
EntityDefinition {name: "eacute", latex_math_mode: false, latex: "\\'{e}", html: "&eacute;", ascii: "e", utf8: "é"},
EntityDefinition {name: "Igrave", latex_math_mode: false, latex: "\\`{I}", html: "&Igrave;", ascii: "I", utf8: "Ì"},
EntityDefinition {name: "igrave", latex_math_mode: false, latex: "\\`{i}", html: "&igrave;", ascii: "i", utf8: "ì"},
EntityDefinition {name: "Iacute", latex_math_mode: false, latex: "\\'{I}", html: "&Iacute;", ascii: "I", utf8: "Í"},
EntityDefinition {name: "iacute", latex_math_mode: false, latex: "\\'{i}", html: "&iacute;", ascii: "i", utf8: "í"},
EntityDefinition {name: "inodot", latex_math_mode: false, latex: "\\i", html: "&inodot;", ascii: "i", utf8: "ı"},
EntityDefinition {name: "Ntilde", latex_math_mode: false, latex: "\\~{N}", html: "&Ntilde;", ascii: "N", utf8: "Ñ"},
EntityDefinition {name: "ntilde", latex_math_mode: false, latex: "\\~{n}", html: "&ntilde;", ascii: "n", utf8: "ñ"},
EntityDefinition {name: "Ograve", latex_math_mode: false, latex: "\\`{O}", html: "&Ograve;", ascii: "O", utf8: "Ò"},
EntityDefinition {name: "ograve", latex_math_mode: false, latex: "\\`{o}", html: "&ograve;", ascii: "o", utf8: "ò"},
EntityDefinition {name: "Oacute", latex_math_mode: false, latex: "\\'{O}", html: "&Oacute;", ascii: "O", utf8: "Ó"},
EntityDefinition {name: "oacute", latex_math_mode: false, latex: "\\'{o}", html: "&oacute;", ascii: "o", utf8: "ó"},
EntityDefinition {name: "Otilde", latex_math_mode: false, latex: "\\~{O}", html: "&Otilde;", ascii: "O", utf8: "Õ"},
EntityDefinition {name: "otilde", latex_math_mode: false, latex: "\\~{o}", html: "&otilde;", ascii: "o", utf8: "õ"},
EntityDefinition {name: "Oslash", latex_math_mode: false, latex: "\\O", html: "&Oslash;", ascii: "O", utf8: "Ø"},
EntityDefinition {name: "oslash", latex_math_mode: false, latex: "\\o{}", html: "&oslash;", ascii: "o", utf8: "ø"},
EntityDefinition {name: "Scaron", latex_math_mode: false, latex: "\\v{S}", html: "&Scaron;", ascii: "S", utf8: "Š"},
EntityDefinition {name: "scaron", latex_math_mode: false, latex: "\\v{s}", html: "&scaron;", ascii: "s", utf8: "š"},
EntityDefinition {name: "Ugrave", latex_math_mode: false, latex: "\\`{U}", html: "&Ugrave;", ascii: "U", utf8: "Ù"},
EntityDefinition {name: "ugrave", latex_math_mode: false, latex: "\\`{u}", html: "&ugrave;", ascii: "u", utf8: "ù"},
EntityDefinition {name: "Uacute", latex_math_mode: false, latex: "\\'{U}", html: "&Uacute;", ascii: "U", utf8: "Ú"},
EntityDefinition {name: "uacute", latex_math_mode: false, latex: "\\'{u}", html: "&uacute;", ascii: "u", utf8: "ú"},
EntityDefinition {name: "Yacute", latex_math_mode: false, latex: "\\'{Y}", html: "&Yacute;", ascii: "Y", utf8: "Ý"},
EntityDefinition {name: "yacute", latex_math_mode: false, latex: "\\'{y}", html: "&yacute;", ascii: "y", utf8: "ý"},
EntityDefinition {name: "weierp", latex_math_mode: true, latex: "\\wp", html: "&weierp;", ascii: "P", utf8: ""},
EntityDefinition {name: "Lambda", latex_math_mode: true, latex: "\\Lambda", html: "&Lambda;", ascii: "Lambda", utf8: "Λ"},
EntityDefinition {name: "lambda", latex_math_mode: true, latex: "\\lambda", html: "&lambda;", ascii: "lambda", utf8: "λ"},
EntityDefinition {name: "sigmaf", latex_math_mode: true, latex: "\\varsigma", html: "&sigmaf;", ascii: "sigmaf", utf8: "ς"},
EntityDefinition {name: "varphi", latex_math_mode: true, latex: "\\varphi", html: "&varphi;", ascii: "varphi", utf8: "φ"},
EntityDefinition {name: "acutex", latex_math_mode: true, latex: "\\acute x", html: "&acute;x", ascii: "'x", utf8: "𝑥́"},
EntityDefinition {name: "hellip", latex_math_mode: false, latex: "\\dots{}", html: "&hellip;", ascii: "...", utf8: ""},
EntityDefinition {name: "middot", latex_math_mode: false, latex: "\\textperiodcentered{}", html: "&middot;", ascii: ".", utf8: "·"},
EntityDefinition {name: "iquest", latex_math_mode: false, latex: "?`", html: "&iquest;", ascii: "?", utf8: "¿"},
EntityDefinition {name: "lsaquo", latex_math_mode: false, latex: "\\guilsinglleft{}", html: "&lsaquo;", ascii: "<", utf8: ""},
EntityDefinition {name: "rsaquo", latex_math_mode: false, latex: "\\guilsinglright{}", html: "&rsaquo;", ascii: ">", utf8: ""},
EntityDefinition {name: "brvbar", latex_math_mode: false, latex: "\\textbrokenbar{}", html: "&brvbar;", ascii: "|", utf8: "¦"},
EntityDefinition {name: "dagger", latex_math_mode: false, latex: "\\textdagger{}", html: "&dagger;", ascii: "[dagger]", utf8: ""},
EntityDefinition {name: "Dagger", latex_math_mode: false, latex: "\\textdaggerdbl{}", html: "&Dagger;", ascii: "[doubledagger]", utf8: ""},
EntityDefinition {name: "thinsp", latex_math_mode: false, latex: "\\hspace*{.2em}", html: "&thinsp;", ascii: " ", utf8: ""},
EntityDefinition {name: "curren", latex_math_mode: false, latex: "\\textcurrency{}", html: "&curren;", ascii: "curr.", utf8: "¤"},
EntityDefinition {name: "dollar", latex_math_mode: false, latex: "\\$", html: "$", ascii: "$", utf8: "$"},
EntityDefinition {name: "plusmn", latex_math_mode: false, latex: "\\textpm{}", html: "&plusmn;", ascii: "+-", utf8: "±"},
EntityDefinition {name: "frac12", latex_math_mode: false, latex: "\\textonehalf{}", html: "&frac12;", ascii: "1/2", utf8: "½"},
EntityDefinition {name: "frac14", latex_math_mode: false, latex: "\\textonequarter{}", html: "&frac14;", ascii: "1/4", utf8: "¼"},
EntityDefinition {name: "frac34", latex_math_mode: false, latex: "\\textthreequarters{}", html: "&frac34;", ascii: "3/4", utf8: "¾"},
EntityDefinition {name: "permil", latex_math_mode: false, latex: "\\textperthousand{}", html: "&permil;", ascii: "per thousand", utf8: ""},
EntityDefinition {name: "propto", latex_math_mode: true, latex: "\\propto", html: "&prop;", ascii: "[proportional to]", utf8: ""},
EntityDefinition {name: "there4", latex_math_mode: true, latex: "\\therefore", html: "&there4;", ascii: "[therefore]", utf8: ""},
EntityDefinition {name: "approx", latex_math_mode: true, latex: "\\approx", html: "&asymp;", ascii: "[almost equal to]", utf8: ""},
EntityDefinition {name: "preceq", latex_math_mode: true, latex: "\\preceq", html: "&prcue;", ascii: "[precedes or equal]", utf8: ""},
EntityDefinition {name: "succeq", latex_math_mode: true, latex: "\\succeq", html: "&sccue;", ascii: "[succeeds or equal]", utf8: ""},
EntityDefinition {name: "subset", latex_math_mode: true, latex: "\\subset", html: "&sub;", ascii: "[subset of]", utf8: ""},
EntityDefinition {name: "supset", latex_math_mode: true, latex: "\\supset", html: "&sup;", ascii: "[superset of]", utf8: ""},
EntityDefinition {name: "forall", latex_math_mode: true, latex: "\\forall", html: "&forall;", ascii: "[for all]", utf8: ""},
EntityDefinition {name: "exists", latex_math_mode: true, latex: "\\exists", html: "&exist;", ascii: "[there exists]", utf8: ""},
EntityDefinition {name: "nexist", latex_math_mode: true, latex: "\\nexists", html: "&exist;", ascii: "[there does not exists]", utf8: ""},
EntityDefinition {name: "lfloor", latex_math_mode: true, latex: "\\lfloor", html: "&lfloor;", ascii: "[left floor]", utf8: ""},
EntityDefinition {name: "rfloor", latex_math_mode: true, latex: "\\rfloor", html: "&rfloor;", ascii: "[right floor]", utf8: ""},
EntityDefinition {name: "langle", latex_math_mode: true, latex: "\\langle", html: "&lang;", ascii: "<", utf8: ""},
EntityDefinition {name: "rangle", latex_math_mode: true, latex: "\\rangle", html: "&rang;", ascii: ">", utf8: ""},
EntityDefinition {name: "arccos", latex_math_mode: true, latex: "\\arccos", html: "arccos", ascii: "arccos", utf8: "arccos"},
EntityDefinition {name: "arcsin", latex_math_mode: true, latex: "\\arcsin", html: "arcsin", ascii: "arcsin", utf8: "arcsin"},
EntityDefinition {name: "arctan", latex_math_mode: true, latex: "\\arctan", html: "arctan", ascii: "arctan", utf8: "arctan"},
EntityDefinition {name: "liminf", latex_math_mode: true, latex: "\\liminf", html: "liminf", ascii: "liminf", utf8: "liminf"},
EntityDefinition {name: "limsup", latex_math_mode: true, latex: "\\limsup", html: "limsup", ascii: "limsup", utf8: "limsup"},
EntityDefinition {name: "bullet", latex_math_mode: false, latex: "\\textbullet{}", html: "&bull;", ascii: "*", utf8: ""},
EntityDefinition {name: "lowast", latex_math_mode: true, latex: "\\ast", html: "&lowast;", ascii: "*", utf8: ""},
EntityDefinition {name: "otimes", latex_math_mode: true, latex: "\\otimes", html: "&otimes;", ascii: "[circled times]", utf8: ""},
EntityDefinition {name: "smiley", latex_math_mode: true, latex: "\\ddot\\smile", html: "&#9786;", ascii: ":-)", utf8: ""},
EntityDefinition {name: "frowny", latex_math_mode: true, latex: "\\ddot\\frown", html: "&#9785;", ascii: ":-(", utf8: ""},
EntityDefinition {name: "spades", latex_math_mode: true, latex: "\\spadesuit", html: "&spades;", ascii: "[spades]", utf8: ""},
EntityDefinition {name: "hearts", latex_math_mode: true, latex: "\\heartsuit", html: "&hearts;", ascii: "[hearts]", utf8: ""},
EntityDefinition {name: "_ ", latex_math_mode: false, latex: "\\hspace*{2.5em}", html: "&ensp;&ensp;&ensp;&ensp;&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "Acirc", latex_math_mode: false, latex: "\\^{A}", html: "&Acirc;", ascii: "A", utf8: "Â"},
EntityDefinition {name: "acirc", latex_math_mode: false, latex: "\\^{a}", html: "&acirc;", ascii: "a", utf8: "â"},
EntityDefinition {name: "Amacr", latex_math_mode: false, latex: "\\={A}", html: "&Amacr;", ascii: "A", utf8: "Ã"},
EntityDefinition {name: "amacr", latex_math_mode: false, latex: "\\={a}", html: "&amacr;", ascii: "a", utf8: "ã"},
EntityDefinition {name: "Aring", latex_math_mode: false, latex: "\\AA{}", html: "&Aring;", ascii: "A", utf8: "Å"},
EntityDefinition {name: "aring", latex_math_mode: false, latex: "\\aa{}", html: "&aring;", ascii: "a", utf8: "å"},
EntityDefinition {name: "AElig", latex_math_mode: false, latex: "\\AE{}", html: "&AElig;", ascii: "AE", utf8: "Æ"},
EntityDefinition {name: "aelig", latex_math_mode: false, latex: "\\ae{}", html: "&aelig;", ascii: "ae", utf8: "æ"},
EntityDefinition {name: "Ecirc", latex_math_mode: false, latex: "\\^{E}", html: "&Ecirc;", ascii: "E", utf8: "Ê"},
EntityDefinition {name: "ecirc", latex_math_mode: false, latex: "\\^{e}", html: "&ecirc;", ascii: "e", utf8: "ê"},
EntityDefinition {name: "Icirc", latex_math_mode: false, latex: "\\^{I}", html: "&Icirc;", ascii: "I", utf8: "Î"},
EntityDefinition {name: "icirc", latex_math_mode: false, latex: "\\^{i}", html: "&icirc;", ascii: "i", utf8: "î"},
EntityDefinition {name: "Ocirc", latex_math_mode: false, latex: "\\^{O}", html: "&Ocirc;", ascii: "O", utf8: "Ô"},
EntityDefinition {name: "ocirc", latex_math_mode: false, latex: "\\^{o}", html: "&ocirc;", ascii: "o", utf8: "ô"},
EntityDefinition {name: "OElig", latex_math_mode: false, latex: "\\OE{}", html: "&OElig;", ascii: "OE", utf8: "Œ"},
EntityDefinition {name: "oelig", latex_math_mode: false, latex: "\\oe{}", html: "&oelig;", ascii: "oe", utf8: "œ"},
EntityDefinition {name: "szlig", latex_math_mode: false, latex: "\\ss{}", html: "&szlig;", ascii: "ss", utf8: "ß"},
EntityDefinition {name: "Ucirc", latex_math_mode: false, latex: "\\^{U}", html: "&Ucirc;", ascii: "U", utf8: "Û"},
EntityDefinition {name: "ucirc", latex_math_mode: false, latex: "\\^{u}", html: "&ucirc;", ascii: "u", utf8: "û"},
EntityDefinition {name: "image", latex_math_mode: true, latex: "\\Im", html: "&image;", ascii: "I", utf8: ""},
EntityDefinition {name: "imath", latex_math_mode: true, latex: "\\imath", html: "&imath;", ascii: "[dotless i]", utf8: "ı"},
EntityDefinition {name: "jmath", latex_math_mode: true, latex: "\\jmath", html: "&jmath;", ascii: "[dotless j]", utf8: "ȷ"},
EntityDefinition {name: "Alpha", latex_math_mode: false, latex: "A", html: "&Alpha;", ascii: "Alpha", utf8: "Α"},
EntityDefinition {name: "alpha", latex_math_mode: true, latex: "\\alpha", html: "&alpha;", ascii: "alpha", utf8: "α"},
EntityDefinition {name: "Gamma", latex_math_mode: true, latex: "\\Gamma", html: "&Gamma;", ascii: "Gamma", utf8: "Γ"},
EntityDefinition {name: "gamma", latex_math_mode: true, latex: "\\gamma", html: "&gamma;", ascii: "gamma", utf8: "γ"},
EntityDefinition {name: "Delta", latex_math_mode: true, latex: "\\Delta", html: "&Delta;", ascii: "Delta", utf8: "Δ"},
EntityDefinition {name: "delta", latex_math_mode: true, latex: "\\delta", html: "&delta;", ascii: "delta", utf8: "δ"},
EntityDefinition {name: "Theta", latex_math_mode: true, latex: "\\Theta", html: "&Theta;", ascii: "Theta", utf8: "Θ"},
EntityDefinition {name: "theta", latex_math_mode: true, latex: "\\theta", html: "&theta;", ascii: "theta", utf8: "θ"},
EntityDefinition {name: "Kappa", latex_math_mode: false, latex: "K", html: "&Kappa;", ascii: "Kappa", utf8: "Κ"},
EntityDefinition {name: "kappa", latex_math_mode: true, latex: "\\kappa", html: "&kappa;", ascii: "kappa", utf8: "κ"},
EntityDefinition {name: "Sigma", latex_math_mode: true, latex: "\\Sigma", html: "&Sigma;", ascii: "Sigma", utf8: "Σ"},
EntityDefinition {name: "sigma", latex_math_mode: true, latex: "\\sigma", html: "&sigma;", ascii: "sigma", utf8: "σ"},
EntityDefinition {name: "upsih", latex_math_mode: true, latex: "\\Upsilon", html: "&upsih;", ascii: "upsilon", utf8: "ϒ"},
EntityDefinition {name: "Omega", latex_math_mode: true, latex: "\\Omega", html: "&Omega;", ascii: "Omega", utf8: "Ω"},
EntityDefinition {name: "omega", latex_math_mode: true, latex: "\\omega", html: "&omega;", ascii: "omega", utf8: "ω"},
EntityDefinition {name: "varpi", latex_math_mode: true, latex: "\\varpi", html: "&piv;", ascii: "omega-pi", utf8: "ϖ"},
EntityDefinition {name: "aleph", latex_math_mode: true, latex: "\\aleph", html: "&aleph;", ascii: "aleph", utf8: ""},
EntityDefinition {name: "gimel", latex_math_mode: true, latex: "\\gimel", html: "&gimel;", ascii: "gimel", utf8: ""},
EntityDefinition {name: "dalet", latex_math_mode: true, latex: "\\daleth", html: "&daleth;", ascii: "dalet", utf8: "ד"},
EntityDefinition {name: "THORN", latex_math_mode: false, latex: "\\TH{}", html: "&THORN;", ascii: "TH", utf8: "Þ"},
EntityDefinition {name: "thorn", latex_math_mode: false, latex: "\\th{}", html: "&thorn;", ascii: "th", utf8: "þ"},
EntityDefinition {name: "cdots", latex_math_mode: true, latex: "\\cdots{}", html: "&ctdot;", ascii: "...", utf8: ""},
EntityDefinition {name: "iexcl", latex_math_mode: false, latex: "!`", html: "&iexcl;", ascii: "!", utf8: "¡"},
EntityDefinition {name: "ndash", latex_math_mode: false, latex: "--", html: "&ndash;", ascii: "-", utf8: ""},
EntityDefinition {name: "mdash", latex_math_mode: false, latex: "---", html: "&mdash;", ascii: "--", utf8: ""},
EntityDefinition {name: "acute", latex_math_mode: false, latex: "\\textasciiacute{}", html: "&acute;", ascii: "'", utf8: "´"},
EntityDefinition {name: "ldquo", latex_math_mode: false, latex: "\\textquotedblleft{}", html: "&ldquo;", ascii: "\"", utf8: ""},
EntityDefinition {name: "rdquo", latex_math_mode: false, latex: "\\textquotedblright{}", html: "&rdquo;", ascii: "\"", utf8: ""},
EntityDefinition {name: "bdquo", latex_math_mode: false, latex: "\\quotedblbase{}", html: "&bdquo;", ascii: "\"", utf8: ""},
EntityDefinition {name: "lsquo", latex_math_mode: false, latex: "\\textquoteleft{}", html: "&lsquo;", ascii: "`", utf8: ""},
EntityDefinition {name: "rsquo", latex_math_mode: false, latex: "\\textquoteright{}", html: "&rsquo;", ascii: "'", utf8: ""},
EntityDefinition {name: "sbquo", latex_math_mode: false, latex: "\\quotesinglbase{}", html: "&sbquo;", ascii: ",", utf8: ""},
EntityDefinition {name: "laquo", latex_math_mode: false, latex: "\\guillemotleft{}", html: "&laquo;", ascii: "<<", utf8: "«"},
EntityDefinition {name: "raquo", latex_math_mode: false, latex: "\\guillemotright{}", html: "&raquo;", ascii: ">>", utf8: "»"},
EntityDefinition {name: "tilde", latex_math_mode: false, latex: "\\textasciitilde{}", html: "~", ascii: "~", utf8: "~"},
EntityDefinition {name: "slash", latex_math_mode: false, latex: "/", html: "/", ascii: "/", utf8: "/"},
EntityDefinition {name: "under", latex_math_mode: false, latex: "\\_", html: "_", ascii: "_", utf8: "_"},
EntityDefinition {name: "equal", latex_math_mode: false, latex: "=", html: "=", ascii: "=", utf8: "="},
EntityDefinition {name: "pound", latex_math_mode: false, latex: "\\pounds{}", html: "&pound;", ascii: "pound", utf8: "£"},
EntityDefinition {name: "trade", latex_math_mode: false, latex: "\\texttrademark{}", html: "&trade;", ascii: "TM", utf8: ""},
EntityDefinition {name: "minus", latex_math_mode: true, latex: "-", html: "&minus;", ascii: "-", utf8: ""},
EntityDefinition {name: "times", latex_math_mode: false, latex: "\\texttimes{}", html: "&times;", ascii: "*", utf8: "×"},
EntityDefinition {name: "frasl", latex_math_mode: false, latex: "/", html: "&frasl;", ascii: "/", utf8: ""},
EntityDefinition {name: "colon", latex_math_mode: true, latex: "\\colon", html: ":", ascii: ":", utf8: ":"},
EntityDefinition {name: "radic", latex_math_mode: true, latex: "\\sqrt{\\,}", html: "&radic;", ascii: "[square root]", utf8: ""},
EntityDefinition {name: "micro", latex_math_mode: false, latex: "\\textmu{}", html: "&micro;", ascii: "micro", utf8: "µ"},
EntityDefinition {name: "prime", latex_math_mode: true, latex: "\\prime", html: "&prime;", ascii: "'", utf8: ""},
EntityDefinition {name: "Prime", latex_math_mode: true, latex: "\\prime{}\\prime", html: "&Prime;", ascii: "''", utf8: ""},
EntityDefinition {name: "infin", latex_math_mode: true, latex: "\\infty", html: "&infin;", ascii: "[infinity]", utf8: ""},
EntityDefinition {name: "infty", latex_math_mode: true, latex: "\\infty", html: "&infin;", ascii: "[infinity]", utf8: ""},
EntityDefinition {name: "wedge", latex_math_mode: true, latex: "\\wedge", html: "&and;", ascii: "[logical and]", utf8: ""},
EntityDefinition {name: "smile", latex_math_mode: true, latex: "\\smile", html: "&smile;", ascii: "[cup product]", utf8: ""},
EntityDefinition {name: "frown", latex_math_mode: true, latex: "\\frown", html: "&frown;", ascii: "[Cap product]", utf8: ""},
EntityDefinition {name: "simeq", latex_math_mode: true, latex: "\\simeq", html: "&cong;", ascii: "[approx. equal to]", utf8: ""},
EntityDefinition {name: "asymp", latex_math_mode: true, latex: "\\asymp", html: "&asymp;", ascii: "[almost equal to]", utf8: ""},
EntityDefinition {name: "equiv", latex_math_mode: true, latex: "\\equiv", html: "&equiv;", ascii: "[identical to]", utf8: ""},
EntityDefinition {name: "exist", latex_math_mode: true, latex: "\\exists", html: "&exist;", ascii: "[there exists]", utf8: ""},
EntityDefinition {name: "empty", latex_math_mode: true, latex: "\\emptyset", html: "&empty;", ascii: "[empty set]", utf8: ""},
EntityDefinition {name: "notin", latex_math_mode: true, latex: "\\notin", html: "&notin;", ascii: "[not an element of]", utf8: ""},
EntityDefinition {name: "nabla", latex_math_mode: true, latex: "\\nabla", html: "&nabla;", ascii: "[nabla]", utf8: ""},
EntityDefinition {name: "angle", latex_math_mode: true, latex: "\\angle", html: "&ang;", ascii: "[angle]", utf8: ""},
EntityDefinition {name: "lceil", latex_math_mode: true, latex: "\\lceil", html: "&lceil;", ascii: "[left ceiling]", utf8: ""},
EntityDefinition {name: "rceil", latex_math_mode: true, latex: "\\rceil", html: "&rceil;", ascii: "[right ceiling]", utf8: ""},
EntityDefinition {name: "crarr", latex_math_mode: true, latex: "\\hookleftarrow", html: "&crarr;", ascii: "<-'", utf8: ""},
EntityDefinition {name: "oplus", latex_math_mode: true, latex: "\\oplus", html: "&oplus;", ascii: "[circled plus]", utf8: ""},
EntityDefinition {name: "check", latex_math_mode: true, latex: "\\checkmark", html: "&checkmark;", ascii: "[checkmark]", utf8: ""},
EntityDefinition {name: "cedil", latex_math_mode: false, latex: "\\c{}", html: "&cedil;", ascii: "[cedilla]", utf8: "¸"},
EntityDefinition {name: "oline", latex_math_mode: true, latex: "\\overline{~}", html: "&oline;", ascii: "[overline]", utf8: ""},
EntityDefinition {name: "clubs", latex_math_mode: true, latex: "\\clubsuit", html: "&clubs;", ascii: "[clubs]", utf8: ""},
EntityDefinition {name: "diams", latex_math_mode: true, latex: "\\diamondsuit", html: "&diams;", ascii: "[diamonds]", utf8: ""},
EntityDefinition {name: "_ ", latex_math_mode: false, latex: "\\hspace*{2.0em}", html: "&ensp;&ensp;&ensp;&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "Auml", latex_math_mode: false, latex: "\\\"{A}", html: "&Auml;", ascii: "Ae", utf8: "Ä"},
EntityDefinition {name: "auml", latex_math_mode: false, latex: "\\\"{a}", html: "&auml;", ascii: "ae", utf8: "ä"},
EntityDefinition {name: "Euml", latex_math_mode: false, latex: "\\\"{E}", html: "&Euml;", ascii: "E", utf8: "Ë"},
EntityDefinition {name: "euml", latex_math_mode: false, latex: "\\\"{e}", html: "&euml;", ascii: "e", utf8: "ë"},
EntityDefinition {name: "Idot", latex_math_mode: false, latex: "\\.{I}", html: "&idot;", ascii: "I", utf8: "İ"},
EntityDefinition {name: "Iuml", latex_math_mode: false, latex: "\\\"{I}", html: "&Iuml;", ascii: "I", utf8: "Ï"},
EntityDefinition {name: "iuml", latex_math_mode: false, latex: "\\\"{i}", html: "&iuml;", ascii: "i", utf8: "ï"},
EntityDefinition {name: "Ouml", latex_math_mode: false, latex: "\\\"{O}", html: "&Ouml;", ascii: "Oe", utf8: "Ö"},
EntityDefinition {name: "ouml", latex_math_mode: false, latex: "\\\"{o}", html: "&ouml;", ascii: "oe", utf8: "ö"},
EntityDefinition {name: "Uuml", latex_math_mode: false, latex: "\\\"{U}", html: "&Uuml;", ascii: "Ue", utf8: "Ü"},
EntityDefinition {name: "uuml", latex_math_mode: false, latex: "\\\"{u}", html: "&uuml;", ascii: "ue", utf8: "ü"},
EntityDefinition {name: "Yuml", latex_math_mode: false, latex: "\\\"{Y}", html: "&Yuml;", ascii: "Y", utf8: "Ÿ"},
EntityDefinition {name: "yuml", latex_math_mode: false, latex: "\\\"{y}", html: "&yuml;", ascii: "y", utf8: "ÿ"},
EntityDefinition {name: "fnof", latex_math_mode: false, latex: "\\textit{f}", html: "&fnof;", ascii: "f", utf8: "ƒ"},
EntityDefinition {name: "real", latex_math_mode: true, latex: "\\Re", html: "&real;", ascii: "R", utf8: ""},
EntityDefinition {name: "Beta", latex_math_mode: false, latex: "B", html: "&Beta;", ascii: "Beta", utf8: "Β"},
EntityDefinition {name: "beta", latex_math_mode: true, latex: "\\beta", html: "&beta;", ascii: "beta", utf8: "β"},
EntityDefinition {name: "Zeta", latex_math_mode: false, latex: "Z", html: "&Zeta;", ascii: "Zeta", utf8: "Ζ"},
EntityDefinition {name: "zeta", latex_math_mode: true, latex: "\\zeta", html: "&zeta;", ascii: "zeta", utf8: "ζ"},
EntityDefinition {name: "Iota", latex_math_mode: false, latex: "I", html: "&Iota;", ascii: "Iota", utf8: "Ι"},
EntityDefinition {name: "iota", latex_math_mode: true, latex: "\\iota", html: "&iota;", ascii: "iota", utf8: "ι"},
EntityDefinition {name: "beth", latex_math_mode: true, latex: "\\beth", html: "&beth;", ascii: "beth", utf8: "ב"},
EntityDefinition {name: "dots", latex_math_mode: false, latex: "\\dots{}", html: "&hellip;", ascii: "...", utf8: ""},
EntityDefinition {name: "quot", latex_math_mode: false, latex: "\\textquotedbl{}", html: "&quot;", ascii: "\"", utf8: "\""},
EntityDefinition {name: "circ", latex_math_mode: false, latex: "\\^{}", html: "&circ;", ascii: "^", utf8: ""},
EntityDefinition {name: "vert", latex_math_mode: true, latex: "\\vert{}", html: "&vert;", ascii: "|", utf8: "|"},
EntityDefinition {name: "vbar", latex_math_mode: false, latex: "|", html: "|", ascii: "|", utf8: "|"},
EntityDefinition {name: "sect", latex_math_mode: false, latex: "\\S", html: "&sect;", ascii: "section", utf8: "§"},
EntityDefinition {name: "para", latex_math_mode: false, latex: "\\P{}", html: "&para;", ascii: "paragraph", utf8: ""},
EntityDefinition {name: "plus", latex_math_mode: false, latex: "+", html: "+", ascii: "+", utf8: "+"},
EntityDefinition {name: "ddag", latex_math_mode: false, latex: "\\ddag{}", html: "&Dagger;", ascii: "[doubledagger]", utf8: ""},
EntityDefinition {name: "nbsp", latex_math_mode: false, latex: "~", html: "&nbsp;", ascii: " ", utf8: " "},
EntityDefinition {name: "ensp", latex_math_mode: false, latex: "\\hspace*{.5em}", html: "&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "emsp", latex_math_mode: false, latex: "\\hspace*{1em}", html: "&emsp;", ascii: " ", utf8: ""},
EntityDefinition {name: "cent", latex_math_mode: false, latex: "\\textcent{}", html: "&cent;", ascii: "cent", utf8: "¢"},
EntityDefinition {name: "euro", latex_math_mode: false, latex: "\\texteuro{}", html: "&euro;", ascii: "EUR", utf8: ""},
EntityDefinition {name: "copy", latex_math_mode: false, latex: "\\textcopyright{}", html: "&copy;", ascii: "(c)", utf8: "©"},
EntityDefinition {name: "sup1", latex_math_mode: false, latex: "\\textonesuperior{}", html: "&sup1;", ascii: "^1", utf8: "¹"},
EntityDefinition {name: "sup2", latex_math_mode: false, latex: "\\texttwosuperior{}", html: "&sup2;", ascii: "^2", utf8: "²"},
EntityDefinition {name: "sup3", latex_math_mode: false, latex: "\\textthreesuperior{}", html: "&sup3;", ascii: "^3", utf8: "³"},
EntityDefinition {name: "prod", latex_math_mode: true, latex: "\\prod", html: "&prod;", ascii: "[product]", utf8: ""},
EntityDefinition {name: "macr", latex_math_mode: false, latex: "\\textasciimacron{}", html: "&macr;", ascii: "[macron]", utf8: "¯"},
EntityDefinition {name: "prop", latex_math_mode: true, latex: "\\propto", html: "&prop;", ascii: "[proportional to]", utf8: ""},
EntityDefinition {name: "land", latex_math_mode: true, latex: "\\land", html: "&and;", ascii: "[logical and]", utf8: ""},
EntityDefinition {name: "cong", latex_math_mode: true, latex: "\\cong", html: "&cong;", ascii: "[approx. equal to]", utf8: ""},
EntityDefinition {name: "prec", latex_math_mode: true, latex: "\\prec", html: "&pr;", ascii: "[precedes]", utf8: ""},
EntityDefinition {name: "succ", latex_math_mode: true, latex: "\\succ", html: "&sc;", ascii: "[succeeds]", utf8: ""},
EntityDefinition {name: "nsub", latex_math_mode: true, latex: "\\not\\subset", html: "&nsub;", ascii: "[not a subset of]", utf8: ""},
EntityDefinition {name: "sube", latex_math_mode: true, latex: "\\subseteq", html: "&sube;", ascii: "[subset of or equal to]", utf8: ""},
EntityDefinition {name: "nsup", latex_math_mode: true, latex: "\\not\\supset", html: "&nsup;", ascii: "[not a superset of]", utf8: ""},
EntityDefinition {name: "supe", latex_math_mode: true, latex: "\\supseteq", html: "&supe;", ascii: "[superset of or equal to]", utf8: ""},
EntityDefinition {name: "isin", latex_math_mode: true, latex: "\\in", html: "&isin;", ascii: "[element of]", utf8: ""},
EntityDefinition {name: "perp", latex_math_mode: true, latex: "\\perp", html: "&perp;", ascii: "[up tack]", utf8: ""},
EntityDefinition {name: "sdot", latex_math_mode: true, latex: "\\cdot", html: "&sdot;", ascii: "[dot]", utf8: ""},
EntityDefinition {name: "cdot", latex_math_mode: true, latex: "\\cdot", html: "&sdot;", ascii: "[dot]", utf8: ""},
EntityDefinition {name: "lang", latex_math_mode: true, latex: "\\langle", html: "&lang;", ascii: "<", utf8: ""},
EntityDefinition {name: "rang", latex_math_mode: true, latex: "\\rangle", html: "&rang;", ascii: ">", utf8: ""},
EntityDefinition {name: "hbar", latex_math_mode: true, latex: "\\hbar", html: "&hbar;", ascii: "hbar", utf8: ""},
EntityDefinition {name: "larr", latex_math_mode: true, latex: "\\leftarrow", html: "&larr;", ascii: "<-", utf8: ""},
EntityDefinition {name: "gets", latex_math_mode: true, latex: "\\gets", html: "&larr;", ascii: "<-", utf8: ""},
EntityDefinition {name: "lArr", latex_math_mode: true, latex: "\\Leftarrow", html: "&lArr;", ascii: "<=", utf8: ""},
EntityDefinition {name: "uarr", latex_math_mode: true, latex: "\\uparrow", html: "&uarr;", ascii: "[uparrow]", utf8: ""},
EntityDefinition {name: "uArr", latex_math_mode: true, latex: "\\Uparrow", html: "&uArr;", ascii: "[dbluparrow]", utf8: ""},
EntityDefinition {name: "rarr", latex_math_mode: true, latex: "\\rightarrow", html: "&rarr;", ascii: "->", utf8: ""},
EntityDefinition {name: "rArr", latex_math_mode: true, latex: "\\Rightarrow", html: "&rArr;", ascii: "=>", utf8: ""},
EntityDefinition {name: "darr", latex_math_mode: true, latex: "\\downarrow", html: "&darr;", ascii: "[downarrow]", utf8: ""},
EntityDefinition {name: "dArr", latex_math_mode: true, latex: "\\Downarrow", html: "&dArr;", ascii: "[dbldownarrow]", utf8: ""},
EntityDefinition {name: "harr", latex_math_mode: true, latex: "\\leftrightarrow", html: "&harr;", ascii: "<->", utf8: ""},
EntityDefinition {name: "hArr", latex_math_mode: true, latex: "\\Leftrightarrow", html: "&hArr;", ascii: "<=>", utf8: ""},
EntityDefinition {name: "cosh", latex_math_mode: true, latex: "\\cosh", html: "cosh", ascii: "cosh", utf8: "cosh"},
EntityDefinition {name: "coth", latex_math_mode: true, latex: "\\coth", html: "coth", ascii: "coth", utf8: "coth"},
EntityDefinition {name: "sinh", latex_math_mode: true, latex: "\\sinh", html: "sinh", ascii: "sinh", utf8: "sinh"},
EntityDefinition {name: "tanh", latex_math_mode: true, latex: "\\tanh", html: "tanh", ascii: "tanh", utf8: "tanh"},
EntityDefinition {name: "bull", latex_math_mode: false, latex: "\\textbullet{}", html: "&bull;", ascii: "*", utf8: ""},
EntityDefinition {name: "star", latex_math_mode: true, latex: "\\star", html: "*", ascii: "*", utf8: ""},
EntityDefinition {name: "odot", latex_math_mode: true, latex: "\\odot", html: "o", ascii: "[circled dot]", utf8: "ʘ"},
EntityDefinition {name: "ordf", latex_math_mode: false, latex: "\\textordfeminine{}", html: "&ordf;", ascii: "_a_", utf8: "ª"},
EntityDefinition {name: "ordm", latex_math_mode: false, latex: "\\textordmasculine{}", html: "&ordm;", ascii: "_o_", utf8: "º"},
EntityDefinition {name: "zwnj", latex_math_mode: false, latex: "\\/{}", html: "&zwnj;", ascii: "", utf8: ""},
EntityDefinition {name: "_ ", latex_math_mode: false, latex: "\\hspace*{1.5em}", html: "&ensp;&ensp;&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "ell", latex_math_mode: true, latex: "\\ell", html: "&ell;", ascii: "ell", utf8: ""},
EntityDefinition {name: "Eta", latex_math_mode: false, latex: "H", html: "&Eta;", ascii: "Eta", utf8: "Η"},
EntityDefinition {name: "eta", latex_math_mode: true, latex: "\\eta", html: "&eta;", ascii: "eta", utf8: "η"},
EntityDefinition {name: "Rho", latex_math_mode: false, latex: "P", html: "&Rho;", ascii: "Rho", utf8: "Ρ"},
EntityDefinition {name: "rho", latex_math_mode: true, latex: "\\rho", html: "&rho;", ascii: "rho", utf8: "ρ"},
EntityDefinition {name: "Tau", latex_math_mode: false, latex: "T", html: "&Tau;", ascii: "Tau", utf8: "Τ"},
EntityDefinition {name: "Phi", latex_math_mode: true, latex: "\\Phi", html: "&Phi;", ascii: "Phi", utf8: "Φ"},
EntityDefinition {name: "phi", latex_math_mode: true, latex: "\\phi", html: "&phi;", ascii: "phi", utf8: "ɸ"},
EntityDefinition {name: "Chi", latex_math_mode: false, latex: "X", html: "&Chi;", ascii: "Chi", utf8: "Χ"},
EntityDefinition {name: "chi", latex_math_mode: true, latex: "\\chi", html: "&chi;", ascii: "chi", utf8: "χ"},
EntityDefinition {name: "Psi", latex_math_mode: true, latex: "\\Psi", html: "&Psi;", ascii: "Psi", utf8: "Ψ"},
EntityDefinition {name: "psi", latex_math_mode: true, latex: "\\psi", html: "&psi;", ascii: "psi", utf8: "ψ"},
EntityDefinition {name: "tau", latex_math_mode: true, latex: "\\tau", html: "&tau;", ascii: "tau", utf8: "τ"},
EntityDefinition {name: "piv", latex_math_mode: true, latex: "\\varpi", html: "&piv;", ascii: "omega-pi", utf8: "ϖ"},
EntityDefinition {name: "ETH", latex_math_mode: false, latex: "\\DH{}", html: "&ETH;", ascii: "D", utf8: "Ð"},
EntityDefinition {name: "eth", latex_math_mode: false, latex: "\\dh{}", html: "&eth;", ascii: "dh", utf8: "ð"},
EntityDefinition {name: "shy", latex_math_mode: false, latex: "\\-", html: "&shy;", ascii: "", utf8: ""},
EntityDefinition {name: "amp", latex_math_mode: false, latex: "\\&", html: "&amp;", ascii: "&", utf8: "&"},
EntityDefinition {name: "dag", latex_math_mode: false, latex: "\\dag{}", html: "&dagger;", ascii: "[dagger]", utf8: ""},
EntityDefinition {name: "yen", latex_math_mode: false, latex: "\\textyen{}", html: "&yen;", ascii: "yen", utf8: "¥"},
EntityDefinition {name: "EUR", latex_math_mode: false, latex: "\\texteuro{}", html: "&euro;", ascii: "EUR", utf8: ""},
EntityDefinition {name: "USD", latex_math_mode: false, latex: "\\$", html: "$", ascii: "$", utf8: "$"},
EntityDefinition {name: "reg", latex_math_mode: false, latex: "\\textregistered{}", html: "&reg;", ascii: "(r)", utf8: "®"},
EntityDefinition {name: "div", latex_math_mode: false, latex: "\\textdiv{}", html: "&divide;", ascii: "/", utf8: "÷"},
EntityDefinition {name: "sum", latex_math_mode: true, latex: "\\sum", html: "&sum;", ascii: "[sum]", utf8: ""},
EntityDefinition {name: "deg", latex_math_mode: false, latex: "\\textdegree{}", html: "&deg;", ascii: "degree", utf8: "°"},
EntityDefinition {name: "not", latex_math_mode: false, latex: "\\textlnot{}", html: "&not;", ascii: "[angled dash]", utf8: "¬"},
EntityDefinition {name: "neg", latex_math_mode: true, latex: "\\neg{}", html: "&not;", ascii: "[angled dash]", utf8: "¬"},
EntityDefinition {name: "lor", latex_math_mode: true, latex: "\\lor", html: "&or;", ascii: "[logical or]", utf8: ""},
EntityDefinition {name: "vee", latex_math_mode: true, latex: "\\vee", html: "&or;", ascii: "[logical or]", utf8: ""},
EntityDefinition {name: "cap", latex_math_mode: true, latex: "\\cap", html: "&cap;", ascii: "[intersection]", utf8: ""},
EntityDefinition {name: "cup", latex_math_mode: true, latex: "\\cup", html: "&cup;", ascii: "[union]", utf8: ""},
EntityDefinition {name: "int", latex_math_mode: true, latex: "\\int", html: "&int;", ascii: "[integral]", utf8: ""},
EntityDefinition {name: "sim", latex_math_mode: true, latex: "\\sim", html: "&sim;", ascii: "~", utf8: ""},
EntityDefinition {name: "neq", latex_math_mode: true, latex: "\\neq", html: "&ne;", ascii: "[not equal to]", utf8: ""},
EntityDefinition {name: "leq", latex_math_mode: true, latex: "\\le", html: "&le;", ascii: "<=", utf8: ""},
EntityDefinition {name: "geq", latex_math_mode: true, latex: "\\ge", html: "&ge;", ascii: ">=", utf8: ""},
EntityDefinition {name: "lll", latex_math_mode: true, latex: "\\lll", html: "&Ll;", ascii: "<<<", utf8: ""},
EntityDefinition {name: "ggg", latex_math_mode: true, latex: "\\ggg", html: "&Gg;", ascii: ">>>", utf8: ""},
EntityDefinition {name: "sub", latex_math_mode: true, latex: "\\subset", html: "&sub;", ascii: "[subset of]", utf8: ""},
EntityDefinition {name: "sup", latex_math_mode: true, latex: "\\supset", html: "&sup;", ascii: "[superset of]", utf8: ""},
EntityDefinition {name: "ang", latex_math_mode: true, latex: "\\angle", html: "&ang;", ascii: "[angle]", utf8: ""},
EntityDefinition {name: "mho", latex_math_mode: true, latex: "\\mho", html: "&mho;", ascii: "mho", utf8: ""},
EntityDefinition {name: "arg", latex_math_mode: true, latex: "\\arg", html: "arg", ascii: "arg", utf8: "arg"},
EntityDefinition {name: "cos", latex_math_mode: true, latex: "\\cos", html: "cos", ascii: "cos", utf8: "cos"},
EntityDefinition {name: "cot", latex_math_mode: true, latex: "\\cot", html: "cot", ascii: "cot", utf8: "cot"},
EntityDefinition {name: "csc", latex_math_mode: true, latex: "\\csc", html: "csc", ascii: "csc", utf8: "csc"},
EntityDefinition {name: "deg", latex_math_mode: true, latex: "\\deg", html: "&deg;", ascii: "deg", utf8: "deg"},
EntityDefinition {name: "det", latex_math_mode: true, latex: "\\det", html: "det", ascii: "det", utf8: "det"},
EntityDefinition {name: "dim", latex_math_mode: true, latex: "\\dim", html: "dim", ascii: "dim", utf8: "dim"},
EntityDefinition {name: "exp", latex_math_mode: true, latex: "\\exp", html: "exp", ascii: "exp", utf8: "exp"},
EntityDefinition {name: "gcd", latex_math_mode: true, latex: "\\gcd", html: "gcd", ascii: "gcd", utf8: "gcd"},
EntityDefinition {name: "hom", latex_math_mode: true, latex: "\\hom", html: "hom", ascii: "hom", utf8: "hom"},
EntityDefinition {name: "inf", latex_math_mode: true, latex: "\\inf", html: "inf", ascii: "inf", utf8: "inf"},
EntityDefinition {name: "ker", latex_math_mode: true, latex: "\\ker", html: "ker", ascii: "ker", utf8: "ker"},
EntityDefinition {name: "lim", latex_math_mode: true, latex: "\\lim", html: "lim", ascii: "lim", utf8: "lim"},
EntityDefinition {name: "log", latex_math_mode: true, latex: "\\log", html: "log", ascii: "log", utf8: "log"},
EntityDefinition {name: "max", latex_math_mode: true, latex: "\\max", html: "max", ascii: "max", utf8: "max"},
EntityDefinition {name: "min", latex_math_mode: true, latex: "\\min", html: "min", ascii: "min", utf8: "min"},
EntityDefinition {name: "sec", latex_math_mode: true, latex: "\\sec", html: "sec", ascii: "sec", utf8: "sec"},
EntityDefinition {name: "sin", latex_math_mode: true, latex: "\\sin", html: "sin", ascii: "sin", utf8: "sin"},
EntityDefinition {name: "sup", latex_math_mode: true, latex: "\\sup", html: "&sup;", ascii: "sup", utf8: "sup"},
EntityDefinition {name: "tan", latex_math_mode: true, latex: "\\tan", html: "tan", ascii: "tan", utf8: "tan"},
EntityDefinition {name: "ast", latex_math_mode: true, latex: "\\ast", html: "&lowast;", ascii: "*", utf8: "*"},
EntityDefinition {name: "uml", latex_math_mode: false, latex: "\\textasciidieresis{}", html: "&uml;", ascii: "[diaeresis]", utf8: "¨"},
EntityDefinition {name: "zwj", latex_math_mode: false, latex: "", html: "&zwj;", ascii: "", utf8: ""},
EntityDefinition {name: "lrm", latex_math_mode: false, latex: "", html: "&lrm;", ascii: "", utf8: ""},
EntityDefinition {name: "rlm", latex_math_mode: false, latex: "", html: "&rlm;", ascii: "", utf8: ""},
EntityDefinition {name: "sad", latex_math_mode: true, latex: "\\ddot\\frown", html: "&#9785;", ascii: ":-(", utf8: ""},
EntityDefinition {name: "loz", latex_math_mode: true, latex: "\\lozenge", html: "&loz;", ascii: "[lozenge]", utf8: ""},
EntityDefinition {name: "_ ", latex_math_mode: false, latex: "\\hspace*{1.0em}", html: "&ensp;&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "AA", latex_math_mode: false, latex: "\\AA{}", html: "&Aring;", ascii: "A", utf8: "Å"},
EntityDefinition {name: "Mu", latex_math_mode: false, latex: "M", html: "&Mu;", ascii: "Mu", utf8: "Μ"},
EntityDefinition {name: "mu", latex_math_mode: true, latex: "\\mu", html: "&mu;", ascii: "mu", utf8: "μ"},
EntityDefinition {name: "nu", latex_math_mode: true, latex: "\\nu", html: "&nu;", ascii: "nu", utf8: "ν"},
EntityDefinition {name: "Nu", latex_math_mode: false, latex: "N", html: "&Nu;", ascii: "Nu", utf8: "Ν"},
EntityDefinition {name: "Xi", latex_math_mode: true, latex: "\\Xi", html: "&Xi;", ascii: "Xi", utf8: "Ξ"},
EntityDefinition {name: "xi", latex_math_mode: true, latex: "\\xi", html: "&xi;", ascii: "xi", utf8: "ξ"},
EntityDefinition {name: "Pi", latex_math_mode: true, latex: "\\Pi", html: "&Pi;", ascii: "Pi", utf8: "Π"},
EntityDefinition {name: "pi", latex_math_mode: true, latex: "\\pi", html: "&pi;", ascii: "pi", utf8: "π"},
EntityDefinition {name: "lt", latex_math_mode: false, latex: "\\textless{}", html: "&lt;", ascii: "<", utf8: "<"},
EntityDefinition {name: "gt", latex_math_mode: false, latex: "\\textgreater{}", html: "&gt;", ascii: ">", utf8: ">"},
EntityDefinition {name: "pm", latex_math_mode: false, latex: "\\textpm{}", html: "&plusmn;", ascii: "+-", utf8: "±"},
EntityDefinition {name: "ne", latex_math_mode: true, latex: "\\ne", html: "&ne;", ascii: "[not equal to]", utf8: ""},
EntityDefinition {name: "le", latex_math_mode: true, latex: "\\le", html: "&le;", ascii: "<=", utf8: ""},
EntityDefinition {name: "ge", latex_math_mode: true, latex: "\\ge", html: "&ge;", ascii: ">=", utf8: ""},
EntityDefinition {name: "ll", latex_math_mode: true, latex: "\\ll", html: "&Lt;", ascii: "<<", utf8: ""},
EntityDefinition {name: "Ll", latex_math_mode: true, latex: "\\lll", html: "&Ll;", ascii: "<<<", utf8: ""},
EntityDefinition {name: "gg", latex_math_mode: true, latex: "\\gg", html: "&Gt;", ascii: ">>", utf8: ""},
EntityDefinition {name: "Gg", latex_math_mode: true, latex: "\\ggg", html: "&Gg;", ascii: ">>>", utf8: ""},
EntityDefinition {name: "in", latex_math_mode: true, latex: "\\in", html: "&isin;", ascii: "[element of]", utf8: ""},
EntityDefinition {name: "ni", latex_math_mode: true, latex: "\\ni", html: "&ni;", ascii: "[contains as member]", utf8: ""},
EntityDefinition {name: "to", latex_math_mode: true, latex: "\\to", html: "&rarr;", ascii: "->", utf8: ""},
EntityDefinition {name: "lg", latex_math_mode: true, latex: "\\lg", html: "lg", ascii: "lg", utf8: "lg"},
EntityDefinition {name: "ln", latex_math_mode: true, latex: "\\ln", html: "ln", ascii: "ln", utf8: "ln"},
EntityDefinition {name: "Pr", latex_math_mode: true, latex: "\\Pr", html: "Pr", ascii: "Pr", utf8: "Pr"},
EntityDefinition {name: "_ ", latex_math_mode: false, latex: "\\hspace*{0.5em}", html: "&ensp;", ascii: " ", utf8: ""},
EntityDefinition {name: "S", latex_math_mode: false, latex: "\\S", html: "&sect;", ascii: "section", utf8: "§"},
EntityDefinition {name: "P", latex_math_mode: false, latex: "\\P{}", html: "&para;", ascii: "paragraph", utf8: ""},
];

View File

@@ -12,7 +12,6 @@ use crate::error::CustomError;
use crate::error::MyError; use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::OrgSource; use crate::parser::OrgSource;
use crate::types::Keyword;
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum ContextElement<'r, 's> { pub(crate) enum ContextElement<'r, 's> {
@@ -22,28 +21,14 @@ pub(crate) enum ContextElement<'r, 's> {
/// Stores the name of the current element to prevent directly nesting elements of the same type. /// Stores the name of the current element to prevent directly nesting elements of the same type.
Context(&'r str), Context(&'r str),
/// Stores the name of the current object to prevent directly nesting elements of the same type.
ContextObject(&'r str),
/// Indicates if elements should consume the whitespace after them. /// Indicates if elements should consume the whitespace after them.
ConsumeTrailingWhitespace(bool), ConsumeTrailingWhitespace(bool),
/// Indicate that we are parsing a paragraph that already has affiliated keywords.
///
/// The value stored is the start of the element after the affiliated keywords. In this way, we can ensure that we do not exit an element immediately after the affiliated keyword had been consumed.
HasAffiliatedKeyword(HasAffiliatedKeywordInner<'r, 's>),
/// This is just here to use the 's lifetime until I'm sure we can eliminate it from ContextElement. /// This is just here to use the 's lifetime until I'm sure we can eliminate it from ContextElement.
#[allow(dead_code)] #[allow(dead_code)]
Placeholder(PhantomData<&'s str>), Placeholder(PhantomData<&'s str>),
} }
#[derive(Debug, Clone)]
pub(crate) struct HasAffiliatedKeywordInner<'r, 's> {
pub(crate) start_after_affiliated_keywords: OrgSource<'s>,
pub(crate) keywords: &'r Vec<Keyword<'s>>,
}
pub(crate) struct ExitMatcherNode<'r> { pub(crate) struct ExitMatcherNode<'r> {
// TODO: Should this be "&'r DynContextMatcher<'c>" ? // TODO: Should this be "&'r DynContextMatcher<'c>" ?
pub(crate) exit_matcher: &'r DynContextMatcher<'r>, pub(crate) exit_matcher: &'r DynContextMatcher<'r>,
@@ -116,7 +101,10 @@ impl<'g, 'r, 's> Context<'g, 'r, 's> {
} }
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(self))
)]
pub(crate) fn check_exit_matcher( pub(crate) fn check_exit_matcher(
&'r self, &'r self,
i: OrgSource<'s>, i: OrgSource<'s>,
@@ -163,7 +151,10 @@ impl<'g, 'r, 's> Context<'g, 'r, 's> {
} }
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(_context))
)]
fn document_end<'b, 'g, 'r, 's>( fn document_end<'b, 'g, 'r, 's>(
_context: RefContext<'b, 'g, 'r, 's>, _context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,

View File

@@ -1,6 +1,12 @@
use std::fmt::Debug; use std::fmt::Debug;
use std::path::PathBuf; use std::path::PathBuf;
#[cfg(any(feature = "compare", feature = "foreign_document_test"))]
pub trait FileAccessInterface: Sync + Debug {
fn read_file(&self, path: &str) -> Result<String, std::io::Error>;
}
#[cfg(not(any(feature = "compare", feature = "foreign_document_test")))]
pub trait FileAccessInterface: Debug { pub trait FileAccessInterface: Debug {
fn read_file(&self, path: &str) -> Result<String, std::io::Error>; fn read_file(&self, path: &str) -> Result<String, std::io::Error>;
} }

View File

@@ -1,8 +1,14 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use super::constants::DEFAULT_ORG_ENTITIES;
use super::constants::DEFAULT_ORG_LINK_PARAMETERS;
use super::FileAccessInterface; use super::FileAccessInterface;
use super::LocalFileAccessInterface; use super::LocalFileAccessInterface;
use crate::context::constants::DEFAULT_ORG_ELEMENT_AFFILIATED_KEYWORDS;
use crate::context::constants::DEFAULT_ORG_ELEMENT_DUAL_KEYWORDS;
use crate::context::constants::DEFAULT_ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST;
use crate::context::constants::DEFAULT_ORG_ELEMENT_PARSED_KEYWORDS;
use crate::types::IndentationLevel; use crate::types::IndentationLevel;
use crate::types::Object; use crate::types::Object;
@@ -34,11 +40,6 @@ pub struct GlobalSettings<'g, 's> {
/// Corresponds to org-footnote-section elisp variable. /// Corresponds to org-footnote-section elisp variable.
pub footnote_section: &'g str, pub footnote_section: &'g str,
/// The label format for references inside src/example blocks.
///
/// Corresponds to org-coderef-label-format elisp variable.
pub coderef_label_format: &'g str,
/// The allowed protocols for links (for example, the "https" in "https://foo.bar/"). /// The allowed protocols for links (for example, the "https" in "https://foo.bar/").
/// ///
/// Corresponds to org-link-parameters elisp variable. /// Corresponds to org-link-parameters elisp variable.
@@ -50,12 +51,53 @@ pub struct GlobalSettings<'g, 's> {
/// ///
/// This is set by including #+LINK in the org-mode document. /// This is set by including #+LINK in the org-mode document.
pub link_templates: BTreeMap<String, String>, pub link_templates: BTreeMap<String, String>,
/// The special characters that can be written in org-mode like \infin for the infinity symbol.
///
/// MUST be sorted with the largest names first. Otherwise the parser may match a shorter substring of a longer entity.
///
/// Corresponds to org-entities elisp variable.
pub entities: &'g [EntityDefinition<'s>],
/// Keywords that contain the standard set of objects (excluding footnote references).
///
/// Corresponds to org-element-parsed-keywords elisp variable.
pub element_parsed_keywords: &'g [&'s str],
/// Keywords that can have a secondary value in square brackets.
///
/// Corresponds to org-element-dual-keywords elisp variable.
pub element_dual_keywords: &'g [&'s str],
/// Keywords that can be affiliated with an element.
///
/// Corresponds to org-element-affiliated-keywords elisp variable.
pub element_affiliated_keywords: &'g [&'s str],
/// Mapping of keyword names.
///
/// Corresponds to org-element-keyword-translation-alist elisp variable.
pub element_keyword_translation_alist: &'g [(&'s str, &'s str)],
} }
pub const DEFAULT_TAB_WIDTH: IndentationLevel = 8; pub const DEFAULT_TAB_WIDTH: IndentationLevel = 8;
#[derive(Debug, Clone)]
pub struct EntityDefinition<'a> {
pub name: &'a str,
pub latex_math_mode: bool,
pub latex: &'a str,
pub html: &'a str,
pub ascii: &'a str,
// Skipping latin1 because it is detrimental to the future. If anyone out there is using latin1, take a long look in the mirror and change your ways.
pub utf8: &'a str,
}
impl<'g, 's> GlobalSettings<'g, 's> { impl<'g, 's> GlobalSettings<'g, 's> {
fn new() -> GlobalSettings<'g, 's> { fn new() -> GlobalSettings<'g, 's> {
debug_assert!(
DEFAULT_ORG_ENTITIES.is_sorted_by(|a, b| b.name.len().partial_cmp(&a.name.len()))
);
GlobalSettings { GlobalSettings {
radio_targets: Vec::new(), radio_targets: Vec::new(),
file_access: &LocalFileAccessInterface { file_access: &LocalFileAccessInterface {
@@ -67,9 +109,13 @@ impl<'g, 's> GlobalSettings<'g, 's> {
tab_width: DEFAULT_TAB_WIDTH, tab_width: DEFAULT_TAB_WIDTH,
odd_levels_only: HeadlineLevelFilter::default(), odd_levels_only: HeadlineLevelFilter::default(),
footnote_section: "Footnotes", footnote_section: "Footnotes",
coderef_label_format: "(ref:%s)",
link_parameters: &DEFAULT_ORG_LINK_PARAMETERS, link_parameters: &DEFAULT_ORG_LINK_PARAMETERS,
link_templates: BTreeMap::new(), link_templates: BTreeMap::new(),
entities: &DEFAULT_ORG_ENTITIES,
element_parsed_keywords: &DEFAULT_ORG_ELEMENT_PARSED_KEYWORDS,
element_dual_keywords: &DEFAULT_ORG_ELEMENT_DUAL_KEYWORDS,
element_affiliated_keywords: &DEFAULT_ORG_ELEMENT_AFFILIATED_KEYWORDS,
element_keyword_translation_alist: &DEFAULT_ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST,
} }
} }
} }
@@ -91,29 +137,3 @@ impl Default for HeadlineLevelFilter {
HeadlineLevelFilter::OddEven HeadlineLevelFilter::OddEven
} }
} }
const DEFAULT_ORG_LINK_PARAMETERS: [&'static str; 23] = [
"id",
"eww",
"rmail",
"mhe",
"irc",
"info",
"gnus",
"docview",
"bibtex",
"bbdb",
"w3m",
"doi",
"file+sys",
"file+emacs",
"shell",
"news",
"mailto",
"https",
"http",
"ftp",
"help",
"file",
"elisp",
];

View File

@@ -1,6 +1,7 @@
use crate::error::Res; use crate::error::Res;
use crate::parser::OrgSource; use crate::parser::OrgSource;
mod constants;
mod context; mod context;
mod exiting; mod exiting;
mod file_access_interface; mod file_access_interface;
@@ -21,10 +22,10 @@ type DynMatcher<'c> = dyn Matcher + 'c;
pub(crate) use context::Context; pub(crate) use context::Context;
pub(crate) use context::ContextElement; pub(crate) use context::ContextElement;
pub(crate) use context::ExitMatcherNode; pub(crate) use context::ExitMatcherNode;
pub(crate) use context::HasAffiliatedKeywordInner;
pub(crate) use exiting::ExitClass; pub(crate) use exiting::ExitClass;
pub use file_access_interface::FileAccessInterface; pub use file_access_interface::FileAccessInterface;
pub use file_access_interface::LocalFileAccessInterface; pub use file_access_interface::LocalFileAccessInterface;
pub use global_settings::EntityDefinition;
pub use global_settings::GlobalSettings; pub use global_settings::GlobalSettings;
pub use global_settings::HeadlineLevelFilter; pub use global_settings::HeadlineLevelFilter;
pub use global_settings::DEFAULT_TAB_WIDTH; pub use global_settings::DEFAULT_TAB_WIDTH;

View File

@@ -11,8 +11,15 @@ macro_rules! children_iter {
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
self.next.next().map(Into::<AstNode>::into) self.next.next().map(Into::<AstNode>::into)
} }
fn size_hint(&self) -> (usize, Option<usize>) {
let size = self.next.len();
(size, Some(size))
}
} }
impl<'r, 's> ExactSizeIterator for $itertype<'r, 's> {}
impl<'r, 's> IntoIterator for &'r $astnodetype { impl<'r, 's> IntoIterator for &'r $astnodetype {
type Item = AstNode<'r, 's>; type Item = AstNode<'r, 's>;
@@ -42,8 +49,14 @@ macro_rules! empty_iter {
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
None None
} }
fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(0))
}
} }
impl<'r, 's> ExactSizeIterator for $itertype<'r, 's> {}
impl<'r, 's> IntoIterator for &'r $astnodetype { impl<'r, 's> IntoIterator for &'r $astnodetype {
type Item = AstNode<'r, 's>; type Item = AstNode<'r, 's>;
@@ -79,8 +92,15 @@ $fieldname: $innertype,
.or_else(|| self.$fieldname.next().map(Into::<AstNode>::into)) .or_else(|| self.$fieldname.next().map(Into::<AstNode>::into))
),* ),*
} }
fn size_hint(&self) -> (usize, Option<usize>) {
let size = self.$firstfieldname.len()$( + self.$fieldname.len() ),*;
(size, Some(size))
}
} }
impl<'r, 's> ExactSizeIterator for $itertype<'r, 's> {}
impl<'r, 's> IntoIterator for &'r $astnodetype { impl<'r, 's> IntoIterator for &'r $astnodetype {
type Item = AstNode<'r, 's>; type Item = AstNode<'r, 's>;

View File

@@ -1,6 +1,7 @@
#![feature(exit_status_error)] #![feature(exit_status_error)]
#![feature(trait_alias)] #![feature(trait_alias)]
#![feature(path_file_prefix)] #![feature(path_file_prefix)]
#![feature(is_sorted)]
// TODO: #![warn(missing_docs)] // TODO: #![warn(missing_docs)]
#[cfg(feature = "compare")] #[cfg(feature = "compare")]

View File

@@ -0,0 +1,160 @@
use std::collections::BTreeMap;
use nom::bytes::complete::tag;
use nom::bytes::complete::take_until;
use nom::character::complete::anychar;
use nom::combinator::all_consuming;
use nom::combinator::eof;
use nom::combinator::map;
use nom::combinator::map_parser;
use nom::combinator::opt;
use nom::combinator::peek;
use nom::combinator::recognize;
use nom::multi::many0;
use nom::multi::many_till;
use nom::sequence::tuple;
use super::object_parser::standard_set_object;
use super::util::confine_context;
use crate::context::parser_with_context;
use crate::context::Context;
use crate::context::ContextElement;
use crate::context::GlobalSettings;
use crate::context::List;
use crate::types::AffiliatedKeywordValue;
use crate::types::AffiliatedKeywords;
use crate::types::Keyword;
pub(crate) fn parse_affiliated_keywords<'g, 's, AK>(
global_settings: &'g GlobalSettings<'g, 's>,
input: AK,
) -> AffiliatedKeywords<'s>
where
AK: IntoIterator<Item = Keyword<'s>>,
{
let mut ret = BTreeMap::new();
for kw in input {
let translated_name = translate_name(global_settings, kw.key);
if is_single_string_keyword(global_settings, translated_name.as_str()) {
ret.insert(
translated_name,
AffiliatedKeywordValue::SingleString(kw.value),
);
} else if is_list_of_single_string_keyword(global_settings, translated_name.as_str()) {
let list_of_strings = ret
.entry(translated_name)
.or_insert_with(|| AffiliatedKeywordValue::ListOfStrings(Vec::with_capacity(1)));
match list_of_strings {
AffiliatedKeywordValue::ListOfStrings(list_of_strings)
if list_of_strings.is_empty() =>
{
list_of_strings.push(kw.value);
}
AffiliatedKeywordValue::ListOfStrings(list_of_strings) => {
list_of_strings.clear();
list_of_strings.push(kw.value);
}
_ => panic!("Invalid AffiliatedKeywordValue type."),
}
} else if is_list_of_objects_keyword(global_settings, translated_name.as_str()) {
let initial_context = ContextElement::document_context();
let initial_context = Context::new(global_settings, List::new(&initial_context));
let (_remaining, optional_objects) = opt(all_consuming(map(
tuple((
take_until("["),
tag("["),
map_parser(
recognize(many_till(anychar, peek(tuple((tag("]"), eof))))),
confine_context(|i| {
all_consuming(many0(parser_with_context!(standard_set_object)(
&initial_context,
)))(i)
}),
),
tag("]"),
eof,
)),
|(_, _, objects, _, _)| objects,
)))(kw.key.into())
.expect("Object parser should always succeed.");
// TODO: This should be omitting footnote references
let (_remaining, objects) = all_consuming(many0(parser_with_context!(
standard_set_object
)(&initial_context)))(kw.value.into())
.expect("Object parser should always succeed.");
let list_of_lists = ret.entry(translated_name).or_insert_with(|| {
AffiliatedKeywordValue::ListOfListsOfObjects(Vec::with_capacity(1))
});
match list_of_lists {
AffiliatedKeywordValue::ListOfListsOfObjects(list_of_lists) => {
list_of_lists.push((optional_objects, objects));
}
_ => panic!("Invalid AffiliatedKeywordValue type."),
}
} else {
let list_of_strings = ret
.entry(translated_name)
.or_insert_with(|| AffiliatedKeywordValue::ListOfStrings(Vec::with_capacity(1)));
match list_of_strings {
AffiliatedKeywordValue::ListOfStrings(list_of_strings) => {
list_of_strings.push(kw.value);
}
_ => panic!("Invalid AffiliatedKeywordValue type."),
}
}
}
AffiliatedKeywords { keywords: ret }
}
fn translate_name<'g, 's>(global_settings: &'g GlobalSettings<'g, 's>, name: &'s str) -> String {
let name_until_optval = name
.split_once("[")
.map(|(before, _after)| before)
.unwrap_or(name);
for (src, dst) in global_settings.element_keyword_translation_alist {
if name_until_optval.eq_ignore_ascii_case(src) {
return dst.to_lowercase();
}
}
name_until_optval.to_lowercase()
}
fn is_single_string_keyword<'g, 's>(
_global_settings: &'g GlobalSettings<'g, 's>,
name: &'s str,
) -> bool {
// TODO: Is this defined by an elisp variable?
for single_string_name in ["plot", "name"] {
if name.eq_ignore_ascii_case(single_string_name) {
return true;
}
}
false
}
fn is_list_of_single_string_keyword<'g, 's>(
_global_settings: &'g GlobalSettings<'g, 's>,
name: &'s str,
) -> bool {
// TODO: Is this defined by an elisp variable?
for single_string_name in ["results"] {
if name.eq_ignore_ascii_case(single_string_name) {
return true;
}
}
false
}
fn is_list_of_objects_keyword<'g, 's>(
global_settings: &'g GlobalSettings<'g, 's>,
name: &'s str,
) -> bool {
for parsed_keyword in global_settings.element_parsed_keywords {
if name.eq_ignore_ascii_case(parsed_keyword) {
return true;
}
}
false
}

View File

@@ -29,7 +29,10 @@ use crate::parser::util::get_consumed;
use crate::types::AngleLink; use crate::types::AngleLink;
use crate::types::LinkType; use crate::types::LinkType;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
pub(crate) fn angle_link<'b, 'g, 'r, 's>( pub(crate) fn angle_link<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
@@ -68,7 +71,10 @@ struct PathAngle<'s> {
application: Option<&'s str>, application: Option<&'s str>,
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
fn path_angle<'b, 'g, 'r, 's>( fn path_angle<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
@@ -83,7 +89,10 @@ fn path_angle<'b, 'g, 'r, 's>(
Ok((remaining, path)) Ok((remaining, path))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(_context))
)]
fn path_angle_end<'b, 'g, 'r, 's>( fn path_angle_end<'b, 'g, 'r, 's>(
_context: RefContext<'b, 'g, 'r, 's>, _context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
@@ -91,7 +100,10 @@ fn path_angle_end<'b, 'g, 'r, 's>(
tag(">")(input) tag(">")(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
fn parse_angle_link<'b, 'g, 'r, 's>( fn parse_angle_link<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
@@ -102,7 +114,10 @@ fn parse_angle_link<'b, 'g, 'r, 's>(
))(input) ))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
fn parse_file_angle_link<'b, 'g, 'r, 's>( fn parse_file_angle_link<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
@@ -137,7 +152,10 @@ fn parse_file_angle_link<'b, 'g, 'r, 's>(
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
fn parse_protocol_angle_link<'b, 'g, 'r, 's>( fn parse_protocol_angle_link<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,

View File

@@ -9,14 +9,12 @@ use nom::combinator::opt;
use nom::combinator::peek; use nom::combinator::peek;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::combinator::verify; use nom::combinator::verify;
use nom::multi::many0;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
use nom::InputTake; use nom::InputTake;
use super::keyword::affiliated_keyword; use super::affiliated_keyword::parse_affiliated_keywords;
use super::org_source::BracketDepth; use super::org_source::BracketDepth;
use super::util::get_name;
use super::util::maybe_consume_trailing_whitespace_if_not_exiting; use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
use super::util::start_of_line; use super::util::start_of_line;
use super::OrgSource; use super::OrgSource;
@@ -28,14 +26,21 @@ use crate::error::Res;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
use crate::parser::util::org_line_ending; use crate::parser::util::org_line_ending;
use crate::types::BabelCall; use crate::types::BabelCall;
use crate::types::Keyword;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
pub(crate) fn babel_call<'b, 'g, 'r, 's>( feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
)]
pub(crate) fn babel_call<'b, 'g, 'r, 's, AK>(
affiliated_keywords: AK,
remaining: OrgSource<'s>,
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, BabelCall<'s>> { ) -> Res<OrgSource<'s>, BabelCall<'s>>
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; where
AK: IntoIterator<Item = Keyword<'s>>,
{
start_of_line(remaining)?; start_of_line(remaining)?;
let (remaining, _) = tuple((space0, tag("#+"), tag_no_case("call"), tag(":")))(remaining)?; let (remaining, _) = tuple((space0, tag("#+"), tag_no_case("call"), tag(":")))(remaining)?;
@@ -47,7 +52,10 @@ pub(crate) fn babel_call<'b, 'g, 'r, 's>(
remaining, remaining,
BabelCall { BabelCall {
source: Into::<&str>::into(source), source: Into::<&str>::into(source),
name: get_name(&affiliated_keywords), affiliated_keywords: parse_affiliated_keywords(
context.get_global_settings(),
affiliated_keywords,
),
value: Into::<&str>::into(line_break.take(0)), value: Into::<&str>::into(line_break.take(0)),
call: None, call: None,
inside_header: None, inside_header: None,
@@ -70,7 +78,10 @@ pub(crate) fn babel_call<'b, 'g, 'r, 's>(
remaining, remaining,
BabelCall { BabelCall {
source: Into::<&str>::into(source), source: Into::<&str>::into(source),
name: get_name(&affiliated_keywords), affiliated_keywords: parse_affiliated_keywords(
context.get_global_settings(),
affiliated_keywords,
),
value: Into::<&str>::into(value).trim_end(), value: Into::<&str>::into(value).trim_end(),
call: call.map(Into::<&str>::into), call: call.map(Into::<&str>::into),
inside_header: inside_header.map(Into::<&str>::into), inside_header: inside_header.map(Into::<&str>::into),

View File

@@ -2,6 +2,7 @@ use nom::branch::alt;
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
use nom::bytes::complete::tag_no_case; use nom::bytes::complete::tag_no_case;
use nom::character::complete::anychar; use nom::character::complete::anychar;
use nom::combinator::map;
use nom::combinator::opt; use nom::combinator::opt;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::combinator::verify; use nom::combinator::verify;
@@ -30,24 +31,27 @@ use crate::parser::util::get_consumed;
use crate::types::Citation; use crate::types::Citation;
use crate::types::Object; use crate::types::Object;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
pub(crate) fn citation<'b, 'g, 'r, 's>( pub(crate) fn citation<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Citation<'s>> { ) -> Res<OrgSource<'s>, Citation<'s>> {
// TODO: Despite being a standard object, citations cannot exist inside the global prefix/suffix for other citations because citations must contain something that matches @key which is forbidden inside the global prefix/suffix. This TODO is to evaluate if its worth putting in an explicit check for this (which can be easily accomplished by checking the output of `get_bracket_depth()`). I suspect its not worth it because I expect, outside of intentionally crafted inputs, this parser will exit immediately inside a citation since it is unlikely to find the "[cite" substring inside a citation global prefix/suffix. // TODO: Despite being a standard object, citations cannot exist inside the global prefix/suffix for other citations because citations must contain something that matches @key which is forbidden inside the global prefix/suffix. This TODO is to evaluate if its worth putting in an explicit check for this (which can be easily accomplished by checking the output of `get_bracket_depth()`). I suspect its not worth it because I expect, outside of intentionally crafted inputs, this parser will exit immediately inside a citation since it is unlikely to find the "[cite" substring inside a citation global prefix/suffix.
let (remaining, _) = tag_no_case("[cite")(input)?; let (remaining, _) = tag_no_case("[cite")(input)?;
let (remaining, _) = opt(citestyle)(remaining)?; let (remaining, style) = opt(citestyle)(remaining)?;
let (remaining, _) = tag(":")(remaining)?; let (remaining, _) = tag(":")(remaining)?;
let (remaining, _prefix) = let (remaining, prefix) =
must_balance_bracket(opt(parser_with_context!(global_prefix)(context)))(remaining)?; must_balance_bracket(opt(parser_with_context!(global_prefix)(context)))(remaining)?;
let (remaining, _references) = let (remaining, references) =
separated_list1(tag(";"), parser_with_context!(citation_reference)(context))(remaining)?; separated_list1(tag(";"), parser_with_context!(citation_reference)(context))(remaining)?;
let (remaining, _suffix) = must_balance_bracket(opt(tuple(( let (remaining, suffix) = must_balance_bracket(opt(map(
tag(";"), tuple((tag(";"), parser_with_context!(global_suffix)(context))),
parser_with_context!(global_suffix)(context), |(_, suffix)| suffix,
))))(remaining)?; )))(remaining)?;
let (remaining, _) = tag("]")(remaining)?; let (remaining, _) = tag("]")(remaining)?;
let (remaining, _trailing_whitespace) = let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?; maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
@@ -56,16 +60,23 @@ pub(crate) fn citation<'b, 'g, 'r, 's>(
remaining, remaining,
Citation { Citation {
source: source.into(), source: source.into(),
style: style.map(Into::<&str>::into),
prefix: prefix.unwrap_or(Vec::new()),
suffix: suffix.unwrap_or(Vec::new()),
children: references,
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn citestyle<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> { fn citestyle<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _) = tuple((tag("/"), style))(input)?; map(
let (remaining, _) = opt(tuple((tag("/"), variant)))(remaining)?; tuple((
let source = get_consumed(input, remaining); tag("/"),
Ok((remaining, source)) recognize(tuple((style, opt(tuple((tag("/"), variant)))))),
)),
|(_, style)| style,
)(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
@@ -82,7 +93,10 @@ fn variant<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
})))(input) })))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
fn global_prefix<'b, 'g, 'r, 's>( fn global_prefix<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
@@ -108,7 +122,10 @@ fn global_prefix_end(starting_bracket_depth: BracketDepth) -> impl ContextMatche
move |context, input: OrgSource<'_>| _global_prefix_end(context, input, starting_bracket_depth) move |context, input: OrgSource<'_>| _global_prefix_end(context, input, starting_bracket_depth)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
fn _global_prefix_end<'b, 'g, 'r, 's>( fn _global_prefix_end<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
@@ -131,7 +148,10 @@ fn _global_prefix_end<'b, 'g, 'r, 's>(
))(input) ))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
fn global_suffix<'b, 'g, 'r, 's>( fn global_suffix<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
@@ -156,7 +176,10 @@ fn global_suffix_end(starting_bracket_depth: BracketDepth) -> impl ContextMatche
move |context, input: OrgSource<'_>| _global_suffix_end(context, input, starting_bracket_depth) move |context, input: OrgSource<'_>| _global_suffix_end(context, input, starting_bracket_depth)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
fn _global_suffix_end<'b, 'g, 'r, 's>( fn _global_suffix_end<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
@@ -186,6 +209,7 @@ mod tests {
use crate::context::GlobalSettings; use crate::context::GlobalSettings;
use crate::context::List; use crate::context::List;
use crate::parser::element_parser::element; use crate::parser::element_parser::element;
use crate::types::CitationReference;
use crate::types::Element; use crate::types::Element;
use crate::types::GetStandardProperties; use crate::types::GetStandardProperties;
@@ -213,7 +237,16 @@ mod tests {
.get(0) .get(0)
.expect("Len already asserted to be 1"), .expect("Len already asserted to be 1"),
&Object::Citation(Citation { &Object::Citation(Citation {
source: "[cite:@foo]" source: "[cite:@foo]",
style: None,
prefix: vec![],
suffix: vec![],
children: vec![CitationReference {
source: "@foo",
key: "foo",
prefix: vec![],
suffix: vec![]
}]
}) })
); );
} }

View File

@@ -1,6 +1,7 @@
use nom::branch::alt; use nom::branch::alt;
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
use nom::character::complete::anychar; use nom::character::complete::anychar;
use nom::combinator::map;
use nom::combinator::not; use nom::combinator::not;
use nom::combinator::opt; use nom::combinator::opt;
use nom::combinator::recognize; use nom::combinator::recognize;
@@ -28,47 +29,65 @@ use crate::parser::util::WORD_CONSTITUENT_CHARACTERS;
use crate::types::CitationReference; use crate::types::CitationReference;
use crate::types::Object; use crate::types::Object;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
pub(crate) fn citation_reference<'b, 'g, 'r, 's>( pub(crate) fn citation_reference<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, CitationReference<'s>> { ) -> Res<OrgSource<'s>, CitationReference<'s>> {
let (remaining, _prefix) = let (remaining, prefix) =
must_balance_bracket(opt(parser_with_context!(key_prefix)(context)))(input)?; must_balance_bracket(opt(parser_with_context!(key_prefix)(context)))(input)?;
let (remaining, _key) = parser_with_context!(citation_reference_key)(context)(remaining)?; let (remaining, key) = parser_with_context!(citation_reference_key)(context)(remaining)?;
let (remaining, _suffix) = let (remaining, suffix) =
must_balance_bracket(opt(parser_with_context!(key_suffix)(context)))(remaining)?; must_balance_bracket(opt(parser_with_context!(key_suffix)(context)))(remaining)?;
let without_closing_semi_remaining = remaining;
let (remaining, _closing_semi) = opt(tag(";"))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok(( Ok((
remaining, without_closing_semi_remaining,
CitationReference { CitationReference {
source: source.into(), source: source.into(),
key: key.into(),
prefix: prefix.unwrap_or(Vec::new()),
suffix: suffix.unwrap_or(Vec::new()),
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
pub(crate) fn citation_reference_key<'b, 'g, 'r, 's>( pub(crate) fn citation_reference_key<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, source) = recognize(tuple(( let (remaining, source) = map(
tag("@"), tuple((
many1(verify( tag("@"),
preceded( recognize(many1(verify(
not(parser_with_context!(exit_matcher_parser)(context)), preceded(
anychar, not(parser_with_context!(exit_matcher_parser)(context)),
), anychar,
|c| { ),
WORD_CONSTITUENT_CHARACTERS.contains(*c) || "-.:?~`'/*@+|(){}<>&_^$#%~".contains(*c) |c| {
}, WORD_CONSTITUENT_CHARACTERS.contains(*c)
|| "-.:?~`'/*@+|(){}<>&_^$#%~".contains(*c)
},
))),
)), )),
)))(input)?; |(_, key)| key,
)(input)?;
Ok((remaining, source)) Ok((remaining, source))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
fn key_prefix<'b, 'g, 'r, 's>( fn key_prefix<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
@@ -89,7 +108,10 @@ fn key_prefix<'b, 'g, 'r, 's>(
Ok((remaining, children)) Ok((remaining, children))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
fn key_suffix<'b, 'g, 'r, 's>( fn key_suffix<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
@@ -114,7 +136,10 @@ fn key_prefix_end(starting_bracket_depth: BracketDepth) -> impl ContextMatcher {
move |context, input: OrgSource<'_>| _key_prefix_end(context, input, starting_bracket_depth) move |context, input: OrgSource<'_>| _key_prefix_end(context, input, starting_bracket_depth)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
fn _key_prefix_end<'b, 'g, 'r, 's>( fn _key_prefix_end<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
@@ -141,7 +166,10 @@ fn key_suffix_end(starting_bracket_depth: BracketDepth) -> impl ContextMatcher {
move |context, input: OrgSource<'_>| _key_suffix_end(context, input, starting_bracket_depth) move |context, input: OrgSource<'_>| _key_suffix_end(context, input, starting_bracket_depth)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(_context))
)]
fn _key_suffix_end<'b, 'g, 'r, 's>( fn _key_suffix_end<'b, 'g, 'r, 's>(
_context: RefContext<'b, 'g, 'r, 's>, _context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,

View File

@@ -24,7 +24,10 @@ use crate::types::Clock;
use crate::types::ClockStatus; use crate::types::ClockStatus;
use crate::types::Timestamp; use crate::types::Timestamp;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
pub(crate) fn clock<'b, 'g, 'r, 's>( pub(crate) fn clock<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
@@ -55,7 +58,10 @@ pub(crate) fn clock<'b, 'g, 'r, 's>(
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
fn clock_timestamp<'b, 'g, 'r, 's>( fn clock_timestamp<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,

Some files were not shown because too many files have changed in this diff Show More