From e6c809ab03c0b50c12dc36f3cb8d781d1f570ee5 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 09:35:19 -0400 Subject: [PATCH 01/54] Compare value for comment block. --- src/compare/diff.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 42a3d9b..1c31a82 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -1513,10 +1513,18 @@ fn compare_comment_block<'b, 's>( emacs: &'b Token<'s>, rust: &'b CommentBlock<'s>, ) -> Result, Box> { - let this_status = DiffStatus::Good; - let message = None; + let mut this_status = DiffStatus::Good; + let mut message = None; - // TODO: Compare :value + // Compare value + let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new()); + if contents != rust.contents { + this_status = DiffStatus::Bad; + message = Some(format!( + "Value mismatch (emacs != rust) {:?} != {:?}", + contents, rust.contents + )); + } Ok(DiffResult { status: this_status, From 00dc7b636cd01edb4841b0f545aca4415ce46aad Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 09:42:36 -0400 Subject: [PATCH 02/54] Add more tests. --- .../lesser_element/lesser_block/comment_with_comma.org | 4 ++++ org_mode_samples/lesser_element/lesser_block/nested.org | 6 ++++++ 2 files changed, 10 insertions(+) create mode 100644 org_mode_samples/lesser_element/lesser_block/comment_with_comma.org create mode 100644 org_mode_samples/lesser_element/lesser_block/nested.org diff --git a/org_mode_samples/lesser_element/lesser_block/comment_with_comma.org b/org_mode_samples/lesser_element/lesser_block/comment_with_comma.org new file mode 100644 index 0000000..c3c7754 --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/comment_with_comma.org @@ -0,0 +1,4 @@ +#+begin_comment +This is a comment +,* with an escaped line. +#+end_comment diff --git a/org_mode_samples/lesser_element/lesser_block/nested.org b/org_mode_samples/lesser_element/lesser_block/nested.org new file mode 100644 index 0000000..dfedca2 --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/nested.org @@ -0,0 +1,6 @@ +# Verse blocks are the only lesser blocks that contain objects +#+begin_verse +#+begin_comment +This is a comment. +#+end_comment +#+end_verse From 2eaef82fdbcef41ca93361196b2af064e81e6e7a Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 09:46:09 -0400 Subject: [PATCH 03/54] Organize lesser block tests into subfolders. --- .../{comment_with_comma.org => comment/with_comma.org} | 0 .../{export_with_no_data.org => export/with_no_data.org} | 0 .../{export_with_two_words.org => export/with_two_words.org} | 0 .../lesser_element/lesser_block/{src_empty.org => src/empty.org} | 0 .../lesser_block/{src_with_no_data.org => src/with_no_data.org} | 0 .../with_space_after_end.org} | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename org_mode_samples/lesser_element/lesser_block/{comment_with_comma.org => comment/with_comma.org} (100%) rename org_mode_samples/lesser_element/lesser_block/{export_with_no_data.org => export/with_no_data.org} (100%) rename org_mode_samples/lesser_element/lesser_block/{export_with_two_words.org => export/with_two_words.org} (100%) rename org_mode_samples/lesser_element/lesser_block/{src_empty.org => src/empty.org} (100%) rename org_mode_samples/lesser_element/lesser_block/{src_with_no_data.org => src/with_no_data.org} (100%) rename org_mode_samples/lesser_element/lesser_block/{src_with_space_after_end.org => src/with_space_after_end.org} (100%) diff --git a/org_mode_samples/lesser_element/lesser_block/comment_with_comma.org b/org_mode_samples/lesser_element/lesser_block/comment/with_comma.org similarity index 100% rename from org_mode_samples/lesser_element/lesser_block/comment_with_comma.org rename to org_mode_samples/lesser_element/lesser_block/comment/with_comma.org diff --git a/org_mode_samples/lesser_element/lesser_block/export_with_no_data.org b/org_mode_samples/lesser_element/lesser_block/export/with_no_data.org similarity index 100% rename from org_mode_samples/lesser_element/lesser_block/export_with_no_data.org rename to org_mode_samples/lesser_element/lesser_block/export/with_no_data.org diff --git a/org_mode_samples/lesser_element/lesser_block/export_with_two_words.org b/org_mode_samples/lesser_element/lesser_block/export/with_two_words.org similarity index 100% rename from org_mode_samples/lesser_element/lesser_block/export_with_two_words.org rename to org_mode_samples/lesser_element/lesser_block/export/with_two_words.org diff --git a/org_mode_samples/lesser_element/lesser_block/src_empty.org b/org_mode_samples/lesser_element/lesser_block/src/empty.org similarity index 100% rename from org_mode_samples/lesser_element/lesser_block/src_empty.org rename to org_mode_samples/lesser_element/lesser_block/src/empty.org diff --git a/org_mode_samples/lesser_element/lesser_block/src_with_no_data.org b/org_mode_samples/lesser_element/lesser_block/src/with_no_data.org similarity index 100% rename from org_mode_samples/lesser_element/lesser_block/src_with_no_data.org rename to org_mode_samples/lesser_element/lesser_block/src/with_no_data.org diff --git a/org_mode_samples/lesser_element/lesser_block/src_with_space_after_end.org b/org_mode_samples/lesser_element/lesser_block/src/with_space_after_end.org similarity index 100% rename from org_mode_samples/lesser_element/lesser_block/src_with_space_after_end.org rename to org_mode_samples/lesser_element/lesser_block/src/with_space_after_end.org From 4cdf88a632047ed90351f40f7433a19977ea84fc Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 09:51:28 -0400 Subject: [PATCH 04/54] Switches are not stored for comment blocks, but they are allowed to appear. --- .../lesser_block/comment/with_switches.org | 3 +++ .../lesser_block/example/line_numbers.org | 7 +++++++ src/compare/diff.rs | 16 +++++++++++++--- src/parser/lesser_block.rs | 7 +------ src/types/lesser_element.rs | 1 - 5 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 org_mode_samples/lesser_element/lesser_block/comment/with_switches.org create mode 100644 org_mode_samples/lesser_element/lesser_block/example/line_numbers.org diff --git a/org_mode_samples/lesser_element/lesser_block/comment/with_switches.org b/org_mode_samples/lesser_element/lesser_block/comment/with_switches.org new file mode 100644 index 0000000..32ea022 --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/comment/with_switches.org @@ -0,0 +1,3 @@ +#+begin_comment -n 20 +foo +#+end_comment diff --git a/org_mode_samples/lesser_element/lesser_block/example/line_numbers.org b/org_mode_samples/lesser_element/lesser_block/example/line_numbers.org new file mode 100644 index 0000000..44bf44c --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/example/line_numbers.org @@ -0,0 +1,7 @@ +#+begin_example -n 5 +foo +#+end_example + +#+begin_example +n 10 +foo +#+end_example diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 1c31a82..cffd9dc 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -1542,10 +1542,20 @@ fn compare_example_block<'b, 's>( emacs: &'b Token<'s>, rust: &'b ExampleBlock<'s>, ) -> Result, Box> { - let this_status = DiffStatus::Good; - let message = None; + let mut this_status = DiffStatus::Good; + let mut message = None; - // TODO: Compare :value :switches :number-lines :preserve-indent :retain-labels :use-labels :label-fmt + // TODO: Compare :switches :number-lines :preserve-indent :retain-labels :use-labels :label-fmt + + // Compare value + let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new()); + if contents != rust.contents { + this_status = DiffStatus::Bad; + message = Some(format!( + "Value mismatch (emacs != rust) {:?} != {:?}", + contents, rust.contents + )); + } Ok(DiffResult { status: this_status, diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 507a851..b3e05cd 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -95,7 +95,7 @@ pub(crate) fn comment_block<'b, 'g, 'r, 's>( input: OrgSource<'s>, ) -> Res, CommentBlock<'s>> { let (remaining, name) = lesser_block_begin("comment")(context, input)?; - let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?; + let (remaining, _parameters) = opt(tuple((space1, data)))(remaining)?; let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; let lesser_block_end_specialized = lesser_block_end("comment"); let contexts = [ @@ -109,10 +109,6 @@ pub(crate) fn comment_block<'b, 'g, 'r, 's>( let parser_context = context.with_additional_node(&contexts[0]); let parser_context = parser_context.with_additional_node(&contexts[1]); let parser_context = parser_context.with_additional_node(&contexts[2]); - let parameters = match parameters { - Some((_ws, parameters)) => Some(parameters), - None => None, - }; let (remaining, contents) = parser_with_context!(text_until_exit)(&parser_context)(remaining)?; let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?; @@ -123,7 +119,6 @@ pub(crate) fn comment_block<'b, 'g, 'r, 's>( CommentBlock { source: source.into(), name: name.into(), - data: parameters.map(|parameters| Into::<&str>::into(parameters)), contents: contents.into(), }, )) diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index 77964cf..251376d 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -33,7 +33,6 @@ pub struct VerseBlock<'s> { pub struct CommentBlock<'s> { pub source: &'s str, pub name: &'s str, - pub data: Option<&'s str>, pub contents: &'s str, } From bf038db31c1b910e6a71ffa17c679a810ba2bd6f Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 09:53:33 -0400 Subject: [PATCH 05/54] Add test showing trailing whitespace is captured in the switches. --- .../lesser_element/lesser_block/example/line_numbers.org | 2 +- .../lesser_block/example/switches_with_trailing_whitespace.org | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 org_mode_samples/lesser_element/lesser_block/example/switches_with_trailing_whitespace.org diff --git a/org_mode_samples/lesser_element/lesser_block/example/line_numbers.org b/org_mode_samples/lesser_element/lesser_block/example/line_numbers.org index 44bf44c..f7a9390 100644 --- a/org_mode_samples/lesser_element/lesser_block/example/line_numbers.org +++ b/org_mode_samples/lesser_element/lesser_block/example/line_numbers.org @@ -3,5 +3,5 @@ foo #+end_example #+begin_example +n 10 -foo +bar #+end_example diff --git a/org_mode_samples/lesser_element/lesser_block/example/switches_with_trailing_whitespace.org b/org_mode_samples/lesser_element/lesser_block/example/switches_with_trailing_whitespace.org new file mode 100644 index 0000000..771dbea --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/example/switches_with_trailing_whitespace.org @@ -0,0 +1,3 @@ +#+begin_example +n 10 +foo +#+end_example From 1d7770e59087cffc6c7bc6501f2414f9ef1e9861 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 09:55:00 -0400 Subject: [PATCH 06/54] Rename data to switches in example and src block. --- .../lesser_block/example/language_and_switches.org | 3 +++ .../lesser_element/lesser_block/example/line_numbers.org | 1 + src/parser/lesser_block.rs | 4 ++-- src/types/lesser_element.rs | 4 ++-- 4 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 org_mode_samples/lesser_element/lesser_block/example/language_and_switches.org diff --git a/org_mode_samples/lesser_element/lesser_block/example/language_and_switches.org b/org_mode_samples/lesser_element/lesser_block/example/language_and_switches.org new file mode 100644 index 0000000..a01b9af --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/example/language_and_switches.org @@ -0,0 +1,3 @@ +#+begin_example elisp -n 5 +foo +#+end_example diff --git a/org_mode_samples/lesser_element/lesser_block/example/line_numbers.org b/org_mode_samples/lesser_element/lesser_block/example/line_numbers.org index f7a9390..371df50 100644 --- a/org_mode_samples/lesser_element/lesser_block/example/line_numbers.org +++ b/org_mode_samples/lesser_element/lesser_block/example/line_numbers.org @@ -2,6 +2,7 @@ foo #+end_example +# Line numbering starts at 15 for the example below since it uses +n. #+begin_example +n 10 bar #+end_example diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index b3e05cd..3199781 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -158,7 +158,7 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( ExampleBlock { source: source.into(), name: source.into(), - data: parameters.map(|parameters| Into::<&str>::into(parameters)), + switches: parameters.map(|parameters| Into::<&str>::into(parameters)), contents: contents.into(), }, )) @@ -240,7 +240,7 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( SrcBlock { source: source.into(), name: name.into(), - data: parameters.map(|parameters| Into::<&str>::into(parameters)), + switches: parameters.map(|parameters| Into::<&str>::into(parameters)), contents: contents.into(), }, )) diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index 251376d..74bc728 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -40,7 +40,7 @@ pub struct CommentBlock<'s> { pub struct ExampleBlock<'s> { pub source: &'s str, pub name: &'s str, - pub data: Option<&'s str>, + pub switches: Option<&'s str>, pub contents: &'s str, } @@ -56,7 +56,7 @@ pub struct ExportBlock<'s> { pub struct SrcBlock<'s> { pub source: &'s str, pub name: &'s str, - pub data: Option<&'s str>, + pub switches: Option<&'s str>, pub contents: &'s str, } From 650cbc17db306a0e5908c1f8ff076b46a635d95e Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 10:03:51 -0400 Subject: [PATCH 07/54] Compare switches for example block. --- src/compare/diff.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/compare/diff.rs b/src/compare/diff.rs index cffd9dc..533854f 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -1545,7 +1545,7 @@ fn compare_example_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; - // TODO: Compare :switches :number-lines :preserve-indent :retain-labels :use-labels :label-fmt + // TODO: Compare :number-lines :preserve-indent :retain-labels :use-labels :label-fmt // Compare value let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new()); @@ -1557,6 +1557,16 @@ fn compare_example_block<'b, 's>( )); } + // Compare switches + let switches = get_property_quoted_string(emacs, ":switches")?; + if switches.as_ref().map(String::as_str) != rust.switches { + this_status = DiffStatus::Bad; + message = Some(format!( + "Switches mismatch (emacs != rust) {:?} != {:?}", + switches, rust.switches + )); + } + Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), From 3d7f411cf90c03cf83829030f37c8dbc46c8d592 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 10:31:01 -0400 Subject: [PATCH 08/54] Compare number lines for example blocks. --- src/compare/diff.rs | 44 ++++++++++++++++++++++++++++++++++++- src/parser/lesser_block.rs | 6 ++--- src/types/lesser_element.rs | 9 ++++++++ src/types/mod.rs | 2 ++ 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 533854f..025c922 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -52,6 +52,7 @@ use crate::types::Keyword; use crate::types::LatexEnvironment; use crate::types::LatexFragment; use crate::types::LineBreak; +use crate::types::LineNumber; use crate::types::Minute; use crate::types::MinuteInner; use crate::types::Month; @@ -83,6 +84,7 @@ use crate::types::StatisticsCookie; use crate::types::StrikeThrough; use crate::types::Subscript; use crate::types::Superscript; +use crate::types::SwitchNumberLines; use crate::types::Table; use crate::types::TableCell; use crate::types::TableRow; @@ -1545,7 +1547,7 @@ fn compare_example_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; - // TODO: Compare :number-lines :preserve-indent :retain-labels :use-labels :label-fmt + // TODO: Compare :preserve-indent :retain-labels :use-labels :label-fmt // Compare value let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new()); @@ -1567,6 +1569,46 @@ fn compare_example_block<'b, 's>( )); } + // Compare number-lines + let number_lines = get_property(emacs, ":number-lines")?; + match (number_lines, &rust.number_lines) { + (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::()) + .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 => {} + _ => { + this_status = DiffStatus::Bad; + message = Some(format!( + "Number lines mismatch (emacs != rust) {:?} != {:?}", + number_lines, rust.number_lines + )); + } + } + } + _ => { + this_status = DiffStatus::Bad; + message = Some(format!( + "Number lines mismatch (emacs != rust) {:?} != {:?}", + number_lines, rust.number_lines + )); + } + }; + Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 3199781..5e97261 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -144,10 +144,7 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( let parser_context = context.with_additional_node(&contexts[0]); let parser_context = parser_context.with_additional_node(&contexts[1]); let parser_context = parser_context.with_additional_node(&contexts[2]); - let parameters = match parameters { - Some((_ws, parameters)) => Some(parameters), - None => None, - }; + let parameters = parameters.map(|(_, parameters)| parameters); let (remaining, contents) = parser_with_context!(text_until_exit)(&parser_context)(remaining)?; let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?; @@ -159,6 +156,7 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( source: source.into(), name: source.into(), switches: parameters.map(|parameters| Into::<&str>::into(parameters)), + number_lines: None, // TODO contents: contents.into(), }, )) diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index 74bc728..7e5e6d4 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -41,6 +41,7 @@ pub struct ExampleBlock<'s> { pub source: &'s str, pub name: &'s str, pub switches: Option<&'s str>, + pub number_lines: Option, pub contents: &'s str, } @@ -107,6 +108,14 @@ pub struct LatexEnvironment<'s> { pub source: &'s str, } +pub type LineNumber = usize; + +#[derive(Debug)] +pub enum SwitchNumberLines { + New(LineNumber), + Continued(LineNumber), +} + impl<'s> Paragraph<'s> { pub(crate) fn of_text(input: &'s str) -> Self { let mut objects = Vec::with_capacity(1); diff --git a/src/types/mod.rs b/src/types/mod.rs index d0b9b97..e517795 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -47,9 +47,11 @@ pub use lesser_element::FixedWidthArea; pub use lesser_element::HorizontalRule; pub use lesser_element::Keyword; pub use lesser_element::LatexEnvironment; +pub use lesser_element::LineNumber; pub use lesser_element::Paragraph; pub use lesser_element::Planning; pub use lesser_element::SrcBlock; +pub use lesser_element::SwitchNumberLines; pub use lesser_element::TableCell; pub use lesser_element::VerseBlock; pub use object::AngleLink; From 317293f0f26424f62f68a28c086830f44be8729e Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 11:31:45 -0400 Subject: [PATCH 09/54] Extract the line number from the switches. --- .../example/line_number_negative.org | 7 ++ .../lesser_block/example/line_number_zero.org | 7 ++ src/parser/lesser_block.rs | 78 ++++++++++++++++++- src/types/lesser_element.rs | 5 +- 4 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 org_mode_samples/lesser_element/lesser_block/example/line_number_negative.org create mode 100644 org_mode_samples/lesser_element/lesser_block/example/line_number_zero.org diff --git a/org_mode_samples/lesser_element/lesser_block/example/line_number_negative.org b/org_mode_samples/lesser_element/lesser_block/example/line_number_negative.org new file mode 100644 index 0000000..d3489ee --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/example/line_number_negative.org @@ -0,0 +1,7 @@ +#+begin_example -n -10 +foo +#+end_example + +#+begin_example +n -15 +bar +#+end_example diff --git a/org_mode_samples/lesser_element/lesser_block/example/line_number_zero.org b/org_mode_samples/lesser_element/lesser_block/example/line_number_zero.org new file mode 100644 index 0000000..023fe68 --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/example/line_number_zero.org @@ -0,0 +1,7 @@ +#+begin_example -n 0 +foo +#+end_example + +#+begin_example +n 0 +bar +#+end_example diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 5e97261..340a1de 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -6,10 +6,12 @@ use nom::character::complete::space0; use nom::character::complete::space1; use nom::combinator::consumed; use nom::combinator::eof; +use nom::combinator::map; use nom::combinator::opt; use nom::combinator::recognize; use nom::combinator::verify; use nom::multi::many_till; +use nom::multi::separated_list1; use nom::sequence::tuple; use super::org_source::OrgSource; @@ -29,9 +31,11 @@ use crate::parser::util::text_until_exit; use crate::types::CommentBlock; use crate::types::ExampleBlock; use crate::types::ExportBlock; +use crate::types::LineNumber; use crate::types::Object; use crate::types::PlainText; use crate::types::SrcBlock; +use crate::types::SwitchNumberLines; use crate::types::VerseBlock; #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] @@ -130,7 +134,7 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( input: OrgSource<'s>, ) -> Res, ExampleBlock<'s>> { let (remaining, _name) = lesser_block_begin("example")(context, input)?; - let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?; + let (remaining, parameters) = opt(tuple((space1, example_switches)))(remaining)?; let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; let lesser_block_end_specialized = lesser_block_end("example"); let contexts = [ @@ -150,13 +154,20 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?; let source = get_consumed(input, remaining); + let (switches, number_lines) = { + if let Some(parameters) = parameters { + (Some(parameters.source), parameters.number_lines) + } else { + (None, None) + } + }; Ok(( remaining, ExampleBlock { source: source.into(), name: source.into(), - switches: parameters.map(|parameters| Into::<&str>::into(parameters)), - number_lines: None, // TODO + switches, + number_lines, contents: contents.into(), }, )) @@ -302,3 +313,64 @@ fn _lesser_block_begin<'b, 'g, 'r, 's, 'c>( ))(remaining)?; Ok((remaining, name)) } + +struct ExampleSwitches<'s> { + source: &'s str, + number_lines: Option, +} + +enum SwitchState { + Normal, + NewLineNumber, + ContinuedLineNumber, +} + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSwitches<'s>> { + let mut number_lines = None; + let (remaining, (source, words)) = consumed(separated_list1( + space1, + map(is_not(" \t\r\n"), |val| Into::<&str>::into(val)), + ))(input)?; + + let mut state = SwitchState::Normal; + for word in words { + state = match (state, word) { + (SwitchState::Normal, "-n") => SwitchState::NewLineNumber, + (SwitchState::Normal, "+n") => SwitchState::ContinuedLineNumber, + (SwitchState::NewLineNumber, _) => { + let val = word + .parse::() + .expect("TODO: I should be able to return CustomError from nom parsers."); + if val < 0 { + number_lines = Some(SwitchNumberLines::New(0)); + } else { + // Note that this can result in a negative 1 if the val is originally 0. + number_lines = Some(SwitchNumberLines::New(val - 1)); + } + SwitchState::Normal + } + (SwitchState::ContinuedLineNumber, _) => { + let val = word + .parse::() + .expect("TODO: I should be able to return CustomError from nom parsers."); + if val < 0 { + number_lines = Some(SwitchNumberLines::Continued(0)); + } else { + // Note that this can result in a negative 1 if the val is originally 0. + number_lines = Some(SwitchNumberLines::Continued(val - 1)); + } + SwitchState::Normal + } + (state @ SwitchState::Normal, _) => state, + }; + } + + Ok(( + remaining, + ExampleSwitches { + source: Into::<&str>::into(source), + number_lines, + }, + )) +} diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index 7e5e6d4..20e932b 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -108,7 +108,10 @@ pub struct LatexEnvironment<'s> { pub source: &'s str, } -pub type LineNumber = usize; +/// A line number used in switches to lesser blocks. +/// +/// This must be signed because emacs subtracts 1 from the actual value in the org-mode text, which makes a 0 turn into a -1. +pub type LineNumber = isize; #[derive(Debug)] pub enum SwitchNumberLines { From 03028889bd081c351728369e08fd272c55242b09 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 11:34:01 -0400 Subject: [PATCH 10/54] Fix capturing trailing whitespace for switches. --- src/parser/lesser_block.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 340a1de..77515e3 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -328,10 +328,13 @@ enum SwitchState { #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSwitches<'s>> { let mut number_lines = None; - let (remaining, (source, words)) = consumed(separated_list1( - space1, - map(is_not(" \t\r\n"), |val| Into::<&str>::into(val)), - ))(input)?; + let (remaining, (source, (words, _))) = consumed(tuple(( + separated_list1( + space1, + map(is_not(" \t\r\n"), |val| Into::<&str>::into(val)), + ), + space0, + )))(input)?; let mut state = SwitchState::Normal; for word in words { From 15030549942f51ef12f54c3417091d162a79a49f Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 11:46:02 -0400 Subject: [PATCH 11/54] Make an argument for the line number switch optional. --- .../lesser_block/example/switches_with_label.org | 3 +++ src/parser/lesser_block.rs | 14 ++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 org_mode_samples/lesser_element/lesser_block/example/switches_with_label.org diff --git a/org_mode_samples/lesser_element/lesser_block/example/switches_with_label.org b/org_mode_samples/lesser_element/lesser_block/example/switches_with_label.org new file mode 100644 index 0000000..e844b0e --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/example/switches_with_label.org @@ -0,0 +1,3 @@ +#+BEGIN_SRC elisp -n -r -l "((%s))" +foo +#+END_SRC diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 77515e3..da6c145 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -342,9 +342,10 @@ fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSwitc (SwitchState::Normal, "-n") => SwitchState::NewLineNumber, (SwitchState::Normal, "+n") => SwitchState::ContinuedLineNumber, (SwitchState::NewLineNumber, _) => { - let val = word - .parse::() - .expect("TODO: I should be able to return CustomError from nom parsers."); + let val = match word.parse::() { + Ok(val) => val, + Err(_) => 1, + }; if val < 0 { number_lines = Some(SwitchNumberLines::New(0)); } else { @@ -354,9 +355,10 @@ fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSwitc SwitchState::Normal } (SwitchState::ContinuedLineNumber, _) => { - let val = word - .parse::() - .expect("TODO: I should be able to return CustomError from nom parsers."); + let val = match word.parse::() { + Ok(val) => val, + Err(_) => 1, + }; if val < 0 { number_lines = Some(SwitchNumberLines::Continued(0)); } else { From b56d847cfa3ee01e08f33eed37b0ffacd1a715d3 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 12:36:38 -0400 Subject: [PATCH 12/54] Compare label format, retain labels, and use labels. --- .../example/switches_with_label.org | 2 +- src/compare/diff.rs | 45 ++++++- src/parser/lesser_block.rs | 124 +++++++++++++----- src/types/lesser_element.rs | 3 + 4 files changed, 135 insertions(+), 39 deletions(-) diff --git a/org_mode_samples/lesser_element/lesser_block/example/switches_with_label.org b/org_mode_samples/lesser_element/lesser_block/example/switches_with_label.org index e844b0e..a450ab8 100644 --- a/org_mode_samples/lesser_element/lesser_block/example/switches_with_label.org +++ b/org_mode_samples/lesser_element/lesser_block/example/switches_with_label.org @@ -1,3 +1,3 @@ #+BEGIN_SRC elisp -n -r -l "((%s))" -foo + foo #+END_SRC diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 025c922..938622d 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -1547,8 +1547,6 @@ fn compare_example_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; - // TODO: Compare :preserve-indent :retain-labels :use-labels :label-fmt - // Compare value let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new()); if contents != rust.contents { @@ -1609,6 +1607,49 @@ fn compare_example_block<'b, 's>( } }; + // Compare preserve-indent + let preserve_indent = get_property_boolean(emacs, ":preserve-indent")?; + if preserve_indent { + // I don't know what :preserve-indent is for, but it seems to always be nil. This is here to alert me to value being non-nil so I can investigate. + this_status = DiffStatus::Bad; + message = Some(format!("Non-nil :preserve-indent {:?}", preserve_indent)); + } + + // Compare retain-labels + let retain_labels = get_property_boolean(emacs, ":retain-labels")?; + if retain_labels != rust.retain_labels { + this_status = DiffStatus::Bad; + message = Some(format!( + "Retain labels mismatch (emacs != rust) {:?} != {:?}", + retain_labels, rust.retain_labels + )); + } + + // Compare use-labels + let use_labels = get_property_boolean(emacs, ":use-labels")?; + if use_labels != rust.use_labels { + this_status = DiffStatus::Bad; + message = Some(format!( + "Use labels mismatch (emacs != rust) {:?} != {:?}", + use_labels, rust.use_labels + )); + } + + // Compare label-fmt + let label_format = get_property_quoted_string(emacs, ":label-fmt")?; + match (label_format.as_ref(), rust.label_format) { + (None, None) => {} + (Some(emacs_label_format), Some(rust_label_format)) + if emacs_label_format == rust_label_format => {} + _ => { + this_status = DiffStatus::Bad; + message = Some(format!( + "Label format mismatch (emacs != rust) {:?} != {:?}", + label_format, rust.label_format + )); + } + } + Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index da6c145..1ba5f6c 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -1,5 +1,6 @@ use nom::branch::alt; use nom::bytes::complete::is_not; +use nom::bytes::complete::tag; use nom::bytes::complete::tag_no_case; use nom::character::complete::line_ending; use nom::character::complete::space0; @@ -154,11 +155,17 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?; let source = get_consumed(input, remaining); - let (switches, number_lines) = { + let (switches, number_lines, retain_labels, use_labels, label_format) = { if let Some(parameters) = parameters { - (Some(parameters.source), parameters.number_lines) + ( + Some(parameters.source), + parameters.number_lines, + parameters.retain_labels, + parameters.use_labels, + parameters.label_format, + ) } else { - (None, None) + (None, None, true, true, None) } }; Ok(( @@ -168,6 +175,9 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( name: source.into(), switches, number_lines, + retain_labels, + use_labels, + label_format, contents: contents.into(), }, )) @@ -317,58 +327,86 @@ fn _lesser_block_begin<'b, 'g, 'r, 's, 'c>( struct ExampleSwitches<'s> { source: &'s str, number_lines: Option, + retain_labels: bool, + use_labels: bool, + label_format: Option<&'s str>, } enum SwitchState { Normal, NewLineNumber, ContinuedLineNumber, + LabelFormat, } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSwitches<'s>> { let mut number_lines = None; + let mut retain_labels = true; + let mut use_labels = true; + let mut label_format = None; let (remaining, (source, (words, _))) = consumed(tuple(( - separated_list1( - space1, - map(is_not(" \t\r\n"), |val| Into::<&str>::into(val)), - ), + separated_list1(space1, map(switch_word, |val| Into::<&str>::into(val))), space0, )))(input)?; let mut state = SwitchState::Normal; for word in words { - state = match (state, word) { - (SwitchState::Normal, "-n") => SwitchState::NewLineNumber, - (SwitchState::Normal, "+n") => SwitchState::ContinuedLineNumber, - (SwitchState::NewLineNumber, _) => { - let val = match word.parse::() { - Ok(val) => val, - Err(_) => 1, - }; - if val < 0 { - number_lines = Some(SwitchNumberLines::New(0)); - } else { - // Note that this can result in a negative 1 if the val is originally 0. - number_lines = Some(SwitchNumberLines::New(val - 1)); + loop { + match (&state, word) { + (SwitchState::Normal, "-n") => { + state = SwitchState::NewLineNumber; } - SwitchState::Normal - } - (SwitchState::ContinuedLineNumber, _) => { - let val = match word.parse::() { - Ok(val) => val, - Err(_) => 1, - }; - if val < 0 { - number_lines = Some(SwitchNumberLines::Continued(0)); - } else { - // Note that this can result in a negative 1 if the val is originally 0. - number_lines = Some(SwitchNumberLines::Continued(val - 1)); + (SwitchState::Normal, "+n") => { + state = SwitchState::ContinuedLineNumber; } - SwitchState::Normal - } - (state @ SwitchState::Normal, _) => state, - }; + (SwitchState::Normal, "-r") => { + retain_labels = false; + use_labels = false; + } + (SwitchState::Normal, "-l") => { + state = SwitchState::LabelFormat; + } + (SwitchState::NewLineNumber, _) => { + let val = word.parse::(); + if let Ok(val) = val { + if val < 0 { + number_lines = Some(SwitchNumberLines::New(0)); + } else { + // Note that this can result in a negative 1 if the val is originally 0. + number_lines = Some(SwitchNumberLines::New(val - 1)); + } + state = SwitchState::Normal; + } else { + number_lines = Some(SwitchNumberLines::New(0)); + state = SwitchState::Normal; + continue; // Re-processes the word + } + } + (SwitchState::ContinuedLineNumber, _) => { + let val = word.parse::(); + if let Ok(val) = val { + if val < 0 { + number_lines = Some(SwitchNumberLines::Continued(0)); + } else { + // Note that this can result in a negative 1 if the val is originally 0. + number_lines = Some(SwitchNumberLines::Continued(val - 1)); + } + state = SwitchState::Normal; + } else { + number_lines = Some(SwitchNumberLines::Continued(0)); + state = SwitchState::Normal; + continue; // Re-processes the word + } + } + (SwitchState::LabelFormat, _) => { + label_format = Some(word); + state = SwitchState::Normal; + } + (SwitchState::Normal, _) => {} + }; + break; + } } Ok(( @@ -376,6 +414,20 @@ fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSwitc ExampleSwitches { source: Into::<&str>::into(source), number_lines, + retain_labels, + use_labels, + label_format, }, )) } + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +fn switch_word<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { + alt(( + map( + tuple((tag(r#"""#), is_not("\"\r\n"), tag(r#"""#))), + |(_, contents, _)| contents, + ), + is_not(" \t\r\n"), + ))(input) +} diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index 20e932b..3d8a21f 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -42,6 +42,9 @@ pub struct ExampleBlock<'s> { pub name: &'s str, pub switches: Option<&'s str>, pub number_lines: Option, + pub retain_labels: bool, + pub use_labels: bool, + pub label_format: Option<&'s str>, pub contents: &'s str, } From afb43ff34fbb611e9c8e5f8762c0306cf98cfdf6 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 12:59:57 -0400 Subject: [PATCH 13/54] Switch to getting the contents with a function to handle the escaped lines. --- .../lesser_block/example/escaped_lines.org | 10 +++++++++ src/compare/diff.rs | 5 +++-- src/types/lesser_element.rs | 22 +++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 org_mode_samples/lesser_element/lesser_block/example/escaped_lines.org diff --git a/org_mode_samples/lesser_element/lesser_block/example/escaped_lines.org b/org_mode_samples/lesser_element/lesser_block/example/escaped_lines.org new file mode 100644 index 0000000..71dcda3 --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/example/escaped_lines.org @@ -0,0 +1,10 @@ +#+begin_example +,* foo +,** bar +,*** baz + lorem + , ipsum +,#+begin_src dolar + +,#+end_src +#+end_example diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 938622d..3e2e44d 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -1549,11 +1549,12 @@ fn compare_example_block<'b, 's>( // Compare value let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new()); - if contents != rust.contents { + let rust_contents = rust.get_contents(); + if contents != rust_contents { this_status = DiffStatus::Bad; message = Some(format!( "Value mismatch (emacs != rust) {:?} != {:?}", - contents, rust.contents + contents, rust_contents )); } diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index 3d8a21f..11e3c6f 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -237,3 +237,25 @@ impl<'s> Comment<'s> { ret } } + +impl<'s> ExampleBlock<'s> { + /// Get the inner contents of the ExampleBlock with the escaping commas removed. + pub fn get_contents(&self) -> String { + let mut ret = String::with_capacity(self.contents.len()); + for line in self.contents.lines() { + let first_comma = line.find(",#+").or_else(|| line.find(",*")); + if let Some(first_comma) = first_comma { + let before_first_comma = &line[..first_comma]; + let is_escaping_comma = before_first_comma.chars().all(char::is_whitespace); + if is_escaping_comma { + ret.push_str(&line[(first_comma + 1)..]); + } else { + ret.push_str(line); + } + } else { + ret.push_str(line); + } + } + ret + } +} From 7ee48ff65c4acf9bd09ce386af89218346207f77 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 13:08:24 -0400 Subject: [PATCH 14/54] Switch to handling the unescaping during the initial parsing. This preserves the line ending characters unlike the rust .lines() iterator. --- src/parser/lesser_block.rs | 19 +++++++++++++++++-- src/types/lesser_element.rs | 21 ++++++--------------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 1ba5f6c..39a44fa 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -2,17 +2,22 @@ use nom::branch::alt; use nom::bytes::complete::is_not; use nom::bytes::complete::tag; use nom::bytes::complete::tag_no_case; +use nom::character::complete::anychar; use nom::character::complete::line_ending; use nom::character::complete::space0; use nom::character::complete::space1; use nom::combinator::consumed; use nom::combinator::eof; use nom::combinator::map; +use nom::combinator::not; use nom::combinator::opt; +use nom::combinator::peek; use nom::combinator::recognize; use nom::combinator::verify; +use nom::multi::many0; use nom::multi::many_till; use nom::multi::separated_list1; +use nom::sequence::preceded; use nom::sequence::tuple; use super::org_source::OrgSource; @@ -151,7 +156,10 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( let parser_context = parser_context.with_additional_node(&contexts[2]); let parameters = parameters.map(|(_, parameters)| parameters); - let (remaining, contents) = parser_with_context!(text_until_exit)(&parser_context)(remaining)?; + let (remaining, contents) = many0(preceded( + not(parser_with_context!(exit_matcher_parser)(&parser_context)), + map(content_line, Into::<&str>::into), + ))(remaining)?; let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?; let source = get_consumed(input, remaining); @@ -178,7 +186,7 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( retain_labels, use_labels, label_format, - contents: contents.into(), + contents, }, )) } @@ -431,3 +439,10 @@ fn switch_word<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { is_not(" \t\r\n"), ))(input) } + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +fn content_line<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { + let (remaining, _) = opt(tuple((space0, tag(","), peek(alt((tag("#+"), tag("*")))))))(input)?; + let (remaining, line_post_escape) = recognize(many_till(anychar, line_ending))(remaining)?; + Ok((remaining, line_post_escape)) +} diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index 11e3c6f..a90c826 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -45,7 +45,7 @@ pub struct ExampleBlock<'s> { pub retain_labels: bool, pub use_labels: bool, pub label_format: Option<&'s str>, - pub contents: &'s str, + pub contents: Vec<&'s str>, } #[derive(Debug)] @@ -241,21 +241,12 @@ impl<'s> Comment<'s> { impl<'s> ExampleBlock<'s> { /// Get the inner contents of the ExampleBlock with the escaping commas removed. pub fn get_contents(&self) -> String { - let mut ret = String::with_capacity(self.contents.len()); - for line in self.contents.lines() { - let first_comma = line.find(",#+").or_else(|| line.find(",*")); - if let Some(first_comma) = first_comma { - let before_first_comma = &line[..first_comma]; - let is_escaping_comma = before_first_comma.chars().all(char::is_whitespace); - if is_escaping_comma { - ret.push_str(&line[(first_comma + 1)..]); - } else { - ret.push_str(line); - } - } else { - ret.push_str(line); - } + let final_size = self.contents.iter().map(|line| line.len()).sum(); + let mut ret = String::with_capacity(final_size); + for line in &self.contents { + ret.push_str(line); } + ret } } From 169bf69f5e419703757e758778f58741d8cde602 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 13:23:57 -0400 Subject: [PATCH 15/54] Preserve the leading whitespace before an escape. --- src/compare/diff.rs | 5 ++--- src/parser/lesser_block.rs | 43 +++++++++++++++++++++++++++---------- src/types/lesser_element.rs | 15 +------------ 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 3e2e44d..938622d 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -1549,12 +1549,11 @@ fn compare_example_block<'b, 's>( // Compare value let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new()); - let rust_contents = rust.get_contents(); - if contents != rust_contents { + if contents != rust.contents { this_status = DiffStatus::Bad; message = Some(format!( "Value mismatch (emacs != rust) {:?} != {:?}", - contents, rust_contents + contents, rust.contents )); } diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 39a44fa..4886965 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -9,15 +9,12 @@ use nom::character::complete::space1; use nom::combinator::consumed; use nom::combinator::eof; use nom::combinator::map; -use nom::combinator::not; use nom::combinator::opt; use nom::combinator::peek; use nom::combinator::recognize; use nom::combinator::verify; -use nom::multi::many0; use nom::multi::many_till; use nom::multi::separated_list1; -use nom::sequence::preceded; use nom::sequence::tuple; use super::org_source::OrgSource; @@ -156,10 +153,7 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( let parser_context = parser_context.with_additional_node(&contexts[2]); let parameters = parameters.map(|(_, parameters)| parameters); - let (remaining, contents) = many0(preceded( - not(parser_with_context!(exit_matcher_parser)(&parser_context)), - map(content_line, Into::<&str>::into), - ))(remaining)?; + let (remaining, contents) = content(&parser_context, remaining)?; let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?; let source = get_consumed(input, remaining); @@ -441,8 +435,35 @@ fn switch_word<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn content_line<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { - let (remaining, _) = opt(tuple((space0, tag(","), peek(alt((tag("#+"), tag("*")))))))(input)?; - let (remaining, line_post_escape) = recognize(many_till(anychar, line_ending))(remaining)?; - Ok((remaining, line_post_escape)) +pub(crate) fn content<'b, 'g, 'r, 's>( + context: RefContext<'b, 'g, 'r, 's>, + input: OrgSource<'s>, +) -> Res, String> { + let mut ret = String::new(); + let mut remaining = input; + let exit_matcher_parser = parser_with_context!(exit_matcher_parser)(context); + loop { + if exit_matcher_parser(remaining).is_ok() { + break; + } + + let (remain, (pre_escape_whitespace, line)) = content_line(remaining)?; + pre_escape_whitespace.map(|val| ret.push_str(Into::<&str>::into(val))); + ret.push_str(line.into()); + remaining = remain; + } + + Ok((remaining, ret)) +} + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +fn content_line<'s>( + input: OrgSource<'s>, +) -> Res, (Option>, OrgSource<'s>)> { + let (remaining, pre_escape_whitespace) = opt(map( + tuple((space0, tag(","), peek(alt((tag("#+"), tag("*")))))), + |(pre_comma, _, _)| pre_comma, + ))(input)?; + let (remaining, line_post_escape) = recognize(many_till(anychar, line_ending))(remaining)?; + Ok((remaining, (pre_escape_whitespace, line_post_escape))) } diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index a90c826..5f1f29c 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -45,7 +45,7 @@ pub struct ExampleBlock<'s> { pub retain_labels: bool, pub use_labels: bool, pub label_format: Option<&'s str>, - pub contents: Vec<&'s str>, + pub contents: String, } #[derive(Debug)] @@ -237,16 +237,3 @@ impl<'s> Comment<'s> { ret } } - -impl<'s> ExampleBlock<'s> { - /// Get the inner contents of the ExampleBlock with the escaping commas removed. - pub fn get_contents(&self) -> String { - let final_size = self.contents.iter().map(|line| line.len()).sum(); - let mut ret = String::with_capacity(final_size); - for line in &self.contents { - ret.push_str(line); - } - - ret - } -} From 32da06776c1b1a47d1bff3a094be2f977a6ed157 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 14:49:08 -0400 Subject: [PATCH 16/54] Handle matching no switches. --- .../example/space_for_parameters.org | 2 ++ src/compare/diff.rs | 20 +++++++++++++------ src/parser/lesser_block.rs | 2 ++ 3 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 org_mode_samples/lesser_element/lesser_block/example/space_for_parameters.org diff --git a/org_mode_samples/lesser_element/lesser_block/example/space_for_parameters.org b/org_mode_samples/lesser_element/lesser_block/example/space_for_parameters.org new file mode 100644 index 0000000..cdd859b --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/example/space_for_parameters.org @@ -0,0 +1,2 @@ +#+begin_example +#+end_example diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 938622d..6b5487f 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -1559,12 +1559,20 @@ fn compare_example_block<'b, 's>( // Compare switches let switches = get_property_quoted_string(emacs, ":switches")?; - if switches.as_ref().map(String::as_str) != rust.switches { - this_status = DiffStatus::Bad; - message = Some(format!( - "Switches mismatch (emacs != rust) {:?} != {:?}", - switches, rust.switches - )); + match (switches.as_ref().map(String::as_str), rust.switches) { + (None, None) => {} + (Some(""), None) => {} + (None, Some("")) => { + unreachable!("The organic parser would return a None instead of an empty string."); + } + (Some(e), Some(r)) if e == r => {} + _ => { + this_status = DiffStatus::Bad; + message = Some(format!( + "Switches mismatch (emacs != rust) {:?} != {:?}", + switches, rust.switches + )); + } } // Compare number-lines diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 4886965..0dde4a0 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -326,6 +326,7 @@ fn _lesser_block_begin<'b, 'g, 'r, 's, 'c>( Ok((remaining, name)) } +#[derive(Debug)] struct ExampleSwitches<'s> { source: &'s str, number_lines: Option, @@ -334,6 +335,7 @@ struct ExampleSwitches<'s> { label_format: Option<&'s str>, } +#[derive(Debug)] enum SwitchState { Normal, NewLineNumber, From 301a6db83e008dd9b44843df5618f49396becc6f Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 15:20:57 -0400 Subject: [PATCH 17/54] Fix retain labels. This is a numeric value based on the character offset of -k from the beginning of the switches. --- .../example/switches_with_label.org | 16 ++++++++-- src/compare/diff.rs | 5 ++-- src/parser/lesser_block.rs | 30 +++++++++++-------- src/types/lesser_element.rs | 4 ++- src/types/mod.rs | 1 + 5 files changed, 39 insertions(+), 17 deletions(-) diff --git a/org_mode_samples/lesser_element/lesser_block/example/switches_with_label.org b/org_mode_samples/lesser_element/lesser_block/example/switches_with_label.org index a450ab8..57b01a2 100644 --- a/org_mode_samples/lesser_element/lesser_block/example/switches_with_label.org +++ b/org_mode_samples/lesser_element/lesser_block/example/switches_with_label.org @@ -1,3 +1,15 @@ -#+BEGIN_SRC elisp -n -r -l "((%s))" +#+BEGIN_EXAMPLE elisp -n -r -l "((%s))" foo -#+END_SRC +#+END_EXAMPLE + +#+BEGIN_EXAMPLE elisp -k -n -r -l "((%s))" + foo +#+END_EXAMPLE + +#+BEGIN_EXAMPLE elisp -k 8 -n -r -l "((%s))" + foo +#+END_EXAMPLE + +#+BEGIN_EXAMPLE elisp -n -r -k -l "((%s))" + foo +#+END_EXAMPLE diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 6b5487f..87dd403 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -18,6 +18,7 @@ use crate::types::AstNode; use crate::types::BabelCall; use crate::types::Bold; use crate::types::CenterBlock; +use crate::types::CharOffsetInLine; use crate::types::CheckboxType; use crate::types::Citation; use crate::types::CitationReference; @@ -1543,7 +1544,7 @@ fn compare_example_block<'b, 's>( _source: &'s str, emacs: &'b Token<'s>, rust: &'b ExampleBlock<'s>, -) -> Result, Box> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut message = None; @@ -1624,7 +1625,7 @@ fn compare_example_block<'b, 's>( } // Compare retain-labels - let retain_labels = get_property_boolean(emacs, ":retain-labels")?; + let retain_labels: Option = get_property_numeric(emacs, ":retain-labels")?; if retain_labels != rust.retain_labels { this_status = DiffStatus::Bad; message = Some(format!( diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 0dde4a0..c225b4e 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -31,6 +31,7 @@ use crate::parser::util::exit_matcher_parser; use crate::parser::util::get_consumed; use crate::parser::util::start_of_line; use crate::parser::util::text_until_exit; +use crate::types::CharOffsetInLine; use crate::types::CommentBlock; use crate::types::ExampleBlock; use crate::types::ExportBlock; @@ -167,7 +168,7 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( parameters.label_format, ) } else { - (None, None, true, true, None) + (None, None, None, true, None) } }; Ok(( @@ -330,7 +331,7 @@ fn _lesser_block_begin<'b, 'g, 'r, 's, 'c>( struct ExampleSwitches<'s> { source: &'s str, number_lines: Option, - retain_labels: bool, + retain_labels: Option, use_labels: bool, label_format: Option<&'s str>, } @@ -346,18 +347,17 @@ enum SwitchState { #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSwitches<'s>> { let mut number_lines = None; - let mut retain_labels = true; + let mut retain_labels = None; let mut use_labels = true; let mut label_format = None; - let (remaining, (source, (words, _))) = consumed(tuple(( - separated_list1(space1, map(switch_word, |val| Into::<&str>::into(val))), - space0, - )))(input)?; + let (remaining, (source, (words, _))) = + consumed(tuple((separated_list1(space1, switch_word), space0)))(input)?; let mut state = SwitchState::Normal; for word in words { + let normalized_word = Into::<&str>::into(word); loop { - match (&state, word) { + match (&state, normalized_word) { (SwitchState::Normal, "-n") => { state = SwitchState::NewLineNumber; } @@ -365,14 +365,20 @@ fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSwitc state = SwitchState::ContinuedLineNumber; } (SwitchState::Normal, "-r") => { - retain_labels = false; use_labels = false; } (SwitchState::Normal, "-l") => { state = SwitchState::LabelFormat; } + (SwitchState::Normal, "-k") => { + let text_until_flag = input.get_until(word); + let character_offset = Into::<&str>::into(text_until_flag).chars().count(); + let character_offset = CharOffsetInLine::try_from(character_offset) + .expect("Character offset should fit in CharOffsetInLine"); + retain_labels = Some(character_offset); + } (SwitchState::NewLineNumber, _) => { - let val = word.parse::(); + let val = normalized_word.parse::(); if let Ok(val) = val { if val < 0 { number_lines = Some(SwitchNumberLines::New(0)); @@ -388,7 +394,7 @@ fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSwitc } } (SwitchState::ContinuedLineNumber, _) => { - let val = word.parse::(); + let val = normalized_word.parse::(); if let Ok(val) = val { if val < 0 { number_lines = Some(SwitchNumberLines::Continued(0)); @@ -404,7 +410,7 @@ fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSwitc } } (SwitchState::LabelFormat, _) => { - label_format = Some(word); + label_format = Some(normalized_word); state = SwitchState::Normal; } (SwitchState::Normal, _) => {} diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index 5f1f29c..b0c261d 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -36,13 +36,15 @@ pub struct CommentBlock<'s> { pub contents: &'s str, } +pub type CharOffsetInLine = u16; + #[derive(Debug)] pub struct ExampleBlock<'s> { pub source: &'s str, pub name: &'s str, pub switches: Option<&'s str>, pub number_lines: Option, - pub retain_labels: bool, + pub retain_labels: Option, pub use_labels: bool, pub label_format: Option<&'s str>, pub contents: String, diff --git a/src/types/mod.rs b/src/types/mod.rs index e517795..4072ae5 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -37,6 +37,7 @@ pub use greater_element::Table; pub use greater_element::TableRow; pub use greater_element::TableRowType; pub use lesser_element::BabelCall; +pub use lesser_element::CharOffsetInLine; pub use lesser_element::Clock; pub use lesser_element::Comment; pub use lesser_element::CommentBlock; From bcade66e683ad1b62ba97a8f9f5d6d632c1056a6 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 15:43:09 -0400 Subject: [PATCH 18/54] Retain labels is actually either a boolean or a number. --- src/compare/diff.rs | 47 +++++++++++++++++++++++++++++++------ src/parser/lesser_block.rs | 15 ++++++++---- src/types/lesser_element.rs | 10 +++++++- src/types/mod.rs | 1 + 4 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 87dd403..5265a39 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -77,6 +77,7 @@ use crate::types::RadioTarget; use crate::types::RegularLink; use crate::types::RepeaterType; use crate::types::RepeaterWarningDelayValueType; +use crate::types::RetainLabels; use crate::types::Section; use crate::types::SpecialBlock; use crate::types::SrcBlock; @@ -1625,13 +1626,45 @@ fn compare_example_block<'b, 's>( } // Compare retain-labels - let retain_labels: Option = get_property_numeric(emacs, ":retain-labels")?; - if retain_labels != rust.retain_labels { - this_status = DiffStatus::Bad; - message = Some(format!( - "Retain labels mismatch (emacs != rust) {:?} != {:?}", - retain_labels, rust.retain_labels - )); + // retain-labels is t by default, nil if -r is set, or a number if -k is set. + let retain_labels = get_property_unquoted_atom(emacs, ":retain-labels")?; + if let Some(retain_labels) = retain_labels { + if retain_labels == "t" { + match rust.retain_labels { + RetainLabels::Yes => {} + _ => { + this_status = DiffStatus::Bad; + message = Some(format!( + "Retain labels mismatch (emacs != rust) {:?} != {:?}", + retain_labels, rust.retain_labels + )); + } + } + } 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.retain_labels) { + (e, RetainLabels::Keep(r)) if e == *r => {} + _ => { + this_status = DiffStatus::Bad; + message = Some(format!( + "Retain labels mismatch (emacs != rust) {:?} != {:?}", + retain_labels, rust.retain_labels + )); + } + } + // foo + } + } else { + match rust.retain_labels { + RetainLabels::No => {} + _ => { + this_status = DiffStatus::Bad; + message = Some(format!( + "Retain labels mismatch (emacs != rust) {:?} != {:?}", + retain_labels, rust.retain_labels + )); + } + } } // Compare use-labels diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index c225b4e..5321c9f 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -38,6 +38,7 @@ use crate::types::ExportBlock; use crate::types::LineNumber; use crate::types::Object; use crate::types::PlainText; +use crate::types::RetainLabels; use crate::types::SrcBlock; use crate::types::SwitchNumberLines; use crate::types::VerseBlock; @@ -168,7 +169,7 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( parameters.label_format, ) } else { - (None, None, None, true, None) + (None, None, RetainLabels::Yes, true, None) } }; Ok(( @@ -331,7 +332,7 @@ fn _lesser_block_begin<'b, 'g, 'r, 's, 'c>( struct ExampleSwitches<'s> { source: &'s str, number_lines: Option, - retain_labels: Option, + retain_labels: RetainLabels, use_labels: bool, label_format: Option<&'s str>, } @@ -347,7 +348,7 @@ enum SwitchState { #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSwitches<'s>> { let mut number_lines = None; - let mut retain_labels = None; + let mut retain_labels = RetainLabels::Yes; let mut use_labels = true; let mut label_format = None; let (remaining, (source, (words, _))) = @@ -366,6 +367,12 @@ fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSwitc } (SwitchState::Normal, "-r") => { use_labels = false; + match retain_labels { + RetainLabels::Yes => { + retain_labels = RetainLabels::No; + } + _ => {} + } } (SwitchState::Normal, "-l") => { state = SwitchState::LabelFormat; @@ -375,7 +382,7 @@ fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSwitc let character_offset = Into::<&str>::into(text_until_flag).chars().count(); let character_offset = CharOffsetInLine::try_from(character_offset) .expect("Character offset should fit in CharOffsetInLine"); - retain_labels = Some(character_offset); + retain_labels = RetainLabels::Keep(character_offset); } (SwitchState::NewLineNumber, _) => { let val = normalized_word.parse::(); diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index b0c261d..f6fe7c9 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -38,13 +38,21 @@ pub struct CommentBlock<'s> { pub type CharOffsetInLine = u16; +#[derive(Debug)] +pub enum RetainLabels { + No, + Yes, + /// When adding -k to the switches on an example or src block, the labels are kept in the source code and links will use line numbers. + Keep(CharOffsetInLine), +} + #[derive(Debug)] pub struct ExampleBlock<'s> { pub source: &'s str, pub name: &'s str, pub switches: Option<&'s str>, pub number_lines: Option, - pub retain_labels: Option, + pub retain_labels: RetainLabels, pub use_labels: bool, pub label_format: Option<&'s str>, pub contents: String, diff --git a/src/types/mod.rs b/src/types/mod.rs index 4072ae5..5242562 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -51,6 +51,7 @@ pub use lesser_element::LatexEnvironment; pub use lesser_element::LineNumber; pub use lesser_element::Paragraph; pub use lesser_element::Planning; +pub use lesser_element::RetainLabels; pub use lesser_element::SrcBlock; pub use lesser_element::SwitchNumberLines; pub use lesser_element::TableCell; From d059afef078ad26090ad253938f8f7e7d1df3b13 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 15:48:57 -0400 Subject: [PATCH 19/54] Add a setting for coderef_label_format. --- src/context/global_settings.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/context/global_settings.rs b/src/context/global_settings.rs index f730c58..8b0a9ec 100644 --- a/src/context/global_settings.rs +++ b/src/context/global_settings.rs @@ -32,6 +32,11 @@ 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, } pub const DEFAULT_TAB_WIDTH: IndentationLevel = 8; @@ -49,6 +54,7 @@ impl<'g, 's> GlobalSettings<'g, 's> { tab_width: DEFAULT_TAB_WIDTH, odd_levels_only: HeadlineLevelFilter::default(), footnote_section: "Footnotes", + coderef_label_format: "(ref:%s)", } } } From da5dcd4c1b518f64cc4c71b0caa611bb06896771 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 16:03:45 -0400 Subject: [PATCH 20/54] Support multiple commas when escaping lines. --- .../lesser_block/example/escaped_lines.org | 4 ++-- src/parser/lesser_block.rs | 13 +++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/org_mode_samples/lesser_element/lesser_block/example/escaped_lines.org b/org_mode_samples/lesser_element/lesser_block/example/escaped_lines.org index 71dcda3..5a90409 100644 --- a/org_mode_samples/lesser_element/lesser_block/example/escaped_lines.org +++ b/org_mode_samples/lesser_element/lesser_block/example/escaped_lines.org @@ -1,7 +1,7 @@ #+begin_example ,* foo -,** bar -,*** baz +,,,** bar + ,*** baz lorem , ipsum ,#+begin_src dolar diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 5321c9f..32a80c4 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -476,8 +476,17 @@ fn content_line<'s>( input: OrgSource<'s>, ) -> Res, (Option>, OrgSource<'s>)> { let (remaining, pre_escape_whitespace) = opt(map( - tuple((space0, tag(","), peek(alt((tag("#+"), tag("*")))))), - |(pre_comma, _, _)| pre_comma, + tuple(( + recognize(tuple(( + space0, + many_till( + tag(","), + peek(tuple((tag(","), alt((tag("#+"), tag("*")))))), + ), + ))), + tag(","), + )), + |(pre_comma, _)| pre_comma, ))(input)?; let (remaining, line_post_escape) = recognize(many_till(anychar, line_ending))(remaining)?; Ok((remaining, (pre_escape_whitespace, line_post_escape))) From 13163f2468e94f5fa617103a449b287c4536cde5 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 16:21:37 -0400 Subject: [PATCH 21/54] Retain labels stays a boolean without -r. --- .../lesser_element/lesser_block/example/only_k.org | 3 +++ src/compare/diff.rs | 3 +-- src/parser/lesser_block.rs | 8 ++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 org_mode_samples/lesser_element/lesser_block/example/only_k.org diff --git a/org_mode_samples/lesser_element/lesser_block/example/only_k.org b/org_mode_samples/lesser_element/lesser_block/example/only_k.org new file mode 100644 index 0000000..fbc3d87 --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/example/only_k.org @@ -0,0 +1,3 @@ +#+begin_example foo -k +bar +#+end_example diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 5265a39..4ca5de1 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -1626,7 +1626,7 @@ fn compare_example_block<'b, 's>( } // Compare retain-labels - // retain-labels is t by default, nil if -r is set, or a number if -k is set. + // retain-labels is t by default, nil if -r is set, or a number if -k and -r is set. let retain_labels = get_property_unquoted_atom(emacs, ":retain-labels")?; if let Some(retain_labels) = retain_labels { if retain_labels == "t" { @@ -1652,7 +1652,6 @@ fn compare_example_block<'b, 's>( )); } } - // foo } } else { match rust.retain_labels { diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 32a80c4..6305b7b 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -351,6 +351,7 @@ fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSwitc let mut retain_labels = RetainLabels::Yes; let mut use_labels = true; let mut label_format = None; + let mut saw_r = false; let (remaining, (source, (words, _))) = consumed(tuple((separated_list1(space1, switch_word), space0)))(input)?; @@ -366,6 +367,7 @@ fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSwitc state = SwitchState::ContinuedLineNumber; } (SwitchState::Normal, "-r") => { + saw_r = true; use_labels = false; match retain_labels { RetainLabels::Yes => { @@ -378,6 +380,7 @@ fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSwitc state = SwitchState::LabelFormat; } (SwitchState::Normal, "-k") => { + use_labels = false; let text_until_flag = input.get_until(word); let character_offset = Into::<&str>::into(text_until_flag).chars().count(); let character_offset = CharOffsetInLine::try_from(character_offset) @@ -426,6 +429,11 @@ fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSwitc } } + let retain_labels = match retain_labels { + RetainLabels::Keep(_) if !saw_r => RetainLabels::Yes, + _ => retain_labels, + }; + Ok(( remaining, ExampleSwitches { From b556f4617f3b97fb4a14bb24777c7a5fa162edae Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 16:58:45 -0400 Subject: [PATCH 22/54] Add src block properties. These are largely the same as example blocks but with a :language property. --- src/compare/diff.rs | 158 +++++++++++++++++++++++++++++++++++- src/parser/lesser_block.rs | 74 ++++++++++++++--- src/types/lesser_element.rs | 9 +- 3 files changed, 223 insertions(+), 18 deletions(-) diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 4ca5de1..f630e39 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -1727,11 +1727,161 @@ fn compare_src_block<'b, 's>( _source: &'s str, emacs: &'b Token<'s>, rust: &'b SrcBlock<'s>, -) -> Result, Box> { - let this_status = DiffStatus::Good; - let message = None; +) -> Result, Box> { + let mut this_status = DiffStatus::Good; + let mut message = None; - // TODO: Compare :language :switches :parameters :number-lines :preserve-indent :retain-labels :use-labels :label-fmt :value + // Compare language + let language = get_property_quoted_string(emacs, ":language")?; + if language.as_ref().map(String::as_str) != rust.language { + this_status = DiffStatus::Bad; + message = Some(format!( + "Language mismatch (emacs != rust) {:?} != {:?}", + language, rust.language + )); + } + + // Compare value + let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new()); + if contents != rust.contents { + this_status = DiffStatus::Bad; + message = Some(format!( + "Value mismatch (emacs != rust) {:?} != {:?}", + contents, rust.contents + )); + } + + // Compare switches + let switches = get_property_quoted_string(emacs, ":switches")?; + match (switches.as_ref().map(String::as_str), rust.switches) { + (None, None) => {} + (Some(""), None) => {} + (None, Some("")) => { + unreachable!("The organic parser would return a None instead of an empty string."); + } + (Some(e), Some(r)) if e == r => {} + _ => { + this_status = DiffStatus::Bad; + message = Some(format!( + "Switches mismatch (emacs != rust) {:?} != {:?}", + switches, rust.switches + )); + } + } + + // Compare number-lines + let number_lines = get_property(emacs, ":number-lines")?; + match (number_lines, &rust.number_lines) { + (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::()) + .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 => {} + _ => { + this_status = DiffStatus::Bad; + message = Some(format!( + "Number lines mismatch (emacs != rust) {:?} != {:?}", + number_lines, rust.number_lines + )); + } + } + } + _ => { + this_status = DiffStatus::Bad; + message = Some(format!( + "Number lines mismatch (emacs != rust) {:?} != {:?}", + number_lines, rust.number_lines + )); + } + }; + + // Compare preserve-indent + let preserve_indent = get_property_boolean(emacs, ":preserve-indent")?; + if preserve_indent { + // I don't know what :preserve-indent is for, but it seems to always be nil. This is here to alert me to value being non-nil so I can investigate. + this_status = DiffStatus::Bad; + message = Some(format!("Non-nil :preserve-indent {:?}", preserve_indent)); + } + + // Compare retain-labels + // retain-labels is t by default, nil if -r is set, or a number if -k and -r is set. + let retain_labels = get_property_unquoted_atom(emacs, ":retain-labels")?; + if let Some(retain_labels) = retain_labels { + if retain_labels == "t" { + match rust.retain_labels { + RetainLabels::Yes => {} + _ => { + this_status = DiffStatus::Bad; + message = Some(format!( + "Retain labels mismatch (emacs != rust) {:?} != {:?}", + retain_labels, rust.retain_labels + )); + } + } + } 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.retain_labels) { + (e, RetainLabels::Keep(r)) if e == *r => {} + _ => { + this_status = DiffStatus::Bad; + message = Some(format!( + "Retain labels mismatch (emacs != rust) {:?} != {:?}", + retain_labels, rust.retain_labels + )); + } + } + } + } else { + match rust.retain_labels { + RetainLabels::No => {} + _ => { + this_status = DiffStatus::Bad; + message = Some(format!( + "Retain labels mismatch (emacs != rust) {:?} != {:?}", + retain_labels, rust.retain_labels + )); + } + } + } + + // Compare use-labels + let use_labels = get_property_boolean(emacs, ":use-labels")?; + if use_labels != rust.use_labels { + this_status = DiffStatus::Bad; + message = Some(format!( + "Use labels mismatch (emacs != rust) {:?} != {:?}", + use_labels, rust.use_labels + )); + } + + // Compare label-fmt + let label_format = get_property_quoted_string(emacs, ":label-fmt")?; + match (label_format.as_ref(), rust.label_format) { + (None, None) => {} + (Some(emacs_label_format), Some(rust_label_format)) + if emacs_label_format == rust_label_format => {} + _ => { + this_status = DiffStatus::Bad; + message = Some(format!( + "Label format mismatch (emacs != rust) {:?} != {:?}", + label_format, rust.label_format + )); + } + } Ok(DiffResult { status: this_status, diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 6305b7b..4651d69 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -176,7 +176,6 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( remaining, ExampleBlock { source: source.into(), - name: source.into(), switches, number_lines, retain_labels, @@ -233,9 +232,9 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, SrcBlock<'s>> { - let (remaining, name) = lesser_block_begin("src")(context, input)?; + let (remaining, _name) = lesser_block_begin("src")(context, input)?; // https://orgmode.org/worg/org-syntax.html#Blocks claims that data is mandatory and must follow the LANGUAGE SWITCHES ARGUMENTS pattern but testing has shown that no data and incorrect data here will still parse to a src block. - let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?; + let (remaining, parameters) = opt(tuple((space1, src_switches)))(remaining)?; let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; let lesser_block_end_specialized = lesser_block_end("src"); let contexts = [ @@ -254,17 +253,35 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( None => None, }; - let (remaining, contents) = parser_with_context!(text_until_exit)(&parser_context)(remaining)?; + let (remaining, contents) = content(&parser_context, remaining)?; let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?; let source = get_consumed(input, remaining); + let (switches, language, number_lines, retain_labels, use_labels, label_format) = { + if let Some(parameters) = parameters { + ( + Some(parameters.source), + parameters.language, + parameters.number_lines, + parameters.retain_labels, + parameters.use_labels, + parameters.label_format, + ) + } else { + (None, None, None, RetainLabels::Yes, true, None) + } + }; Ok(( remaining, SrcBlock { source: source.into(), - name: name.into(), - switches: parameters.map(|parameters| Into::<&str>::into(parameters)), - contents: contents.into(), + switches, + language, + number_lines, + retain_labels, + use_labels, + label_format, + contents, }, )) } @@ -329,8 +346,9 @@ fn _lesser_block_begin<'b, 'g, 'r, 's, 'c>( } #[derive(Debug)] -struct ExampleSwitches<'s> { +struct ExampleSrcSwitches<'s> { source: &'s str, + language: Option<&'s str>, number_lines: Option, retain_labels: RetainLabels, use_labels: bool, @@ -346,17 +364,50 @@ enum SwitchState { } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSwitches<'s>> { +fn src_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSrcSwitches<'s>> { + example_src_switches(true)(input) +} + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSrcSwitches<'s>> { + let (remaining, switches) = example_src_switches(false)(input)?; + debug_assert!(switches.language.is_none()); + Ok((remaining, switches)) +} + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +fn example_src_switches( + grab_language: bool, +) -> impl for<'s> Fn(OrgSource<'s>) -> Res, ExampleSrcSwitches<'s>> { + move |input| _example_src_switches(input, grab_language) +} + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +fn _example_src_switches<'s>( + input: OrgSource<'s>, + grab_language: bool, +) -> Res, ExampleSrcSwitches<'s>> { let mut number_lines = None; let mut retain_labels = RetainLabels::Yes; let mut use_labels = true; let mut label_format = None; let mut saw_r = false; + let mut language = None; let (remaining, (source, (words, _))) = consumed(tuple((separated_list1(space1, switch_word), space0)))(input)?; + let mut words_iter = words.into_iter(); + if grab_language { + match words_iter.next() { + Some(l) => { + language = Some(Into::<&str>::into(l)); + } + None => {} + } + } + let mut state = SwitchState::Normal; - for word in words { + for word in words_iter { let normalized_word = Into::<&str>::into(word); loop { match (&state, normalized_word) { @@ -436,8 +487,9 @@ fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSwitc Ok(( remaining, - ExampleSwitches { + ExampleSrcSwitches { source: Into::<&str>::into(source), + language, number_lines, retain_labels, use_labels, diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index f6fe7c9..76b2645 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -49,7 +49,6 @@ pub enum RetainLabels { #[derive(Debug)] pub struct ExampleBlock<'s> { pub source: &'s str, - pub name: &'s str, pub switches: Option<&'s str>, pub number_lines: Option, pub retain_labels: RetainLabels, @@ -69,9 +68,13 @@ pub struct ExportBlock<'s> { #[derive(Debug)] pub struct SrcBlock<'s> { pub source: &'s str, - pub name: &'s str, pub switches: Option<&'s str>, - pub contents: &'s str, + pub language: Option<&'s str>, + pub number_lines: Option, + pub retain_labels: RetainLabels, + pub use_labels: bool, + pub label_format: Option<&'s str>, + pub contents: String, } #[derive(Debug)] From a548c7e1704b3043d8f93e14738aed420965a52a Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 17:11:13 -0400 Subject: [PATCH 23/54] Exclude language from the switches property. --- src/parser/lesser_block.rs | 39 ++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 4651d69..2829366 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -14,6 +14,7 @@ use nom::combinator::peek; use nom::combinator::recognize; use nom::combinator::verify; use nom::multi::many_till; +use nom::multi::separated_list0; use nom::multi::separated_list1; use nom::sequence::tuple; @@ -162,7 +163,11 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( let (switches, number_lines, retain_labels, use_labels, label_format) = { if let Some(parameters) = parameters { ( - Some(parameters.source), + if parameters.source.len() == 0 { + None + } else { + Some(parameters.source) + }, parameters.number_lines, parameters.retain_labels, parameters.use_labels, @@ -260,7 +265,11 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( let (switches, language, number_lines, retain_labels, use_labels, label_format) = { if let Some(parameters) = parameters { ( - Some(parameters.source), + if parameters.source.len() == 0 { + None + } else { + Some(parameters.source) + }, parameters.language, parameters.number_lines, parameters.retain_labels, @@ -393,21 +402,23 @@ fn _example_src_switches<'s>( let mut label_format = None; let mut saw_r = false; let mut language = None; - let (remaining, (source, (words, _))) = - consumed(tuple((separated_list1(space1, switch_word), space0)))(input)?; - let mut words_iter = words.into_iter(); - if grab_language { - match words_iter.next() { - Some(l) => { - language = Some(Into::<&str>::into(l)); - } - None => {} - } - } + let remaining = if grab_language { + let (remain, first_word) = opt(map(tuple((switch_word, space0)), |(word, _)| word))(input)?; + language = first_word.map(Into::<&str>::into); + remain + } else { + input + }; + + let (remaining, (source, (words, _))) = if language.is_none() { + consumed(tuple((separated_list1(space1, switch_word), space0)))(remaining)? + } else { + consumed(tuple((separated_list0(space1, switch_word), space0)))(remaining)? + }; let mut state = SwitchState::Normal; - for word in words_iter { + for word in words { let normalized_word = Into::<&str>::into(word); loop { match (&state, normalized_word) { From 1ab7d2f2d75b3a3836a69d2330ce12983a0a8deb Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 17:20:46 -0400 Subject: [PATCH 24/54] Add a test showing we are not handling exports flags properly. --- Makefile | 7 +++++++ .../lesser_element/lesser_block/example/exports_flag.org | 7 +++++++ .../lesser_element/lesser_block/src/exports_flag.org | 7 +++++++ .../lesser_block/src/{with_no_data.org => no_data.org} | 0 .../src/{with_space_after_end.org => space_after_end.org} | 0 src/parser/lesser_block.rs | 4 +--- src/types/lesser_element.rs | 1 - 7 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 org_mode_samples/lesser_element/lesser_block/example/exports_flag.org create mode 100644 org_mode_samples/lesser_element/lesser_block/src/exports_flag.org rename org_mode_samples/lesser_element/lesser_block/src/{with_no_data.org => no_data.org} (100%) rename org_mode_samples/lesser_element/lesser_block/src/{with_space_after_end.org => space_after_end.org} (100%) diff --git a/Makefile b/Makefile index 763a441..a7412bc 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,13 @@ dockertest: > $(MAKE) -C docker/organic_test > docker run --init --rm -i -t --read-only -v "$$(readlink -f ./):/source:ro" --mount type=tmpfs,destination=/tmp --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test --no-default-features --features compare --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS) +.PHONY: buildtest +buildtest: +> cargo build --no-default-features +> cargo build --no-default-features --features compare +> cargo build --no-default-features --features tracing +> cargo build --no-default-features --features compare,tracing + .PHONY: foreign_document_test foreign_document_test: > $(MAKE) -C docker/organic_test run_foreign_document_test diff --git a/org_mode_samples/lesser_element/lesser_block/example/exports_flag.org b/org_mode_samples/lesser_element/lesser_block/example/exports_flag.org new file mode 100644 index 0000000..e9cc9b0 --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/example/exports_flag.org @@ -0,0 +1,7 @@ +#+begin_example python :exports results +print("foo") +#+end_example + +#+begin_example python -n :exports results +print("foo") +#+end_example diff --git a/org_mode_samples/lesser_element/lesser_block/src/exports_flag.org b/org_mode_samples/lesser_element/lesser_block/src/exports_flag.org new file mode 100644 index 0000000..87c7fab --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/src/exports_flag.org @@ -0,0 +1,7 @@ +#+begin_src python :exports results +print("foo") +#+end_src + +#+begin_src python -n :exports results +print("foo") +#+end_src diff --git a/org_mode_samples/lesser_element/lesser_block/src/with_no_data.org b/org_mode_samples/lesser_element/lesser_block/src/no_data.org similarity index 100% rename from org_mode_samples/lesser_element/lesser_block/src/with_no_data.org rename to org_mode_samples/lesser_element/lesser_block/src/no_data.org diff --git a/org_mode_samples/lesser_element/lesser_block/src/with_space_after_end.org b/org_mode_samples/lesser_element/lesser_block/src/space_after_end.org similarity index 100% rename from org_mode_samples/lesser_element/lesser_block/src/with_space_after_end.org rename to org_mode_samples/lesser_element/lesser_block/src/space_after_end.org diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 2829366..5f909a6 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -104,7 +104,7 @@ pub(crate) fn comment_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, CommentBlock<'s>> { - let (remaining, name) = lesser_block_begin("comment")(context, input)?; + let (remaining, _name) = lesser_block_begin("comment")(context, input)?; let (remaining, _parameters) = opt(tuple((space1, data)))(remaining)?; let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; let lesser_block_end_specialized = lesser_block_end("comment"); @@ -128,7 +128,6 @@ pub(crate) fn comment_block<'b, 'g, 'r, 's>( remaining, CommentBlock { source: source.into(), - name: name.into(), contents: contents.into(), }, )) @@ -384,7 +383,6 @@ fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSrcSw Ok((remaining, switches)) } -#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn example_src_switches( grab_language: bool, ) -> impl for<'s> Fn(OrgSource<'s>) -> Res, ExampleSrcSwitches<'s>> { diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index 76b2645..d8e21d6 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -32,7 +32,6 @@ pub struct VerseBlock<'s> { #[derive(Debug)] pub struct CommentBlock<'s> { pub source: &'s str, - pub name: &'s str, pub contents: &'s str, } From f046b16c110d7c8b7a34abfd71d23d837bf1ad97 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 18:02:50 -0400 Subject: [PATCH 25/54] Compare src block parameters. --- src/compare/diff.rs | 20 +++++++++ src/parser/lesser_block.rs | 81 ++++++++++++++++++++++++++++--------- src/types/lesser_element.rs | 3 +- 3 files changed, 83 insertions(+), 21 deletions(-) diff --git a/src/compare/diff.rs b/src/compare/diff.rs index f630e39..cb02531 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -1731,6 +1731,8 @@ fn compare_src_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // TODO: Compare :parameters + // Compare language let language = get_property_quoted_string(emacs, ":language")?; if language.as_ref().map(String::as_str) != rust.language { @@ -1769,6 +1771,24 @@ fn compare_src_block<'b, 's>( } } + // Compare switches + let parameters = get_property_quoted_string(emacs, ":parameters")?; + match (parameters.as_ref().map(String::as_str), rust.parameters) { + (None, None) => {} + (Some(""), None) => {} + (None, Some("")) => { + unreachable!("The organic parser would return a None instead of an empty string."); + } + (Some(e), Some(r)) if e == r => {} + _ => { + this_status = DiffStatus::Bad; + message = Some(format!( + "Parameters mismatch (emacs != rust) {:?} != {:?}", + parameters, rust.parameters + )); + } + } + // Compare number-lines let number_lines = get_property(emacs, ":number-lines")?; match (number_lines, &rust.number_lines) { diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 5f909a6..a04e562 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -9,6 +9,7 @@ use nom::character::complete::space1; use nom::combinator::consumed; use nom::combinator::eof; use nom::combinator::map; +use nom::combinator::not; use nom::combinator::opt; use nom::combinator::peek; use nom::combinator::recognize; @@ -24,6 +25,7 @@ use crate::context::ContextElement; use crate::context::ContextMatcher; use crate::context::ExitClass; use crate::context::ExitMatcherNode; +use crate::context::Matcher; use crate::context::RefContext; use crate::error::Res; use crate::parser::object_parser::standard_set_object; @@ -238,7 +240,10 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( ) -> Res, SrcBlock<'s>> { let (remaining, _name) = lesser_block_begin("src")(context, input)?; // https://orgmode.org/worg/org-syntax.html#Blocks claims that data is mandatory and must follow the LANGUAGE SWITCHES ARGUMENTS pattern but testing has shown that no data and incorrect data here will still parse to a src block. - let (remaining, parameters) = opt(tuple((space1, src_switches)))(remaining)?; + let (remaining, switches) = opt(tuple((space1, src_switches)))(remaining)?; + let (remaining, parameters) = opt(map(tuple((space0, src_parameters)), |(_, parameters)| { + parameters + }))(remaining)?; let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; let lesser_block_end_specialized = lesser_block_end("src"); let contexts = [ @@ -252,8 +257,8 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( let parser_context = context.with_additional_node(&contexts[0]); let parser_context = parser_context.with_additional_node(&contexts[1]); let parser_context = parser_context.with_additional_node(&contexts[2]); - let parameters = match parameters { - Some((_ws, parameters)) => Some(parameters), + let switches = match switches { + Some((_ws, switches)) => Some(switches), None => None, }; @@ -262,18 +267,18 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( let source = get_consumed(input, remaining); let (switches, language, number_lines, retain_labels, use_labels, label_format) = { - if let Some(parameters) = parameters { + if let Some(switches) = switches { ( - if parameters.source.len() == 0 { + if switches.source.len() == 0 { None } else { - Some(parameters.source) + Some(switches.source) }, - parameters.language, - parameters.number_lines, - parameters.retain_labels, - parameters.use_labels, - parameters.label_format, + switches.language, + switches.number_lines, + switches.retain_labels, + switches.use_labels, + switches.label_format, ) } else { (None, None, None, RetainLabels::Yes, true, None) @@ -283,8 +288,9 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( remaining, SrcBlock { source: source.into(), - switches, language, + switches, + parameters: parameters.map(Into::<&str>::into), number_lines, retain_labels, use_labels, @@ -371,28 +377,35 @@ enum SwitchState { LabelFormat, } +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +fn src_parameters<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { + recognize(is_not("\r\n"))(input) +} + #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn src_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSrcSwitches<'s>> { - example_src_switches(true)(input) + example_src_switches(true, true)(input) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSrcSwitches<'s>> { - let (remaining, switches) = example_src_switches(false)(input)?; + let (remaining, switches) = example_src_switches(false, false)(input)?; debug_assert!(switches.language.is_none()); Ok((remaining, switches)) } fn example_src_switches( grab_language: bool, + stop_at_parameters: bool, ) -> impl for<'s> Fn(OrgSource<'s>) -> Res, ExampleSrcSwitches<'s>> { - move |input| _example_src_switches(input, grab_language) + move |input| _example_src_switches(input, grab_language, stop_at_parameters) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn _example_src_switches<'s>( input: OrgSource<'s>, grab_language: bool, + stop_at_parameters: bool, ) -> Res, ExampleSrcSwitches<'s>> { let mut number_lines = None; let mut retain_labels = RetainLabels::Yes; @@ -402,17 +415,23 @@ fn _example_src_switches<'s>( let mut language = None; let remaining = if grab_language { - let (remain, first_word) = opt(map(tuple((switch_word, space0)), |(word, _)| word))(input)?; + let (remain, first_word) = opt(switch_word(stop_at_parameters))(input)?; language = first_word.map(Into::<&str>::into); remain } else { input }; - let (remaining, (source, (words, _))) = if language.is_none() { - consumed(tuple((separated_list1(space1, switch_word), space0)))(remaining)? + let (remaining, (source, words)) = if language.is_none() { + consumed(separated_list1(space1, switch_word(stop_at_parameters)))(remaining)? } else { - consumed(tuple((separated_list0(space1, switch_word), space0)))(remaining)? + map( + tuple(( + space1, + consumed(separated_list0(space1, switch_word(stop_at_parameters))), + )), + |(_, words)| words, + )(remaining)? }; let mut state = SwitchState::Normal; @@ -489,6 +508,18 @@ fn _example_src_switches<'s>( } } + // Handle state that didn't get processed because we ran out of words. + match state { + SwitchState::Normal => {} + SwitchState::NewLineNumber => { + number_lines = Some(SwitchNumberLines::New(0)); + } + SwitchState::ContinuedLineNumber => { + number_lines = Some(SwitchNumberLines::Continued(0)); + } + SwitchState::LabelFormat => {} + } + let retain_labels = match retain_labels { RetainLabels::Keep(_) if !saw_r => RetainLabels::Yes, _ => retain_labels, @@ -507,8 +538,18 @@ fn _example_src_switches<'s>( )) } +fn switch_word<'s>(stop_at_parameters: bool) -> impl Matcher { + move |input| _switch_word(input, stop_at_parameters) +} + #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn switch_word<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { +fn _switch_word<'s>( + input: OrgSource<'s>, + stop_at_parameters: bool, +) -> Res, OrgSource<'s>> { + if stop_at_parameters { + not(tag(":"))(input)?; + } alt(( map( tuple((tag(r#"""#), is_not("\"\r\n"), tag(r#"""#))), diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index d8e21d6..572626e 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -67,8 +67,9 @@ pub struct ExportBlock<'s> { #[derive(Debug)] pub struct SrcBlock<'s> { pub source: &'s str, - pub switches: Option<&'s str>, pub language: Option<&'s str>, + pub switches: Option<&'s str>, + pub parameters: Option<&'s str>, pub number_lines: Option, pub retain_labels: RetainLabels, pub use_labels: bool, From 8e70773b15ee2bbbaae24076c3d88a844c3f784d Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 18:11:38 -0400 Subject: [PATCH 26/54] Fix handling cases where only language is specified before parameters. --- src/parser/lesser_block.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index a04e562..411edc0 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -15,9 +15,9 @@ use nom::combinator::peek; use nom::combinator::recognize; use nom::combinator::verify; use nom::multi::many_till; -use nom::multi::separated_list0; use nom::multi::separated_list1; use nom::sequence::tuple; +use nom::InputTake; use super::org_source::OrgSource; use crate::context::parser_with_context; @@ -425,13 +425,18 @@ fn _example_src_switches<'s>( let (remaining, (source, words)) = if language.is_none() { consumed(separated_list1(space1, switch_word(stop_at_parameters)))(remaining)? } else { - map( + let (remain, maybe_words) = opt(map( tuple(( space1, - consumed(separated_list0(space1, switch_word(stop_at_parameters))), + consumed(separated_list1(space1, switch_word(stop_at_parameters))), )), |(_, words)| words, - )(remaining)? + ))(remaining)?; + if let Some((source, words)) = maybe_words { + (remain, (source, words)) + } else { + (remain, (remaining.take(0), Vec::new())) + } }; let mut state = SwitchState::Normal; From c8771165401bc22b3321ee3848655fd72b3ddbcf Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 18:57:51 -0400 Subject: [PATCH 27/54] Fix handling of spaces between language, switches, and parameters. --- src/compare/compare.rs | 13 +++++++++ src/compare/diff.rs | 6 ++-- src/parser/lesser_block.rs | 60 ++++++++++---------------------------- 3 files changed, 31 insertions(+), 48 deletions(-) diff --git a/src/compare/compare.rs b/src/compare/compare.rs index f77c808..b8b9c0f 100644 --- a/src/compare/compare.rs +++ b/src/compare/compare.rs @@ -1,6 +1,7 @@ use std::path::Path; use crate::compare::diff::compare_document; +use crate::compare::diff::DiffResult; use crate::compare::parse::emacs_parse_anonymous_org_document; use crate::compare::parse::emacs_parse_file_org_document; use crate::compare::parse::get_emacs_version; @@ -43,6 +44,12 @@ pub fn run_anonymous_compare_with_settings>( if diff_result.is_bad() { Err("Diff results do not match.")?; + } else { + println!( + "{color}Entire document passes.{reset}", + color = DiffResult::foreground_color(0, 255, 0), + reset = DiffResult::reset_color(), + ); } Ok(()) @@ -83,6 +90,12 @@ pub fn run_compare_on_file_with_settings>( if diff_result.is_bad() { Err("Diff results do not match.")?; + } else { + println!( + "{color}Entire document passes.{reset}", + color = DiffResult::foreground_color(0, 255, 0), + reset = DiffResult::reset_color(), + ); } Ok(()) diff --git a/src/compare/diff.rs b/src/compare/diff.rs index cb02531..451943d 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -235,7 +235,7 @@ impl<'b, 's> DiffResult<'b, 's> { .any(|child| child.is_immediately_bad() || child.has_bad_children()) } - fn foreground_color(red: u8, green: u8, blue: u8) -> String { + pub(crate) fn foreground_color(red: u8, green: u8, blue: u8) -> String { if DiffResult::should_use_color() { format!( "\x1b[38;2;{red};{green};{blue}m", @@ -249,7 +249,7 @@ impl<'b, 's> DiffResult<'b, 's> { } #[allow(dead_code)] - fn background_color(red: u8, green: u8, blue: u8) -> String { + pub(crate) fn background_color(red: u8, green: u8, blue: u8) -> String { if DiffResult::should_use_color() { format!( "\x1b[48;2;{red};{green};{blue}m", @@ -262,7 +262,7 @@ impl<'b, 's> DiffResult<'b, 's> { } } - fn reset_color() -> &'static str { + pub(crate) fn reset_color() -> &'static str { if DiffResult::should_use_color() { "\x1b[0m" } else { diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 411edc0..a43d8c5 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -17,7 +17,6 @@ use nom::combinator::verify; use nom::multi::many_till; use nom::multi::separated_list1; use nom::sequence::tuple; -use nom::InputTake; use super::org_source::OrgSource; use crate::context::parser_with_context; @@ -240,8 +239,12 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( ) -> Res, SrcBlock<'s>> { let (remaining, _name) = lesser_block_begin("src")(context, input)?; // https://orgmode.org/worg/org-syntax.html#Blocks claims that data is mandatory and must follow the LANGUAGE SWITCHES ARGUMENTS pattern but testing has shown that no data and incorrect data here will still parse to a src block. - let (remaining, switches) = opt(tuple((space1, src_switches)))(remaining)?; - let (remaining, parameters) = opt(map(tuple((space0, src_parameters)), |(_, parameters)| { + let (remaining, language) = opt(map(tuple((space1, switch_word(true))), |(_, language)| { + language + }))(remaining)?; + let (remaining, switches) = + opt(map(tuple((space1, src_switches)), |(_, switches)| switches))(remaining)?; + let (remaining, parameters) = opt(map(tuple((space1, src_parameters)), |(_, parameters)| { parameters }))(remaining)?; let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; @@ -257,16 +260,11 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( let parser_context = context.with_additional_node(&contexts[0]); let parser_context = parser_context.with_additional_node(&contexts[1]); let parser_context = parser_context.with_additional_node(&contexts[2]); - let switches = match switches { - Some((_ws, switches)) => Some(switches), - None => None, - }; - let (remaining, contents) = content(&parser_context, remaining)?; let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?; let source = get_consumed(input, remaining); - let (switches, language, number_lines, retain_labels, use_labels, label_format) = { + let (switches, number_lines, retain_labels, use_labels, label_format) = { if let Some(switches) = switches { ( if switches.source.len() == 0 { @@ -274,21 +272,20 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( } else { Some(switches.source) }, - switches.language, switches.number_lines, switches.retain_labels, switches.use_labels, switches.label_format, ) } else { - (None, None, None, RetainLabels::Yes, true, None) + (None, None, RetainLabels::Yes, true, None) } }; Ok(( remaining, SrcBlock { source: source.into(), - language, + language: language.map(Into::<&str>::into), switches, parameters: parameters.map(Into::<&str>::into), number_lines, @@ -362,7 +359,6 @@ fn _lesser_block_begin<'b, 'g, 'r, 's, 'c>( #[derive(Debug)] struct ExampleSrcSwitches<'s> { source: &'s str, - language: Option<&'s str>, number_lines: Option, retain_labels: RetainLabels, use_labels: bool, @@ -384,27 +380,24 @@ fn src_parameters<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn src_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSrcSwitches<'s>> { - example_src_switches(true, true)(input) + example_src_switches(true)(input) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSrcSwitches<'s>> { - let (remaining, switches) = example_src_switches(false, false)(input)?; - debug_assert!(switches.language.is_none()); + let (remaining, switches) = example_src_switches(false)(input)?; Ok((remaining, switches)) } fn example_src_switches( - grab_language: bool, stop_at_parameters: bool, ) -> impl for<'s> Fn(OrgSource<'s>) -> Res, ExampleSrcSwitches<'s>> { - move |input| _example_src_switches(input, grab_language, stop_at_parameters) + move |input| _example_src_switches(input, stop_at_parameters) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn _example_src_switches<'s>( input: OrgSource<'s>, - grab_language: bool, stop_at_parameters: bool, ) -> Res, ExampleSrcSwitches<'s>> { let mut number_lines = None; @@ -412,32 +405,10 @@ fn _example_src_switches<'s>( let mut use_labels = true; let mut label_format = None; let mut saw_r = false; - let mut language = None; - let remaining = if grab_language { - let (remain, first_word) = opt(switch_word(stop_at_parameters))(input)?; - language = first_word.map(Into::<&str>::into); - remain - } else { - input - }; - - let (remaining, (source, words)) = if language.is_none() { - consumed(separated_list1(space1, switch_word(stop_at_parameters)))(remaining)? - } else { - let (remain, maybe_words) = opt(map( - tuple(( - space1, - consumed(separated_list1(space1, switch_word(stop_at_parameters))), - )), - |(_, words)| words, - ))(remaining)?; - if let Some((source, words)) = maybe_words { - (remain, (source, words)) - } else { - (remain, (remaining.take(0), Vec::new())) - } - }; + let (remaining, words) = separated_list1(space1, switch_word(stop_at_parameters))(input)?; + let (remaining, _post_spaces) = opt(tuple((space0, peek(line_ending))))(remaining)?; + let source = input.get_until(remaining); let mut state = SwitchState::Normal; for word in words { @@ -534,7 +505,6 @@ fn _example_src_switches<'s>( remaining, ExampleSrcSwitches { source: Into::<&str>::into(source), - language, number_lines, retain_labels, use_labels, From e1e4ac75e4b05c1c3fe87a5285c0f29d9851af6c Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 19:04:35 -0400 Subject: [PATCH 28/54] Add a test for preserve indent. --- .../lesser_block/example/preserve_indent.org | 15 +++++++++++++++ .../lesser_block/src/preserve_indent.org | 15 +++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 org_mode_samples/lesser_element/lesser_block/example/preserve_indent.org create mode 100644 org_mode_samples/lesser_element/lesser_block/src/preserve_indent.org diff --git a/org_mode_samples/lesser_element/lesser_block/example/preserve_indent.org b/org_mode_samples/lesser_element/lesser_block/example/preserve_indent.org new file mode 100644 index 0000000..d3fecbc --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/example/preserve_indent.org @@ -0,0 +1,15 @@ +#+begin_example text -i + foo +#+end_example + +#+begin_example text -n -i + foo +#+end_example + +#+begin_example text + foo +#+end_example + +#+begin_example text -n -r -k + foo +#+end_example diff --git a/org_mode_samples/lesser_element/lesser_block/src/preserve_indent.org b/org_mode_samples/lesser_element/lesser_block/src/preserve_indent.org new file mode 100644 index 0000000..5609392 --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/src/preserve_indent.org @@ -0,0 +1,15 @@ +#+begin_src text -i + foo +#+end_src + +#+begin_src text -n -i + foo +#+end_src + +#+begin_src text + foo +#+end_src + +#+begin_src text -n -r -k + foo +#+end_src From 87ac18e6b2d69af05c264de58c2036a787940bba Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 19:23:47 -0400 Subject: [PATCH 29/54] Add real handling for preserve indent. Now that I know which flag changes this setting, we can handle it properly. --- src/compare/diff.rs | 26 ++++++++++++++----------- src/parser/lesser_block.rs | 39 +++++++++++++++++++++++++++---------- src/types/lesser_element.rs | 2 ++ 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 451943d..3d8c553 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -1618,11 +1618,14 @@ fn compare_example_block<'b, 's>( }; // Compare preserve-indent - let preserve_indent = get_property_boolean(emacs, ":preserve-indent")?; - if preserve_indent { - // I don't know what :preserve-indent is for, but it seems to always be nil. This is here to alert me to value being non-nil so I can investigate. + let preserve_indent: Option = + get_property_numeric(emacs, ":preserve-indent")?; + if preserve_indent != rust.preserve_indent { this_status = DiffStatus::Bad; - message = Some(format!("Non-nil :preserve-indent {:?}", preserve_indent)); + message = Some(format!( + "Prserve indent mismatch (emacs != rust) {:?} != {:?}", + preserve_indent, rust.preserve_indent + )); } // Compare retain-labels @@ -1731,8 +1734,6 @@ fn compare_src_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; - // TODO: Compare :parameters - // Compare language let language = get_property_quoted_string(emacs, ":language")?; if language.as_ref().map(String::as_str) != rust.language { @@ -1771,7 +1772,7 @@ fn compare_src_block<'b, 's>( } } - // Compare switches + // Compare parameters let parameters = get_property_quoted_string(emacs, ":parameters")?; match (parameters.as_ref().map(String::as_str), rust.parameters) { (None, None) => {} @@ -1830,11 +1831,14 @@ fn compare_src_block<'b, 's>( }; // Compare preserve-indent - let preserve_indent = get_property_boolean(emacs, ":preserve-indent")?; - if preserve_indent { - // I don't know what :preserve-indent is for, but it seems to always be nil. This is here to alert me to value being non-nil so I can investigate. + let preserve_indent: Option = + get_property_numeric(emacs, ":preserve-indent")?; + if preserve_indent != rust.preserve_indent { this_status = DiffStatus::Bad; - message = Some(format!("Non-nil :preserve-indent {:?}", preserve_indent)); + message = Some(format!( + "Prserve indent mismatch (emacs != rust) {:?} != {:?}", + preserve_indent, rust.preserve_indent + )); } // Compare retain-labels diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index a43d8c5..17b239c 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -160,7 +160,7 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?; let source = get_consumed(input, remaining); - let (switches, number_lines, retain_labels, use_labels, label_format) = { + let (switches, number_lines, preserve_indent, retain_labels, use_labels, label_format) = { if let Some(parameters) = parameters { ( if parameters.source.len() == 0 { @@ -169,12 +169,13 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( Some(parameters.source) }, parameters.number_lines, + parameters.preserve_indent, parameters.retain_labels, parameters.use_labels, parameters.label_format, ) } else { - (None, None, RetainLabels::Yes, true, None) + (None, None, None, RetainLabels::Yes, true, None) } }; Ok(( @@ -183,6 +184,7 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( source: source.into(), switches, number_lines, + preserve_indent, retain_labels, use_labels, label_format, @@ -242,8 +244,7 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( let (remaining, language) = opt(map(tuple((space1, switch_word(true))), |(_, language)| { language }))(remaining)?; - let (remaining, switches) = - opt(map(tuple((space1, src_switches)), |(_, switches)| switches))(remaining)?; + let (remaining, switches) = opt(src_switches)(remaining)?; let (remaining, parameters) = opt(map(tuple((space1, src_parameters)), |(_, parameters)| { parameters }))(remaining)?; @@ -264,7 +265,7 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?; let source = get_consumed(input, remaining); - let (switches, number_lines, retain_labels, use_labels, label_format) = { + let (switches, number_lines, preserve_indent, retain_labels, use_labels, label_format) = { if let Some(switches) = switches { ( if switches.source.len() == 0 { @@ -273,12 +274,13 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( Some(switches.source) }, switches.number_lines, + switches.preserve_indent, switches.retain_labels, switches.use_labels, switches.label_format, ) } else { - (None, None, RetainLabels::Yes, true, None) + (None, None, None, RetainLabels::Yes, true, None) } }; Ok(( @@ -289,6 +291,7 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( switches, parameters: parameters.map(Into::<&str>::into), number_lines, + preserve_indent, retain_labels, use_labels, label_format, @@ -361,6 +364,7 @@ struct ExampleSrcSwitches<'s> { source: &'s str, number_lines: Option, retain_labels: RetainLabels, + preserve_indent: Option, use_labels: bool, label_format: Option<&'s str>, } @@ -380,28 +384,35 @@ fn src_parameters<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn src_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSrcSwitches<'s>> { - example_src_switches(true)(input) + let (remaining, leading_spaces) = space1(input)?; + let offset = Into::<&str>::into(leading_spaces).chars().count(); + let offset = CharOffsetInLine::try_from(offset) + .expect("Character offset should fit in CharOffsetInLine"); + example_src_switches(true, offset)(remaining) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn example_switches<'s>(input: OrgSource<'s>) -> Res, ExampleSrcSwitches<'s>> { - let (remaining, switches) = example_src_switches(false)(input)?; + let (remaining, switches) = example_src_switches(false, 0)(input)?; Ok((remaining, switches)) } fn example_src_switches( stop_at_parameters: bool, + additional_char_offset: CharOffsetInLine, ) -> impl for<'s> Fn(OrgSource<'s>) -> Res, ExampleSrcSwitches<'s>> { - move |input| _example_src_switches(input, stop_at_parameters) + move |input| _example_src_switches(input, stop_at_parameters, additional_char_offset) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn _example_src_switches<'s>( input: OrgSource<'s>, stop_at_parameters: bool, + additional_char_offset: CharOffsetInLine, ) -> Res, ExampleSrcSwitches<'s>> { let mut number_lines = None; let mut retain_labels = RetainLabels::Yes; + let mut preserve_indent = None; let mut use_labels = true; let mut label_format = None; let mut saw_r = false; @@ -440,7 +451,14 @@ fn _example_src_switches<'s>( let character_offset = Into::<&str>::into(text_until_flag).chars().count(); let character_offset = CharOffsetInLine::try_from(character_offset) .expect("Character offset should fit in CharOffsetInLine"); - retain_labels = RetainLabels::Keep(character_offset); + retain_labels = RetainLabels::Keep(character_offset + additional_char_offset); + } + (SwitchState::Normal, "-i") => { + let text_until_flag = input.get_until(word); + let character_offset = Into::<&str>::into(text_until_flag).chars().count(); + let character_offset = CharOffsetInLine::try_from(character_offset) + .expect("Character offset should fit in CharOffsetInLine"); + preserve_indent = Some(character_offset + additional_char_offset); } (SwitchState::NewLineNumber, _) => { let val = normalized_word.parse::(); @@ -507,6 +525,7 @@ fn _example_src_switches<'s>( source: Into::<&str>::into(source), number_lines, retain_labels, + preserve_indent, use_labels, label_format, }, diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index 572626e..eed5082 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -50,6 +50,7 @@ pub struct ExampleBlock<'s> { pub source: &'s str, pub switches: Option<&'s str>, pub number_lines: Option, + pub preserve_indent: Option, pub retain_labels: RetainLabels, pub use_labels: bool, pub label_format: Option<&'s str>, @@ -71,6 +72,7 @@ pub struct SrcBlock<'s> { pub switches: Option<&'s str>, pub parameters: Option<&'s str>, pub number_lines: Option, + pub preserve_indent: Option, pub retain_labels: RetainLabels, pub use_labels: bool, pub label_format: Option<&'s str>, From 258e9485de6d32ee9e5c618d8f1c66faaadefe74 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 19:34:10 -0400 Subject: [PATCH 30/54] Add tests for names and references in src and example blocks. --- .../lesser_element/lesser_block/example/name.org | 4 ++++ .../lesser_element/lesser_block/example/reference.org | 6 ++++++ org_mode_samples/lesser_element/lesser_block/src/name.org | 4 ++++ .../lesser_element/lesser_block/src/reference.org | 6 ++++++ src/compare/diff.rs | 2 ++ 5 files changed, 22 insertions(+) create mode 100644 org_mode_samples/lesser_element/lesser_block/example/name.org create mode 100644 org_mode_samples/lesser_element/lesser_block/example/reference.org create mode 100644 org_mode_samples/lesser_element/lesser_block/src/name.org create mode 100644 org_mode_samples/lesser_element/lesser_block/src/reference.org diff --git a/org_mode_samples/lesser_element/lesser_block/example/name.org b/org_mode_samples/lesser_element/lesser_block/example/name.org new file mode 100644 index 0000000..84b09c5 --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/example/name.org @@ -0,0 +1,4 @@ +#+NAME: foo +#+begin_example text +bar +#+end_example diff --git a/org_mode_samples/lesser_element/lesser_block/example/reference.org b/org_mode_samples/lesser_element/lesser_block/example/reference.org new file mode 100644 index 0000000..6043e3d --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/example/reference.org @@ -0,0 +1,6 @@ +#+begin_example text + foo + bar (ref:here) + baz +#+end_example +Link to the reference: [[(here)]] diff --git a/org_mode_samples/lesser_element/lesser_block/src/name.org b/org_mode_samples/lesser_element/lesser_block/src/name.org new file mode 100644 index 0000000..31af9eb --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/src/name.org @@ -0,0 +1,4 @@ +#+NAME: foo +#+begin_src text +bar +#+end_src diff --git a/org_mode_samples/lesser_element/lesser_block/src/reference.org b/org_mode_samples/lesser_element/lesser_block/src/reference.org new file mode 100644 index 0000000..9cde62c --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/src/reference.org @@ -0,0 +1,6 @@ +#+begin_src text + foo + bar (ref:here) + baz +#+end_src +Link to the reference: [[(here)]] diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 3d8c553..f377c89 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -1549,6 +1549,8 @@ fn compare_example_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // TODO: Compare :name + // Compare value let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new()); if contents != rust.contents { From 4fc81e983a1ce0ecdbd110de9094b9876bd92b67 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 19:37:14 -0400 Subject: [PATCH 31/54] Add tests for names for lesser blocks. --- .../lesser_element/lesser_block/comment/name.org | 4 ++++ .../lesser_element/lesser_block/export/name.org | 4 ++++ .../lesser_element/lesser_block/verse/name.org | 4 ++++ src/compare/diff.rs | 8 +++++++- src/parser/lesser_block.rs | 7 +++++-- src/types/lesser_element.rs | 7 +++++-- 6 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 org_mode_samples/lesser_element/lesser_block/comment/name.org create mode 100644 org_mode_samples/lesser_element/lesser_block/export/name.org create mode 100644 org_mode_samples/lesser_element/lesser_block/verse/name.org diff --git a/org_mode_samples/lesser_element/lesser_block/comment/name.org b/org_mode_samples/lesser_element/lesser_block/comment/name.org new file mode 100644 index 0000000..ad25dbd --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/comment/name.org @@ -0,0 +1,4 @@ +#+NAME: foo +#+begin_comment text +bar +#+end_comment diff --git a/org_mode_samples/lesser_element/lesser_block/export/name.org b/org_mode_samples/lesser_element/lesser_block/export/name.org new file mode 100644 index 0000000..cfba5df --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/export/name.org @@ -0,0 +1,4 @@ +#+NAME: foo +#+begin_export text +bar +#+end_export diff --git a/org_mode_samples/lesser_element/lesser_block/verse/name.org b/org_mode_samples/lesser_element/lesser_block/verse/name.org new file mode 100644 index 0000000..94e5877 --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/verse/name.org @@ -0,0 +1,4 @@ +#+NAME: foo +#+begin_verse text +bar +#+end_verse diff --git a/src/compare/diff.rs b/src/compare/diff.rs index f377c89..6883c3b 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -1499,6 +1499,8 @@ fn compare_verse_block<'b, 's>( let this_status = DiffStatus::Good; let message = None; + // TODO: Compare :name + for (_emacs_child, _rust_child) in children.iter().skip(2).zip(rust.children.iter()) {} Ok(DiffResult { @@ -1520,6 +1522,8 @@ fn compare_comment_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // TODO: Compare :name + // Compare value let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new()); if contents != rust.contents { @@ -1715,7 +1719,7 @@ fn compare_export_block<'b, 's>( let this_status = DiffStatus::Good; let message = None; - // TODO: Compare :type :value + // TODO: Compare :type :value :name Ok(DiffResult { status: this_status, @@ -1736,6 +1740,8 @@ fn compare_src_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // TODO: Compare :name + // Compare language let language = get_property_quoted_string(emacs, ":language")?; if language.as_ref().map(String::as_str) != rust.language { diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 17b239c..0aa1487 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -93,7 +93,7 @@ pub(crate) fn verse_block<'b, 'g, 'r, 's>( remaining, VerseBlock { source: source.into(), - name: name.into(), + name: None, // TODO data: parameters.map(|parameters| Into::<&str>::into(parameters)), children, }, @@ -129,6 +129,7 @@ pub(crate) fn comment_block<'b, 'g, 'r, 's>( remaining, CommentBlock { source: source.into(), + name: None, // TODO contents: contents.into(), }, )) @@ -182,6 +183,7 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( remaining, ExampleBlock { source: source.into(), + name: None, // TODO switches, number_lines, preserve_indent, @@ -227,7 +229,7 @@ pub(crate) fn export_block<'b, 'g, 'r, 's>( remaining, ExportBlock { source: source.into(), - name: name.into(), + name: None, // TODO data: parameters.map(|parameters| Into::<&str>::into(parameters)), contents: contents.into(), }, @@ -287,6 +289,7 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( remaining, SrcBlock { source: source.into(), + name: None, // TODO language: language.map(Into::<&str>::into), switches, parameters: parameters.map(Into::<&str>::into), diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index eed5082..92302e3 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -24,7 +24,7 @@ pub struct TableCell<'s> { #[derive(Debug)] pub struct VerseBlock<'s> { pub source: &'s str, - pub name: &'s str, + pub name: Option<&'s str>, pub data: Option<&'s str>, pub children: Vec>, } @@ -32,6 +32,7 @@ pub struct VerseBlock<'s> { #[derive(Debug)] pub struct CommentBlock<'s> { pub source: &'s str, + pub name: Option<&'s str>, pub contents: &'s str, } @@ -48,6 +49,7 @@ pub enum RetainLabels { #[derive(Debug)] pub struct ExampleBlock<'s> { pub source: &'s str, + pub name: Option<&'s str>, pub switches: Option<&'s str>, pub number_lines: Option, pub preserve_indent: Option, @@ -60,7 +62,7 @@ pub struct ExampleBlock<'s> { #[derive(Debug)] pub struct ExportBlock<'s> { pub source: &'s str, - pub name: &'s str, + pub name: Option<&'s str>, pub data: Option<&'s str>, pub contents: &'s str, } @@ -68,6 +70,7 @@ pub struct ExportBlock<'s> { #[derive(Debug)] pub struct SrcBlock<'s> { pub source: &'s str, + pub name: Option<&'s str>, pub language: Option<&'s str>, pub switches: Option<&'s str>, pub parameters: Option<&'s str>, From 057c8a1387758e7e56adae926d53be43d465c82b Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 19:42:14 -0400 Subject: [PATCH 32/54] Compare name. --- src/compare/diff.rs | 60 ++++++++++++++++++++++++++++++++------ src/parser/lesser_block.rs | 10 +++---- 2 files changed, 56 insertions(+), 14 deletions(-) diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 6883c3b..085a804 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -1496,10 +1496,18 @@ fn compare_verse_block<'b, 's>( ) -> Result, Box> { let children = emacs.as_list()?; let child_status = Vec::new(); - let this_status = DiffStatus::Good; - let message = None; + let mut this_status = DiffStatus::Good; + let mut message = None; - // TODO: Compare :name + // Compare name + let name = get_property_quoted_string(emacs, ":name")?; + if name.as_ref().map(String::as_str) != rust.name { + this_status = DiffStatus::Bad; + message = Some(format!( + "Name mismatch (emacs != rust) {:?} != {:?}", + name, rust.name + )); + } for (_emacs_child, _rust_child) in children.iter().skip(2).zip(rust.children.iter()) {} @@ -1522,7 +1530,15 @@ fn compare_comment_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; - // TODO: Compare :name + // Compare name + let name = get_property_quoted_string(emacs, ":name")?; + if name.as_ref().map(String::as_str) != rust.name { + this_status = DiffStatus::Bad; + message = Some(format!( + "Name mismatch (emacs != rust) {:?} != {:?}", + name, rust.name + )); + } // Compare value let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new()); @@ -1553,7 +1569,15 @@ fn compare_example_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; - // TODO: Compare :name + // Compare name + let name = get_property_quoted_string(emacs, ":name")?; + if name.as_ref().map(String::as_str) != rust.name { + this_status = DiffStatus::Bad; + message = Some(format!( + "Name mismatch (emacs != rust) {:?} != {:?}", + name, rust.name + )); + } // Compare value let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new()); @@ -1716,10 +1740,20 @@ fn compare_export_block<'b, 's>( emacs: &'b Token<'s>, rust: &'b ExportBlock<'s>, ) -> Result, Box> { - let this_status = DiffStatus::Good; - let message = None; + let mut this_status = DiffStatus::Good; + let mut message = None; - // TODO: Compare :type :value :name + // TODO: Compare :type :value + + // Compare name + let name = get_property_quoted_string(emacs, ":name")?; + if name.as_ref().map(String::as_str) != rust.name { + this_status = DiffStatus::Bad; + message = Some(format!( + "Name mismatch (emacs != rust) {:?} != {:?}", + name, rust.name + )); + } Ok(DiffResult { status: this_status, @@ -1740,7 +1774,15 @@ fn compare_src_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; - // TODO: Compare :name + // Compare name + let name = get_property_quoted_string(emacs, ":name")?; + if name.as_ref().map(String::as_str) != rust.name { + this_status = DiffStatus::Bad; + message = Some(format!( + "Name mismatch (emacs != rust) {:?} != {:?}", + name, rust.name + )); + } // Compare language let language = get_property_quoted_string(emacs, ":language")?; diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 0aa1487..050dbc9 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -50,7 +50,7 @@ pub(crate) fn verse_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, VerseBlock<'s>> { - let (remaining, name) = lesser_block_begin("verse")(context, input)?; + let (remaining, _) = lesser_block_begin("verse")(context, input)?; let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?; let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; let lesser_block_end_specialized = lesser_block_end("verse"); @@ -105,7 +105,7 @@ pub(crate) fn comment_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, CommentBlock<'s>> { - let (remaining, _name) = lesser_block_begin("comment")(context, input)?; + let (remaining, _) = lesser_block_begin("comment")(context, input)?; let (remaining, _parameters) = opt(tuple((space1, data)))(remaining)?; let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; let lesser_block_end_specialized = lesser_block_end("comment"); @@ -140,7 +140,7 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, ExampleBlock<'s>> { - let (remaining, _name) = lesser_block_begin("example")(context, input)?; + let (remaining, _) = lesser_block_begin("example")(context, input)?; let (remaining, parameters) = opt(tuple((space1, example_switches)))(remaining)?; let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; let lesser_block_end_specialized = lesser_block_end("example"); @@ -200,7 +200,7 @@ pub(crate) fn export_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, ExportBlock<'s>> { - let (remaining, name) = lesser_block_begin("export")(context, input)?; + let (remaining, _) = lesser_block_begin("export")(context, input)?; // https://orgmode.org/worg/org-syntax.html#Blocks claims that export blocks must have a single word for data but testing shows no data and multi-word data still parses as an export block. let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?; let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; @@ -241,7 +241,7 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, SrcBlock<'s>> { - let (remaining, _name) = lesser_block_begin("src")(context, input)?; + let (remaining, _) = lesser_block_begin("src")(context, input)?; // https://orgmode.org/worg/org-syntax.html#Blocks claims that data is mandatory and must follow the LANGUAGE SWITCHES ARGUMENTS pattern but testing has shown that no data and incorrect data here will still parse to a src block. let (remaining, language) = opt(map(tuple((space1, switch_word(true))), |(_, language)| { language From a26640355c140eebad26ee45961b37bd88b60000 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 19:56:39 -0400 Subject: [PATCH 33/54] Add check for name on paragraph. --- org_mode_samples/lesser_element/paragraph/name.org | 2 ++ src/compare/diff.rs | 14 ++++++++++++-- src/parser/element_parser.rs | 10 +++++++++- src/parser/paragraph.rs | 1 + src/types/lesser_element.rs | 2 ++ 5 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 org_mode_samples/lesser_element/paragraph/name.org diff --git a/org_mode_samples/lesser_element/paragraph/name.org b/org_mode_samples/lesser_element/paragraph/name.org new file mode 100644 index 0000000..5cc5a63 --- /dev/null +++ b/org_mode_samples/lesser_element/paragraph/name.org @@ -0,0 +1,2 @@ +#+NAME: foo +bar diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 085a804..f4e82d0 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -841,8 +841,18 @@ fn compare_paragraph<'b, 's>( ) -> Result, Box> { let children = emacs.as_list()?; let mut child_status = Vec::new(); - let this_status = DiffStatus::Good; - let message = None; + let mut this_status = DiffStatus::Good; + let mut message = None; + + // Compare name + let name = get_property_quoted_string(emacs, ":name")?; + if name.as_ref().map(String::as_str) != rust.name { + this_status = DiffStatus::Bad; + message = Some(format!( + "Name mismatch (emacs != rust) {:?} != {:?}", + name, rust.name + )); + } for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) { child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?); diff --git a/src/parser/element_parser.rs b/src/parser/element_parser.rs index 2fd0f6c..5bc4933 100644 --- a/src/parser/element_parser.rs +++ b/src/parser/element_parser.rs @@ -1,6 +1,8 @@ use nom::branch::alt; use nom::combinator::map; +use nom::combinator::not; use nom::multi::many0; +use nom::sequence::tuple; use super::clock::clock; use super::comment::comment; @@ -98,7 +100,13 @@ fn _element<'b, 'g, 'r, 's>( map(horizontal_rule_matcher, Element::HorizontalRule), map(latex_environment_matcher, Element::LatexEnvironment), map(babel_keyword_matcher, Element::BabelCall), - map(keyword_matcher, Element::Keyword), + map( + map( + tuple((not(affiliated_keyword_matcher), keyword_matcher)), + |(_, kw)| kw, + ), + Element::Keyword, + ), ))(remaining) { the_ok @ Ok(_) => the_ok, diff --git a/src/parser/paragraph.rs b/src/parser/paragraph.rs index ba5aeee..049fe78 100644 --- a/src/parser/paragraph.rs +++ b/src/parser/paragraph.rs @@ -47,6 +47,7 @@ pub(crate) fn paragraph<'b, 'g, 'r, 's>( remaining, Paragraph { source: source.into(), + name: None, // TODO children, }, )) diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index 92302e3..759ad99 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -6,6 +6,7 @@ use super::Timestamp; #[derive(Debug)] pub struct Paragraph<'s> { pub source: &'s str, + pub name: Option<&'s str>, pub children: Vec>, } @@ -146,6 +147,7 @@ impl<'s> Paragraph<'s> { objects.push(Object::PlainText(PlainText { source: input })); Paragraph { source: input, + name: None, // TODO children: objects, } } From d8102b7bc24e5b5fe316a835f35fc41017062939 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 20:01:09 -0400 Subject: [PATCH 34/54] Move the affiliated keywords parser inside the specific element parsers. We need access to the affiliated keywords to do things like set the name of the element, and only half the element parsers are allowed to have affiliated keywords, so it makes sense to move it inside the specific parsers. --- .../greater_element/drawer/name.org | 10 + .../greater_element/dynamic_block/name.org | 7 + .../greater_block/center/name.org | 4 + .../greater_block/quote/name.org | 4 + .../greater_block/special/name.org | 6 + .../greater_element/plain_list/name.org | 2 + .../lesser_element/babel_call/name.org | 2 + .../babel_call.org => babel_call/simple.org} | 0 .../lesser_element/clock/name.org | 3 + .../lesser_element/comment/name.org | 2 + .../lesser_element/diary_sexp/name.org | 2 + .../lesser_element/fixed_width_area/name.org | 2 + .../lesser_element/horizontal_rule/name.org | 2 + .../lesser_element/keyword/name.org | 2 + .../lesser_element/latex_environment/name.org | 4 + src/compare/diff.rs | 197 +++++++++++++++--- src/parser/diary_sexp.rs | 8 +- src/parser/drawer.rs | 7 +- src/parser/dynamic_block.rs | 7 +- src/parser/element_parser.rs | 20 +- src/parser/fixed_width_area.rs | 4 + src/parser/footnote_definition.rs | 4 + src/parser/greater_block.rs | 18 +- src/parser/horizontal_rule.rs | 8 +- src/parser/keyword.rs | 13 +- src/parser/latex_environment.rs | 5 + src/parser/paragraph.rs | 5 + src/parser/plain_list.rs | 5 + src/parser/property_drawer.rs | 4 +- src/parser/table.rs | 4 + src/types/greater_element.rs | 16 +- src/types/lesser_element.rs | 6 + 32 files changed, 324 insertions(+), 59 deletions(-) create mode 100644 org_mode_samples/greater_element/drawer/name.org create mode 100644 org_mode_samples/greater_element/dynamic_block/name.org create mode 100644 org_mode_samples/greater_element/greater_block/center/name.org create mode 100644 org_mode_samples/greater_element/greater_block/quote/name.org create mode 100644 org_mode_samples/greater_element/greater_block/special/name.org create mode 100644 org_mode_samples/greater_element/plain_list/name.org create mode 100644 org_mode_samples/lesser_element/babel_call/name.org rename org_mode_samples/lesser_element/{keyword/babel_call.org => babel_call/simple.org} (100%) create mode 100644 org_mode_samples/lesser_element/clock/name.org create mode 100644 org_mode_samples/lesser_element/comment/name.org create mode 100644 org_mode_samples/lesser_element/diary_sexp/name.org create mode 100644 org_mode_samples/lesser_element/fixed_width_area/name.org create mode 100644 org_mode_samples/lesser_element/horizontal_rule/name.org create mode 100644 org_mode_samples/lesser_element/keyword/name.org create mode 100644 org_mode_samples/lesser_element/latex_environment/name.org diff --git a/org_mode_samples/greater_element/drawer/name.org b/org_mode_samples/greater_element/drawer/name.org new file mode 100644 index 0000000..41b7c3d --- /dev/null +++ b/org_mode_samples/greater_element/drawer/name.org @@ -0,0 +1,10 @@ +* Headline +before +#+NAME: foo +:candle: +inside + +the drawer +:end: + +after diff --git a/org_mode_samples/greater_element/dynamic_block/name.org b/org_mode_samples/greater_element/dynamic_block/name.org new file mode 100644 index 0000000..4da2f79 --- /dev/null +++ b/org_mode_samples/greater_element/dynamic_block/name.org @@ -0,0 +1,7 @@ +#+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: diff --git a/org_mode_samples/greater_element/greater_block/center/name.org b/org_mode_samples/greater_element/greater_block/center/name.org new file mode 100644 index 0000000..e0f9c0c --- /dev/null +++ b/org_mode_samples/greater_element/greater_block/center/name.org @@ -0,0 +1,4 @@ +#+NAME: foo +#+begin_center + +#+end_center diff --git a/org_mode_samples/greater_element/greater_block/quote/name.org b/org_mode_samples/greater_element/greater_block/quote/name.org new file mode 100644 index 0000000..fe2fa3a --- /dev/null +++ b/org_mode_samples/greater_element/greater_block/quote/name.org @@ -0,0 +1,4 @@ +#+NAME: foo +#+begin_quote + +#+end_quote diff --git a/org_mode_samples/greater_element/greater_block/special/name.org b/org_mode_samples/greater_element/greater_block/special/name.org new file mode 100644 index 0000000..6b513b1 --- /dev/null +++ b/org_mode_samples/greater_element/greater_block/special/name.org @@ -0,0 +1,6 @@ +#+NAME: foo +#+begin_defun +foo + +{{{bar(baz)}}} +#+end_defun diff --git a/org_mode_samples/greater_element/plain_list/name.org b/org_mode_samples/greater_element/plain_list/name.org new file mode 100644 index 0000000..d80b7ba --- /dev/null +++ b/org_mode_samples/greater_element/plain_list/name.org @@ -0,0 +1,2 @@ +#+NAME: foo +1. bar diff --git a/org_mode_samples/lesser_element/babel_call/name.org b/org_mode_samples/lesser_element/babel_call/name.org new file mode 100644 index 0000000..da7aea9 --- /dev/null +++ b/org_mode_samples/lesser_element/babel_call/name.org @@ -0,0 +1,2 @@ +#+NAME: foo +#+call: foo(bar="baz") diff --git a/org_mode_samples/lesser_element/keyword/babel_call.org b/org_mode_samples/lesser_element/babel_call/simple.org similarity index 100% rename from org_mode_samples/lesser_element/keyword/babel_call.org rename to org_mode_samples/lesser_element/babel_call/simple.org diff --git a/org_mode_samples/lesser_element/clock/name.org b/org_mode_samples/lesser_element/clock/name.org new file mode 100644 index 0000000..c169fba --- /dev/null +++ b/org_mode_samples/lesser_element/clock/name.org @@ -0,0 +1,3 @@ +CLOCK: [2023-04-21 Fri 19:32]--[2023-04-21 Fri 19:35] => 0:03 +#+NAME: foo +CLOCK: [2023-04-21 Fri 19:43] diff --git a/org_mode_samples/lesser_element/comment/name.org b/org_mode_samples/lesser_element/comment/name.org new file mode 100644 index 0000000..815b8d4 --- /dev/null +++ b/org_mode_samples/lesser_element/comment/name.org @@ -0,0 +1,2 @@ +#+NAME: foo +# Comments cannot have affiliated keywords. diff --git a/org_mode_samples/lesser_element/diary_sexp/name.org b/org_mode_samples/lesser_element/diary_sexp/name.org new file mode 100644 index 0000000..34e6155 --- /dev/null +++ b/org_mode_samples/lesser_element/diary_sexp/name.org @@ -0,0 +1,2 @@ +#+NAME: foo +%%(foo) diff --git a/org_mode_samples/lesser_element/fixed_width_area/name.org b/org_mode_samples/lesser_element/fixed_width_area/name.org new file mode 100644 index 0000000..482c0d2 --- /dev/null +++ b/org_mode_samples/lesser_element/fixed_width_area/name.org @@ -0,0 +1,2 @@ +#+NAME: foo +: bar diff --git a/org_mode_samples/lesser_element/horizontal_rule/name.org b/org_mode_samples/lesser_element/horizontal_rule/name.org new file mode 100644 index 0000000..252fc4f --- /dev/null +++ b/org_mode_samples/lesser_element/horizontal_rule/name.org @@ -0,0 +1,2 @@ +#+NAME: foo +----- diff --git a/org_mode_samples/lesser_element/keyword/name.org b/org_mode_samples/lesser_element/keyword/name.org new file mode 100644 index 0000000..51bc2fa --- /dev/null +++ b/org_mode_samples/lesser_element/keyword/name.org @@ -0,0 +1,2 @@ +#+NAME: foo +#+FOO: BAR diff --git a/org_mode_samples/lesser_element/latex_environment/name.org b/org_mode_samples/lesser_element/latex_environment/name.org new file mode 100644 index 0000000..51a6471 --- /dev/null +++ b/org_mode_samples/lesser_element/latex_environment/name.org @@ -0,0 +1,4 @@ +#+NAME: foo +\begin{foo} +bar +\end{foo} diff --git a/src/compare/diff.rs b/src/compare/diff.rs index f4e82d0..a4ba083 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -879,6 +879,16 @@ fn compare_plain_list<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // Compare name + let name = get_property_quoted_string(emacs, ":name")?; + if name.as_ref().map(String::as_str) != rust.name { + this_status = DiffStatus::Bad; + message = Some(format!( + "Name mismatch (emacs != rust) {:?} != {:?}", + name, rust.name + )); + } + // Compare type // :type is an unquoted atom of either descriptive, ordered, or unordered let list_type = get_property_unquoted_atom(emacs, ":type")?; @@ -1037,8 +1047,18 @@ fn compare_center_block<'b, 's>( ) -> Result, Box> { let children = emacs.as_list()?; let mut child_status = Vec::new(); - let this_status = DiffStatus::Good; - let message = None; + let mut this_status = DiffStatus::Good; + let mut message = None; + + // Compare name + let name = get_property_quoted_string(emacs, ":name")?; + if name.as_ref().map(String::as_str) != rust.name { + this_status = DiffStatus::Bad; + message = Some(format!( + "Name mismatch (emacs != rust) {:?} != {:?}", + name, rust.name + )); + } for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) { child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?); @@ -1062,8 +1082,18 @@ fn compare_quote_block<'b, 's>( ) -> Result, Box> { let children = emacs.as_list()?; let mut child_status = Vec::new(); - let this_status = DiffStatus::Good; - let message = None; + let mut this_status = DiffStatus::Good; + let mut message = None; + + // Compare name + let name = get_property_quoted_string(emacs, ":name")?; + if name.as_ref().map(String::as_str) != rust.name { + this_status = DiffStatus::Bad; + message = Some(format!( + "Name mismatch (emacs != rust) {:?} != {:?}", + name, rust.name + )); + } for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) { child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?); @@ -1090,14 +1120,24 @@ fn compare_special_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; - // Compare type - let special_block_type = - get_property_quoted_string(emacs, ":type")?.ok_or("Special blocks should have a name.")?; - if special_block_type != rust.name { + // Compare name + let name = get_property_quoted_string(emacs, ":name")?; + if name.as_ref().map(String::as_str) != rust.name { this_status = DiffStatus::Bad; message = Some(format!( "Name mismatch (emacs != rust) {:?} != {:?}", - special_block_type, rust.name + name, rust.name + )); + } + + // Compare type + let special_block_type = + get_property_quoted_string(emacs, ":type")?.ok_or("Special blocks should have a name.")?; + if special_block_type != rust.block_type { + this_status = DiffStatus::Bad; + message = Some(format!( + "Name mismatch (emacs != rust) {:?} != {:?}", + special_block_type, rust.block_type )); } @@ -1140,14 +1180,24 @@ fn compare_dynamic_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; - // Compare block-name - let block_name = get_property_quoted_string(emacs, ":block-name")? - .ok_or("Dynamic blocks should have a name.")?; - if block_name != rust.name { + // Compare name + let name = get_property_quoted_string(emacs, ":name")?; + if name.as_ref().map(String::as_str) != rust.name { this_status = DiffStatus::Bad; message = Some(format!( "Name mismatch (emacs != rust) {:?} != {:?}", - block_name, rust.name + name, rust.name + )); + } + + // Compare block-name + let block_name = get_property_quoted_string(emacs, ":block-name")? + .ok_or("Dynamic blocks should have a name.")?; + if block_name != rust.block_name { + this_status = DiffStatus::Bad; + message = Some(format!( + "Name mismatch (emacs != rust) {:?} != {:?}", + block_name, rust.block_name )); } @@ -1191,6 +1241,16 @@ fn compare_footnote_definition<'b, 's>( let mut message = None; // TODO: Compare :pre-blank + // Compare name + let name = get_property_quoted_string(emacs, ":name")?; + if name.as_ref().map(String::as_str) != rust.name { + this_status = DiffStatus::Bad; + message = Some(format!( + "Name mismatch (emacs != rust) {:?} != {:?}", + name, rust.name + )); + } + // Compare label let label = get_property_quoted_string(emacs, ":label")? .ok_or("Footnote definitions should have a name.")?; @@ -1259,10 +1319,9 @@ fn compare_drawer<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; - // Compare drawer-name - let name = - get_property_quoted_string(emacs, ":drawer-name")?.ok_or("Drawers should have a name.")?; - if name != rust.name { + // Compare name + let name = get_property_quoted_string(emacs, ":name")?; + if name.as_ref().map(String::as_str) != rust.name { this_status = DiffStatus::Bad; message = Some(format!( "Name mismatch (emacs != rust) {:?} != {:?}", @@ -1270,6 +1329,17 @@ fn compare_drawer<'b, 's>( )); } + // Compare drawer-name + let drawer_name = + get_property_quoted_string(emacs, ":drawer-name")?.ok_or("Drawers should have a name.")?; + if drawer_name != rust.drawer_name { + this_status = DiffStatus::Bad; + message = Some(format!( + "Drawer name mismatch (emacs != rust) {:?} != {:?}", + drawer_name, rust.drawer_name + )); + } + for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) { child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?); } @@ -1322,11 +1392,11 @@ fn compare_node_property<'b, 's>( // Compare key let key = get_property_quoted_string(emacs, ":key")?.ok_or("Node properties should have a key.")?; - if key != rust.name { + if key != rust.property_name { this_status = DiffStatus::Bad; message = Some(format!( "Key mismatch (emacs != rust) {:?} != {:?}", - key, rust.name + key, rust.property_name )); } @@ -1365,6 +1435,16 @@ fn compare_table<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // Compare name + let name = get_property_quoted_string(emacs, ":name")?; + if name.as_ref().map(String::as_str) != rust.name { + this_status = DiffStatus::Bad; + message = Some(format!( + "Name mismatch (emacs != rust) {:?} != {:?}", + name, rust.name + )); + } + // Compare formulas // // :tblfm is either nil or a list () filled with quoted strings containing the value for any tblfm keywords at the end of the table. @@ -2004,11 +2084,21 @@ fn compare_diary_sexp<'b, 's>( emacs: &'b Token<'s>, rust: &'b DiarySexp<'s>, ) -> Result, Box> { - let this_status = DiffStatus::Good; - let message = None; + let mut this_status = DiffStatus::Good; + let mut message = None; // TODO: Compare :value + // Compare name + let name = get_property_quoted_string(emacs, ":name")?; + if name.as_ref().map(String::as_str) != rust.name { + this_status = DiffStatus::Bad; + message = Some(format!( + "Name mismatch (emacs != rust) {:?} != {:?}", + name, rust.name + )); + } + Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), @@ -2097,11 +2187,21 @@ fn compare_fixed_width_area<'b, 's>( rust: &'b FixedWidthArea<'s>, ) -> Result, Box> { let child_status = Vec::new(); - let this_status = DiffStatus::Good; - let message = None; + let mut this_status = DiffStatus::Good; + let mut message = None; // TODO: Compare :value + // Compare name + let name = get_property_quoted_string(emacs, ":name")?; + if name.as_ref().map(String::as_str) != rust.name { + this_status = DiffStatus::Bad; + message = Some(format!( + "Name mismatch (emacs != rust) {:?} != {:?}", + name, rust.name + )); + } + Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), @@ -2119,8 +2219,18 @@ fn compare_horizontal_rule<'b, 's>( rust: &'b HorizontalRule<'s>, ) -> Result, Box> { let child_status = Vec::new(); - let this_status = DiffStatus::Good; - let message = None; + let mut this_status = DiffStatus::Good; + let mut message = None; + + // Compare name + let name = get_property_quoted_string(emacs, ":name")?; + if name.as_ref().map(String::as_str) != rust.name { + this_status = DiffStatus::Bad; + message = Some(format!( + "Name mismatch (emacs != rust) {:?} != {:?}", + name, rust.name + )); + } Ok(DiffResult { status: this_status, @@ -2142,6 +2252,16 @@ fn compare_keyword<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // Compare name + let name = get_property_quoted_string(emacs, ":name")?; + if name.as_ref().map(String::as_str) != rust.name { + this_status = DiffStatus::Bad; + message = Some(format!( + "Name mismatch (emacs != rust) {:?} != {:?}", + name, rust.name + )); + } + let key = unquote( get_property(emacs, ":key")? .ok_or("Emacs keywords should have a :key")? @@ -2188,6 +2308,17 @@ fn compare_babel_call<'b, 's>( let mut message = None; // TODO: Compare :call :inside-header :arguments :end-header + + // Compare name + let name = get_property_quoted_string(emacs, ":name")?; + if name.as_ref().map(String::as_str) != rust.name { + this_status = DiffStatus::Bad; + message = Some(format!( + "Name mismatch (emacs != rust) {:?} != {:?}", + name, rust.name + )); + } + let value = unquote( get_property(emacs, ":value")? .ok_or("Emacs keywords should have a :value")? @@ -2218,11 +2349,21 @@ fn compare_latex_environment<'b, 's>( rust: &'b LatexEnvironment<'s>, ) -> Result, Box> { let child_status = Vec::new(); - let this_status = DiffStatus::Good; - let message = None; + let mut this_status = DiffStatus::Good; + let mut message = None; // TODO: Compare :value + // Compare name + let name = get_property_quoted_string(emacs, ":name")?; + if name.as_ref().map(String::as_str) != rust.name { + this_status = DiffStatus::Bad; + message = Some(format!( + "Name mismatch (emacs != rust) {:?} != {:?}", + name, rust.name + )); + } + Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), diff --git a/src/parser/diary_sexp.rs b/src/parser/diary_sexp.rs index ac1957b..1a82ff0 100644 --- a/src/parser/diary_sexp.rs +++ b/src/parser/diary_sexp.rs @@ -3,9 +3,12 @@ use nom::bytes::complete::is_not; use nom::bytes::complete::tag; use nom::character::complete::line_ending; use nom::combinator::eof; +use nom::multi::many0; use nom::sequence::tuple; +use super::keyword::affiliated_keyword; use super::org_source::OrgSource; +use crate::context::parser_with_context; use crate::context::RefContext; use crate::error::Res; use crate::parser::util::get_consumed; @@ -14,9 +17,11 @@ use crate::types::DiarySexp; #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn diary_sexp<'b, 'g, 'r, 's>( - _context: RefContext<'b, 'g, 'r, 's>, + context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, DiarySexp<'s>> { + let (input, affiliated_keywords) = + many0(parser_with_context!(affiliated_keyword)(context))(input)?; start_of_line(input)?; let (remaining, _clock) = tag("%%(")(input)?; let (remaining, _contents) = is_not("\r\n")(remaining)?; @@ -27,6 +32,7 @@ pub(crate) fn diary_sexp<'b, 'g, 'r, 's>( remaining, DiarySexp { source: source.into(), + name: None, // TODO }, )) } diff --git a/src/parser/drawer.rs b/src/parser/drawer.rs index 0d28e45..630488e 100644 --- a/src/parser/drawer.rs +++ b/src/parser/drawer.rs @@ -7,9 +7,11 @@ use nom::character::complete::space0; use nom::combinator::eof; use nom::combinator::not; use nom::combinator::recognize; +use nom::multi::many0; use nom::multi::many_till; use nom::sequence::tuple; +use super::keyword::affiliated_keyword; use super::org_source::OrgSource; use crate::context::parser_with_context; use crate::context::ContextElement; @@ -41,6 +43,8 @@ pub(crate) fn drawer<'b, 'g, 'r, 's>( "Cannot nest objects of the same element".into(), )))); } + let (input, affiliated_keywords) = + many0(parser_with_context!(affiliated_keyword)(context))(input)?; start_of_line(input)?; let (remaining, _leading_whitespace) = space0(input)?; let (remaining, (_open_colon, drawer_name, _close_colon, _new_line)) = tuple(( @@ -90,7 +94,8 @@ pub(crate) fn drawer<'b, 'g, 'r, 's>( remaining, Drawer { source: source.into(), - name: drawer_name.into(), + name: None, // TODO + drawer_name: drawer_name.into(), children, }, )) diff --git a/src/parser/dynamic_block.rs b/src/parser/dynamic_block.rs index 5368148..1872c0f 100644 --- a/src/parser/dynamic_block.rs +++ b/src/parser/dynamic_block.rs @@ -17,6 +17,7 @@ use nom::multi::many_till; use nom::sequence::preceded; use nom::sequence::tuple; +use super::keyword::affiliated_keyword; use super::org_source::OrgSource; use crate::context::parser_with_context; use crate::context::ContextElement; @@ -47,6 +48,9 @@ pub(crate) fn dynamic_block<'b, 'g, 'r, 's>( "Cannot nest objects of the same element".into(), )))); } + let (input, affiliated_keywords) = + many0(parser_with_context!(affiliated_keyword)(context))(input)?; + start_of_line(input)?; let (remaining, _leading_whitespace) = space0(input)?; let (remaining, (_, name, parameters, _, _)) = tuple(( @@ -97,7 +101,8 @@ pub(crate) fn dynamic_block<'b, 'g, 'r, 's>( remaining, DynamicBlock { source: source.into(), - name: name.into(), + name: None, // TODO + block_name: name.into(), parameters: parameters.map(|val| val.into()), children, }, diff --git a/src/parser/element_parser.rs b/src/parser/element_parser.rs index 5bc4933..5349650 100644 --- a/src/parser/element_parser.rs +++ b/src/parser/element_parser.rs @@ -1,8 +1,5 @@ use nom::branch::alt; use nom::combinator::map; -use nom::combinator::not; -use nom::multi::many0; -use nom::sequence::tuple; use super::clock::clock; use super::comment::comment; @@ -79,8 +76,6 @@ fn _element<'b, 'g, 'r, 's>( let paragraph_matcher = parser_with_context!(paragraph)(context); let latex_environment_matcher = parser_with_context!(latex_environment)(context); - // TODO: Affiliated keywords cannot be on comments, clocks, headings, inlinetasks, items, node properties, planning, property drawers, sections, and table rows - let (remaining, mut affiliated_keywords) = many0(affiliated_keyword_matcher)(input)?; let (remaining, mut element) = match alt(( map(plain_list_matcher, Element::PlainList), greater_block_matcher, @@ -100,28 +95,20 @@ fn _element<'b, 'g, 'r, 's>( map(horizontal_rule_matcher, Element::HorizontalRule), map(latex_environment_matcher, Element::LatexEnvironment), map(babel_keyword_matcher, Element::BabelCall), - map( - map( - tuple((not(affiliated_keyword_matcher), keyword_matcher)), - |(_, kw)| kw, - ), - Element::Keyword, - ), - ))(remaining) + map(keyword_matcher, Element::Keyword), + ))(input) { the_ok @ Ok(_) => the_ok, Err(_) => { if can_be_paragraph { - match map(paragraph_matcher, Element::Paragraph)(remaining) { + match map(paragraph_matcher, Element::Paragraph)(input) { the_ok @ Ok(_) => the_ok, Err(_) => { // TODO: Because this function expects a single element, if there are multiple affiliated keywords before an element that cannot have affiliated keywords, we end up re-parsing the affiliated keywords many times. - affiliated_keywords.clear(); map(affiliated_keyword_matcher, Element::Keyword)(input) } } } else { - affiliated_keywords.clear(); map(affiliated_keyword_matcher, Element::Keyword)(input) } } @@ -149,6 +136,7 @@ fn _detect_element<'b, 'g, 'r, 's>( input: OrgSource<'s>, can_be_paragraph: bool, ) -> Res, ()> { + // TODO: What about affiliated keywords in the detect_* functions? if alt(( parser_with_context!(detect_plain_list)(context), detect_footnote_definition, diff --git a/src/parser/fixed_width_area.rs b/src/parser/fixed_width_area.rs index f501591..0d67770 100644 --- a/src/parser/fixed_width_area.rs +++ b/src/parser/fixed_width_area.rs @@ -10,6 +10,7 @@ use nom::multi::many0; use nom::sequence::preceded; use nom::sequence::tuple; +use super::keyword::affiliated_keyword; use super::org_source::OrgSource; use super::util::only_space1; use super::util::org_line_ending; @@ -26,6 +27,8 @@ pub(crate) fn fixed_width_area<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, FixedWidthArea<'s>> { + let (input, affiliated_keywords) = + many0(parser_with_context!(affiliated_keyword)(context))(input)?; let fixed_width_area_line_matcher = parser_with_context!(fixed_width_area_line)(context); let exit_matcher = parser_with_context!(exit_matcher_parser)(context); let (remaining, _first_line) = fixed_width_area_line_matcher(input)?; @@ -37,6 +40,7 @@ pub(crate) fn fixed_width_area<'b, 'g, 'r, 's>( remaining, FixedWidthArea { source: source.into(), + name: None, // TODO }, )) } diff --git a/src/parser/footnote_definition.rs b/src/parser/footnote_definition.rs index 857c0a0..a7660ae 100644 --- a/src/parser/footnote_definition.rs +++ b/src/parser/footnote_definition.rs @@ -11,6 +11,7 @@ use nom::multi::many1; use nom::multi::many_till; use nom::sequence::tuple; +use super::keyword::affiliated_keyword; use super::org_source::OrgSource; use super::util::include_input; use super::util::WORD_CONSTITUENT_CHARACTERS; @@ -41,6 +42,8 @@ pub(crate) fn footnote_definition<'b, 'g, 'r, 's>( "Cannot nest objects of the same element".into(), )))); } + let (input, affiliated_keywords) = + many0(parser_with_context!(affiliated_keyword)(context))(input)?; start_of_line(input)?; // Cannot be indented. let (remaining, (_, lbl, _, _, _)) = tuple(( @@ -85,6 +88,7 @@ pub(crate) fn footnote_definition<'b, 'g, 'r, 's>( remaining, FootnoteDefinition { source: source.into(), + name: None, // TODO label: lbl.into(), children: children.into_iter().map(|(_, item)| item).collect(), }, diff --git a/src/parser/greater_block.rs b/src/parser/greater_block.rs index d61fb21..47c0bf7 100644 --- a/src/parser/greater_block.rs +++ b/src/parser/greater_block.rs @@ -17,6 +17,7 @@ use nom::multi::many_till; use nom::sequence::preceded; use nom::sequence::tuple; +use super::keyword::affiliated_keyword; use super::org_source::OrgSource; use super::util::in_section; use crate::context::parser_with_context; @@ -45,6 +46,8 @@ pub(crate) fn greater_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, Element<'s>> { + let (input, affiliated_keywords) = + many0(parser_with_context!(affiliated_keyword)(context))(input)?; start_of_line(input)?; let (remaining, _leading_whitespace) = space0(input)?; let (remaining, (_begin, name)) = tuple(( @@ -75,7 +78,11 @@ fn center_block<'b, 'g, 'r, 's>( greater_block_body(context, input, original_input, "center", "center block")?; Ok(( remaining, - Element::CenterBlock(CenterBlock { source, children }), + Element::CenterBlock(CenterBlock { + source, + name: None, // TODO + children, + }), )) } @@ -89,7 +96,11 @@ fn quote_block<'b, 'g, 'r, 's>( greater_block_body(context, input, original_input, "quote", "quote block")?; Ok(( remaining, - Element::QuoteBlock(QuoteBlock { source, children }), + Element::QuoteBlock(QuoteBlock { + source, + name: None, // TODO + children, + }), )) } @@ -122,8 +133,9 @@ fn _special_block<'c, 'b, 'g, 'r, 's>( remaining, Element::SpecialBlock(SpecialBlock { source, + name: None, // TODO children, - name, + block_type: name, parameters: parameters.map(|(_, parameters)| Into::<&str>::into(parameters)), }), )) diff --git a/src/parser/horizontal_rule.rs b/src/parser/horizontal_rule.rs index 9a7413a..a471dc2 100644 --- a/src/parser/horizontal_rule.rs +++ b/src/parser/horizontal_rule.rs @@ -5,10 +5,13 @@ use nom::character::complete::space0; use nom::combinator::eof; use nom::combinator::recognize; use nom::combinator::verify; +use nom::multi::many0; use nom::multi::many1_count; use nom::sequence::tuple; +use super::keyword::affiliated_keyword; use super::org_source::OrgSource; +use crate::context::parser_with_context; use crate::context::RefContext; use crate::error::Res; use crate::parser::util::start_of_line; @@ -16,9 +19,11 @@ use crate::types::HorizontalRule; #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn horizontal_rule<'b, 'g, 'r, 's>( - _context: RefContext<'b, 'g, 'r, 's>, + context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, HorizontalRule<'s>> { + let (input, affiliated_keywords) = + many0(parser_with_context!(affiliated_keyword)(context))(input)?; start_of_line(input)?; let (remaining, rule) = recognize(tuple(( space0, @@ -30,6 +35,7 @@ pub(crate) fn horizontal_rule<'b, 'g, 'r, 's>( remaining, HorizontalRule { source: rule.into(), + name: None, // TODO }, )) } diff --git a/src/parser/keyword.rs b/src/parser/keyword.rs index 9253590..4026ab3 100644 --- a/src/parser/keyword.rs +++ b/src/parser/keyword.rs @@ -13,11 +13,13 @@ use nom::combinator::not; use nom::combinator::peek; use nom::combinator::recognize; use nom::combinator::verify; +use nom::multi::many0; use nom::multi::many_till; use nom::sequence::tuple; use super::org_source::BracketDepth; use super::org_source::OrgSource; +use crate::context::parser_with_context; use crate::context::Matcher; use crate::context::RefContext; use crate::error::CustomError; @@ -61,6 +63,7 @@ fn _filtered_keyword<'s, F: Matcher>( remaining, Keyword { source: consumed_input.into(), + name: None, // TODO key: parsed_key.into(), value: "".into(), }, @@ -78,6 +81,7 @@ fn _filtered_keyword<'s, F: Matcher>( remaining, Keyword { source: consumed_input.into(), + name: None, // TODO key: parsed_key.into(), value: parsed_value.into(), }, @@ -86,9 +90,11 @@ fn _filtered_keyword<'s, F: Matcher>( #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn keyword<'b, 'g, 'r, 's>( - _context: RefContext<'b, 'g, 'r, 's>, + context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, Keyword<'s>> { + let (input, affiliated_keywords) = + many0(parser_with_context!(affiliated_keyword)(context))(input)?; filtered_keyword(regular_keyword_key)(input) } @@ -102,14 +108,17 @@ pub(crate) fn affiliated_keyword<'b, 'g, 'r, 's>( #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn babel_call_keyword<'b, 'g, 'r, 's>( - _context: RefContext<'b, 'g, 'r, 's>, + context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, BabelCall<'s>> { + let (input, affiliated_keywords) = + many0(parser_with_context!(affiliated_keyword)(context))(input)?; let (remaining, kw) = filtered_keyword(babel_call_key)(input)?; Ok(( remaining, BabelCall { source: kw.source, + name: None, // TODO key: kw.key, value: kw.value, }, diff --git a/src/parser/latex_environment.rs b/src/parser/latex_environment.rs index 9635ad8..97469a3 100644 --- a/src/parser/latex_environment.rs +++ b/src/parser/latex_environment.rs @@ -8,9 +8,11 @@ use nom::character::complete::space0; use nom::combinator::eof; use nom::combinator::peek; use nom::combinator::recognize; +use nom::multi::many0; use nom::multi::many_till; use nom::sequence::tuple; +use super::keyword::affiliated_keyword; use super::org_source::OrgSource; use super::util::get_consumed; use crate::context::parser_with_context; @@ -29,6 +31,8 @@ pub(crate) fn latex_environment<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, LatexEnvironment<'s>> { + let (input, affiliated_keywords) = + many0(parser_with_context!(affiliated_keyword)(context))(input)?; start_of_line(input)?; let (remaining, _leading_whitespace) = space0(input)?; let (remaining, (_opening, name, _open_close_brace, _ws, _line_ending)) = tuple(( @@ -54,6 +58,7 @@ pub(crate) fn latex_environment<'b, 'g, 'r, 's>( remaining, LatexEnvironment { source: source.into(), + name: None, // TODO }, )) } diff --git a/src/parser/paragraph.rs b/src/parser/paragraph.rs index 049fe78..591e4db 100644 --- a/src/parser/paragraph.rs +++ b/src/parser/paragraph.rs @@ -2,11 +2,13 @@ use nom::branch::alt; use nom::combinator::eof; use nom::combinator::recognize; use nom::combinator::verify; +use nom::multi::many0; use nom::multi::many1; use nom::multi::many_till; use nom::sequence::tuple; use super::element_parser::detect_element; +use super::keyword::affiliated_keyword; use super::org_source::OrgSource; use super::util::blank_line; use super::util::get_consumed; @@ -26,6 +28,9 @@ pub(crate) fn paragraph<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, Paragraph<'s>> { + let (input, affiliated_keywords) = + many0(parser_with_context!(affiliated_keyword)(context))(input)?; + let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode { class: ExitClass::Gamma, exit_matcher: ¶graph_end, diff --git a/src/parser/plain_list.rs b/src/parser/plain_list.rs index 61f8b6b..164ed9c 100644 --- a/src/parser/plain_list.rs +++ b/src/parser/plain_list.rs @@ -19,6 +19,7 @@ use nom::multi::many_till; use nom::sequence::tuple; use super::element_parser::element; +use super::keyword::affiliated_keyword; use super::object_parser::standard_set_object; use super::org_source::OrgSource; use super::util::include_input; @@ -78,6 +79,9 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, PlainList<'s>> { + let (input, affiliated_keywords) = + many0(parser_with_context!(affiliated_keyword)(context))(input)?; + let contexts = [ ContextElement::Context("plain list"), ContextElement::ConsumeTrailingWhitespace(true), @@ -150,6 +154,7 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>( remaining, PlainList { source: source.into(), + name: None, // TODO list_type: first_item_list_type.expect("Plain lists require at least one element."), children: children.into_iter().map(|(_start, item)| item).collect(), }, diff --git a/src/parser/property_drawer.rs b/src/parser/property_drawer.rs index 06a4ac5..99f0177 100644 --- a/src/parser/property_drawer.rs +++ b/src/parser/property_drawer.rs @@ -120,7 +120,7 @@ fn node_property<'b, 'g, 'r, 's>( remaining, NodeProperty { source: source.into(), - name: Into::<&str>::into(name), + property_name: Into::<&str>::into(name), value: None, }, )) @@ -133,7 +133,7 @@ fn node_property<'b, 'g, 'r, 's>( remaining, NodeProperty { source: source.into(), - name: Into::<&str>::into(name), + property_name: Into::<&str>::into(name), value: Some(value.into()), }, )) diff --git a/src/parser/table.rs b/src/parser/table.rs index 1d27ed6..e9445b7 100644 --- a/src/parser/table.rs +++ b/src/parser/table.rs @@ -13,6 +13,7 @@ use nom::multi::many1; use nom::multi::many_till; use nom::sequence::tuple; +use super::keyword::affiliated_keyword; use super::keyword::table_formula_keyword; use super::object_parser::table_cell_set_object; use super::org_source::OrgSource; @@ -38,6 +39,8 @@ pub(crate) fn org_mode_table<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, Table<'s>> { + let (input, affiliated_keywords) = + many0(parser_with_context!(affiliated_keyword)(context))(input)?; start_of_line(input)?; peek(tuple((space0, tag("|"))))(input)?; @@ -68,6 +71,7 @@ pub(crate) fn org_mode_table<'b, 'g, 'r, 's>( remaining, Table { source: source.into(), + name: None, // TODO formulas, children, }, diff --git a/src/types/greater_element.rs b/src/types/greater_element.rs index 17d7cd2..7f2bede 100644 --- a/src/types/greater_element.rs +++ b/src/types/greater_element.rs @@ -7,6 +7,7 @@ use super::StandardProperties; #[derive(Debug)] pub struct PlainList<'s> { pub source: &'s str, + pub name: Option<&'s str>, pub list_type: PlainListType, pub children: Vec>, } @@ -46,19 +47,22 @@ pub enum CheckboxType { #[derive(Debug)] pub struct CenterBlock<'s> { pub source: &'s str, + pub name: Option<&'s str>, pub children: Vec>, } #[derive(Debug)] pub struct QuoteBlock<'s> { pub source: &'s str, + pub name: Option<&'s str>, pub children: Vec>, } #[derive(Debug)] pub struct SpecialBlock<'s> { pub source: &'s str, - pub name: &'s str, + pub name: Option<&'s str>, + pub block_type: &'s str, pub parameters: Option<&'s str>, pub children: Vec>, } @@ -66,7 +70,8 @@ pub struct SpecialBlock<'s> { #[derive(Debug)] pub struct DynamicBlock<'s> { pub source: &'s str, - pub name: &'s str, + pub name: Option<&'s str>, + pub block_name: &'s str, pub parameters: Option<&'s str>, pub children: Vec>, } @@ -74,6 +79,7 @@ pub struct DynamicBlock<'s> { #[derive(Debug)] pub struct FootnoteDefinition<'s> { pub source: &'s str, + pub name: Option<&'s str>, pub label: &'s str, pub children: Vec>, } @@ -81,7 +87,8 @@ pub struct FootnoteDefinition<'s> { #[derive(Debug)] pub struct Drawer<'s> { pub source: &'s str, - pub name: &'s str, + pub name: Option<&'s str>, + pub drawer_name: &'s str, pub children: Vec>, } @@ -94,13 +101,14 @@ pub struct PropertyDrawer<'s> { #[derive(Debug)] pub struct NodeProperty<'s> { pub source: &'s str, - pub name: &'s str, + pub property_name: &'s str, pub value: Option<&'s str>, } #[derive(Debug)] pub struct Table<'s> { pub source: &'s str, + pub name: Option<&'s str>, pub formulas: Vec>, pub children: Vec>, } diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index 759ad99..a7cfa4d 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -91,6 +91,7 @@ pub struct Clock<'s> { #[derive(Debug)] pub struct DiarySexp<'s> { pub source: &'s str, + pub name: Option<&'s str>, } #[derive(Debug)] @@ -104,16 +105,19 @@ pub struct Planning<'s> { #[derive(Debug)] pub struct FixedWidthArea<'s> { pub source: &'s str, + pub name: Option<&'s str>, } #[derive(Debug)] pub struct HorizontalRule<'s> { pub source: &'s str, + pub name: Option<&'s str>, } #[derive(Debug)] pub struct Keyword<'s> { pub source: &'s str, + pub name: Option<&'s str>, pub key: &'s str, pub value: &'s str, } @@ -121,6 +125,7 @@ pub struct Keyword<'s> { #[derive(Debug)] pub struct BabelCall<'s> { pub source: &'s str, + pub name: Option<&'s str>, pub key: &'s str, pub value: &'s str, } @@ -128,6 +133,7 @@ pub struct BabelCall<'s> { #[derive(Debug)] pub struct LatexEnvironment<'s> { pub source: &'s str, + pub name: Option<&'s str>, } /// A line number used in switches to lesser blocks. From 786521ad4ab25a8b439cb6f07214ccde82e7143d Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 21:03:32 -0400 Subject: [PATCH 35/54] Add affiliated keyword matching to the detect_* functions. --- src/parser/diary_sexp.rs | 4 ++-- src/parser/drawer.rs | 3 +-- src/parser/dynamic_block.rs | 3 +-- src/parser/element_parser.rs | 5 ++--- src/parser/fixed_width_area.rs | 4 ++-- src/parser/footnote_definition.rs | 4 ++-- src/parser/greater_block.rs | 3 +-- src/parser/horizontal_rule.rs | 3 +-- src/parser/keyword.rs | 11 +++-------- src/parser/latex_environment.rs | 3 +-- src/parser/paragraph.rs | 3 +-- src/parser/plain_list.rs | 4 ++-- src/parser/table.rs | 4 ++-- 13 files changed, 21 insertions(+), 33 deletions(-) diff --git a/src/parser/diary_sexp.rs b/src/parser/diary_sexp.rs index 1a82ff0..5edaad9 100644 --- a/src/parser/diary_sexp.rs +++ b/src/parser/diary_sexp.rs @@ -20,8 +20,7 @@ pub(crate) fn diary_sexp<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, DiarySexp<'s>> { - let (input, affiliated_keywords) = - many0(parser_with_context!(affiliated_keyword)(context))(input)?; + let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; start_of_line(input)?; let (remaining, _clock) = tag("%%(")(input)?; let (remaining, _contents) = is_not("\r\n")(remaining)?; @@ -39,6 +38,7 @@ pub(crate) fn diary_sexp<'b, 'g, 'r, 's>( #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn detect_diary_sexp<'s>(input: OrgSource<'s>) -> Res, ()> { + let (input, _) = many0(affiliated_keyword)(input)?; tuple((start_of_line, tag("%%(")))(input)?; Ok((input, ())) } diff --git a/src/parser/drawer.rs b/src/parser/drawer.rs index 630488e..7c30357 100644 --- a/src/parser/drawer.rs +++ b/src/parser/drawer.rs @@ -43,8 +43,7 @@ pub(crate) fn drawer<'b, 'g, 'r, 's>( "Cannot nest objects of the same element".into(), )))); } - let (input, affiliated_keywords) = - many0(parser_with_context!(affiliated_keyword)(context))(input)?; + let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; start_of_line(input)?; let (remaining, _leading_whitespace) = space0(input)?; let (remaining, (_open_colon, drawer_name, _close_colon, _new_line)) = tuple(( diff --git a/src/parser/dynamic_block.rs b/src/parser/dynamic_block.rs index 1872c0f..85001d5 100644 --- a/src/parser/dynamic_block.rs +++ b/src/parser/dynamic_block.rs @@ -48,8 +48,7 @@ pub(crate) fn dynamic_block<'b, 'g, 'r, 's>( "Cannot nest objects of the same element".into(), )))); } - let (input, affiliated_keywords) = - many0(parser_with_context!(affiliated_keyword)(context))(input)?; + let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; start_of_line(input)?; let (remaining, _leading_whitespace) = space0(input)?; diff --git a/src/parser/element_parser.rs b/src/parser/element_parser.rs index 5349650..9fe87a1 100644 --- a/src/parser/element_parser.rs +++ b/src/parser/element_parser.rs @@ -71,7 +71,6 @@ fn _element<'b, 'g, 'r, 's>( let fixed_width_area_matcher = parser_with_context!(fixed_width_area)(context); let horizontal_rule_matcher = parser_with_context!(horizontal_rule)(context); let keyword_matcher = parser_with_context!(keyword)(context); - let affiliated_keyword_matcher = parser_with_context!(affiliated_keyword)(context); let babel_keyword_matcher = parser_with_context!(babel_call_keyword)(context); let paragraph_matcher = parser_with_context!(paragraph)(context); let latex_environment_matcher = parser_with_context!(latex_environment)(context); @@ -105,11 +104,11 @@ fn _element<'b, 'g, 'r, 's>( the_ok @ Ok(_) => the_ok, Err(_) => { // TODO: Because this function expects a single element, if there are multiple affiliated keywords before an element that cannot have affiliated keywords, we end up re-parsing the affiliated keywords many times. - map(affiliated_keyword_matcher, Element::Keyword)(input) + map(affiliated_keyword, Element::Keyword)(input) } } } else { - map(affiliated_keyword_matcher, Element::Keyword)(input) + map(affiliated_keyword, Element::Keyword)(input) } } }?; diff --git a/src/parser/fixed_width_area.rs b/src/parser/fixed_width_area.rs index 0d67770..1cf0a74 100644 --- a/src/parser/fixed_width_area.rs +++ b/src/parser/fixed_width_area.rs @@ -27,8 +27,7 @@ pub(crate) fn fixed_width_area<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, FixedWidthArea<'s>> { - let (input, affiliated_keywords) = - many0(parser_with_context!(affiliated_keyword)(context))(input)?; + let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; let fixed_width_area_line_matcher = parser_with_context!(fixed_width_area_line)(context); let exit_matcher = parser_with_context!(exit_matcher_parser)(context); let (remaining, _first_line) = fixed_width_area_line_matcher(input)?; @@ -63,6 +62,7 @@ fn fixed_width_area_line<'b, 'g, 'r, 's>( #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn detect_fixed_width_area<'s>(input: OrgSource<'s>) -> Res, ()> { + let (input, _) = many0(affiliated_keyword)(input)?; tuple(( start_of_line, space0, diff --git a/src/parser/footnote_definition.rs b/src/parser/footnote_definition.rs index a7660ae..937d920 100644 --- a/src/parser/footnote_definition.rs +++ b/src/parser/footnote_definition.rs @@ -42,8 +42,7 @@ pub(crate) fn footnote_definition<'b, 'g, 'r, 's>( "Cannot nest objects of the same element".into(), )))); } - let (input, affiliated_keywords) = - many0(parser_with_context!(affiliated_keyword)(context))(input)?; + let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; start_of_line(input)?; // Cannot be indented. let (remaining, (_, lbl, _, _, _)) = tuple(( @@ -123,6 +122,7 @@ fn footnote_definition_end<'b, 'g, 'r, 's>( #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn detect_footnote_definition<'s>(input: OrgSource<'s>) -> Res, ()> { + let (input, _) = many0(affiliated_keyword)(input)?; tuple((start_of_line, tag_no_case("[fn:"), label, tag("]")))(input)?; Ok((input, ())) } diff --git a/src/parser/greater_block.rs b/src/parser/greater_block.rs index 47c0bf7..15e7637 100644 --- a/src/parser/greater_block.rs +++ b/src/parser/greater_block.rs @@ -46,8 +46,7 @@ pub(crate) fn greater_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, Element<'s>> { - let (input, affiliated_keywords) = - many0(parser_with_context!(affiliated_keyword)(context))(input)?; + let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; start_of_line(input)?; let (remaining, _leading_whitespace) = space0(input)?; let (remaining, (_begin, name)) = tuple(( diff --git a/src/parser/horizontal_rule.rs b/src/parser/horizontal_rule.rs index a471dc2..f19c56e 100644 --- a/src/parser/horizontal_rule.rs +++ b/src/parser/horizontal_rule.rs @@ -22,8 +22,7 @@ pub(crate) fn horizontal_rule<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, HorizontalRule<'s>> { - let (input, affiliated_keywords) = - many0(parser_with_context!(affiliated_keyword)(context))(input)?; + let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; start_of_line(input)?; let (remaining, rule) = recognize(tuple(( space0, diff --git a/src/parser/keyword.rs b/src/parser/keyword.rs index 4026ab3..4581d1a 100644 --- a/src/parser/keyword.rs +++ b/src/parser/keyword.rs @@ -93,16 +93,12 @@ pub(crate) fn keyword<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, Keyword<'s>> { - let (input, affiliated_keywords) = - many0(parser_with_context!(affiliated_keyword)(context))(input)?; + let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; filtered_keyword(regular_keyword_key)(input) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -pub(crate) fn affiliated_keyword<'b, 'g, 'r, 's>( - _context: RefContext<'b, 'g, 'r, 's>, - input: OrgSource<'s>, -) -> Res, Keyword<'s>> { +pub(crate) fn affiliated_keyword<'s>(input: OrgSource<'s>) -> Res, Keyword<'s>> { filtered_keyword(affiliated_key)(input) } @@ -111,8 +107,7 @@ pub(crate) fn babel_call_keyword<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, BabelCall<'s>> { - let (input, affiliated_keywords) = - many0(parser_with_context!(affiliated_keyword)(context))(input)?; + let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; let (remaining, kw) = filtered_keyword(babel_call_key)(input)?; Ok(( remaining, diff --git a/src/parser/latex_environment.rs b/src/parser/latex_environment.rs index 97469a3..7e64567 100644 --- a/src/parser/latex_environment.rs +++ b/src/parser/latex_environment.rs @@ -31,8 +31,7 @@ pub(crate) fn latex_environment<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, LatexEnvironment<'s>> { - let (input, affiliated_keywords) = - many0(parser_with_context!(affiliated_keyword)(context))(input)?; + let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; start_of_line(input)?; let (remaining, _leading_whitespace) = space0(input)?; let (remaining, (_opening, name, _open_close_brace, _ws, _line_ending)) = tuple(( diff --git a/src/parser/paragraph.rs b/src/parser/paragraph.rs index 591e4db..2576e35 100644 --- a/src/parser/paragraph.rs +++ b/src/parser/paragraph.rs @@ -28,8 +28,7 @@ pub(crate) fn paragraph<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, Paragraph<'s>> { - let (input, affiliated_keywords) = - many0(parser_with_context!(affiliated_keyword)(context))(input)?; + let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode { class: ExitClass::Gamma, diff --git a/src/parser/plain_list.rs b/src/parser/plain_list.rs index 164ed9c..92e6007 100644 --- a/src/parser/plain_list.rs +++ b/src/parser/plain_list.rs @@ -54,6 +54,7 @@ pub(crate) fn detect_plain_list<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, ()> { + let (input, _) = many0(affiliated_keyword)(input)?; if verify( tuple(( start_of_line, @@ -79,8 +80,7 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, PlainList<'s>> { - let (input, affiliated_keywords) = - many0(parser_with_context!(affiliated_keyword)(context))(input)?; + let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; let contexts = [ ContextElement::Context("plain list"), diff --git a/src/parser/table.rs b/src/parser/table.rs index e9445b7..e1de64c 100644 --- a/src/parser/table.rs +++ b/src/parser/table.rs @@ -39,8 +39,7 @@ pub(crate) fn org_mode_table<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, Table<'s>> { - let (input, affiliated_keywords) = - many0(parser_with_context!(affiliated_keyword)(context))(input)?; + let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; start_of_line(input)?; peek(tuple((space0, tag("|"))))(input)?; @@ -80,6 +79,7 @@ pub(crate) fn org_mode_table<'b, 'g, 'r, 's>( #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn detect_table<'s>(input: OrgSource<'s>) -> Res, ()> { + let (input, _) = many0(affiliated_keyword)(input)?; tuple((start_of_line, space0, tag("|")))(input)?; Ok((input, ())) } From ab4a0c1224579214eff01948b9cc8272a038e6ad Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 21:05:20 -0400 Subject: [PATCH 36/54] Clean up. --- src/parser/diary_sexp.rs | 3 +-- src/parser/horizontal_rule.rs | 3 +-- src/parser/keyword.rs | 5 ++--- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/parser/diary_sexp.rs b/src/parser/diary_sexp.rs index 5edaad9..774ac01 100644 --- a/src/parser/diary_sexp.rs +++ b/src/parser/diary_sexp.rs @@ -8,7 +8,6 @@ use nom::sequence::tuple; use super::keyword::affiliated_keyword; use super::org_source::OrgSource; -use crate::context::parser_with_context; use crate::context::RefContext; use crate::error::Res; use crate::parser::util::get_consumed; @@ -17,7 +16,7 @@ use crate::types::DiarySexp; #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn diary_sexp<'b, 'g, 'r, 's>( - context: RefContext<'b, 'g, 'r, 's>, + _context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, DiarySexp<'s>> { let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; diff --git a/src/parser/horizontal_rule.rs b/src/parser/horizontal_rule.rs index f19c56e..d90923d 100644 --- a/src/parser/horizontal_rule.rs +++ b/src/parser/horizontal_rule.rs @@ -11,7 +11,6 @@ use nom::sequence::tuple; use super::keyword::affiliated_keyword; use super::org_source::OrgSource; -use crate::context::parser_with_context; use crate::context::RefContext; use crate::error::Res; use crate::parser::util::start_of_line; @@ -19,7 +18,7 @@ use crate::types::HorizontalRule; #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn horizontal_rule<'b, 'g, 'r, 's>( - context: RefContext<'b, 'g, 'r, 's>, + _context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, HorizontalRule<'s>> { let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; diff --git a/src/parser/keyword.rs b/src/parser/keyword.rs index 4581d1a..5ea956d 100644 --- a/src/parser/keyword.rs +++ b/src/parser/keyword.rs @@ -19,7 +19,6 @@ use nom::sequence::tuple; use super::org_source::BracketDepth; use super::org_source::OrgSource; -use crate::context::parser_with_context; use crate::context::Matcher; use crate::context::RefContext; use crate::error::CustomError; @@ -90,7 +89,7 @@ fn _filtered_keyword<'s, F: Matcher>( #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn keyword<'b, 'g, 'r, 's>( - context: RefContext<'b, 'g, 'r, 's>, + _context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, Keyword<'s>> { let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; @@ -104,7 +103,7 @@ pub(crate) fn affiliated_keyword<'s>(input: OrgSource<'s>) -> Res, #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn babel_call_keyword<'b, 'g, 'r, 's>( - context: RefContext<'b, 'g, 'r, 's>, + _context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, BabelCall<'s>> { let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; From 5b308ea76f06218ff3bd1cb835b05cb1103d72f0 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 21:12:06 -0400 Subject: [PATCH 37/54] Implement a function to read the name from the affiliated keywords. --- .../affiliated_keyword/case_insensitive.org | 8 ++++++++ src/parser/util.rs | 12 ++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 org_mode_samples/affiliated_keyword/case_insensitive.org diff --git a/org_mode_samples/affiliated_keyword/case_insensitive.org b/org_mode_samples/affiliated_keyword/case_insensitive.org new file mode 100644 index 0000000..33ea9e2 --- /dev/null +++ b/org_mode_samples/affiliated_keyword/case_insensitive.org @@ -0,0 +1,8 @@ +#+NAME: foo +bar + +#+NaMe: baz +cat + +#+name: lorem +ipsum diff --git a/src/parser/util.rs b/src/parser/util.rs index 7c5d733..8f63eaf 100644 --- a/src/parser/util.rs +++ b/src/parser/util.rs @@ -22,6 +22,7 @@ use crate::error::CustomError; use crate::error::MyError; use crate::error::Res; use crate::types::IndentationLevel; +use crate::types::Keyword; pub(crate) const WORD_CONSTITUENT_CHARACTERS: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; @@ -275,3 +276,14 @@ pub(crate) fn indentation_level<'b, 'g, 'r, 's>( .sum(); Ok((remaining, (indentation_level, leading_whitespace))) } + +pub(crate) fn get_name<'s>(affiliated_keywords: &Vec>) -> Option<&'s str> { + let name_keyword = affiliated_keywords + .iter() + .filter(|kw| match kw.name { + Some(name) if name.eq_ignore_ascii_case("name") => true, + _ => false, + }) + .last(); + name_keyword.map(|kw| kw.value) +} From 93fe46e4e7da8e09c2fadb90ccca72f8addb969c Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 21:21:26 -0400 Subject: [PATCH 38/54] Populate the name field on elements. --- .../center/empty_with_name_inside.org | 8 +++++ src/parser/diary_sexp.rs | 3 +- src/parser/drawer.rs | 3 +- src/parser/dynamic_block.rs | 3 +- src/parser/fixed_width_area.rs | 3 +- src/parser/footnote_definition.rs | 3 +- src/parser/greater_block.rs | 29 ++++++++++++++----- src/parser/horizontal_rule.rs | 3 +- src/parser/keyword.rs | 11 ++++--- src/parser/latex_environment.rs | 3 +- src/parser/lesser_block.rs | 18 ++++++++---- src/parser/paragraph.rs | 3 +- src/parser/plain_list.rs | 3 +- src/parser/table.rs | 3 +- src/types/lesser_element.rs | 5 +++- 15 files changed, 73 insertions(+), 28 deletions(-) create mode 100644 org_mode_samples/greater_element/greater_block/center/empty_with_name_inside.org diff --git a/org_mode_samples/greater_element/greater_block/center/empty_with_name_inside.org b/org_mode_samples/greater_element/greater_block/center/empty_with_name_inside.org new file mode 100644 index 0000000..50f1988 --- /dev/null +++ b/org_mode_samples/greater_element/greater_block/center/empty_with_name_inside.org @@ -0,0 +1,8 @@ +#+begin_center + +#+end_center + +#+begin_center +#+NAME: foo + +#+end_center diff --git a/src/parser/diary_sexp.rs b/src/parser/diary_sexp.rs index 774ac01..5c506f5 100644 --- a/src/parser/diary_sexp.rs +++ b/src/parser/diary_sexp.rs @@ -8,6 +8,7 @@ use nom::sequence::tuple; use super::keyword::affiliated_keyword; use super::org_source::OrgSource; +use super::util::get_name; use crate::context::RefContext; use crate::error::Res; use crate::parser::util::get_consumed; @@ -30,7 +31,7 @@ pub(crate) fn diary_sexp<'b, 'g, 'r, 's>( remaining, DiarySexp { source: source.into(), - name: None, // TODO + name: get_name(&affiliated_keywords), }, )) } diff --git a/src/parser/drawer.rs b/src/parser/drawer.rs index 7c30357..c78f80d 100644 --- a/src/parser/drawer.rs +++ b/src/parser/drawer.rs @@ -13,6 +13,7 @@ use nom::sequence::tuple; use super::keyword::affiliated_keyword; use super::org_source::OrgSource; +use super::util::get_name; use crate::context::parser_with_context; use crate::context::ContextElement; use crate::context::ExitClass; @@ -93,7 +94,7 @@ pub(crate) fn drawer<'b, 'g, 'r, 's>( remaining, Drawer { source: source.into(), - name: None, // TODO + name: get_name(&affiliated_keywords), drawer_name: drawer_name.into(), children, }, diff --git a/src/parser/dynamic_block.rs b/src/parser/dynamic_block.rs index 85001d5..b9a9d5e 100644 --- a/src/parser/dynamic_block.rs +++ b/src/parser/dynamic_block.rs @@ -19,6 +19,7 @@ use nom::sequence::tuple; use super::keyword::affiliated_keyword; use super::org_source::OrgSource; +use super::util::get_name; use crate::context::parser_with_context; use crate::context::ContextElement; use crate::context::ExitClass; @@ -100,7 +101,7 @@ pub(crate) fn dynamic_block<'b, 'g, 'r, 's>( remaining, DynamicBlock { source: source.into(), - name: None, // TODO + name: get_name(&affiliated_keywords), block_name: name.into(), parameters: parameters.map(|val| val.into()), children, diff --git a/src/parser/fixed_width_area.rs b/src/parser/fixed_width_area.rs index 1cf0a74..951455d 100644 --- a/src/parser/fixed_width_area.rs +++ b/src/parser/fixed_width_area.rs @@ -12,6 +12,7 @@ use nom::sequence::tuple; use super::keyword::affiliated_keyword; use super::org_source::OrgSource; +use super::util::get_name; use super::util::only_space1; use super::util::org_line_ending; use crate::context::parser_with_context; @@ -39,7 +40,7 @@ pub(crate) fn fixed_width_area<'b, 'g, 'r, 's>( remaining, FixedWidthArea { source: source.into(), - name: None, // TODO + name: get_name(&affiliated_keywords), }, )) } diff --git a/src/parser/footnote_definition.rs b/src/parser/footnote_definition.rs index 937d920..dee1b9d 100644 --- a/src/parser/footnote_definition.rs +++ b/src/parser/footnote_definition.rs @@ -13,6 +13,7 @@ use nom::sequence::tuple; use super::keyword::affiliated_keyword; use super::org_source::OrgSource; +use super::util::get_name; use super::util::include_input; use super::util::WORD_CONSTITUENT_CHARACTERS; use crate::context::parser_with_context; @@ -87,7 +88,7 @@ pub(crate) fn footnote_definition<'b, 'g, 'r, 's>( remaining, FootnoteDefinition { source: source.into(), - name: None, // TODO + name: get_name(&affiliated_keywords), label: lbl.into(), children: children.into_iter().map(|(_, item)| item).collect(), }, diff --git a/src/parser/greater_block.rs b/src/parser/greater_block.rs index 15e7637..338b26f 100644 --- a/src/parser/greater_block.rs +++ b/src/parser/greater_block.rs @@ -19,6 +19,7 @@ use nom::sequence::tuple; use super::keyword::affiliated_keyword; use super::org_source::OrgSource; +use super::util::get_name; use super::util::in_section; use crate::context::parser_with_context; use crate::context::ContextElement; @@ -36,6 +37,7 @@ use crate::parser::util::get_consumed; use crate::parser::util::start_of_line; use crate::types::CenterBlock; use crate::types::Element; +use crate::types::Keyword; use crate::types::Paragraph; use crate::types::QuoteBlock; use crate::types::SetSource; @@ -60,9 +62,9 @@ pub(crate) fn greater_block<'b, 'g, 'r, 's>( ))(remaining)?; let name = Into::<&str>::into(name); let (remaining, element) = match name.to_lowercase().as_str() { - "center" => center_block(context, remaining, input)?, - "quote" => quote_block(context, remaining, input)?, - _ => special_block(name)(context, remaining, input)?, + "center" => center_block(context, remaining, input, &affiliated_keywords)?, + "quote" => quote_block(context, remaining, input, &affiliated_keywords)?, + _ => special_block(name)(context, remaining, input, &affiliated_keywords)?, }; Ok((remaining, element)) } @@ -72,6 +74,7 @@ fn center_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, original_input: OrgSource<'s>, + affiliated_keywords: &Vec>, ) -> Res, Element<'s>> { let (remaining, (source, children)) = greater_block_body(context, input, original_input, "center", "center block")?; @@ -79,7 +82,7 @@ fn center_block<'b, 'g, 'r, 's>( remaining, Element::CenterBlock(CenterBlock { source, - name: None, // TODO + name: get_name(&affiliated_keywords), children, }), )) @@ -90,6 +93,7 @@ fn quote_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, original_input: OrgSource<'s>, + affiliated_keywords: &Vec>, ) -> Res, Element<'s>> { let (remaining, (source, children)) = greater_block_body(context, input, original_input, "quote", "quote block")?; @@ -97,7 +101,7 @@ fn quote_block<'b, 'g, 'r, 's>( remaining, Element::QuoteBlock(QuoteBlock { source, - name: None, // TODO + name: get_name(&affiliated_keywords), children, }), )) @@ -109,11 +113,19 @@ fn special_block<'s>( RefContext<'b, 'g, 'r, 's>, OrgSource<'s>, OrgSource<'s>, + &Vec>, ) -> Res, Element<'s>> + 's { let context_name = format!("special block {}", name); - move |context, input, original_input| { - _special_block(context, input, original_input, name, context_name.as_str()) + move |context, input, original_input, affiliated_keywords| { + _special_block( + context, + input, + original_input, + name, + context_name.as_str(), + affiliated_keywords, + ) } } @@ -124,6 +136,7 @@ fn _special_block<'c, 'b, 'g, 'r, 's>( original_input: OrgSource<'s>, name: &'s str, context_name: &'c str, + affiliated_keywords: &Vec>, ) -> Res, Element<'s>> { let (remaining, parameters) = opt(tuple((space1, parameters)))(input)?; let (remaining, (source, children)) = @@ -132,7 +145,7 @@ fn _special_block<'c, 'b, 'g, 'r, 's>( remaining, Element::SpecialBlock(SpecialBlock { source, - name: None, // TODO + name: get_name(&affiliated_keywords), children, block_type: name, parameters: parameters.map(|(_, parameters)| Into::<&str>::into(parameters)), diff --git a/src/parser/horizontal_rule.rs b/src/parser/horizontal_rule.rs index d90923d..7dca25c 100644 --- a/src/parser/horizontal_rule.rs +++ b/src/parser/horizontal_rule.rs @@ -11,6 +11,7 @@ use nom::sequence::tuple; use super::keyword::affiliated_keyword; use super::org_source::OrgSource; +use super::util::get_name; use crate::context::RefContext; use crate::error::Res; use crate::parser::util::start_of_line; @@ -33,7 +34,7 @@ pub(crate) fn horizontal_rule<'b, 'g, 'r, 's>( remaining, HorizontalRule { source: rule.into(), - name: None, // TODO + name: get_name(&affiliated_keywords), }, )) } diff --git a/src/parser/keyword.rs b/src/parser/keyword.rs index 5ea956d..34739bc 100644 --- a/src/parser/keyword.rs +++ b/src/parser/keyword.rs @@ -19,6 +19,7 @@ use nom::sequence::tuple; use super::org_source::BracketDepth; use super::org_source::OrgSource; +use super::util::get_name; use crate::context::Matcher; use crate::context::RefContext; use crate::error::CustomError; @@ -62,7 +63,7 @@ fn _filtered_keyword<'s, F: Matcher>( remaining, Keyword { source: consumed_input.into(), - name: None, // TODO + name: None, // To be populated by the caller if this keyword is in a context to support affiliated keywords. key: parsed_key.into(), value: "".into(), }, @@ -80,7 +81,7 @@ fn _filtered_keyword<'s, F: Matcher>( remaining, Keyword { source: consumed_input.into(), - name: None, // TODO + name: None, // To be populated by the caller if this keyword is in a context to support affiliated keywords. key: parsed_key.into(), value: parsed_value.into(), }, @@ -93,7 +94,9 @@ pub(crate) fn keyword<'b, 'g, 'r, 's>( input: OrgSource<'s>, ) -> Res, Keyword<'s>> { let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; - filtered_keyword(regular_keyword_key)(input) + let (remaining, mut kw) = filtered_keyword(regular_keyword_key)(input)?; + kw.name = get_name(&affiliated_keywords); + Ok((remaining, kw)) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] @@ -112,7 +115,7 @@ pub(crate) fn babel_call_keyword<'b, 'g, 'r, 's>( remaining, BabelCall { source: kw.source, - name: None, // TODO + name: get_name(&affiliated_keywords), key: kw.key, value: kw.value, }, diff --git a/src/parser/latex_environment.rs b/src/parser/latex_environment.rs index 7e64567..b175d70 100644 --- a/src/parser/latex_environment.rs +++ b/src/parser/latex_environment.rs @@ -15,6 +15,7 @@ use nom::sequence::tuple; use super::keyword::affiliated_keyword; use super::org_source::OrgSource; use super::util::get_consumed; +use super::util::get_name; use crate::context::parser_with_context; use crate::context::ContextElement; use crate::context::ContextMatcher; @@ -57,7 +58,7 @@ pub(crate) fn latex_environment<'b, 'g, 'r, 's>( remaining, LatexEnvironment { source: source.into(), - name: None, // TODO + name: get_name(&affiliated_keywords), }, )) } diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 050dbc9..a1fedb5 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -14,11 +14,14 @@ use nom::combinator::opt; use nom::combinator::peek; use nom::combinator::recognize; use nom::combinator::verify; +use nom::multi::many0; use nom::multi::many_till; use nom::multi::separated_list1; use nom::sequence::tuple; +use super::keyword::affiliated_keyword; use super::org_source::OrgSource; +use super::util::get_name; use crate::context::parser_with_context; use crate::context::ContextElement; use crate::context::ContextMatcher; @@ -50,6 +53,7 @@ pub(crate) fn verse_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, VerseBlock<'s>> { + let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; let (remaining, _) = lesser_block_begin("verse")(context, input)?; let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?; let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; @@ -93,7 +97,7 @@ pub(crate) fn verse_block<'b, 'g, 'r, 's>( remaining, VerseBlock { source: source.into(), - name: None, // TODO + name: get_name(&affiliated_keywords), data: parameters.map(|parameters| Into::<&str>::into(parameters)), children, }, @@ -105,6 +109,7 @@ pub(crate) fn comment_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, CommentBlock<'s>> { + let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; let (remaining, _) = lesser_block_begin("comment")(context, input)?; let (remaining, _parameters) = opt(tuple((space1, data)))(remaining)?; let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; @@ -129,7 +134,7 @@ pub(crate) fn comment_block<'b, 'g, 'r, 's>( remaining, CommentBlock { source: source.into(), - name: None, // TODO + name: get_name(&affiliated_keywords), contents: contents.into(), }, )) @@ -140,6 +145,7 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, ExampleBlock<'s>> { + let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; let (remaining, _) = lesser_block_begin("example")(context, input)?; let (remaining, parameters) = opt(tuple((space1, example_switches)))(remaining)?; let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; @@ -183,7 +189,7 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( remaining, ExampleBlock { source: source.into(), - name: None, // TODO + name: get_name(&affiliated_keywords), switches, number_lines, preserve_indent, @@ -200,6 +206,7 @@ pub(crate) fn export_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, ExportBlock<'s>> { + let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; let (remaining, _) = lesser_block_begin("export")(context, input)?; // https://orgmode.org/worg/org-syntax.html#Blocks claims that export blocks must have a single word for data but testing shows no data and multi-word data still parses as an export block. let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?; @@ -229,7 +236,7 @@ pub(crate) fn export_block<'b, 'g, 'r, 's>( remaining, ExportBlock { source: source.into(), - name: None, // TODO + name: get_name(&affiliated_keywords), data: parameters.map(|parameters| Into::<&str>::into(parameters)), contents: contents.into(), }, @@ -241,6 +248,7 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, SrcBlock<'s>> { + let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; let (remaining, _) = lesser_block_begin("src")(context, input)?; // https://orgmode.org/worg/org-syntax.html#Blocks claims that data is mandatory and must follow the LANGUAGE SWITCHES ARGUMENTS pattern but testing has shown that no data and incorrect data here will still parse to a src block. let (remaining, language) = opt(map(tuple((space1, switch_word(true))), |(_, language)| { @@ -289,7 +297,7 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( remaining, SrcBlock { source: source.into(), - name: None, // TODO + name: get_name(&affiliated_keywords), language: language.map(Into::<&str>::into), switches, parameters: parameters.map(Into::<&str>::into), diff --git a/src/parser/paragraph.rs b/src/parser/paragraph.rs index 2576e35..82a8f6c 100644 --- a/src/parser/paragraph.rs +++ b/src/parser/paragraph.rs @@ -12,6 +12,7 @@ use super::keyword::affiliated_keyword; use super::org_source::OrgSource; use super::util::blank_line; use super::util::get_consumed; +use super::util::get_name; use crate::context::parser_with_context; use crate::context::ContextElement; use crate::context::ExitClass; @@ -51,7 +52,7 @@ pub(crate) fn paragraph<'b, 'g, 'r, 's>( remaining, Paragraph { source: source.into(), - name: None, // TODO + name: get_name(&affiliated_keywords), children, }, )) diff --git a/src/parser/plain_list.rs b/src/parser/plain_list.rs index 92e6007..f33ba0d 100644 --- a/src/parser/plain_list.rs +++ b/src/parser/plain_list.rs @@ -22,6 +22,7 @@ use super::element_parser::element; use super::keyword::affiliated_keyword; use super::object_parser::standard_set_object; use super::org_source::OrgSource; +use super::util::get_name; use super::util::include_input; use super::util::indentation_level; use super::util::non_whitespace_character; @@ -154,7 +155,7 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>( remaining, PlainList { source: source.into(), - name: None, // TODO + name: get_name(&affiliated_keywords), list_type: first_item_list_type.expect("Plain lists require at least one element."), children: children.into_iter().map(|(_start, item)| item).collect(), }, diff --git a/src/parser/table.rs b/src/parser/table.rs index e1de64c..2fc7967 100644 --- a/src/parser/table.rs +++ b/src/parser/table.rs @@ -18,6 +18,7 @@ use super::keyword::table_formula_keyword; use super::object_parser::table_cell_set_object; use super::org_source::OrgSource; use super::util::exit_matcher_parser; +use super::util::get_name; use super::util::org_line_ending; use crate::context::parser_with_context; use crate::context::ContextElement; @@ -70,7 +71,7 @@ pub(crate) fn org_mode_table<'b, 'g, 'r, 's>( remaining, Table { source: source.into(), - name: None, // TODO + name: get_name(&affiliated_keywords), formulas, children, }, diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index a7cfa4d..91091fc 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -148,12 +148,15 @@ pub enum SwitchNumberLines { } impl<'s> Paragraph<'s> { + /// Generate a paragraph of the passed in text with no additional properties. + /// + /// This is used for elements that support an "empty" content like greater blocks. pub(crate) fn of_text(input: &'s str) -> Self { let mut objects = Vec::with_capacity(1); objects.push(Object::PlainText(PlainText { source: input })); Paragraph { source: input, - name: None, // TODO + name: None, children: objects, } } From b82d4c0eca307060f681e37ed746838784797a48 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 21:32:10 -0400 Subject: [PATCH 39/54] Fix finding name keyword in affiliated keywords list. --- src/parser/util.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/parser/util.rs b/src/parser/util.rs index 8f63eaf..a36c53a 100644 --- a/src/parser/util.rs +++ b/src/parser/util.rs @@ -280,10 +280,7 @@ pub(crate) fn indentation_level<'b, 'g, 'r, 's>( pub(crate) fn get_name<'s>(affiliated_keywords: &Vec>) -> Option<&'s str> { let name_keyword = affiliated_keywords .iter() - .filter(|kw| match kw.name { - Some(name) if name.eq_ignore_ascii_case("name") => true, - _ => false, - }) + .filter(|kw| kw.key.eq_ignore_ascii_case("name")) .last(); name_keyword.map(|kw| kw.value) } From 65eda088437bbe8318d07432dd248248a73e9a79 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 22:47:13 -0400 Subject: [PATCH 40/54] Re-work the element parser to handle affiliated keywords before elements that cannot have affiliated keywords. --- src/parser/element_parser.rs | 138 ++++++++++++++++++++++++++--------- 1 file changed, 102 insertions(+), 36 deletions(-) diff --git a/src/parser/element_parser.rs b/src/parser/element_parser.rs index 9fe87a1..55bc649 100644 --- a/src/parser/element_parser.rs +++ b/src/parser/element_parser.rs @@ -1,5 +1,10 @@ use nom::branch::alt; use nom::combinator::map; +use nom::combinator::opt; +use nom::combinator::peek; +use nom::sequence::tuple; +#[cfg(feature = "tracing")] +use tracing::span; use super::clock::clock; use super::comment::comment; @@ -75,43 +80,104 @@ fn _element<'b, 'g, 'r, 's>( let paragraph_matcher = parser_with_context!(paragraph)(context); let latex_environment_matcher = parser_with_context!(latex_environment)(context); - let (remaining, mut element) = match alt(( - map(plain_list_matcher, Element::PlainList), - greater_block_matcher, - map(dynamic_block_matcher, Element::DynamicBlock), - map(footnote_definition_matcher, Element::FootnoteDefinition), - map(comment_matcher, Element::Comment), - map(drawer_matcher, Element::Drawer), - map(table_matcher, Element::Table), - map(verse_block_matcher, Element::VerseBlock), - map(comment_block_matcher, Element::CommentBlock), - map(example_block_matcher, Element::ExampleBlock), - map(export_block_matcher, Element::ExportBlock), - map(src_block_matcher, Element::SrcBlock), - map(clock_matcher, Element::Clock), - map(diary_sexp_matcher, Element::DiarySexp), - map(fixed_width_area_matcher, Element::FixedWidthArea), - map(horizontal_rule_matcher, Element::HorizontalRule), - map(latex_environment_matcher, Element::LatexEnvironment), - map(babel_keyword_matcher, Element::BabelCall), - map(keyword_matcher, Element::Keyword), - ))(input) - { - the_ok @ Ok(_) => the_ok, - Err(_) => { - if can_be_paragraph { - match map(paragraph_matcher, Element::Paragraph)(input) { - the_ok @ Ok(_) => the_ok, - Err(_) => { - // TODO: Because this function expects a single element, if there are multiple affiliated keywords before an element that cannot have affiliated keywords, we end up re-parsing the affiliated keywords many times. - map(affiliated_keyword, Element::Keyword)(input) - } - } - } else { - map(affiliated_keyword, Element::Keyword)(input) - } + let (mut remaining, mut maybe_element) = { + #[cfg(feature = "tracing")] + let span = span!(tracing::Level::DEBUG, "Main element block"); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + opt(alt(( + map(plain_list_matcher, Element::PlainList), + greater_block_matcher, + map(dynamic_block_matcher, Element::DynamicBlock), + map(footnote_definition_matcher, Element::FootnoteDefinition), + map(comment_matcher, Element::Comment), + map(drawer_matcher, Element::Drawer), + map(table_matcher, Element::Table), + map(verse_block_matcher, Element::VerseBlock), + map(comment_block_matcher, Element::CommentBlock), + map(example_block_matcher, Element::ExampleBlock), + map(export_block_matcher, Element::ExportBlock), + map(src_block_matcher, Element::SrcBlock), + map(clock_matcher, Element::Clock), + map(diary_sexp_matcher, Element::DiarySexp), + map(fixed_width_area_matcher, Element::FixedWidthArea), + map(horizontal_rule_matcher, Element::HorizontalRule), + map(latex_environment_matcher, Element::LatexEnvironment), + map(babel_keyword_matcher, Element::BabelCall), + map(keyword_matcher, Element::Keyword), + )))(input)? + }; + + if maybe_element.is_none() && can_be_paragraph { + #[cfg(feature = "tracing")] + let span = span!(tracing::Level::DEBUG, "Paragraph with affiliated keyword."); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let (remain, paragraph_with_affiliated_keyword) = opt(map( + tuple(( + peek(affiliated_keyword), + map(paragraph_matcher, Element::Paragraph), + )), + |(_, paragraph)| paragraph, + ))(remaining)?; + if paragraph_with_affiliated_keyword.is_some() { + remaining = remain; + maybe_element = paragraph_with_affiliated_keyword; } - }?; + } + + // if maybe_element.is_none() { + // #[cfg(feature = "tracing")] + // let span = span!(tracing::Level::DEBUG, "Regular keyword."); + // #[cfg(feature = "tracing")] + // let _enter = span.enter(); + + // let (remain, kw) = opt(map(keyword_matcher, Element::Keyword))(remaining)?; + // if kw.is_some() { + // maybe_element = kw; + // remaining = remain; + // } + // } + + if maybe_element.is_none() { + #[cfg(feature = "tracing")] + let span = span!( + tracing::Level::DEBUG, + "Affiliated keyword as regular keyword." + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let (remain, kw) = opt(map(affiliated_keyword, Element::Keyword))(remaining)?; + if kw.is_some() { + maybe_element = kw; + remaining = remain; + } + } + + if maybe_element.is_none() && can_be_paragraph { + #[cfg(feature = "tracing")] + let span = span!( + tracing::Level::DEBUG, + "Paragraph without affiliated keyword." + ); + #[cfg(feature = "tracing")] + let _enter = span.enter(); + + let (remain, paragraph_without_affiliated_keyword) = + map(paragraph_matcher, Element::Paragraph)(remaining)?; + remaining = remain; + maybe_element = Some(paragraph_without_affiliated_keyword); + } + + if maybe_element.is_none() { + return Err(nom::Err::Error(CustomError::MyError(MyError( + "No element.", + )))); + } + let mut element = maybe_element.expect("The above if-statement ensures this is Some()."); let (remaining, _trailing_ws) = maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; From 1351577c5a79bf096fe37e91cbc2e7be87dcdb33 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 4 Oct 2023 23:40:38 -0400 Subject: [PATCH 41/54] Fix handling affiliated keywords before invalid paragraphs. --- src/context/context.rs | 12 ++++++++++++ src/context/mod.rs | 1 + src/parser/org_source.rs | 2 +- src/parser/paragraph.rs | 39 ++++++++++++++++++++++++++++++--------- src/parser/util.rs | 18 ++++++++++++++++++ 5 files changed, 62 insertions(+), 10 deletions(-) diff --git a/src/context/context.rs b/src/context/context.rs index 22c4798..def26bf 100644 --- a/src/context/context.rs +++ b/src/context/context.rs @@ -12,6 +12,7 @@ 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> { @@ -27,11 +28,22 @@ pub(crate) enum ContextElement<'r, 's> { /// 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>, +} + pub(crate) struct ExitMatcherNode<'r> { // TODO: Should this be "&'r DynContextMatcher<'c>" ? pub(crate) exit_matcher: &'r DynContextMatcher<'r>, diff --git a/src/context/mod.rs b/src/context/mod.rs index 7e45fdd..27e6a6d 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -21,6 +21,7 @@ type DynMatcher<'c> = dyn Matcher + 'c; pub(crate) use context::Context; pub(crate) use context::ContextElement; pub(crate) use context::ExitMatcherNode; +pub(crate) use context::HasAffiliatedKeywordInner; pub(crate) use exiting::ExitClass; pub use file_access_interface::FileAccessInterface; pub use file_access_interface::LocalFileAccessInterface; diff --git a/src/parser/org_source.rs b/src/parser/org_source.rs index 1aa29a7..eb827a0 100644 --- a/src/parser/org_source.rs +++ b/src/parser/org_source.rs @@ -14,7 +14,7 @@ use crate::error::MyError; pub(crate) type BracketDepth = i16; -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq)] pub(crate) struct OrgSource<'s> { full_source: &'s str, start: usize, diff --git a/src/parser/paragraph.rs b/src/parser/paragraph.rs index 82a8f6c..2bce4b5 100644 --- a/src/parser/paragraph.rs +++ b/src/parser/paragraph.rs @@ -12,12 +12,16 @@ use super::keyword::affiliated_keyword; use super::org_source::OrgSource; use super::util::blank_line; use super::util::get_consumed; +use super::util::get_has_affiliated_keyword; use super::util::get_name; use crate::context::parser_with_context; use crate::context::ContextElement; use crate::context::ExitClass; use crate::context::ExitMatcherNode; +use crate::context::HasAffiliatedKeywordInner; use crate::context::RefContext; +use crate::error::CustomError; +use crate::error::MyError; use crate::error::Res; use crate::parser::object_parser::standard_set_object; use crate::parser::util::exit_matcher_parser; @@ -30,12 +34,18 @@ pub(crate) fn paragraph<'b, 'g, 'r, 's>( input: OrgSource<'s>, ) -> Res, Paragraph<'s>> { let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; - - let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Gamma, - exit_matcher: ¶graph_end, - }); - let parser_context = context.with_additional_node(&parser_context); + let contexts = [ + ContextElement::HasAffiliatedKeyword(HasAffiliatedKeywordInner { + start_after_affiliated_keywords: input, + keywords: &affiliated_keywords, + }), + ContextElement::ExitMatcherNode(ExitMatcherNode { + class: ExitClass::Gamma, + exit_matcher: ¶graph_end, + }), + ]; + let parser_context = context.with_additional_node(&contexts[0]); + let parser_context = parser_context.with_additional_node(&contexts[1]); let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context); let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); @@ -63,10 +73,21 @@ fn paragraph_end<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, OrgSource<'s>> { - let non_paragraph_element_matcher = parser_with_context!(detect_element(false))(context); + let regular_end = recognize(tuple((start_of_line, many1(blank_line))))(input); + if regular_end.is_ok() { + return regular_end; + } + match get_has_affiliated_keyword(context) { + Some(start_post_affiliated_keywords) if input == start_post_affiliated_keywords => { + return Err(nom::Err::Error(CustomError::MyError(MyError( + "No exit due to affiliated keywords.", + )))); + } + _ => {} + } + // Check to see if input is the start of a HasAffiliatedKeyword alt(( - recognize(tuple((start_of_line, many1(blank_line)))), - recognize(non_paragraph_element_matcher), + recognize(parser_with_context!(detect_element(false))(context)), eof, ))(input) } diff --git a/src/parser/util.rs b/src/parser/util.rs index a36c53a..02dcfc6 100644 --- a/src/parser/util.rs +++ b/src/parser/util.rs @@ -284,3 +284,21 @@ pub(crate) fn get_name<'s>(affiliated_keywords: &Vec>) -> Option<&'s .last(); name_keyword.map(|kw| kw.value) } + +pub(crate) fn get_has_affiliated_keyword<'b, 'g, 'r, 's>( + context: RefContext<'b, 'g, 'r, 's>, +) -> Option> { + for context in context.iter() { + match context { + ContextElement::HasAffiliatedKeyword(inner) => { + if !inner.keywords.is_empty() { + return Some(inner.start_after_affiliated_keywords); + } else { + return None; + } + } + _ => {} + } + } + None +} From da2d7535e8f16139caae016805c10833082bcf27 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 5 Oct 2023 00:17:06 -0400 Subject: [PATCH 42/54] Add synonyms for name. --- .../affiliated_keyword/all_name.org | 17 +++++++++++++++++ .../affiliated_keyword/tblname_on_non_table.org | 2 ++ org_mode_samples/greater_element/table/name.org | 2 ++ src/parser/util.rs | 9 ++++++++- 4 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 org_mode_samples/affiliated_keyword/all_name.org create mode 100644 org_mode_samples/affiliated_keyword/tblname_on_non_table.org create mode 100644 org_mode_samples/greater_element/table/name.org diff --git a/org_mode_samples/affiliated_keyword/all_name.org b/org_mode_samples/affiliated_keyword/all_name.org new file mode 100644 index 0000000..1934c83 --- /dev/null +++ b/org_mode_samples/affiliated_keyword/all_name.org @@ -0,0 +1,17 @@ +#+name: foo +: bar + +#+source: foo +: bar + +#+tblname: foo +: bar + +#+resname: foo +: bar + +#+srcname: foo +: bar + +#+label: foo +: bar diff --git a/org_mode_samples/affiliated_keyword/tblname_on_non_table.org b/org_mode_samples/affiliated_keyword/tblname_on_non_table.org new file mode 100644 index 0000000..4c8a0e0 --- /dev/null +++ b/org_mode_samples/affiliated_keyword/tblname_on_non_table.org @@ -0,0 +1,2 @@ +#+tblname: foo +: bar diff --git a/org_mode_samples/greater_element/table/name.org b/org_mode_samples/greater_element/table/name.org new file mode 100644 index 0000000..b5d3a56 --- /dev/null +++ b/org_mode_samples/greater_element/table/name.org @@ -0,0 +1,2 @@ +#+NAME: foo +| foo | bar | diff --git a/src/parser/util.rs b/src/parser/util.rs index 02dcfc6..8d4ce02 100644 --- a/src/parser/util.rs +++ b/src/parser/util.rs @@ -280,7 +280,14 @@ pub(crate) fn indentation_level<'b, 'g, 'r, 's>( pub(crate) fn get_name<'s>(affiliated_keywords: &Vec>) -> Option<&'s str> { let name_keyword = affiliated_keywords .iter() - .filter(|kw| kw.key.eq_ignore_ascii_case("name")) + .filter(|kw| { + kw.key.eq_ignore_ascii_case("name") + || kw.key.eq_ignore_ascii_case("source") + || kw.key.eq_ignore_ascii_case("tblname") + || kw.key.eq_ignore_ascii_case("resname") + || kw.key.eq_ignore_ascii_case("srcname") + || kw.key.eq_ignore_ascii_case("label") + }) .last(); name_keyword.map(|kw| kw.value) } From 50a3631b79a959a0499c267cd0a842904410d76c Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 5 Oct 2023 00:43:03 -0400 Subject: [PATCH 43/54] Handle whitespace after parameters in src blocks. --- .../lesser_block/src/parameters_with_trailing_space.org | 7 +++++++ src/parser/lesser_block.rs | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 org_mode_samples/lesser_element/lesser_block/src/parameters_with_trailing_space.org diff --git a/org_mode_samples/lesser_element/lesser_block/src/parameters_with_trailing_space.org b/org_mode_samples/lesser_element/lesser_block/src/parameters_with_trailing_space.org new file mode 100644 index 0000000..bb86222 --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/src/parameters_with_trailing_space.org @@ -0,0 +1,7 @@ +#+begin_src python :exports results +print("foo") +#+end_src + +#+begin_src python -n :exports results +print("foo") +#+end_src diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index a1fedb5..e3dcca4 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -390,7 +390,7 @@ enum SwitchState { #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn src_parameters<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { - recognize(is_not("\r\n"))(input) + recognize(many_till(anychar, peek(tuple((space0, line_ending)))))(input) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] From 11a7234900380fed842e1872f2b87a76864718ad Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 5 Oct 2023 00:49:15 -0400 Subject: [PATCH 44/54] Handle empty parameters with whitespace preceding. --- src/parser/lesser_block.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index e3dcca4..761e74c 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -258,6 +258,7 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( let (remaining, parameters) = opt(map(tuple((space1, src_parameters)), |(_, parameters)| { parameters }))(remaining)?; + let parameters = parameters.flatten(); let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; let lesser_block_end_specialized = lesser_block_end("src"); let contexts = [ @@ -389,8 +390,15 @@ enum SwitchState { } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn src_parameters<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { - recognize(many_till(anychar, peek(tuple((space0, line_ending)))))(input) +fn src_parameters<'s>(input: OrgSource<'s>) -> Res, Option>> { + let (remaining, parameters) = + recognize(many_till(anychar, peek(tuple((space0, line_ending)))))(input)?; + + if parameters.len() == 0 { + Ok((remaining, None)) + } else { + Ok((remaining, Some(parameters))) + } } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] From 3742f4fa08b72b7fdd5a98d29b390542443b84ec Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 5 Oct 2023 00:53:11 -0400 Subject: [PATCH 45/54] Remove double option. --- src/parser/lesser_block.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 761e74c..5289c6b 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -258,7 +258,6 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( let (remaining, parameters) = opt(map(tuple((space1, src_parameters)), |(_, parameters)| { parameters }))(remaining)?; - let parameters = parameters.flatten(); let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; let lesser_block_end_specialized = lesser_block_end("src"); let contexts = [ @@ -390,15 +389,11 @@ enum SwitchState { } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn src_parameters<'s>(input: OrgSource<'s>) -> Res, Option>> { - let (remaining, parameters) = - recognize(many_till(anychar, peek(tuple((space0, line_ending)))))(input)?; - - if parameters.len() == 0 { - Ok((remaining, None)) - } else { - Ok((remaining, Some(parameters))) - } +fn src_parameters<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { + verify( + recognize(many_till(anychar, peek(tuple((space0, line_ending))))), + |parameters: &OrgSource<'_>| parameters.len() > 0, + )(input) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] From d8ea450a46629631ec42ba91e7ca7f3cb2a3b69f Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 5 Oct 2023 01:06:30 -0400 Subject: [PATCH 46/54] Add TODOs for comparing caption to elems that can have affiliated keywords. --- src/compare/diff.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/compare/diff.rs b/src/compare/diff.rs index a4ba083..22e9eb4 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -844,6 +844,7 @@ fn compare_paragraph<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { @@ -879,6 +880,7 @@ fn compare_plain_list<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { @@ -1050,6 +1052,7 @@ fn compare_center_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { @@ -1085,6 +1088,7 @@ fn compare_quote_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { @@ -1120,6 +1124,7 @@ fn compare_special_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { @@ -1180,6 +1185,7 @@ fn compare_dynamic_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { @@ -1241,6 +1247,7 @@ fn compare_footnote_definition<'b, 's>( let mut message = None; // TODO: Compare :pre-blank + // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { @@ -1319,6 +1326,7 @@ fn compare_drawer<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { @@ -1435,6 +1443,7 @@ fn compare_table<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { @@ -1589,6 +1598,7 @@ fn compare_verse_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { @@ -1620,6 +1630,7 @@ fn compare_comment_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { @@ -1659,6 +1670,7 @@ fn compare_example_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { @@ -1835,6 +1847,7 @@ fn compare_export_block<'b, 's>( // TODO: Compare :type :value + // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { @@ -1864,6 +1877,7 @@ fn compare_src_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { @@ -2089,6 +2103,7 @@ fn compare_diary_sexp<'b, 's>( // TODO: Compare :value + // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { @@ -2192,6 +2207,7 @@ fn compare_fixed_width_area<'b, 's>( // TODO: Compare :value + // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { @@ -2222,6 +2238,7 @@ fn compare_horizontal_rule<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { @@ -2252,6 +2269,7 @@ fn compare_keyword<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; + // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { @@ -2309,6 +2327,7 @@ fn compare_babel_call<'b, 's>( // TODO: Compare :call :inside-header :arguments :end-header + // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { @@ -2354,6 +2373,7 @@ fn compare_latex_environment<'b, 's>( // TODO: Compare :value + // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { From 5f84cd974d3706f674ed5724c91ff48a7541813a Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 5 Oct 2023 01:10:50 -0400 Subject: [PATCH 47/54] Add test showing the first unrecognized word is the end of the switches for src blocks. --- .../lesser_block/src/parameters_without_colon.org | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 org_mode_samples/lesser_element/lesser_block/src/parameters_without_colon.org diff --git a/org_mode_samples/lesser_element/lesser_block/src/parameters_without_colon.org b/org_mode_samples/lesser_element/lesser_block/src/parameters_without_colon.org new file mode 100644 index 0000000..cdf851a --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/src/parameters_without_colon.org @@ -0,0 +1,3 @@ +#+begin_src foo -n bar -k baz + +#+end_src From 386ad5091d3463dd1673c2dac00edb4199342a2d Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 5 Oct 2023 01:55:33 -0400 Subject: [PATCH 48/54] End switches at the first unrecognized word in src blocks. --- .../example/parameters_without_colon.org | 3 + src/parser/lesser_block.rs | 60 ++++++++++++------- 2 files changed, 40 insertions(+), 23 deletions(-) create mode 100644 org_mode_samples/lesser_element/lesser_block/example/parameters_without_colon.org diff --git a/org_mode_samples/lesser_element/lesser_block/example/parameters_without_colon.org b/org_mode_samples/lesser_element/lesser_block/example/parameters_without_colon.org new file mode 100644 index 0000000..8780c85 --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/example/parameters_without_colon.org @@ -0,0 +1,3 @@ +#+begin_example foo -n bar -k baz + +#+end_example diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 5289c6b..f4cf792 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -9,14 +9,12 @@ use nom::character::complete::space1; use nom::combinator::consumed; use nom::combinator::eof; use nom::combinator::map; -use nom::combinator::not; use nom::combinator::opt; use nom::combinator::peek; use nom::combinator::recognize; use nom::combinator::verify; use nom::multi::many0; use nom::multi::many_till; -use nom::multi::separated_list1; use nom::sequence::tuple; use super::keyword::affiliated_keyword; @@ -27,8 +25,9 @@ use crate::context::ContextElement; use crate::context::ContextMatcher; use crate::context::ExitClass; use crate::context::ExitMatcherNode; -use crate::context::Matcher; use crate::context::RefContext; +use crate::error::CustomError; +use crate::error::MyError; use crate::error::Res; use crate::parser::object_parser::standard_set_object; use crate::parser::util::blank_line; @@ -251,9 +250,8 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; let (remaining, _) = lesser_block_begin("src")(context, input)?; // https://orgmode.org/worg/org-syntax.html#Blocks claims that data is mandatory and must follow the LANGUAGE SWITCHES ARGUMENTS pattern but testing has shown that no data and incorrect data here will still parse to a src block. - let (remaining, language) = opt(map(tuple((space1, switch_word(true))), |(_, language)| { - language - }))(remaining)?; + let (remaining, language) = + opt(map(tuple((space1, switch_word)), |(_, language)| language))(remaining)?; let (remaining, switches) = opt(src_switches)(remaining)?; let (remaining, parameters) = opt(map(tuple((space1, src_parameters)), |(_, parameters)| { parameters @@ -430,14 +428,21 @@ fn _example_src_switches<'s>( let mut use_labels = true; let mut label_format = None; let mut saw_r = false; - - let (remaining, words) = separated_list1(space1, switch_word(stop_at_parameters))(input)?; - let (remaining, _post_spaces) = opt(tuple((space0, peek(line_ending))))(remaining)?; - let source = input.get_until(remaining); - + let mut matched_a_word = false; + let mut remaining = input; + let mut last_match_remaining = input; let mut state = SwitchState::Normal; - for word in words { + 'outer: loop { + let (remain, word) = opt(switch_word)(remaining)?; + let word = match word { + Some(word) => word, + None => { + break; + } + }; + let normalized_word = Into::<&str>::into(word); + loop { match (&state, normalized_word) { (SwitchState::Normal, "-n") => { @@ -510,11 +515,30 @@ fn _example_src_switches<'s>( label_format = Some(normalized_word); state = SwitchState::Normal; } + (SwitchState::Normal, _) if stop_at_parameters => { + break 'outer; + } (SwitchState::Normal, _) => {} }; + matched_a_word = true; + remaining = remain; + last_match_remaining = remain; + + let (remain, divider) = opt(space1)(remaining)?; + if divider.is_none() { + break 'outer; + } + remaining = remain; break; } } + if !matched_a_word { + return Err(nom::Err::Error(CustomError::MyError(MyError("No words.")))); + } + let remaining = last_match_remaining; + + let (remaining, _post_spaces) = opt(tuple((space0, peek(line_ending))))(remaining)?; + let source = input.get_until(remaining); // Handle state that didn't get processed because we ran out of words. match state { @@ -546,18 +570,8 @@ fn _example_src_switches<'s>( )) } -fn switch_word<'s>(stop_at_parameters: bool) -> impl Matcher { - move |input| _switch_word(input, stop_at_parameters) -} - #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn _switch_word<'s>( - input: OrgSource<'s>, - stop_at_parameters: bool, -) -> Res, OrgSource<'s>> { - if stop_at_parameters { - not(tag(":"))(input)?; - } +fn switch_word<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { alt(( map( tuple((tag(r#"""#), is_not("\"\r\n"), tag(r#"""#))), From 1da521b08a2074ebbb7a61f97f807b08a0de12c6 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 5 Oct 2023 02:06:26 -0400 Subject: [PATCH 49/54] Compare value for export block. --- .../lesser_block/comment/escaped_lines.org | 10 ++++++++++ .../lesser_block/export/escaped_lines.org | 10 ++++++++++ .../export/{with_no_data.org => no_data.org} | 0 .../lesser_block/export/one_word.org | 3 +++ .../export/{with_two_words.org => two_words.org} | 0 src/compare/diff.rs | 14 ++++++++++++-- src/parser/lesser_block.rs | 4 ++-- src/types/lesser_element.rs | 2 +- 8 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 org_mode_samples/lesser_element/lesser_block/comment/escaped_lines.org create mode 100644 org_mode_samples/lesser_element/lesser_block/export/escaped_lines.org rename org_mode_samples/lesser_element/lesser_block/export/{with_no_data.org => no_data.org} (100%) create mode 100644 org_mode_samples/lesser_element/lesser_block/export/one_word.org rename org_mode_samples/lesser_element/lesser_block/export/{with_two_words.org => two_words.org} (100%) diff --git a/org_mode_samples/lesser_element/lesser_block/comment/escaped_lines.org b/org_mode_samples/lesser_element/lesser_block/comment/escaped_lines.org new file mode 100644 index 0000000..f45bc0d --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/comment/escaped_lines.org @@ -0,0 +1,10 @@ +#+begin_comment +,* foo +,,,** bar + ,*** baz + lorem + , ipsum +,#+begin_src dolar + +,#+end_src +#+end_comment diff --git a/org_mode_samples/lesser_element/lesser_block/export/escaped_lines.org b/org_mode_samples/lesser_element/lesser_block/export/escaped_lines.org new file mode 100644 index 0000000..fe8f05b --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/export/escaped_lines.org @@ -0,0 +1,10 @@ +#+begin_export html +,* foo +,,,** bar + ,*** baz + lorem + , ipsum +,#+begin_src dolar + +,#+end_src +#+end_export diff --git a/org_mode_samples/lesser_element/lesser_block/export/with_no_data.org b/org_mode_samples/lesser_element/lesser_block/export/no_data.org similarity index 100% rename from org_mode_samples/lesser_element/lesser_block/export/with_no_data.org rename to org_mode_samples/lesser_element/lesser_block/export/no_data.org diff --git a/org_mode_samples/lesser_element/lesser_block/export/one_word.org b/org_mode_samples/lesser_element/lesser_block/export/one_word.org new file mode 100644 index 0000000..77bf40f --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/export/one_word.org @@ -0,0 +1,3 @@ +#+begin_export latex +This would be LaTeX code. +#+end_export diff --git a/org_mode_samples/lesser_element/lesser_block/export/with_two_words.org b/org_mode_samples/lesser_element/lesser_block/export/two_words.org similarity index 100% rename from org_mode_samples/lesser_element/lesser_block/export/with_two_words.org rename to org_mode_samples/lesser_element/lesser_block/export/two_words.org diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 22e9eb4..9c82f01 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -1845,9 +1845,19 @@ fn compare_export_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; - // TODO: Compare :type :value - + // TODO: Compare :type // TODO: Compare :caption + + // Compare value + let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new()); + if contents != rust.contents { + this_status = DiffStatus::Bad; + message = Some(format!( + "Value mismatch (emacs != rust) {:?} != {:?}", + contents, rust.contents + )); + } + // Compare name let name = get_property_quoted_string(emacs, ":name")?; if name.as_ref().map(String::as_str) != rust.name { diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index f4cf792..994f4f7 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -227,7 +227,7 @@ pub(crate) fn export_block<'b, 'g, 'r, 's>( None => None, }; - let (remaining, contents) = parser_with_context!(text_until_exit)(&parser_context)(remaining)?; + let (remaining, contents) = content(&parser_context, remaining)?; let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?; let source = get_consumed(input, remaining); @@ -237,7 +237,7 @@ pub(crate) fn export_block<'b, 'g, 'r, 's>( source: source.into(), name: get_name(&affiliated_keywords), data: parameters.map(|parameters| Into::<&str>::into(parameters)), - contents: contents.into(), + contents, }, )) } diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index 91091fc..b62f61c 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -65,7 +65,7 @@ pub struct ExportBlock<'s> { pub source: &'s str, pub name: Option<&'s str>, pub data: Option<&'s str>, - pub contents: &'s str, + pub contents: String, } #[derive(Debug)] From 58ca9569a6c41e9665694b022a790788c5be20c7 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 5 Oct 2023 02:15:32 -0400 Subject: [PATCH 50/54] Compare export type. --- src/compare/diff.rs | 12 +++++++++++- src/parser/lesser_block.rs | 4 ++++ src/types/lesser_element.rs | 7 +++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 9c82f01..cef815e 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -1845,9 +1845,19 @@ fn compare_export_block<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; - // TODO: Compare :type // TODO: Compare :caption + // Compare type + let export_type = get_property_quoted_string(emacs, ":type")?; + let rust_export_type = rust.get_export_type(); + if export_type != rust_export_type { + this_status = DiffStatus::Bad; + message = Some(format!( + "Export type mismatch (emacs != rust) {:?} != {:?}", + export_type, rust.export_type + )); + } + // Compare value let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new()); if contents != rust.contents { diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 994f4f7..fe987cb 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -208,6 +208,9 @@ pub(crate) fn export_block<'b, 'g, 'r, 's>( let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; let (remaining, _) = lesser_block_begin("export")(context, input)?; // https://orgmode.org/worg/org-syntax.html#Blocks claims that export blocks must have a single word for data but testing shows no data and multi-word data still parses as an export block. + let (remaining, export_type) = opt(map(tuple((space1, switch_word)), |(_, export_type)| { + export_type + }))(remaining)?; let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?; let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; let lesser_block_end_specialized = lesser_block_end("export"); @@ -236,6 +239,7 @@ pub(crate) fn export_block<'b, 'g, 'r, 's>( ExportBlock { source: source.into(), name: get_name(&affiliated_keywords), + export_type: export_type.map(Into::<&str>::into), data: parameters.map(|parameters| Into::<&str>::into(parameters)), contents, }, diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index b62f61c..4b41a56 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -64,6 +64,7 @@ pub struct ExampleBlock<'s> { pub struct ExportBlock<'s> { pub source: &'s str, pub name: Option<&'s str>, + pub export_type: Option<&'s str>, pub data: Option<&'s str>, pub contents: String, } @@ -266,3 +267,9 @@ impl<'s> Comment<'s> { ret } } + +impl<'s> ExportBlock<'s> { + pub fn get_export_type(&self) -> Option { + self.export_type.map(|s| s.to_uppercase()) + } +} From ac7125d9b65b38742db79de11d66489cf9e06841 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 5 Oct 2023 02:17:53 -0400 Subject: [PATCH 51/54] Only allow a single export type for export blocks. --- src/parser/lesser_block.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index fe987cb..5e6c9d0 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -208,10 +208,12 @@ pub(crate) fn export_block<'b, 'g, 'r, 's>( let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; let (remaining, _) = lesser_block_begin("export")(context, input)?; // https://orgmode.org/worg/org-syntax.html#Blocks claims that export blocks must have a single word for data but testing shows no data and multi-word data still parses as an export block. - let (remaining, export_type) = opt(map(tuple((space1, switch_word)), |(_, export_type)| { - export_type - }))(remaining)?; - let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?; + let (remaining, export_type) = opt(map( + tuple((space1, switch_word, peek(tuple((space0, line_ending))))), + |(_, export_type, _)| export_type, + ))(remaining)?; + let (remaining, parameters) = + opt(map(tuple((space1, data)), |(_, parameters)| parameters))(remaining)?; let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; let lesser_block_end_specialized = lesser_block_end("export"); let contexts = [ @@ -225,10 +227,6 @@ pub(crate) fn export_block<'b, 'g, 'r, 's>( let parser_context = context.with_additional_node(&contexts[0]); let parser_context = parser_context.with_additional_node(&contexts[1]); let parser_context = parser_context.with_additional_node(&contexts[2]); - let parameters = match parameters { - Some((_ws, parameters)) => Some(parameters), - None => None, - }; let (remaining, contents) = content(&parser_context, remaining)?; let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?; From ef8a6884fe0ead551772b588f200c8ae3c027fe4 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 5 Oct 2023 02:20:25 -0400 Subject: [PATCH 52/54] Remove outdated TODO. --- src/parser/element_parser.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/parser/element_parser.rs b/src/parser/element_parser.rs index 55bc649..238dade 100644 --- a/src/parser/element_parser.rs +++ b/src/parser/element_parser.rs @@ -201,7 +201,6 @@ fn _detect_element<'b, 'g, 'r, 's>( input: OrgSource<'s>, can_be_paragraph: bool, ) -> Res, ()> { - // TODO: What about affiliated keywords in the detect_* functions? if alt(( parser_with_context!(detect_plain_list)(context), detect_footnote_definition, From e21701b97c96ef33cf9977720e3d6f10bf864133 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 5 Oct 2023 02:26:11 -0400 Subject: [PATCH 53/54] Cleanup. --- src/parser/element_parser.rs | 13 ------------- src/types/lesser_element.rs | 3 +++ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/parser/element_parser.rs b/src/parser/element_parser.rs index 238dade..81dc435 100644 --- a/src/parser/element_parser.rs +++ b/src/parser/element_parser.rs @@ -128,19 +128,6 @@ fn _element<'b, 'g, 'r, 's>( } } - // if maybe_element.is_none() { - // #[cfg(feature = "tracing")] - // let span = span!(tracing::Level::DEBUG, "Regular keyword."); - // #[cfg(feature = "tracing")] - // let _enter = span.enter(); - - // let (remain, kw) = opt(map(keyword_matcher, Element::Keyword))(remaining)?; - // if kw.is_some() { - // maybe_element = kw; - // remaining = remain; - // } - // } - if maybe_element.is_none() { #[cfg(feature = "tracing")] let span = span!( diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index 4b41a56..3a78c9d 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -269,6 +269,9 @@ impl<'s> Comment<'s> { } impl<'s> ExportBlock<'s> { + /// Gets the export type capitalized. + /// + /// Upstream Emacs Org-mode capitalizes the export type. pub fn get_export_type(&self) -> Option { self.export_type.map(|s| s.to_uppercase()) } From 40685f05cc3d474d797d66dceb2fa75616fcdc57 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 5 Oct 2023 02:28:24 -0400 Subject: [PATCH 54/54] Add a test for multiple names on a single element. --- org_mode_samples/affiliated_keyword/multiple_name.org | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 org_mode_samples/affiliated_keyword/multiple_name.org diff --git a/org_mode_samples/affiliated_keyword/multiple_name.org b/org_mode_samples/affiliated_keyword/multiple_name.org new file mode 100644 index 0000000..eea2539 --- /dev/null +++ b/org_mode_samples/affiliated_keyword/multiple_name.org @@ -0,0 +1,6 @@ +#+name: foo +#+source: bar +#+name: baz +#+tblname: lorem +#+label: ipsum +: dolar