From 6670f8c76887041a8e274c75b749238c08ff4d70 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 29 Sep 2023 14:26:39 -0400 Subject: [PATCH 1/8] Add tests for alphabetic counter sets. --- .../plain_list/alphabetic_counter_set.org | 22 ++++++++++++++ .../alphabetic_list_larger_than_26.org | 30 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 org_mode_samples/greater_element/plain_list/alphabetic_counter_set.org create mode 100644 org_mode_samples/greater_element/plain_list/alphabetic_list_larger_than_26.org diff --git a/org_mode_samples/greater_element/plain_list/alphabetic_counter_set.org b/org_mode_samples/greater_element/plain_list/alphabetic_counter_set.org new file mode 100644 index 0000000..6fbf660 --- /dev/null +++ b/org_mode_samples/greater_element/plain_list/alphabetic_counter_set.org @@ -0,0 +1,22 @@ +# An ordered list starting at 3 +1. [@3] foo + + +# An ordered list starting at 11 +1. [@D] bar + + +# An ordered list starting at 1 with the contents of "[@kk] baz" +1. [@kk] baz + + +# A paragraph when org-list-allow-alphabetical is nil +m. lorem + + +# A paragraph when org-list-allow-alphabetical is nil +m. [@k] ipsum + + +# An unordered list with :counter set to 3 +- [@3] dolar diff --git a/org_mode_samples/greater_element/plain_list/alphabetic_list_larger_than_26.org b/org_mode_samples/greater_element/plain_list/alphabetic_list_larger_than_26.org new file mode 100644 index 0000000..f8f0216 --- /dev/null +++ b/org_mode_samples/greater_element/plain_list/alphabetic_list_larger_than_26.org @@ -0,0 +1,30 @@ +# Alphabetic lists larger than 26 elements should become numbered. From M-x describe-variable org-list-allow-alphabetical: +# +# > Lists with more than 26 items will fallback to standard numbering. +a. 1 +a. 2 +a. 3 +a. 4 +a. 5 +a. 6 +a. 7 +a. 8 +a. 9 +a. 10 +a. 11 +a. 12 +a. 13 +a. 14 +a. 15 +a. 16 +a. 17 +a. 18 +a. 19 +a. 20 +a. 21 +a. 22 +a. 23 +a. 24 +a. 25 +a. 26 +a. 27 From 3a422e6435ebf30b1cce4618fd711b936c10e2ae Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 29 Sep 2023 14:32:41 -0400 Subject: [PATCH 2/8] Counter set always allows alphabetic values regardless of org-list-allow-alphabetical. --- src/parser/plain_list.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/parser/plain_list.rs b/src/parser/plain_list.rs index f2e4313..5b55611 100644 --- a/src/parser/plain_list.rs +++ b/src/parser/plain_list.rs @@ -166,12 +166,8 @@ fn plain_list_item<'b, 'g, 'r, 's>( |(_bullet_type, bull)| Into::<&str>::into(bull) != "*" || indent_level > 0, )(remaining)?; - let (remaining, _maybe_counter_set) = opt(tuple(( - space1, - tag("[@"), - parser_with_context!(counter)(context), - tag("]"), - )))(remaining)?; + let (remaining, _maybe_counter_set) = + opt(tuple((space1, tag("[@"), counter_set_value, tag("]"))))(remaining)?; let (remaining, maybe_checkbox) = opt(tuple((space1, item_checkbox)))(remaining)?; @@ -314,6 +310,16 @@ fn counter<'b, 'g, 'r, 's>( } } +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +fn counter_set_value<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { + alt(( + recognize(one_of( + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + )), + digit1, + ))(input) +} + #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn plain_list_end<'b, 'g, 'r, 's>( _context: RefContext<'b, 'g, 'r, 's>, From f1261ddce84aa9bb8fe8126ac7d328909c897f27 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 29 Sep 2023 14:33:52 -0400 Subject: [PATCH 3/8] Remove "org_" prefix from list_allow_alphabetical. These settings are exclusively for parsing org-mode so the prefix is redundant. --- src/context/global_settings.rs | 4 ++-- src/parser/plain_list.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/context/global_settings.rs b/src/context/global_settings.rs index e91c93a..5932cc1 100644 --- a/src/context/global_settings.rs +++ b/src/context/global_settings.rs @@ -16,7 +16,7 @@ pub struct GlobalSettings<'g, 's> { /// Set to true to allow for plain lists using single letters as the bullet in the same way that numbers are used. /// /// Corresponds to the org-list-allow-alphabetical elisp variable. - pub org_list_allow_alphabetical: bool, + pub list_allow_alphabetical: bool, /// How many spaces a tab should be equal to. /// @@ -38,7 +38,7 @@ impl<'g, 's> GlobalSettings<'g, 's> { }, in_progress_todo_keywords: BTreeSet::new(), complete_todo_keywords: BTreeSet::new(), - org_list_allow_alphabetical: false, + list_allow_alphabetical: false, tab_width: 8, odd_levels_only: HeadlineLevelFilter::OddEven, } diff --git a/src/parser/plain_list.rs b/src/parser/plain_list.rs index 5b55611..3b10d55 100644 --- a/src/parser/plain_list.rs +++ b/src/parser/plain_list.rs @@ -298,7 +298,7 @@ fn counter<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, OrgSource<'s>> { - if context.get_global_settings().org_list_allow_alphabetical { + if context.get_global_settings().list_allow_alphabetical { alt(( recognize(one_of( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", From 2012e5a6d5c4840e647513a66b84f448ed8f9883 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 29 Sep 2023 15:30:38 -0400 Subject: [PATCH 4/8] Test org_mode_samples both with and without alphabetical lists enabled. --- build.rs | 27 ++++++------------------ notes/test_names.org | 5 +++++ src/compare/compare.rs | 29 +++++++++++++++++++++++++ src/compare/mod.rs | 1 + src/compare/parse.rs | 48 ++++++++++++++++++++++++++++++++++++++++++ tests/test_loader.rs | 5 ++++- tests/test_template | 17 ++++++++++++++- 7 files changed, 109 insertions(+), 23 deletions(-) create mode 100644 notes/test_names.org diff --git a/build.rs b/build.rs index 6fbdbe8..307b9f4 100644 --- a/build.rs +++ b/build.rs @@ -19,8 +19,6 @@ fn main() { // Re-generate the tests if any org-mode files change println!("cargo:rerun-if-changed=org_mode_samples"); - write_header(&mut test_file); - let test_files = WalkDir::new("org_mode_samples") .into_iter() .filter(|e| match e { @@ -54,28 +52,15 @@ fn write_test(test_file: &mut File, test: &walkdir::DirEntry) { .strip_suffix(".org") .expect("Should have .org extension") .replace("/", "_"); - let test_name = format!("autogen_{}", test_name); - if let Some(_reason) = is_expect_fail(test_name.as_str()) { - write!(test_file, "#[ignore]\n").unwrap(); - } write!( test_file, include_str!("./tests/test_template"), name = test_name, - path = test.path().display() - ) - .unwrap(); -} - -#[cfg(feature = "compare")] -fn write_header(test_file: &mut File) { - write!( - test_file, - r#" -#[feature(exit_status_error)] - -"# + path = test.path().display(), + expect_fail = is_expect_fail(test_name.as_str()) + .map(|_| "#[ignore]\n") + .unwrap_or("") ) .unwrap(); } @@ -83,8 +68,8 @@ fn write_header(test_file: &mut File) { #[cfg(feature = "compare")] fn is_expect_fail(name: &str) -> Option<&str> { match name { - "autogen_greater_element_drawer_drawer_with_headline_inside" => Some("Apparently lines with :end: become their own paragraph. This odd behavior needs to be investigated more."), - "autogen_element_container_priority_footnote_definition_dynamic_block" => Some("Apparently broken begin lines become their own paragraph."), + "greater_element_drawer_drawer_with_headline_inside" => Some("Apparently lines with :end: become their own paragraph. This odd behavior needs to be investigated more."), + "element_container_priority_footnote_definition_dynamic_block" => Some("Apparently broken begin lines become their own paragraph."), _ => None, } } diff --git a/notes/test_names.org b/notes/test_names.org new file mode 100644 index 0000000..cf2bc18 --- /dev/null +++ b/notes/test_names.org @@ -0,0 +1,5 @@ +* Autogen tests +The autogen tests are the tests automatically generated to compare the output of Organic vs the upstream Emacs Org-mode parser using the sample documents in the =org_mode_samples= folder. They will have a prefix based on the settings for each test. + +- No prefix :: The test is run with the default settings (The upstream Emacs Org-mode determines the default settings) +- la :: Short for "list alphabetic". Enables alphabetic plain lists. diff --git a/src/compare/compare.rs b/src/compare/compare.rs index 5d6cd46..d887d24 100644 --- a/src/compare/compare.rs +++ b/src/compare/compare.rs @@ -2,6 +2,7 @@ use std::path::Path; use crate::compare::diff::compare_document; use crate::compare::parse::emacs_parse_anonymous_org_document; +use crate::compare::parse::emacs_parse_anonymous_org_document_with_settings; use crate::compare::parse::emacs_parse_file_org_document; use crate::compare::parse::get_emacs_version; use crate::compare::parse::get_org_mode_version; @@ -75,3 +76,31 @@ pub fn run_compare_on_file>(org_path: P) -> Result<(), Box>( + org_contents: P, + global_settings: &GlobalSettings, +) -> Result<(), Box> { + // 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(); + eprintln!("Using emacs version: {}", get_emacs_version()?.trim()); + eprintln!("Using org-mode version: {}", get_org_mode_version()?.trim()); + let rust_parsed = parse_with_settings(org_contents, global_settings)?; + let org_sexp = emacs_parse_anonymous_org_document_with_settings(org_contents, global_settings)?; + 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); + + // 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 diff_result.is_bad() { + Err("Diff results do not match.")?; + } + + Ok(()) +} diff --git a/src/compare/mod.rs b/src/compare/mod.rs index 5c729a8..f901679 100644 --- a/src/compare/mod.rs +++ b/src/compare/mod.rs @@ -5,4 +5,5 @@ mod parse; mod sexp; mod util; pub use compare::run_anonymous_compare; +pub use compare::run_anonymous_compare_with_settings; pub use compare::run_compare_on_file; diff --git a/src/compare/parse.rs b/src/compare/parse.rs index 6170ce7..f60ef2e 100644 --- a/src/compare/parse.rs +++ b/src/compare/parse.rs @@ -1,6 +1,19 @@ use std::path::Path; use std::process::Command; +use crate::GlobalSettings; + +/// Generate elisp to configure org-mode parsing settings +/// +/// Currently only org-list-allow-alphabetical is supported. +fn global_settings_elisp(global_settings: &GlobalSettings) -> String { + let mut ret = "".to_owned(); + if global_settings.list_allow_alphabetical { + ret += "(setq org-list-allow-alphabetical t)\n" + } + ret +} + pub fn emacs_parse_anonymous_org_document( file_contents: C, ) -> Result> @@ -33,6 +46,41 @@ where Ok(String::from_utf8(org_sexp)?) } +pub fn emacs_parse_anonymous_org_document_with_settings( + file_contents: C, + global_settings: &GlobalSettings, +) -> Result> +where + C: AsRef, +{ + let escaped_file_contents = escape_elisp_string(file_contents); + let elisp_script = format!( + r#"(progn + (erase-buffer) + (require 'org) + (defun org-table-align () t) + (insert "{escaped_file_contents}") + {global_settings} + (org-mode) + (message "%s" (pp-to-string (org-element-parse-buffer))) +)"#, + escaped_file_contents = escaped_file_contents, + global_settings = global_settings_elisp(global_settings) + ); + let mut cmd = Command::new("emacs"); + let cmd = cmd + .arg("-q") + .arg("--no-site-file") + .arg("--no-splash") + .arg("--batch") + .arg("--eval") + .arg(elisp_script); + let out = cmd.output()?; + out.status.exit_ok()?; + let org_sexp = out.stderr; + Ok(String::from_utf8(org_sexp)?) +} + pub fn emacs_parse_file_org_document

