235 Commits

Author SHA1 Message Date
Tom Alexander
acc29e7977 Publish version 0.1.11.
Some checks failed
rustfmt Build rustfmt has succeeded
clippy Build clippy 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-16 19:53:46 -04:00
Tom Alexander
ebc0a30035 Merge branch 'clippy_ci_job' 2023-10-16 19:50:01 -04:00
Tom Alexander
e2d55e13d3 Fix some clippy errors that didn't appear on my host version of clippy.
Some checks failed
clippy Build clippy 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 succeeded
2023-10-16 19:43:15 -04:00
Tom Alexander
e4d9c5f467 Add makefile command to run clippy through docker. 2023-10-16 19:38:45 -04:00
Tom Alexander
d8e3a85ef7 We need to add dependencies so we are now building a container. 2023-10-16 19:34:53 -04:00
Tom Alexander
464685b52b Use a cargo cache for the clippy CI job.
Some checks failed
clippy Build clippy has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
2023-10-16 19:27:15 -04:00
Tom Alexander
5fed4e80a7 Add a CI job to run clippy for every push. 2023-10-16 19:22:59 -04:00
Tom Alexander
e53140426f Merge branch 'clippy'
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 succeeded
2023-10-16 19:14:19 -04:00
Tom Alexander
9a4d290cf8 Apply more suggestions. 2023-10-16 19:12:25 -04:00
Tom Alexander
acd24d6198 Apply more suggestions. 2023-10-16 19:02:34 -04:00
Tom Alexander
880b00ef3f Apply more suggestions. 2023-10-16 18:54:41 -04:00
Tom Alexander
3069711447 Apply more suggestions. 2023-10-16 18:29:21 -04:00
Tom Alexander
4b6c717812 Apply more suggestions. 2023-10-16 17:58:52 -04:00
Tom Alexander
1d329cc310 Apply more suggestions. 2023-10-16 17:28:28 -04:00
Tom Alexander
b4f9a3b9b6 Apply more suggestions. 2023-10-16 17:14:44 -04:00
Tom Alexander
2dd5246506 Apply more suggestions. 2023-10-16 17:03:39 -04:00
Tom Alexander
4ba0e3611b Apply more suggestions. 2023-10-16 17:03:39 -04:00
Tom Alexander
728f79b86c Apply more suggestions. 2023-10-16 17:03:39 -04:00
Tom Alexander
192a4a2891 Remove unnecessary lifetimes. 2023-10-16 17:03:39 -04:00
Tom Alexander
fafd85fb30 Apply some clippy fixes. 2023-10-16 17:03:39 -04:00
Tom Alexander
1c23065329 Add a clippy command to the makefile. 2023-10-16 17:03:39 -04:00
Tom Alexander
ed105b04ad Merge branch 'cargo_bench' 2023-10-16 16:03:56 -04:00
Tom Alexander
f10efec21d No performance change switching affiliated_key to using element macro. 2023-10-16 15:57:18 -04:00
Tom Alexander
72b4cf8e71 Add the first use of the rust benchmark tests. 2023-10-16 15:50:08 -04:00
Tom Alexander
547fc40dbe No measurable performance improvement with native builds over LTO release builds.
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 succeeded
Leaving the code for this commented out because it involved an unstable cargo feature without showing any benefit. I would like to revisit this later.
2023-10-16 15:21:36 -04:00
Tom Alexander
9f1671658d Merge branch 'object_parser_perf'
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 succeeded
2023-10-16 15:04:04 -04:00
Tom Alexander
18d0676fad Clean up. 2023-10-16 15:03:23 -04:00
Tom Alexander
7833a58461 Apply a similar optimization to the detect element parser but also unify detection of affiliated keywords. 2023-10-16 14:55:40 -04:00
Tom Alexander
0020d71089 Extend that optimization to more object parsers. 2023-10-16 14:41:12 -04:00
Tom Alexander
cfdf39d1fa Significantly reduce the use of closures in the object parsers. 2023-10-16 14:25:02 -04:00
Tom Alexander
26f1eae9a1 Merge branch 'planning_before_property_drawer'
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 succeeded
2023-10-16 13:48:03 -04:00
Tom Alexander
3eff85059a Add support for planning before property drawer when calculating additional properties for headlines. 2023-10-16 13:35:03 -04:00
Tom Alexander
d2d0e9e5dd Merge branch 'optval_affiliated_keywords'
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 succeeded
2023-10-16 13:16:44 -04:00
Tom Alexander
c86d1000c0 Do not clear values in lists of strings.
Some checks failed
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
This is a hold-over from when I had list of single string which was a misunderstanding of the optional pair type.
2023-10-16 12:58:20 -04:00
Tom Alexander
911634cb42 Attr_ affiliated keywords should be lists of strings. 2023-10-16 12:55:18 -04:00
Tom Alexander
0aa746fb1e Implement comparison for object tree. 2023-10-16 12:50:53 -04:00
Tom Alexander
33800c4a88 Implement comparison for optional pair. 2023-10-16 12:05:36 -04:00
Tom Alexander
909ccadfa1 Beginning update to compare_affiliated_keywords. 2023-10-16 11:45:54 -04:00
Tom Alexander
e352deb989 Update parse_affiliated_keywords for handling optional pairs. 2023-10-16 11:42:20 -04:00
Tom Alexander
f5a6a26c43 Disable the existing handling of affiliated keywords. 2023-10-15 20:31:14 -04:00
Tom Alexander
dd7184da54 Add analysis from test. 2023-10-15 20:22:46 -04:00
Tom Alexander
1168ddb1fe Start an investigation into affiliated keyword behavior. 2023-10-15 17:38:56 -04:00
Tom Alexander
77ab636e6a Merge branch 'unify_keyword_constants' 2023-10-15 15:59:21 -04:00
Tom Alexander
f5dcacc79d Do not match keyword name if a longer keyword name would match. 2023-10-15 15:55:19 -04:00
Tom Alexander
e7c3c7aab6 Switch the keyword parsers over to using the settings from GlobalSettings. 2023-10-15 15:17:08 -04:00
Tom Alexander
7603b0a1cc Add a test showing we are not handling optval properly. 2023-10-15 15:16:23 -04:00
Tom Alexander
dea3721b1c Fix reporting errors in tests.
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-15 15:16:06 -04:00
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
168 changed files with 9537 additions and 4206 deletions

1
.gitignore vendored
View File

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

View File

