diff --git a/README.md b/README.md index 467b285..1608ed3 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,26 @@ Organic is an emacs-less implementation of an [org-mode](https://orgmode.org/) p ## Project Status -This project is a personal learning project to grow my experience in [rust](https://www.rust-lang.org/). It is under development and at this time I would not recommend anyone use this code. The goal is to turn this into a project others can use, at which point more information will appear in this README. +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. + +### 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. + - 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 +- This project will not include an elisp engine since that would drastically increase the complexity of the code. Any features requiring an elisp engine will not be implemented (for example, Emacs supports embedded eval expressions in documents but this parser will never support that). +- This project is exclusively an org-mode **parser**. This limits its scope to roughly the output of `(org-element-parse-buffer)`. It will not render org-mode documents in other formats like HTML or LaTeX. +### Project Maybe-Goals +- table.el support. Currently we support org-mode tables but org-mode also allows table.el tables. So far, their use in org-mode documents seems rather uncommon so this is a low-priority feature. +- Document editing support. I do not anticipate any advanced editing features to make editing ergonomic, but it should be relatively easy to be able to parse an org-mode document and serialize it back into org-mode. This would enable cool features to be built on top of the library like auto-formatters. To accomplish this feature, We'd have to capture all of the various separators and whitespace that we are currently simply throwing away. This would add many additional fields to the parsed structs and it would add more noise to the parsers themselves, so I do not want to approach this feature until the parser is more complete since it would make modifications and refactoring more difficult. ## Using this library -TODO: Add section on using Organic as a library (which is the intended use for this project). +TODO: Add section on using Organic as a library (which is the intended use for this project). This will be added when we have a bit more API stability since currently the library is under heavy development. ### The parse binary This program takes org-mode input either streamed in on stdin or as paths to files passed in as arguments. It then parses them using Organic and dumps the result to stdout. This program is intended solely as a development tool. Examples: diff --git a/src/compare/diff.rs b/src/compare/diff.rs index ac5b4ee..2a0e25d 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -1,3 +1,4 @@ +// TODO: Add a check for unexpected keys in the properties use std::collections::BTreeSet; use std::collections::HashSet; @@ -387,7 +388,9 @@ pub fn compare_document<'s>( this_status = DiffStatus::Bad; } - // Skipping "org-data" and the first parameter which is often nil + // TODO: Compare :path :CATEGORY + + // Skipping "org-data" and its properties for (i, token) in children.iter().skip(2).enumerate() { let section_or_headline = token.as_list()?; let first_cell = section_or_headline @@ -618,7 +621,9 @@ fn compare_heading<'s>( (None, false) | (Some(_), true) => {} } - // TODO: Compare :footnote-section-p + // TODO: Compare :pre-blank :raw-value :footnote-section-p :scheduled :closed + // + // :scheduled and :closed seem to only appear when the headline has a planning // Compare section let section_status = children @@ -732,7 +737,7 @@ fn compare_plain_list<'s>( Ok(_) => {} }; - // TODO compare :type + // TODO: Compare :type // // :type is an unquoted atom of either descriptive, ordered, or unordered @@ -804,7 +809,7 @@ fn compare_plain_list_item<'s>( contents_status, )?); - // TODO: compare :bullet :counter :pre-blank + // TODO: Compare :bullet :counter :pre-blank // Compare checkbox let checkbox = get_property(emacs, ":checkbox")? @@ -862,6 +867,9 @@ fn compare_greater_block<'s>( Ok(_) => {} }; + // TODO: Special block compare :type :parameters + // Center and quote block has no additional properties + for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) { child_status.push(compare_element(source, emacs_child, rust_child)?); } @@ -937,6 +945,8 @@ fn compare_footnote_definition<'s>( Ok(_) => {} }; + // TODO: Compare :label :pre-blank + for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) { child_status.push(compare_element(source, emacs_child, rust_child)?); } @@ -973,6 +983,8 @@ fn compare_comment<'s>( Ok(_) => {} }; + // TODO: Compare :value + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -1006,6 +1018,8 @@ fn compare_drawer<'s>( Ok(_) => {} }; + // TODO: Compare :drawer-name + for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) { child_status.push(compare_element(source, emacs_child, rust_child)?); } @@ -1152,6 +1166,8 @@ fn compare_table<'s>( } } + // TODO: Compare :type :value + for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) { child_status.push(compare_table_row(source, emacs_child, rust_child)?); } @@ -1298,6 +1314,8 @@ fn compare_comment_block<'s>( Ok(_) => {} }; + // TODO: Compare :value + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -1329,6 +1347,8 @@ fn compare_example_block<'s>( Ok(_) => {} }; + // TODO: Compare :value :switches :number-lines :preserve-indent :retain-labels :use-labels :label-fmt + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -1360,6 +1380,8 @@ fn compare_export_block<'s>( Ok(_) => {} }; + // TODO: Compare :type :value + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -1424,6 +1446,8 @@ fn compare_clock<'s>( Ok(_) => {} }; + // TODO: Compare :status :value :duration + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -1455,6 +1479,8 @@ fn compare_diary_sexp<'s>( Ok(_) => {} }; + // TODO: Compare :value + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -1486,6 +1512,8 @@ fn compare_planning<'s>( Ok(_) => {} }; + // TODO: Compare :closed :deadline :scheduled + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -1518,6 +1546,8 @@ fn compare_fixed_width_area<'s>( Ok(_) => {} }; + // TODO: Compare :value + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -1639,7 +1669,7 @@ fn compare_babel_call<'s>( Ok(_) => {} }; - // TODO: compare :call :inside-header :arguments :end-header + // TODO: Compare :call :inside-header :arguments :end-header let value = unquote( get_property(emacs, ":value")? .ok_or("Emacs keywords should have a :value")? @@ -1685,6 +1715,8 @@ fn compare_latex_environment<'s>( Ok(_) => {} }; + // TODO: Compare :value + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -1859,6 +1891,8 @@ fn compare_verbatim<'s>( Ok(_) => {} }; + // TODO: Compare :value + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -1890,6 +1924,8 @@ fn compare_code<'s>( Ok(_) => {} }; + // TODO: Compare :value + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -1985,6 +2021,8 @@ fn compare_radio_link<'s>( Ok(_) => {} }; + // TODO: Compare :type :path :format :raw-link :application :search-option + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -2016,6 +2054,8 @@ fn compare_radio_target<'s>( Ok(_) => {} }; + // TODO: Compare :value + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -2047,6 +2087,8 @@ fn compare_plain_link<'s>( Ok(_) => {} }; + // TODO: Compare :type :path :format :raw-link :application :search-option + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -2078,6 +2120,8 @@ fn compare_angle_link<'s>( Ok(_) => {} }; + // TODO: Compare :type :path :format :raw-link :application :search-option + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -2109,6 +2153,8 @@ fn compare_org_macro<'s>( Ok(_) => {} }; + // TODO: Compare :key :value :args + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -2140,6 +2186,8 @@ fn compare_entity<'s>( Ok(_) => {} }; + // TODO: Compare :name :latex :latex-math-p :html :ascii :latin1 :utf-8 :use-brackets-p + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -2171,6 +2219,8 @@ fn compare_latex_fragment<'s>( Ok(_) => {} }; + // TODO: Compare :value + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -2202,6 +2252,8 @@ fn compare_export_snippet<'s>( Ok(_) => {} }; + // TODO: Compare :back-end :value + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -2233,6 +2285,8 @@ fn compare_footnote_reference<'s>( Ok(_) => {} }; + // TODO: Compare :label :type + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -2264,6 +2318,8 @@ fn compare_citation<'s>( Ok(_) => {} }; + // TODO: Compare :style :prefix :suffix + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -2295,6 +2351,8 @@ fn compare_citation_reference<'s>( Ok(_) => {} }; + // TODO: Compare :key :prefix :suffix + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -2326,6 +2384,8 @@ fn compare_inline_babel_call<'s>( Ok(_) => {} }; + // TODO: Compare :call :inside-header :arguments :end-header :value + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -2357,6 +2417,8 @@ fn compare_inline_source_block<'s>( Ok(_) => {} }; + // TODO: Compare :language :value :parameters + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -2419,6 +2481,8 @@ fn compare_target<'s>( Ok(_) => {} }; + // TODO: Compare :value + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -2450,6 +2514,8 @@ fn compare_statistics_cookie<'s>( Ok(_) => {} }; + // TODO: Compare :value + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(), @@ -2481,7 +2547,7 @@ fn compare_subscript<'s>( Ok(_) => {} }; - // TODO compare :use-brackets-p + // TODO: Compare :use-brackets-p Ok(DiffResult { status: this_status, @@ -2514,7 +2580,7 @@ fn compare_superscript<'s>( Ok(_) => {} }; - // TODO compare :use-brackets-p + // TODO: Compare :use-brackets-p Ok(DiffResult { status: this_status, @@ -2547,6 +2613,8 @@ fn compare_timestamp<'s>( Ok(_) => {} }; + // TODO: Compare :type :range-type :raw-value :year-start :month-start :day-start :hour-start :minute-start :year-end :month-end :day-end :hour-end :minute-end + Ok(DiffResult { status: this_status, name: emacs_name.to_owned(),