(file_path: P) -> Result> where P: AsRef, diff --git a/tests/test_loader.rs b/tests/test_loader.rs index 9d63cc1..076f485 100644 --- a/tests/test_loader.rs +++ b/tests/test_loader.rs @@ -1,2 +1,5 @@ -#[cfg(feature = "compare")] +#![cfg(feature = "compare")] + +#[feature(exit_status_error)] + include!(concat!(env!("OUT_DIR"), "/tests.rs")); diff --git a/tests/test_template b/tests/test_template index 4454eb9..dd1b569 100644 --- a/tests/test_template +++ b/tests/test_template @@ -1,7 +1,22 @@ +{expect_fail} #[test] -fn {name}() -> Result<(), Box> {{ +fn autogen_{name}() -> Result<(), Box> {{ let org_path = "{path}"; let org_contents = std::fs::read_to_string(org_path).expect("Read org file."); organic::compare::run_anonymous_compare(org_contents.as_str())?; Ok(()) }} + +{expect_fail} +#[test] +fn autogen_la_{name}() -> Result<(), Box> {{ + let org_path = "{path}"; + let org_contents = std::fs::read_to_string(org_path).expect("Read org file."); + let global_settings = {{ + let mut global_settings = organic::GlobalSettings::default(); + global_settings.list_allow_alphabetical = true; + global_settings + }}; + organic::compare::run_anonymous_compare_with_settings(org_contents.as_str(), &global_settings)?; + Ok(()) +}} From 7a854838efbab397784732299d58ab781052afe0 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 29 Sep 2023 15:35:57 -0400 Subject: [PATCH 5/8] Clean up code duplication. --- src/compare/compare.rs | 27 ++------------------------- src/compare/parse.rs | 34 +--------------------------------- 2 files changed, 3 insertions(+), 58 deletions(-) diff --git a/src/compare/compare.rs b/src/compare/compare.rs index d887d24..f57abbc 100644 --- a/src/compare/compare.rs +++ b/src/compare/compare.rs @@ -2,12 +2,10 @@ use std::path::Path; use crate::compare::diff::compare_document; use crate::compare::parse::emacs_parse_anonymous_org_document; -use crate::compare::parse::emacs_parse_anonymous_org_document_with_settings; use crate::compare::parse::emacs_parse_file_org_document; use crate::compare::parse::get_emacs_version; use crate::compare::parse::get_org_mode_version; use crate::compare::sexp::sexp; -use crate::parser::parse; use crate::parser::parse_with_settings; use crate::GlobalSettings; use crate::LocalFileAccessInterface; @@ -15,28 +13,7 @@ use crate::LocalFileAccessInterface; pub fn run_anonymous_compare>( org_contents: P, ) -> Result<(), Box> { - // 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(); - eprintln!("Using emacs version: {}", get_emacs_version()?.trim()); - eprintln!("Using org-mode version: {}", get_org_mode_version()?.trim()); - let rust_parsed = parse(org_contents)?; - let org_sexp = emacs_parse_anonymous_org_document(org_contents)?; - 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); - - // 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 diff_result.is_bad() { - Err("Diff results do not match.")?; - } - - Ok(()) + run_anonymous_compare_with_settings(org_contents, &GlobalSettings::default()) } pub fn run_compare_on_file>(org_path: P) -> Result<(), Box> { @@ -87,7 +64,7 @@ pub fn run_anonymous_compare_with_settings>( eprintln!("Using emacs version: {}", get_emacs_version()?.trim()); eprintln!("Using org-mode version: {}", get_org_mode_version()?.trim()); let rust_parsed = parse_with_settings(org_contents, global_settings)?; - let org_sexp = emacs_parse_anonymous_org_document_with_settings(org_contents, global_settings)?; + let org_sexp = emacs_parse_anonymous_org_document(org_contents, global_settings)?; let (_remaining, parsed_sexp) = sexp(org_sexp.as_str()).map_err(|e| e.to_string())?; println!("{}\n\n\n", org_contents); diff --git a/src/compare/parse.rs b/src/compare/parse.rs index f60ef2e..5440749 100644 --- a/src/compare/parse.rs +++ b/src/compare/parse.rs @@ -14,39 +14,7 @@ fn global_settings_elisp(global_settings: &GlobalSettings) -> String { ret } -pub fn emacs_parse_anonymous_org_document( - file_contents: C, -) -> Result> -where - C: AsRef, -{ - let escaped_file_contents = escape_elisp_string(file_contents); - let elisp_script = format!( - r#"(progn - (erase-buffer) - (require 'org) - (defun org-table-align () t) - (insert "{escaped_file_contents}") - (org-mode) - (message "%s" (pp-to-string (org-element-parse-buffer))) -)"#, - escaped_file_contents = escaped_file_contents - ); - let mut cmd = Command::new("emacs"); - let cmd = cmd - .arg("-q") - .arg("--no-site-file") - .arg("--no-splash") - .arg("--batch") - .arg("--eval") - .arg(elisp_script); - let out = cmd.output()?; - out.status.exit_ok()?; - let org_sexp = out.stderr; - Ok(String::from_utf8(org_sexp)?) -} - -pub fn emacs_parse_anonymous_org_document_with_settings( +pub(crate) fn emacs_parse_anonymous_org_document( file_contents: C, global_settings: &GlobalSettings, ) -> Result> From 9a479b33e060d1f66183919a4c7bf18d13c332f8 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 29 Sep 2023 15:42:07 -0400 Subject: [PATCH 6/8] Make the same changes we did for stdin compare to comparing files. --- src/compare/compare.rs | 77 +++++++++++++++++++++++------------------- src/compare/mod.rs | 1 + src/compare/parse.rs | 9 +++-- 3 files changed, 50 insertions(+), 37 deletions(-) diff --git a/src/compare/compare.rs b/src/compare/compare.rs index f57abbc..4a1c033 100644 --- a/src/compare/compare.rs +++ b/src/compare/compare.rs @@ -17,41 +17,7 @@ pub fn run_anonymous_compare>( } pub fn run_compare_on_file>(org_path: P) -> Result<(), Box> { - let org_path = org_path.as_ref(); - eprintln!("Using emacs version: {}", get_emacs_version()?.trim()); - eprintln!("Using org-mode version: {}", get_org_mode_version()?.trim()); - let parent_directory = org_path - .parent() - .ok_or("Should be contained inside a directory.")?; - let org_contents = std::fs::read_to_string(org_path)?; - // 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.replace("\r\n", "\n"); - let org_contents = org_contents.as_str(); - let file_access_interface = LocalFileAccessInterface { - working_directory: Some(parent_directory.to_path_buf()), - }; - let global_settings = { - let mut global_settings = GlobalSettings::default(); - global_settings.file_access = &file_access_interface; - global_settings - }; - let rust_parsed = parse_with_settings(org_contents, &global_settings)?; - let org_sexp = emacs_parse_file_org_document(org_path)?; - 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); - - // 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 diff_result.is_bad() { - Err("Diff results do not match.")?; - } - - Ok(()) + run_compare_on_file_with_settings(org_path, &GlobalSettings::default()) } pub fn run_anonymous_compare_with_settings>( @@ -81,3 +47,44 @@ pub fn run_anonymous_compare_with_settings>( Ok(()) } + +pub fn run_compare_on_file_with_settings>( + org_path: P, + global_settings: &GlobalSettings, +) -> Result<(), Box> { + let org_path = org_path.as_ref(); + eprintln!("Using emacs version: {}", get_emacs_version()?.trim()); + eprintln!("Using org-mode version: {}", get_org_mode_version()?.trim()); + let parent_directory = org_path + .parent() + .ok_or("Should be contained inside a directory.")?; + let org_contents = std::fs::read_to_string(org_path)?; + // 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.replace("\r\n", "\n"); + let org_contents = org_contents.as_str(); + let file_access_interface = LocalFileAccessInterface { + working_directory: Some(parent_directory.to_path_buf()), + }; + let global_settings = { + let mut global_settings = global_settings.clone(); + global_settings.file_access = &file_access_interface; + global_settings + }; + let rust_parsed = parse_with_settings(org_contents, &global_settings)?; + let org_sexp = emacs_parse_file_org_document(org_path, &global_settings)?; + 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); + + // 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 diff_result.is_bad() { + Err("Diff results do not match.")?; + } + + Ok(()) +} diff --git a/src/compare/mod.rs b/src/compare/mod.rs index f901679..1a75e2e 100644 --- a/src/compare/mod.rs +++ b/src/compare/mod.rs @@ -7,3 +7,4 @@ mod util; 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; diff --git a/src/compare/parse.rs b/src/compare/parse.rs index 5440749..37cf4e1 100644 --- a/src/compare/parse.rs +++ b/src/compare/parse.rs @@ -49,7 +49,10 @@ where Ok(String::from_utf8(org_sexp)?) } -pub fn emacs_parse_file_org_document