@@ -0,0 +1,191 @@
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
name: clippy
spec:
pipelineSpec:
params:
- name: image-name
description: The name for the built image
type: string
- name: path-to-image-context
description: The path to the build context
type: string
- name: path-to-dockerfile
description: The path to the Dockerfile
type: string
- name: GIT_USER_NAME
description: The username for git
type: string
default: "fluxcdbot"
- name: GIT_USER_EMAIL
description: The email for git
type: string
default: "fluxcdbot@users.noreply.github.com"
tasks:
- name: do-stuff
taskSpec:
metadata: {}
stepTemplate:
image: alpine:3.18
name: ""
resources:
requests:
cpu: 10m
memory: 600Mi
workingDir: /workspace/source
steps:
- image: alpine:3.18
name: do-stuff-step
script: |
#!/usr/bin/env sh
echo "hello world"
- name: report-pending
taskRef:
name: gitea-set-status
runAfter:
- fetch-repository
params:
- name: CONTEXT
value: "$(params.JOB_NAME)"
- name: REPO_FULL_NAME
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
- name: GITEA_HOST_URL
value: code.fizz.buzz
- name: SHA
value: "$(tasks.fetch-repository.results.commit)"
- name: DESCRIPTION
value: "Build $(params.JOB_NAME) has started"
- name: STATE
value: pending
- name: TARGET_URL
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
- name: fetch-repository
taskRef:
name: git-clone
workspaces:
- name: output
workspace: git-source
params:
- name: url
value: $(params.REPO_URL)
- name: revision
value: $(params.PULL_BASE_SHA)
- name: deleteExisting
value: "true"
- name: build-image
taskRef:
name: kaniko
params:
- name: IMAGE
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
- name: CONTEXT
value: $(params.path-to-image-context)
- name: DOCKERFILE
value: $(params.path-to-dockerfile)
- name: BUILDER_IMAGE
value: "gcr.io/kaniko-project/executor:v1.12.1"
- name: EXTRA_ARGS
value:
- --cache=true
- --cache-copy-layers
- --cache-repo=harbor.fizz.buzz/kanikocache/cache
- --use-new-run # Should result in a speed-up
- --reproducible # To remove timestamps so layer caching works.
- --snapshot-mode=redo
- --skip-unused-stages=true
- --registry-mirror=dockerhub.dockerhub.svc.cluster.local
workspaces:
- name: source
workspace: git-source
- name: dockerconfig
workspace: docker-credentials
runAfter:
- fetch-repository
- name: clippy
taskRef:
name: run-docker-image
workspaces:
- name: source
workspace: git-source
- name: cargo-cache
workspace: cargo-cache
runAfter:
- build-image
params:
- name: docker-image
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
finally:
- name: report-success
when:
- input: "$(tasks.status)"
operator: in
values: ["Succeeded", "Completed"]
taskRef:
name: gitea-set-status
params:
- name: CONTEXT
value: "$(params.JOB_NAME)"
- name: REPO_FULL_NAME
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
- name: GITEA_HOST_URL
value: code.fizz.buzz
- name: SHA
value: "$(tasks.fetch-repository.results.commit)"
- name: DESCRIPTION
value: "Build $(params.JOB_NAME) has succeeded"
- name: STATE
value: success
- name: TARGET_URL
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
- name: report-failure
when:
- input: "$(tasks.status)"
operator: in
values: ["Failed"]
taskRef:
name: gitea-set-status
params:
- name: CONTEXT
value: "$(params.JOB_NAME)"
- name: REPO_FULL_NAME
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
- name: GITEA_HOST_URL
value: code.fizz.buzz
- name: SHA
value: "$(tasks.fetch-repository.results.commit)"
- name: DESCRIPTION
value: "Build $(params.JOB_NAME) has failed"
- name: STATE
value: failure
- name: TARGET_URL
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
workspaces:
- name: git-source
- name: docker-credentials
workspaces:
- name: git-source
volumeClaimTemplate:
spec:
storageClassName: "nfs-client"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
subPath: rust-source
- name: cargo-cache
persistentVolumeClaim:
claimName: organic-cargo-cache-clippy
- name: docker-credentials
secret:
secretName: harbor-plain
serviceAccountName: build-bot
timeout: 240h0m0s
params:
- name: image-name
value: "harbor.fizz.buzz/private/organic-clippy"
- name: path-to-image-context
value: docker/organic_clippy/
- name: path-to-dockerfile
value: docker/organic_clippy/Dockerfile

View File

@@ -137,7 +137,7 @@ spec:
value: []
- name: docker-image
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
- name: run-image-all
- name: run-image-tracing-compare
taskRef:
name: run-docker-image
workspaces:
@@ -152,6 +152,46 @@ spec:
value: ["--no-default-features", "--features", "tracing,compare"]
- name: docker-image
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:
- name: report-success
when:

View File

@@ -30,3 +30,10 @@ spec:
skip_branches:
# We already run on every commit, so running when the semver tags get pushed is causing needless double-processing.
- "^v[0-9]+\\.[0-9]+\\.[0-9]+$"
- name: clippy
source: "pipeline-clippy.yaml"
# Override https-based url from lighthouse events.
clone_uri: "git@code.fizz.buzz:talexander/organic.git"
skip_branches:
# We already run on every commit, so running when the semver tags get pushed is causing needless double-processing.
- "^v[0-9]+\\.[0-9]+\\.[0-9]+$"

View File

@@ -1,6 +1,8 @@
# cargo-features = ["profile-rustflags"]
[package]
name = "organic"
version = "0.1.9"
version = "0.1.11"
authors = ["Tom Alexander <tom@fizz.buzz>"]
description = "An org-mode parser."
edition = "2021"
@@ -31,7 +33,14 @@ path = "src/lib.rs"
path = "src/bin_compare.rs"
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]
futures = { version = "0.3.28", optional = true }
nom = "7.1.1"
opentelemetry = { version = "0.20.0", optional = true, default-features = false, features = ["trace", "rt-tokio"] }
opentelemetry-otlp = { version = "0.13.0", optional = true }
@@ -40,13 +49,15 @@ tokio = { version = "1.30.0", optional = true, default-features = false, feature
tracing = { version = "0.1.37", optional = true }
tracing-opentelemetry = { version = "0.20.0", optional = true }
tracing-subscriber = { version = "0.3.17", optional = true, features = ["env-filter"] }
walkdir = { version = "2.3.3", optional = true }
[build-dependencies]
walkdir = "2.3.3"
[features]
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"]
# Optimized build for any sort of release.
@@ -55,6 +66,13 @@ inherits = "release"
lto = true
strip = "symbols"
# Optimized build for local execution.
# [profile.native]
# inherits = "release"
# lto = true
# strip = "symbols"
# rustflags = ["-C", "target-cpu=native"]
# Profile for performance testing with the "perf" tool. Notably keeps debug enabled and does not strip symbols to make reading the perf output easier.
[profile.perf]
inherits = "release"

View File

@@ -37,6 +37,18 @@ clean:
format:
> $(MAKE) -C docker/cargo_fmt run
.PHONY: dockerclippy
dockerclippy:
> $(MAKE) -C docker/organic_clippy run
.PHONY: clippy
clippy:
> cargo clippy --no-deps --all-targets --all-features -- -D warnings
.PHONY: clippyfix
clippyfix:
> cargo clippy --fix --lib -p organic --all-features
.PHONY: test
test:
> cargo test --no-default-features --features compare --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
@@ -52,6 +64,8 @@ buildtest:
> cargo build --no-default-features --features compare
> cargo build --no-default-features --features 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
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.
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
- 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 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 a C library for native linking to other code. This is more of a maybe-goal for the project.
### Project Non-Goals

View File

@@ -14,7 +14,7 @@ use walkdir::WalkDir;
fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
let destination = Path::new(&out_dir).join("tests.rs");
let mut test_file = File::create(&destination).unwrap();
let mut test_file = File::create(destination).unwrap();
// Re-generate the tests if any org-mode files change
println!("cargo:rerun-if-changed=org_mode_samples");
@@ -51,7 +51,7 @@ fn write_test(test_file: &mut File, test: &walkdir::DirEntry) {
.to_lowercase()
.strip_suffix(".org")
.expect("Should have .org extension")
.replace("/", "_");
.replace('/', "_");
write!(
test_file,

View File

@@ -0,0 +1,5 @@
FROM rustlang/rust:nightly-alpine3.17
RUN apk add --no-cache musl-dev
ENTRYPOINT ["cargo", "clippy", "--no-deps", "--all-targets", "--all-features", "--", "-D", "warnings"]

View File

@@ -0,0 +1,37 @@
IMAGE_NAME:=organic-clippy
# REMOTE_REPO:=harbor.fizz.buzz/private
.PHONY: all
all: build push
.PHONY: build
build:
docker build -t $(IMAGE_NAME) -f Dockerfile .
.PHONY: push
push:
ifdef REMOTE_REPO
docker tag $(IMAGE_NAME) $(REMOTE_REPO)/$(IMAGE_NAME)
docker push $(REMOTE_REPO)/$(IMAGE_NAME)
else
@echo "REMOTE_REPO not defined, not pushing to a remote repo."
endif
.PHONY: clean
clean:
docker rmi $(IMAGE_NAME)
ifdef REMOTE_REPO
docker rmi $(REMOTE_REPO)/$(IMAGE_NAME)
else
@echo "REMOTE_REPO not defined, not removing from remote repo."
endif
docker volume rm cargo-cache
# NOTE: This target will write to folders underneath the git-root
.PHONY: run
run: build
docker run --rm --init --read-only --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry $(IMAGE_NAME)
.PHONY: shell
shell: build
docker run --rm -i -t --entrypoint /bin/sh --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry $(IMAGE_NAME)

View File

@@ -14,7 +14,7 @@ RUN make DESTDIR="/root/dist" install
FROM build AS build-org-mode
ARG ORG_VERSION=c703541ffcc14965e3567f928de1683a1c1e33f6
ARG ORG_VERSION=abf5156096c06ee5aa05795c3dc5a065f76ada97
COPY --from=build-emacs /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.
@@ -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=build-org-mode /root/org-mode /foreign_documents/org-mode
COPY --from=build-emacs /root/emacs /foreign_documents/emacs
COPY foreign_document_test_entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
ENTRYPOINT ["cargo", "run", "--bin", "foreign_document_test", "--features", "compare,foreign_document_test", "--profile", "release-lto"]

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)
(when (listp var)
(message "\"%s\"," (nth 0 var))
)
(dolist (var (sort (seq-filter 'listp org-entities)
(lambda (x y) (> (length (nth 0 x)) (length (nth 0 y))))))
(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,42 @@
* Elisp Structure
| Keyword | Single | Double | Single Optval | Double Optval |
|---------+---------------+---------------+---------------+---------------|
| CAPTION | objtree | objtree | objtree | objtree |
| DATA | quoted(:name) | quoted(:name) | - | - |
| HEADER | list(quoted) | list(quoted) | - | - |
| NAME | quoted(:name) | quoted(:name) | - | - |
| PLOT | quoted(:plot) | quoted(:plot) | - | - |
| RESULTS | optional pair | optional pair | optional pair | optional pair |
* types
** objtree
Outer list: 1 per keyword
next list: first entry = list of objects for value. remaining entries = optval
** list(quoted)
List of quoted strings, 1 per keyword
** quoted(NAME)
Quoted string under the NAME property (for example quoted(:name))
** optional pair
When optval is supplied this is an alist with the field value being the real value and the 3nd value being the optval.
#+begin_src elisp
("*f*" . "*bar*")
#+end_src
When optval is not supplied this is a list containing a single string of the last occurrence of this keyword.
#+begin_src elisp
("*c*")
#+end_src
* Default settings
#+begin_src text
org-element-dual-keywords ("CAPTION" "RESULTS")
org-element-parsed-keywords ("CAPTION")
org-element-multiple-keywords ("CAPTION" "HEADER")
org-babel-results-keyword "RESULTS"
#+end_src
* Analysis
We don't have an example of a parsed non-dual keyword
Looks like multiple triggers list 1 per keyword
dual triggers support for optval
parsed triggers objects

View File

@@ -0,0 +1,31 @@
#!/usr/bin/env bash
#
set -euo pipefail
IFS=$'\n\t'
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
file_path="${DIR}/test_document.org"
for TARGET_VARIABLE in RESULTS CAPTION HEADER DATA NAME PLOT; do
INIT_SCRIPT=$(cat <<EOF
(progn
(erase-buffer)
(require 'org)
(defun org-table-align () t)
(setq vc-handled-backends nil)
(find-file "/input/${file_path}")
(org-mode)
(replace-regexp-in-region "foo" "${TARGET_VARIABLE}")
(message "%s" (pp-to-string (org-element-parse-buffer)))
)
EOF
)
docker run --init --rm -i --mount type=tmpfs,destination=/tmp -v "/:/input:ro" -w /input --entrypoint "" organic-test emacs -q --no-site-file --no-splash --batch --eval "$INIT_SCRIPT" 2> "${DIR}/${TARGET_VARIABLE}"
done
# exec docker run --init --rm -i -t --mount type=tmpfs,destination=/tmp -v "/:/input:ro" -w /input --entrypoint "" organic-test emacs -q --no-site-file --no-splash --eval "$INIT_SCRIPT"
# org-element-dual-keywords ("CAPTION" "RESULTS")
# org-element-parsed-keywords ("CAPTION")
# org-element-multiple-keywords ("CAPTION" "HEADER")
# org-babel-results-keyword "RESULTS"

View File

@@ -0,0 +1,25 @@
# Single instance
#+foo: *a*
#+begin_example
#+end_example
# Two instances
#+foo: *b*
#+foo: *c*
#+begin_example
#+end_example
# Single with optval
#+foo[*bar*]: *d*
#+begin_example
#+end_example
# Two with optval
#+foo[*bar*]: *e*
#+foo[*bar*]: *f*
#+begin_example
#+end_example

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,7 @@
** Foo
DEADLINE: <2023-10-16 Mon>
:PROPERTIES:
:foo: *a*
:Bar: *b*
:BAZ: *c*
: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
#+caption: bar
#+caption: baz
[[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
#+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

@@ -0,0 +1,16 @@
#+results[foo]: bar
#+results[lorem]: ipsum
#+begin_example
baz
#+end_example
#+caption[lorem]: ipsum
#+caption[foo]: bar
#+begin_example
baz
#+end_example
#+header[foo]: bar
#+begin_example
baz
#+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_bar[header](arguments)
call_baz(arguments)[header]

View File

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

View File

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

View File

@@ -1 +1,3 @@
**foo**
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,30 +14,40 @@ mod init_tracing;
#[cfg(not(feature = "tracing"))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
main_body()
let rt = tokio::runtime::Runtime::new()?;
rt.block_on(async {
let main_body_result = main_body().await;
main_body_result
})
}
#[cfg(feature = "tracing")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let rt = tokio::runtime::Runtime::new()?;
let result = rt.block_on(async {
rt.block_on(async {
init_telemetry()?;
let main_body_result = main_body();
let main_body_result = main_body().await;
shutdown_telemetry()?;
main_body_result
});
result
})
}
#[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);
if args.is_empty() {
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 {
for arg in args {
run_compare_on_file(arg)?
if run_compare_on_file(arg).await? {
} else {
Err("Diff results do not match.")?;
}
}
Ok(())
}

View File

@@ -0,0 +1,394 @@
#![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()?;
rt.block_on(async {
let main_body_result = main_body().await;
main_body_result
})
}
#[cfg(feature = "tracing")]
fn main() -> Result<ExitCode, Box<dyn std::error::Error>> {
let rt = tokio::runtime::Runtime::new()?;
rt.block_on(async {
init_telemetry()?;
let main_body_result = main_body().await;
shutdown_telemetry()?;
main_body_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")
}));
layer.chain(compare_group("veep", || {
compare_all_org_document("/foreign_documents/howardabrams/veep")
}))
}
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,
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,
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_with_settings;
pub fn run_anonymous_compare<P: AsRef<str>>(
pub async fn run_anonymous_compare<P: AsRef<str>>(
org_contents: P,
) -> Result<(), Box<dyn std::error::Error>> {
run_anonymous_compare_with_settings(org_contents, &GlobalSettings::default())
) -> Result<bool, Box<dyn std::error::Error>> {
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>> {
run_compare_on_file_with_settings(org_path, &GlobalSettings::default())
pub async fn run_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(), false).await
}
pub fn run_anonymous_compare_with_settings<P: AsRef<str>>(
pub async fn silent_anonymous_compare<P: AsRef<str>>(
org_contents: P,
global_settings: &GlobalSettings,
) -> Result<(), Box<dyn std::error::Error>> {
) -> Result<bool, 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.
let org_contents = org_contents.as_ref().replace("\r\n", "\n");
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 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())?;
println!("{}\n\n\n", org_contents);
println!("{}", org_sexp);
println!("{:#?}", rust_parsed);
if !silent {
println!("{}\n\n\n", org_contents);
println!("{}", org_sexp);
println!("{:#?}", rust_parsed);
}
// We do the diffing after printing out both parsed forms in case the diffing panics
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() {
Err("Diff results do not match.")?;
} else {
return Ok(false);
} else if !silent {
println!(
"{color}Entire document passes.{reset}",
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,
global_settings: &GlobalSettings,
) -> Result<(), Box<dyn std::error::Error>> {
global_settings: &GlobalSettings<'g, 's>,
silent: bool,
) -> Result<bool, Box<dyn std::error::Error>> {
let org_path = org_path.as_ref();
print_versions()?;
if !silent {
print_versions().await?;
}
let parent_directory = org_path
.parent()
.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
};
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())?;
println!("{}\n\n\n", org_contents);
println!("{}", org_sexp);
println!("{:#?}", rust_parsed);
if !silent {
println!("{}\n\n\n", org_contents);
println!("{}", org_sexp);
println!("{:#?}", rust_parsed);
}
// We do the diffing after printing out both parsed forms in case the diffing panics
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() {
Err("Diff results do not match.")?;
} else {
return Ok(false);
} else if !silent {
println!(
"{color}Entire document passes.{reset}",
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>> {
eprintln!("Using emacs version: {}", get_emacs_version()?.trim());
eprintln!("Using org-mode version: {}", get_org_mode_version()?.trim());
async fn print_versions() -> Result<(), Box<dyn std::error::Error>> {
eprintln!("Using emacs version: {}", get_emacs_version().await?.trim());
eprintln!(
"Using org-mode version: {}",
get_org_mode_version().await?.trim()
);
Ok(())
}

View File

@@ -1,49 +1,86 @@
use std::collections::BTreeSet;
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::sexp::unquote;
use super::sexp::Token;
use super::util::get_property;
use super::util::get_property_numeric;
use super::util::get_property_quoted_string;
use super::util::get_property_unquoted_atom;
use crate::types::AstNode;
use crate::types::CharOffsetInLine;
use crate::types::LineNumber;
use crate::types::RetainLabels;
use crate::types::SwitchNumberLines;
#[derive(Debug)]
pub(crate) enum EmacsField<'s> {
Required(&'s str),
#[allow(dead_code)]
Optional(&'s str),
}
/// 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.
#[allow(dead_code)]
pub(crate) fn compare_noop<'b, 's, 'x, R, RG>(
_emacs: &'b Token<'s>,
_rust_node: R,
_emacs_field: &'x str,
_rust_value_getter: RG,
) -> Result<Option<(DiffStatus, Option<String>)>, Box<dyn std::error::Error>> {
Ok(None)
#[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.
///
/// 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_noop<'b, 's, R, RG>(
_source: &'s str,
_emacs: &'b Token<'s>,
_rust_node: R,
_emacs_field: &str,
_rust_value_getter: RG,
) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>> {
Ok(ComparePropertiesResult::NoChange)
}
/// 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.
pub(crate) fn compare_identity() {}
/// Assert that the emacs value is always nil or absent.
///
/// 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, R, RG>(
_source: &'s str,
emacs: &'b Token<'s>,
_rust_node: R,
emacs_field: &'x str,
emacs_field: &str,
_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)?;
if value.is_some() {
let this_status = DiffStatus::Bad;
@@ -51,45 +88,46 @@ pub(crate) fn compare_property_always_nil<'b, 's, 'x, R, RG>(
"{} was expected to always be nil: {:?}",
emacs_field, value
));
Ok(Some((this_status, message)))
Ok(ComparePropertiesResult::SelfChange(this_status, message))
} else {
Ok(None)
Ok(ComparePropertiesResult::NoChange)
}
}
pub(crate) fn compare_property_quoted_string<
'b,
's,
'x,
R,
RV: AsRef<str> + std::fmt::Debug,
RG: Fn(R) -> Option<RV>,
>(
_source: &'s str,
emacs: &'b Token<'s>,
rust_node: R,
emacs_field: &'x str,
emacs_field: &str,
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 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_deref() {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rust_value
));
Ok(Some((this_status, message)))
Ok(ComparePropertiesResult::SelfChange(this_status, message))
} else {
Ok(None)
Ok(ComparePropertiesResult::NoChange)
}
}
pub(crate) fn compare_property_unquoted_atom<'b, 's, 'x, R, RG: Fn(R) -> Option<&'s str>>(
_source: &'s str,
emacs: &'b Token<'s>,
rust_node: R,
emacs_field: &'x str,
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 rust_value = rust_value_getter(rust_node);
if rust_value != value {
@@ -98,26 +136,58 @@ pub(crate) fn compare_property_unquoted_atom<'b, 's, 'x, R, RG: Fn(R) -> Option<
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rust_value
));
Ok(Some((this_status, message)))
Ok(ComparePropertiesResult::SelfChange(this_status, message))
} 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,
emacs_field: &str,
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)?
.map(Token::as_list)
.map_or(Ok(None), |r| r.map(Some))?;
@@ -132,7 +202,7 @@ pub(crate) fn compare_property_list_of_quoted_string<
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rust_value
));
return Ok(Some((this_status, message)));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
(Some(el), Some(rl)) if el.len() != rl.len() => {
let this_status = DiffStatus::Bad;
@@ -140,7 +210,7 @@ pub(crate) fn compare_property_list_of_quoted_string<
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rust_value
));
return Ok(Some((this_status, message)));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
(Some(el), Some(rl)) => {
for (e, r) in el.iter().zip(rl) {
@@ -152,10 +222,524 @@ pub(crate) fn compare_property_list_of_quoted_string<
"{} mismatch (emacs != rust) {:?} != {:?}. Full list: {:?} != {:?}",
emacs_field, e, r, value, rust_value
));
return Ok(Some((this_status, message)));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
}
}
}
Ok(None)
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).copied().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_optional_pair<
'b,
's,
R,
RV: AsRef<str> + std::fmt::Debug,
ROV: AsRef<str> + std::fmt::Debug,
RG: Fn(R) -> Option<(Option<ROV>, RV)>,
>(
_source: &'s str,
emacs: &'b Token<'s>,
rust_node: R,
emacs_field: &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);
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((Some(_), _))) if el.len() != 3 => {
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((None, _))) if el.len() != 1 => {
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((Some(orl), rl))) => {
let e = el
.first()
.map(Token::as_atom)
.map_or(Ok(None), |r| r.map(Some))?
.map(unquote)
.map_or(Ok(None), |r| r.map(Some))?
.expect("Above match proved length to be 3.");
let oe = el
.get(2)
.map(Token::as_atom)
.map_or(Ok(None), |r| r.map(Some))?
.map(unquote)
.map_or(Ok(None), |r| r.map(Some))?
.expect("Above match proved length to be 3.");
let r = rl.as_ref();
let or = orl.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));
}
if oe != or {
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));
}
}
(Some(el), Some((None, rl))) => {
let e = el
.first()
.map(Token::as_atom)
.map_or(Ok(None), |r| r.map(Some))?
.map(unquote)
.map_or(Ok(None), |r| r.map(Some))?
.expect("Above match proved length to be 1.");
let r = rl.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_boolean<'b, 's, R, RG: Fn(R) -> bool>(
_source: &'s str,
emacs: &'b Token<'s>,
rust_node: R,
emacs_field: &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.iter().all(|t| matches!(t.as_atom(), Ok(r#""""#))) => {}
(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)
}
pub(crate) fn compare_property_object_tree<
'b,
's,
'x,
R,
RV: std::fmt::Debug + 'b,
ROV: std::fmt::Debug + 'b,
RI: Iterator<Item = &'b (Option<Vec<ROV>>, Vec<RV>)> + ExactSizeIterator + std::fmt::Debug,
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<&'b RV>,
AstNode<'b, 's>: From<&'b ROV>,
{
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);
let (outer_emacs_list, outer_rust_list) = match (value, rust_value) {
(None, None) => {
return Ok(ComparePropertiesResult::NoChange);
}
(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)) => (el, rl),
};
let mut full_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(outer_rust_list.len());
for (kw_e, kw_r) in outer_emacs_list.iter().zip(outer_rust_list) {
let kw_e = kw_e.as_list()?;
let child_status_length = kw_r.1.len() + kw_r.0.as_ref().map(|opt| opt.len()).unwrap_or(0);
let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(child_status_length);
if let Some(or) = &kw_r.0 {
// if optional value
let mut kw_e = kw_e.iter();
// First element is a list representing the mandatory value.
if let Some(val_e) = kw_e.next() {
let el = val_e.as_list()?;
if el.len() != kw_r.1.len() {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, kw_e, kw_r
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
for (e, r) in el.iter().zip(kw_r.1.iter()) {
child_status.push(compare_ast_node(source, e, r.into())?);
}
} else {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, kw_e, kw_r
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
// Remaining elements are the optional value.
if kw_e.len() != or.len() {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, kw_e, kw_r
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
for (e, r) in kw_e.zip(or.iter()) {
child_status.push(compare_ast_node(source, e, r.into())?);
}
} else {
// if no optional value
if !kw_e.len() == 1 {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, kw_e, kw_r
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
let e = kw_e
.first()
.map(Token::as_list)
.map_or(Ok(None), |r| r.map(Some))?
.expect("The above if-statement proves this will be Some.")
.iter();
let r = kw_r.1.iter();
if e.len() != r.len() {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, kw_e, kw_r
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
for (e, r) in e.zip(r) {
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 {
($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 message: Option<String> = None;
let mut new_status = Vec::new();
let children = $emacs.as_list()?;
let attributes_child = children
.iter()
@@ -44,10 +43,9 @@ macro_rules! compare_properties {
if emacs_keys.contains(":standard-properties") {
emacs_keys.remove(":standard-properties");
} else {
this_status = DiffStatus::Bad;
message = Some(format!(
new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
"Emacs token lacks :standard-properties field.",
));
))));
}
$(
match $emacs_field {
@@ -58,24 +56,22 @@ macro_rules! compare_properties {
emacs_keys.remove(name);
},
EmacsField::Required(name) => {
this_status = DiffStatus::Bad;
message = Some(format!(
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(", ");
this_status = DiffStatus::Bad;
message = Some(format!(
new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
"Emacs token had extra field(s): {}",
unexpected_keys
));
))));
}
$(
@@ -87,33 +83,23 @@ macro_rules! compare_properties {
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 {
Some((DiffStatus::Good, _)) => unreachable!("No comparison functions should return Some() when DiffStatus is good."),
Some((status, msg)) => {
this_status = status;
message = msg;
},
_ => {}
}
)+
match this_status {
DiffStatus::Good => {
let result: Result<_, Box<dyn std::error::Error>> = Ok(None);
result
},
_ => {
Ok(Some((this_status, message)))
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) => {
// For elements with affiliated keywords
($source:expr, $emacs:expr, $rust:expr, [], $(($emacs_field:expr, $rust_value_getter:expr, $compare_fn: expr)),*) => {
{
let mut this_status = DiffStatus::Good;
let mut message: Option<String> = None;
let mut new_status = Vec::new();
let children = $emacs.as_list()?;
let attributes_child = children
.iter()
@@ -124,30 +110,193 @@ macro_rules! compare_properties {
if emacs_keys.contains(":standard-properties") {
emacs_keys.remove(":standard-properties");
} else {
this_status = DiffStatus::Bad;
message = Some(format!(
new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
"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() {
let unexpected_keys: Vec<&str> = emacs_keys.into_iter().collect();
let unexpected_keys = unexpected_keys.join(", ");
this_status = DiffStatus::Bad;
message = Some(format!(
new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
"Emacs token had extra field(s): {}",
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

@@ -1,3 +1,4 @@
#[allow(clippy::module_inception)]
mod compare;
mod compare_field;
mod diff;
@@ -10,3 +11,5 @@ pub use compare::run_anonymous_compare;
pub use compare::run_anonymous_compare_with_settings;
pub use compare::run_compare_on_file;
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::process::Command;
use tokio::process::Command;
use crate::context::HeadlineLevelFilter;
use crate::settings::GlobalSettings;
@@ -25,9 +26,9 @@ fn global_settings_elisp(global_settings: &GlobalSettings) -> String {
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,
global_settings: &GlobalSettings,
global_settings: &GlobalSettings<'g, 's>,
) -> Result<String, Box<dyn std::error::Error>>
where
C: AsRef<str>,
@@ -54,15 +55,24 @@ where
.arg("--batch")
.arg("--eval")
.arg(elisp_script);
let out = cmd.output()?;
out.status.exit_ok()?;
let out = cmd.output().await?;
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;
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,
global_settings: &GlobalSettings,
global_settings: &GlobalSettings<'g, 's>,
) -> Result<String, Box<dyn std::error::Error>>
where
P: AsRef<Path>,
@@ -97,8 +107,17 @@ where
.arg("--batch")
.arg("--eval")
.arg(elisp_script);
let out = cmd.output()?;
out.status.exit_ok()?;
let out = cmd.output().await?;
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;
Ok(String::from_utf8(org_sexp)?)
}
@@ -125,7 +144,7 @@ where
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
(message "%s" (version))
)"#;
@@ -138,12 +157,12 @@ pub fn get_emacs_version() -> Result<String, Box<dyn std::error::Error>> {
.arg("--eval")
.arg(elisp_script);
let out = cmd.output()?;
let out = cmd.output().await?;
out.status.exit_ok()?;
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
(org-mode)
(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(elisp_script);
let out = cmd.output()?;
let out = cmd.output().await?;
out.status.exit_ok()?;
Ok(String::from_utf8(out.stderr)?)
}

View File

@@ -113,24 +113,21 @@ fn is_slice_of(parent: &str, child: &str) -> bool {
/// Get a slice of the string that was consumed in a parser using the original input to the parser and the remaining input after the parser.
fn get_consumed<'s>(input: &'s str, remaining: &'s str) -> &'s str {
debug_assert!(is_slice_of(input, remaining));
let source = {
let offset = remaining.as_ptr() as usize - input.as_ptr() as usize;
&input[..offset]
};
source.into()
let offset = remaining.as_ptr() as usize - input.as_ptr() as usize;
&input[..offset]
}
pub(crate) fn unquote(text: &str) -> Result<String, Box<dyn std::error::Error>> {
let mut out: Vec<u8> = Vec::with_capacity(text.len());
if !text.starts_with(r#"""#) {
if !text.starts_with('"') {
return Err("Quoted text does not start with quote.".into());
}
if !text.ends_with(r#"""#) {
if !text.ends_with('"') {
return Err("Quoted text does not end with quote.".into());
}
let interior_text = &text[1..(text.len() - 1)];
let mut state = ParseState::Normal;
for current_char in interior_text.bytes().into_iter() {
for current_char in interior_text.bytes() {
// Check to see if octal finished
state = match (state, current_char) {
(ParseState::Octal(octal), b'0'..=b'7') if octal.len() < MAX_OCTAL_LENGTH => {
@@ -229,11 +226,9 @@ fn atom<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn unquoted_atom<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
let (remaining, body) = take_till1(|c| match c {
' ' | '\t' | '\r' | '\n' | ')' | ']' => true,
_ => false,
})(input)?;
Ok((remaining, Token::Atom(body.into())))
let (remaining, body) =
take_till1(|c| matches!(c, ' ' | '\t' | '\r' | '\n' | ')' | ']'))(input)?;
Ok((remaining, Token::Atom(body)))
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
@@ -264,22 +259,19 @@ fn quoted_atom<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
}
let (remaining, _) = tag(r#"""#)(remaining)?;
let source = get_consumed(input, remaining);
Ok((remaining, Token::Atom(source.into())))
Ok((remaining, Token::Atom(source)))
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn hash_notation<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
let (remaining, _) = tag("#<")(input)?;
let (remaining, _body) = take_till1(|c| match c {
'>' => true,
_ => false,
})(remaining)?;
let (remaining, _body) = take_till1(|c| matches!(c, '>'))(remaining)?;
let (remaining, _) = tag(">")(remaining)?;
let source = get_consumed(input, remaining);
Ok((remaining, Token::Atom(source.into())))
Ok((remaining, Token::Atom(source)))
}
fn text_with_properties<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
fn text_with_properties(input: &str) -> Res<&str, Token<'_>> {
let (remaining, _) = tag("#(")(input)?;
let (remaining, (text, props)) = delimited(
multispace0,
@@ -348,10 +340,7 @@ mod tests {
let input = r#" (foo "b(a)r" baz ) "#;
let (remaining, parsed) = sexp(input).expect("Parse the input");
assert_eq!(remaining, "");
assert!(match parsed {
Token::List(_) => true,
_ => false,
});
assert!(matches!(parsed, Token::List(_)));
let children = match parsed {
Token::List(children) => children,
_ => panic!("Should be a list."),
@@ -364,14 +353,14 @@ mod tests {
r#"foo"#
);
assert_eq!(
match children.iter().nth(1) {
match children.get(1) {
Some(Token::Atom(body)) => *body,
_ => panic!("Second child should be an atom."),
},
r#""b(a)r""#
);
assert_eq!(
match children.iter().nth(2) {
match children.get(2) {
Some(Token::Atom(body)) => *body,
_ => panic!("Third child should be an atom."),
},

View File

@@ -1,8 +1,19 @@
use std::str::FromStr;
use super::compare_field::compare_property_list_of_quoted_string;
use super::compare_field::compare_property_object_tree;
use super::compare_field::compare_property_optional_pair;
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::sexp::Token;
use crate::compare::diff::compare_ast_node;
use crate::compare::sexp::unquote;
use crate::types::AffiliatedKeywordValue;
use crate::types::AstNode;
use crate::types::GetAffiliatedKeywords;
use crate::types::GetStandardProperties;
use crate::types::StandardProperties;
@@ -43,8 +54,8 @@ pub(crate) fn compare_standard_properties<
Ok(())
}
pub(crate) fn assert_name<'b, 's, S: AsRef<str>>(
emacs: &'b Token<'s>,
pub(crate) fn assert_name<S: AsRef<str>>(
emacs: &Token<'_>,
name: S,
) -> Result<(), Box<dyn std::error::Error>> {
let name = name.as_ref();
@@ -79,9 +90,9 @@ pub(crate) fn assert_bounds<'b, 's, S: StandardProperties<'s> + ?Sized>(
standard_properties.end.ok_or("Token should have an end.")?,
);
let (rust_begin, rust_end) = get_rust_byte_offsets(original_document, rust); // 0-based
let rust_begin_char_offset = (&original_document[..rust_begin]).chars().count() + 1; // 1-based
let rust_begin_char_offset = original_document[..rust_begin].chars().count() + 1; // 1-based
let rust_end_char_offset =
rust_begin_char_offset + (&original_document[rust_begin..rust_end]).chars().count(); // 1-based
rust_begin_char_offset + original_document[rust_begin..rust_end].chars().count(); // 1-based
if rust_begin_char_offset != begin || rust_end_char_offset != end {
Err(format!("Rust bounds (in chars) ({rust_begin}, {rust_end}) do not match emacs bounds ({emacs_begin}, {emacs_end})", rust_begin = rust_begin_char_offset, rust_end = rust_end_char_offset, emacs_begin=begin, emacs_end=end))?;
}
@@ -102,21 +113,18 @@ struct EmacsStandardProperties {
post_blank: Option<usize>,
}
fn get_emacs_standard_properties<'b, 's>(
emacs: &'b Token<'s>,
fn get_emacs_standard_properties(
emacs: &Token<'_>,
) -> Result<EmacsStandardProperties, Box<dyn std::error::Error>> {
let children = emacs.as_list()?;
let attributes_child = children
.iter()
.nth(1)
.ok_or("Should have an attributes child.")?;
let attributes_child = children.get(1).ok_or("Should have an attributes child.")?;
let attributes_map = attributes_child.as_map()?;
let standard_properties = attributes_map.get(":standard-properties");
Ok(if standard_properties.is_some() {
let mut std_props = standard_properties
.expect("if statement proves its Some")
.as_vector()?
.into_iter();
.iter();
let begin = maybe_token_to_usize(std_props.next())?;
let post_affiliated = maybe_token_to_usize(std_props.next())?;
let contents_begin = maybe_token_to_usize(std_props.next())?;
@@ -132,16 +140,13 @@ fn get_emacs_standard_properties<'b, 's>(
post_blank,
}
} else {
let begin = maybe_token_to_usize(attributes_map.get(":begin").map(|token| *token))?;
let end = maybe_token_to_usize(attributes_map.get(":end").map(|token| *token))?;
let contents_begin =
maybe_token_to_usize(attributes_map.get(":contents-begin").map(|token| *token))?;
let contents_end =
maybe_token_to_usize(attributes_map.get(":contents-end").map(|token| *token))?;
let post_blank =
maybe_token_to_usize(attributes_map.get(":post-blank").map(|token| *token))?;
let begin = maybe_token_to_usize(attributes_map.get(":begin").copied())?;
let end = maybe_token_to_usize(attributes_map.get(":end").copied())?;
let contents_begin = maybe_token_to_usize(attributes_map.get(":contents-begin").copied())?;
let contents_end = maybe_token_to_usize(attributes_map.get(":contents-end").copied())?;
let post_blank = maybe_token_to_usize(attributes_map.get(":post-blank").copied())?;
let post_affiliated =
maybe_token_to_usize(attributes_map.get(":post-affiliated").map(|token| *token))?;
maybe_token_to_usize(attributes_map.get(":post-affiliated").copied())?;
EmacsStandardProperties {
begin,
post_affiliated,
@@ -159,78 +164,57 @@ fn maybe_token_to_usize(
Ok(token
.map(|token| token.as_atom())
.map_or(Ok(None), |r| r.map(Some))?
.map(|val| {
.and_then(|val| {
if val == "nil" {
None
} else {
Some(val.parse::<usize>())
}
})
.flatten() // Outer option is whether or not the param exists, inner option is whether or not it is nil
.map_or(Ok(None), |r| r.map(Some))?)
}
/// Get a named property from the emacs token.
///
/// Returns Ok(None) if value is nil or absent.
pub(crate) fn get_property<'b, 's, 'x>(
pub(crate) fn get_property<'b, 's>(
emacs: &'b Token<'s>,
key: &'x str,
key: &str,
) -> Result<Option<&'b Token<'s>>, Box<dyn std::error::Error>> {
let children = emacs.as_list()?;
let attributes_child = children
.iter()
.nth(1)
.ok_or("Should have an attributes child.")?;
let attributes_child = children.get(1).ok_or("Should have an attributes child.")?;
let attributes_map = attributes_child.as_map()?;
let prop = attributes_map.get(key).map(|token| *token);
match prop.map(|token| token.as_atom()) {
Some(Ok("nil")) => return Ok(None),
_ => {}
};
let prop = attributes_map.get(key).copied();
if let Some(Ok("nil")) = prop.map(Token::as_atom) {
return Ok(None);
}
Ok(prop)
}
/// Get a named property containing an unquoted atom from the emacs token.
///
/// Returns None if key is not found.
pub(crate) fn get_property_unquoted_atom<'b, 's, 'x>(
emacs: &'b Token<'s>,
key: &'x str,
pub(crate) fn get_property_unquoted_atom<'s>(
emacs: &Token<'s>,
key: &str,
) -> Result<Option<&'s str>, Box<dyn std::error::Error>> {
Ok(get_property(emacs, key)?
get_property(emacs, key)?
.map(Token::as_atom)
.map_or(Ok(None), |r| r.map(Some))?)
.map_or(Ok(None), |r| r.map(Some))
}
/// Get a named property containing an quoted string from the emacs token.
///
/// Returns None if key is not found.
pub(crate) fn get_property_quoted_string<'b, 's, 'x>(
emacs: &'b Token<'s>,
key: &'x str,
pub(crate) fn get_property_quoted_string(
emacs: &Token<'_>,
key: &str,
) -> Result<Option<String>, Box<dyn std::error::Error>> {
Ok(get_property(emacs, key)?
get_property(emacs, key)?
.map(Token::as_atom)
.map_or(Ok(None), |r| r.map(Some))?
.map(unquote)
.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")
.map_or(Ok(None), |r| r.map(Some))
}
/// Get a named property containing an unquoted numeric value.
@@ -252,3 +236,167 @@ where
.map_or(Ok(None), |r| r.map(Some))?;
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(
emacs: &Token<'_>,
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_deref() {
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::OptionalPair { optval, val } => {
let diff = compare_property_optional_pair(
source,
emacs,
rust,
emacs_property_name.as_str(),
|_| Some((*optval, *val)),
)?;
ret.push(diff);
}
AffiliatedKeywordValue::ObjectTree(rust_value) => {
let diff = compare_property_object_tree(
source,
emacs,
rust,
emacs_property_name.as_str(),
|_| Some(rust_value.iter()),
)?;
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: [&str; 1] = ["CAPTION"];
pub(crate) const DEFAULT_ORG_ELEMENT_DUAL_KEYWORDS: [&str; 2] = ["CAPTION", "RESULTS"];
pub(crate) const DEFAULT_ORG_ELEMENT_AFFILIATED_KEYWORDS: [&str; 13] = [
"CAPTION", "DATA", "HEADER", "HEADERS", "LABEL", "NAME", "PLOT", "RESNAME", "RESULT",
"RESULTS", "SOURCE", "SRCNAME", "TBLNAME",
];
pub(crate) const DEFAULT_ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST: [(&str, &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: [&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::Res;
use crate::parser::OrgSource;
use crate::types::Keyword;
#[derive(Debug)]
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.
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.
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.
#[allow(dead_code)]
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> {
// TODO: Should this be "&'r DynContextMatcher<'c>" ?
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(
&'r self,
i: OrgSource<'s>,
@@ -124,23 +112,18 @@ impl<'g, 'r, 's> Context<'g, 'r, 's> {
let mut current_class_filter = ExitClass::Gamma;
for current_node in self.iter_context() {
let context_element = current_node.get_data();
match context_element {
ContextElement::ExitMatcherNode(exit_matcher) => {
if exit_matcher.class as u32 <= current_class_filter as u32 {
current_class_filter = exit_matcher.class;
let local_result = (exit_matcher.exit_matcher)(&current_node, i);
if local_result.is_ok() {
return local_result;
}
if let ContextElement::ExitMatcherNode(exit_matcher) = context_element {
if exit_matcher.class as u32 <= current_class_filter as u32 {
current_class_filter = exit_matcher.class;
let local_result = (exit_matcher.exit_matcher)(&current_node, i);
if local_result.is_ok() {
return local_result;
}
}
_ => {}
};
}
}
// TODO: Make this a specific error instead of just a generic MyError
return Err(nom::Err::Error(CustomError::MyError(MyError(
"NoExit".into(),
))));
return Err(nom::Err::Error(CustomError::MyError(MyError("NoExit"))));
}
/// Indicates if elements should consume the whitespace after them.
@@ -152,18 +135,18 @@ impl<'g, 'r, 's> Context<'g, 'r, 's> {
fn _should_consume_trailing_whitespace(&self) -> Option<bool> {
for current_node in self.iter() {
match current_node {
ContextElement::ConsumeTrailingWhitespace(should) => {
return Some(*should);
}
_ => {}
if let ContextElement::ConsumeTrailingWhitespace(should) = current_node {
return Some(*should);
}
}
None
}
}
#[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>(
_context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,

View File

@@ -1,6 +1,12 @@
use std::fmt::Debug;
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 {
fn read_file(&self, path: &str) -> Result<String, std::io::Error>;
}
@@ -14,10 +20,9 @@ impl FileAccessInterface for LocalFileAccessInterface {
fn read_file(&self, path: &str) -> Result<String, std::io::Error> {
let final_path = self
.working_directory
.as_ref()
.map(PathBuf::as_path)
.as_deref()
.map(|pb| pb.join(path))
.unwrap_or_else(|| PathBuf::from(path));
Ok(std::fs::read_to_string(final_path)?)
std::fs::read_to_string(final_path)
}
}

View File

@@ -1,8 +1,14 @@
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use super::constants::DEFAULT_ORG_ENTITIES;
use super::constants::DEFAULT_ORG_LINK_PARAMETERS;
use super::FileAccessInterface;
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::Object;
@@ -34,11 +40,6 @@ pub struct GlobalSettings<'g, 's> {
/// Corresponds to org-footnote-section elisp variable.
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/").
///
/// 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.
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;
#[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> {
fn new() -> GlobalSettings<'g, 's> {
debug_assert!(
DEFAULT_ORG_ENTITIES.is_sorted_by(|a, b| b.name.len().partial_cmp(&a.name.len()))
);
GlobalSettings {
radio_targets: Vec::new(),
file_access: &LocalFileAccessInterface {
@@ -67,9 +109,13 @@ impl<'g, 's> GlobalSettings<'g, 's> {
tab_width: DEFAULT_TAB_WIDTH,
odd_levels_only: HeadlineLevelFilter::default(),
footnote_section: "Footnotes",
coderef_label_format: "(ref:%s)",
link_parameters: &DEFAULT_ORG_LINK_PARAMETERS,
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,
}
}
}
@@ -80,40 +126,10 @@ impl<'g, 's> Default for GlobalSettings<'g, 's> {
}
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub enum HeadlineLevelFilter {
Odd,
#[default]
OddEven,
}
impl Default for HeadlineLevelFilter {
fn default() -> Self {
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",
];

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