(file_path: P) -> Result> +pub(crate) fn emacs_parse_file_org_document

( + file_path: P, + global_settings: &GlobalSettings, +) -> Result> where P: AsRef, { @@ -62,9 +65,11 @@ where r#"(progn (require 'org) (defun org-table-align () t) + {global_settings} (org-mode) (message "%s" (pp-to-string (org-element-parse-buffer))) -)"# +)"#, + global_settings = global_settings_elisp(global_settings) ); let mut cmd = Command::new("emacs"); let cmd = cmd From 0c363c8dd69a4dac15664fe4f76b2967a0704c40 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 29 Sep 2023 15:47:58 -0400 Subject: [PATCH 7/8] Add tests for tab width. --- notes/test_names.org | 1 + .../tab_width_indentation_level.org | 5 ++++ src/compare/parse.rs | 2 ++ tests/test_template | 30 +++++++++++++++++++ 4 files changed, 38 insertions(+) create mode 100644 org_mode_samples/greater_element/plain_list/tab_width_indentation_level.org diff --git a/notes/test_names.org b/notes/test_names.org index cf2bc18..d9e4f54 100644 --- a/notes/test_names.org +++ b/notes/test_names.org @@ -3,3 +3,4 @@ The autogen tests are the tests automatically generated to compare the output of - No prefix :: The test is run with the default settings (The upstream Emacs Org-mode determines the default settings) - la :: Short for "list alphabetic". Enables alphabetic plain lists. +- t# :: Sets the tab-width to # (as in t4 sets the tab-width to 4). diff --git a/org_mode_samples/greater_element/plain_list/tab_width_indentation_level.org b/org_mode_samples/greater_element/plain_list/tab_width_indentation_level.org new file mode 100644 index 0000000..2dbe900 --- /dev/null +++ b/org_mode_samples/greater_element/plain_list/tab_width_indentation_level.org @@ -0,0 +1,5 @@ +# "lorem" is prefixed by a tab instead of spaces, so the editor's tab-width value determines whether lorem is a sibling of baz (tab-width 8), a sibling of bar (tab-width < 8), or a child of baz (tab-width > 8). +1. foo + 1. bar + 1. baz + 1. lorem diff --git a/src/compare/parse.rs b/src/compare/parse.rs index 37cf4e1..0a35077 100644 --- a/src/compare/parse.rs +++ b/src/compare/parse.rs @@ -7,10 +7,12 @@ use crate::GlobalSettings; /// /// Currently only org-list-allow-alphabetical is supported. fn global_settings_elisp(global_settings: &GlobalSettings) -> String { + // This string concatenation is wildly inefficient but its only called in tests 🤷. let mut ret = "".to_owned(); if global_settings.list_allow_alphabetical { ret += "(setq org-list-allow-alphabetical t)\n" } + ret += format!("(setq-default tab-width {})", global_settings.tab_width).as_str(); ret } diff --git a/tests/test_template b/tests/test_template index dd1b569..1376ed3 100644 --- a/tests/test_template +++ b/tests/test_template @@ -1,3 +1,5 @@ +// TODO: Investigate writing a proc macro to make specifying these permutations easier. + {expect_fail} #[test] fn autogen_{name}() -> Result<(), Box> {{ @@ -20,3 +22,31 @@ fn autogen_la_{name}() -> Result<(), Box> {{ organic::compare::run_anonymous_compare_with_settings(org_contents.as_str(), &global_settings)?; Ok(()) }} + +{expect_fail} +#[test] +fn autogen_t1_{name}() -> Result<(), Box> {{ + let org_path = "{path}"; + let org_contents = std::fs::read_to_string(org_path).expect("Read org file."); + let global_settings = {{ + let mut global_settings = organic::GlobalSettings::default(); + global_settings.tab_width = 1; + global_settings + }}; + organic::compare::run_anonymous_compare_with_settings(org_contents.as_str(), &global_settings)?; + Ok(()) +}} + +{expect_fail} +#[test] +fn autogen_t16_{name}() -> Result<(), Box> {{ + let org_path = "{path}"; + let org_contents = std::fs::read_to_string(org_path).expect("Read org file."); + let global_settings = {{ + let mut global_settings = organic::GlobalSettings::default(); + global_settings.tab_width = 16; + global_settings + }}; + organic::compare::run_anonymous_compare_with_settings(org_contents.as_str(), &global_settings)?; + Ok(()) +}} From 07e11e359afd366f67b12acc083ac17c8f51b8fe Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 29 Sep 2023 16:37:22 -0400 Subject: [PATCH 8/8] Add tests for odd headline levels. --- notes/test_names.org | 3 ++- src/compare/compare.rs | 4 ++-- src/compare/parse.rs | 13 +++++++++++-- src/context/global_settings.rs | 14 +++++++++++--- src/context/mod.rs | 1 + src/lib.rs | 10 +++++++--- src/main.rs | 4 ++-- src/parser/in_buffer_settings.rs | 2 +- tests/test_template | 24 +++++++++++++++++++----- 9 files changed, 56 insertions(+), 19 deletions(-) diff --git a/notes/test_names.org b/notes/test_names.org index d9e4f54..fa0dde3 100644 --- a/notes/test_names.org +++ b/notes/test_names.org @@ -1,6 +1,7 @@ * Autogen tests The autogen tests are the tests automatically generated to compare the output of Organic vs the upstream Emacs Org-mode parser using the sample documents in the =org_mode_samples= folder. They will have a prefix based on the settings for each test. -- No prefix :: The test is run with the default settings (The upstream Emacs Org-mode determines the default settings) +- default :: The test is run with the default settings (The upstream Emacs Org-mode determines the default settings) - la :: Short for "list alphabetic". Enables alphabetic plain lists. - t# :: Sets the tab-width to # (as in t4 sets the tab-width to 4). +- odd :: Sets the org-odd-levels-only setting to true (meaning "odd" as opposed to "oddeven"). diff --git a/src/compare/compare.rs b/src/compare/compare.rs index 4a1c033..0224267 100644 --- a/src/compare/compare.rs +++ b/src/compare/compare.rs @@ -6,9 +6,9 @@ use crate::compare::parse::emacs_parse_file_org_document; use crate::compare::parse::get_emacs_version; use crate::compare::parse::get_org_mode_version; use crate::compare::sexp::sexp; +use crate::context::GlobalSettings; +use crate::context::LocalFileAccessInterface; use crate::parser::parse_with_settings; -use crate::GlobalSettings; -use crate::LocalFileAccessInterface; pub fn run_anonymous_compare>( org_contents: P, diff --git a/src/compare/parse.rs b/src/compare/parse.rs index 0a35077..cfa3e17 100644 --- a/src/compare/parse.rs +++ b/src/compare/parse.rs @@ -1,7 +1,8 @@ use std::path::Path; use std::process::Command; -use crate::GlobalSettings; +use crate::context::HeadlineLevelFilter; +use crate::settings::GlobalSettings; /// Generate elisp to configure org-mode parsing settings /// @@ -12,7 +13,15 @@ fn global_settings_elisp(global_settings: &GlobalSettings) -> String { if global_settings.list_allow_alphabetical { ret += "(setq org-list-allow-alphabetical t)\n" } - ret += format!("(setq-default tab-width {})", global_settings.tab_width).as_str(); + if global_settings.tab_width != crate::settings::DEFAULT_TAB_WIDTH { + ret += format!("(setq-default tab-width {})", global_settings.tab_width).as_str(); + } + if global_settings.odd_levels_only != HeadlineLevelFilter::default() { + ret += match global_settings.odd_levels_only { + HeadlineLevelFilter::Odd => "(setq org-odd-levels-only t)\n", + HeadlineLevelFilter::OddEven => "(setq org-odd-levels-only nil)\n", + }; + } ret } diff --git a/src/context/global_settings.rs b/src/context/global_settings.rs index 5932cc1..c12bbcf 100644 --- a/src/context/global_settings.rs +++ b/src/context/global_settings.rs @@ -29,6 +29,8 @@ pub struct GlobalSettings<'g, 's> { pub odd_levels_only: HeadlineLevelFilter, } +pub const DEFAULT_TAB_WIDTH: IndentationLevel = 8; + impl<'g, 's> GlobalSettings<'g, 's> { fn new() -> GlobalSettings<'g, 's> { GlobalSettings { @@ -39,8 +41,8 @@ impl<'g, 's> GlobalSettings<'g, 's> { in_progress_todo_keywords: BTreeSet::new(), complete_todo_keywords: BTreeSet::new(), list_allow_alphabetical: false, - tab_width: 8, - odd_levels_only: HeadlineLevelFilter::OddEven, + tab_width: DEFAULT_TAB_WIDTH, + odd_levels_only: HeadlineLevelFilter::default(), } } } @@ -51,8 +53,14 @@ impl<'g, 's> Default for GlobalSettings<'g, 's> { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum HeadlineLevelFilter { Odd, OddEven, } + +impl Default for HeadlineLevelFilter { + fn default() -> Self { + HeadlineLevelFilter::OddEven + } +} diff --git a/src/context/mod.rs b/src/context/mod.rs index c2d4a0e..7e45fdd 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -26,5 +26,6 @@ pub use file_access_interface::FileAccessInterface; pub use file_access_interface::LocalFileAccessInterface; pub use global_settings::GlobalSettings; pub use global_settings::HeadlineLevelFilter; +pub use global_settings::DEFAULT_TAB_WIDTH; pub(crate) use list::List; pub(crate) use parser_with_context::parser_with_context; diff --git a/src/lib.rs b/src/lib.rs index de24125..b8a8d10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,10 @@ mod iter; pub mod parser; pub mod types; -pub use context::FileAccessInterface; -pub use context::GlobalSettings; -pub use context::LocalFileAccessInterface; +pub mod settings { + pub use crate::context::FileAccessInterface; + pub use crate::context::GlobalSettings; + pub use crate::context::HeadlineLevelFilter; + pub use crate::context::LocalFileAccessInterface; + pub use crate::context::DEFAULT_TAB_WIDTH; +} diff --git a/src/main.rs b/src/main.rs index e6e7880..6cdd851 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,8 +5,8 @@ use std::path::Path; use ::organic::parser::parse; use organic::parser::parse_with_settings; -use organic::GlobalSettings; -use organic::LocalFileAccessInterface; +use organic::settings::GlobalSettings; +use organic::settings::LocalFileAccessInterface; #[cfg(feature = "tracing")] use crate::init_tracing::init_telemetry; diff --git a/src/parser/in_buffer_settings.rs b/src/parser/in_buffer_settings.rs index 58976ab..167d428 100644 --- a/src/parser/in_buffer_settings.rs +++ b/src/parser/in_buffer_settings.rs @@ -11,8 +11,8 @@ use super::OrgSource; use crate::context::HeadlineLevelFilter; use crate::error::CustomError; use crate::error::Res; +use crate::settings::GlobalSettings; use crate::types::Keyword; -use crate::GlobalSettings; #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn scan_for_in_buffer_settings<'s>( diff --git a/tests/test_template b/tests/test_template index 1376ed3..108ac0c 100644 --- a/tests/test_template +++ b/tests/test_template @@ -1,8 +1,8 @@ -// TODO: Investigate writing a proc macro to make specifying these permutations easier. +// TODO: Investigate writing a proc macro to make specifying these combinations easier. For example, currently I am only setting 1 setting per test to keep the repetition reasonable when I should be mixing all the different combinations. {expect_fail} #[test] -fn autogen_{name}() -> Result<(), Box> {{ +fn autogen_default_{name}() -> Result<(), Box> {{ let org_path = "{path}"; let org_contents = std::fs::read_to_string(org_path).expect("Read org file."); organic::compare::run_anonymous_compare(org_contents.as_str())?; @@ -15,7 +15,7 @@ fn autogen_la_{name}() -> Result<(), Box> {{ let org_path = "{path}"; let org_contents = std::fs::read_to_string(org_path).expect("Read org file."); let global_settings = {{ - let mut global_settings = organic::GlobalSettings::default(); + let mut global_settings = organic::settings::GlobalSettings::default(); global_settings.list_allow_alphabetical = true; global_settings }}; @@ -29,7 +29,7 @@ fn autogen_t1_{name}() -> Result<(), Box> {{ let org_path = "{path}"; let org_contents = std::fs::read_to_string(org_path).expect("Read org file."); let global_settings = {{ - let mut global_settings = organic::GlobalSettings::default(); + let mut global_settings = organic::settings::GlobalSettings::default(); global_settings.tab_width = 1; global_settings }}; @@ -43,10 +43,24 @@ fn autogen_t16_{name}() -> Result<(), Box> {{ let org_path = "{path}"; let org_contents = std::fs::read_to_string(org_path).expect("Read org file."); let global_settings = {{ - let mut global_settings = organic::GlobalSettings::default(); + let mut global_settings = organic::settings::GlobalSettings::default(); global_settings.tab_width = 16; global_settings }}; organic::compare::run_anonymous_compare_with_settings(org_contents.as_str(), &global_settings)?; Ok(()) }} + +{expect_fail} +#[test] +fn autogen_odd_{name}() -> Result<(), Box> {{ + let org_path = "{path}"; + let org_contents = std::fs::read_to_string(org_path).expect("Read org file."); + let global_settings = {{ + let mut global_settings = organic::settings::GlobalSettings::default(); + global_settings.odd_levels_only = organic::settings::HeadlineLevelFilter::Odd; + global_settings + }}; + organic::compare::run_anonymous_compare_with_settings(org_contents.as_str(), &global_settings)?; + Ok(()) +}}