23 Commits

Author SHA1 Message Date
Tom Alexander
59222c58b1 Publish version 0.1.13.
All checks were successful
rustfmt Build rustfmt has succeeded
clippy Build clippy has succeeded
rust-build Build rust-build has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
rust-test Build rust-test has succeeded
2023-12-15 21:10:21 -05:00
Tom Alexander
4d95a7f244 Merge branch 'post_blank'
All checks were successful
clippy Build clippy has succeeded
rustfmt Build rustfmt has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
2023-12-15 20:11:20 -05:00
Tom Alexander
5a8159eed7 Fix clippy.
All checks were successful
clippy Build clippy has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-12-15 19:57:35 -05:00
Tom Alexander
e24fcb9ded Add dummy values for new fields for plaintext. 2023-12-15 19:54:03 -05:00
Tom Alexander
4b94dc60d2 Fix handling of documents containing only whitespace. 2023-12-15 19:49:12 -05:00
Tom Alexander
2046603d01 Fix handling post blank for org documents. 2023-12-15 19:42:43 -05:00
Tom Alexander
30412361e1 Fix handling fixed width area post-blank inside a list. 2023-12-15 19:37:33 -05:00
Tom Alexander
e846c85188 Fix handling fixed width areas with empty lines in the middle. 2023-12-15 19:17:16 -05:00
Tom Alexander
99b74095e6 Fix heading post-blank. 2023-12-15 19:10:14 -05:00
Tom Alexander
6b802d36bf Implement the new fields for target. 2023-12-15 18:57:19 -05:00
Tom Alexander
33ca43ca40 Remove the old Paragraph::of_text function.
Some checks failed
clippy Build clippy has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
2023-12-15 18:06:48 -05:00
Tom Alexander
f5280a3090 Implement the new fields for bullshitium broken dynamic block. 2023-12-15 18:04:42 -05:00
Tom Alexander
c28d8ccea4 Fix post-blank for headlines containing only whitespace. 2023-12-15 17:59:47 -05:00
Tom Alexander
9690545901 Fix setting contents for broken end bullshitium when there is a paragraph present.
Some checks failed
clippy Build clippy has failed
rust-build Build rust-build has succeeded
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-test Build rust-test has failed
2023-12-15 17:40:08 -05:00
Tom Alexander
eba4fb94cf Implement the new fields for dynamic block. 2023-12-15 17:26:01 -05:00
Tom Alexander
565978225a Implement the new fields for table.
Some checks failed
clippy Build clippy has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-build Build rust-build has succeeded
rust-test Build rust-test has failed
2023-12-15 17:16:49 -05:00
Tom Alexander
cce9ca87fa Fix handling of leading blank lines in greater blocks.
Some checks failed
clippy Build clippy has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-build Build rust-build has succeeded
rust-test Build rust-test has failed
2023-12-15 16:55:47 -05:00
Tom Alexander
683c523ece Implement the new fields for greater block. 2023-12-15 16:15:22 -05:00
Tom Alexander
7a4dc20dc9 Implement the new fields for plain list.
Some checks failed
clippy Build clippy has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-build Build rust-build has succeeded
rust-test Build rust-test has failed
2023-12-15 15:59:00 -05:00
Tom Alexander
022dda06eb Implement the new fields for plain list item. 2023-12-15 15:52:53 -05:00
Tom Alexander
7b88a2d248 Implement the new fields for broken end bullshitium. 2023-12-15 15:40:17 -05:00
Tom Alexander
fce5b92091 Remove leading blank lines from document contents. 2023-12-15 15:30:46 -05:00
Tom Alexander
45a506334c Remove leading blank lines from heading contents. 2023-12-15 15:20:31 -05:00
25 changed files with 282 additions and 160 deletions

View File

@@ -2,7 +2,7 @@
[package] [package]
name = "organic" name = "organic"
version = "0.1.12" version = "0.1.13"
authors = ["Tom Alexander <tom@fizz.buzz>"] authors = ["Tom Alexander <tom@fizz.buzz>"]
description = "An org-mode parser." description = "An org-mode parser."
edition = "2021" edition = "2021"

View File

View File

@@ -0,0 +1,4 @@

View File

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

View File

@@ -0,0 +1,3 @@
: foo
:
: bar

View File

@@ -0,0 +1,6 @@
1. foo
#+begin_src text
#+end_src
2. baz

View File

@@ -0,0 +1,3 @@
<<FOO>> bar
[[FOO][baz]]

View File

@@ -0,0 +1,5 @@
* foo
** bar
* baz

View File

@@ -285,7 +285,7 @@ where
pub(crate) fn compare_children<'b, 's, 'x, RC>( pub(crate) fn compare_children<'b, 's, 'x, RC>(
source: &'s str, source: &'s str,
emacs: &'b Token<'s>, emacs: &'b Token<'s>,
rust_children: &'x Vec<RC>, rust_children: &'x [RC],
child_status: &mut Vec<DiffEntry<'b, 's>>, child_status: &mut Vec<DiffEntry<'b, 's>>,
this_status: &mut DiffStatus, this_status: &mut DiffStatus,
message: &mut Option<String>, message: &mut Option<String>,

View File

@@ -27,7 +27,7 @@ pub(crate) fn record_event(event_type: EventType, input: OrgSource<'_>) {
pub fn report(original_document: &str) { pub fn report(original_document: &str) {
let mut db = GLOBAL_DATA.lock().unwrap(); let mut db = GLOBAL_DATA.lock().unwrap();
let db = db.get_or_insert_with(HashMap::new); let db = db.get_or_insert_with(HashMap::new);
let mut results: Vec<_> = db.iter().map(|(k, v)| (k, v)).collect(); let mut results: Vec<_> = db.iter().collect();
results.sort_by_key(|(_k, v)| *v); results.sort_by_key(|(_k, v)| *v);
// This would put the most common at the top, but that is a pain when there is already a lot of output from the parser. // This would put the most common at the top, but that is a pain when there is already a lot of output from the parser.
// results.sort_by(|(_ak, av), (_bk, bv)| bv.cmp(av)); // results.sort_by(|(_ak, av), (_bk, bv)| bv.cmp(av));

View File

@@ -63,6 +63,7 @@ pub(crate) fn broken_end<'b, 'g, 'r, 's>(
match paragraph.children.first_mut() { match paragraph.children.first_mut() {
Some(Object::PlainText(plain_text)) => { Some(Object::PlainText(plain_text)) => {
plain_text.source = input.get_until_end_of_str(plain_text.source).into(); plain_text.source = input.get_until_end_of_str(plain_text.source).into();
paragraph.contents = Some(input.get_until_end_of_str(plain_text.source).into());
} }
Some(obj) => { Some(obj) => {
panic!("Unhandled first object type inside bullshitium {:?}", obj); panic!("Unhandled first object type inside bullshitium {:?}", obj);
@@ -73,14 +74,18 @@ pub(crate) fn broken_end<'b, 'g, 'r, 's>(
}; };
Ok((remaining, paragraph)) Ok((remaining, paragraph))
} else { } else {
let (remaining, _trailing_ws) = let (remaining, post_blank) =
maybe_consume_trailing_whitespace_if_not_exiting(context, lead_in_remaining)?; maybe_consume_trailing_whitespace_if_not_exiting(context, lead_in_remaining)?;
let body = Into::<&str>::into(input.get_until(lead_in_remaining));
Ok(( Ok((
remaining, remaining,
Paragraph::of_text( Paragraph::of_text(
input.get_until(remaining).into(), input.get_until(remaining).into(),
input.get_until(lead_in_remaining).into(), body,
if !body.is_empty() { Some(body) } else { None },
post_blank.map(Into::<&str>::into),
), ),
)) ))
} }
@@ -119,6 +124,7 @@ pub(crate) fn broken_dynamic_block<'b, 'g, 'r, 's>(
match paragraph.children.first_mut() { match paragraph.children.first_mut() {
Some(Object::PlainText(plain_text)) => { Some(Object::PlainText(plain_text)) => {
plain_text.source = input.get_until_end_of_str(plain_text.source).into(); plain_text.source = input.get_until_end_of_str(plain_text.source).into();
paragraph.contents = Some(input.get_until_end_of_str(plain_text.source).into());
} }
Some(obj) => { Some(obj) => {
panic!("Unhandled first object type inside bullshitium {:?}", obj); panic!("Unhandled first object type inside bullshitium {:?}", obj);
@@ -129,14 +135,18 @@ pub(crate) fn broken_dynamic_block<'b, 'g, 'r, 's>(
}; };
Ok((remaining, paragraph)) Ok((remaining, paragraph))
} else { } else {
let (remaining, _trailing_ws) = let (remaining, post_blank) =
maybe_consume_trailing_whitespace_if_not_exiting(context, lead_in_remaining)?; maybe_consume_trailing_whitespace_if_not_exiting(context, lead_in_remaining)?;
let body = Into::<&str>::into(input.get_until(lead_in_remaining));
Ok(( Ok((
remaining, remaining,
Paragraph::of_text( Paragraph::of_text(
input.get_until(remaining).into(), input.get_until(remaining).into(),
input.get_until(lead_in_remaining).into(), body,
if !body.is_empty() { Some(body) } else { None },
post_blank.map(Into::<&str>::into),
), ),
)) ))
} }

View File

@@ -3,6 +3,7 @@ use std::path::Path;
use nom::combinator::all_consuming; use nom::combinator::all_consuming;
use nom::combinator::opt; use nom::combinator::opt;
use nom::multi::many0; use nom::multi::many0;
use nom::InputTake;
use super::headline::heading; use super::headline::heading;
use super::in_buffer_settings::apply_in_buffer_settings; use super::in_buffer_settings::apply_in_buffer_settings;
@@ -181,8 +182,10 @@ fn _document<'b, 'g, 'r, 's>(
let zeroth_section_matcher = parser_with_context!(zeroth_section)(context); let zeroth_section_matcher = parser_with_context!(zeroth_section)(context);
let heading_matcher = parser_with_context!(heading(0))(context); let heading_matcher = parser_with_context!(heading(0))(context);
let (remaining, _blank_lines) = many0(blank_line)(input)?; let (remaining, _blank_lines) = many0(blank_line)(input)?;
let contents_begin = remaining;
let (remaining, zeroth_section) = opt(zeroth_section_matcher)(remaining)?; let (remaining, zeroth_section) = opt(zeroth_section_matcher)(remaining)?;
let (remaining, children) = many0(heading_matcher)(remaining)?; let (remaining, children) = many0(heading_matcher)(remaining)?;
let contents = get_consumed(contents_begin, remaining);
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok(( Ok((
remaining, remaining,
@@ -192,6 +195,11 @@ fn _document<'b, 'g, 'r, 's>(
path: None, path: None,
zeroth_section, zeroth_section,
children, children,
contents: if contents.len() > 0 {
Into::<&str>::into(contents)
} else {
Into::<&str>::into(remaining.take(0))
},
}, },
)) ))
} }

View File

@@ -6,20 +6,20 @@ use nom::character::complete::anychar;
use nom::character::complete::line_ending; use nom::character::complete::line_ending;
use nom::character::complete::space0; use nom::character::complete::space0;
use nom::character::complete::space1; use nom::character::complete::space1;
use nom::combinator::consumed;
use nom::combinator::eof; use nom::combinator::eof;
use nom::combinator::not; use nom::combinator::not;
use nom::combinator::opt; use nom::combinator::opt;
use nom::combinator::peek; use nom::combinator::peek;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::multi::many0;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::preceded;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::affiliated_keyword::parse_affiliated_keywords; use super::affiliated_keyword::parse_affiliated_keywords;
use super::greater_block::leading_blank_lines_end;
use super::org_source::OrgSource; use super::org_source::OrgSource;
use super::paragraph::empty_paragraph;
use super::util::maybe_consume_trailing_whitespace_if_not_exiting; use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
use crate::context::bind_context;
use crate::context::parser_with_context; use crate::context::parser_with_context;
use crate::context::ContextElement; use crate::context::ContextElement;
use crate::context::ExitClass; use crate::context::ExitClass;
@@ -28,7 +28,6 @@ use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::Res; use crate::error::Res;
use crate::parser::element_parser::element; use crate::parser::element_parser::element;
use crate::parser::util::blank_line;
use crate::parser::util::exit_matcher_parser; use crate::parser::util::exit_matcher_parser;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
use crate::parser::util::immediate_in_section; use crate::parser::util::immediate_in_section;
@@ -36,7 +35,6 @@ use crate::parser::util::start_of_line;
use crate::types::DynamicBlock; use crate::types::DynamicBlock;
use crate::types::Element; use crate::types::Element;
use crate::types::Keyword; use crate::types::Keyword;
use crate::types::Paragraph;
#[cfg_attr( #[cfg_attr(
feature = "tracing", feature = "tracing",
@@ -81,23 +79,25 @@ where
let element_matcher = parser_with_context!(element(true))(&parser_context); let element_matcher = parser_with_context!(element(true))(&parser_context);
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
not(exit_matcher)(remaining)?; not(exit_matcher)(remaining)?;
let (remaining, leading_blank_lines) = opt(consumed(tuple(( let contents_begin = remaining;
blank_line, let blank_line_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
many0(preceded(not(exit_matcher), blank_line)), class: ExitClass::Alpha,
))))(remaining)?; exit_matcher: &leading_blank_lines_end,
let leading_blank_lines =
leading_blank_lines.map(|(source, (first_line, _remaining_lines))| {
Element::Paragraph(Paragraph::of_text(source.into(), first_line.into()))
}); });
let blank_line_context = parser_context.with_additional_node(&blank_line_context);
let (remaining, leading_blank_lines) =
opt(bind_context!(empty_paragraph, &blank_line_context))(remaining)?;
let (remaining, (mut children, _exit_contents)) = let (remaining, (mut children, _exit_contents)) =
many_till(element_matcher, exit_matcher)(remaining)?; many_till(element_matcher, exit_matcher)(remaining)?;
if let Some(lines) = leading_blank_lines { if let Some(lines) = leading_blank_lines {
children.insert(0, lines); children.insert(0, Element::Paragraph(lines));
} }
let contents = get_consumed(contents_begin, remaining);
let (remaining, _end) = dynamic_block_end(&parser_context, remaining)?; let (remaining, _end) = dynamic_block_end(&parser_context, remaining)?;
let (remaining, _trailing_ws) = let (remaining, post_blank) =
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok(( Ok((
@@ -111,6 +111,12 @@ where
block_name: name.into(), block_name: name.into(),
parameters: parameters.map(|val| val.into()), parameters: parameters.map(|val| val.into()),
children, children,
contents: if contents.len() > 0 {
Some(Into::<&str>::into(contents))
} else {
None
},
post_blank: post_blank.map(Into::<&str>::into),
}, },
)) ))
} }

View File

@@ -14,7 +14,7 @@ use nom::InputTake;
use super::affiliated_keyword::parse_affiliated_keywords; use super::affiliated_keyword::parse_affiliated_keywords;
use super::org_source::OrgSource; use super::org_source::OrgSource;
use super::util::maybe_consume_trailing_whitespace_if_not_exiting_mid_line; use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
use super::util::org_line_ending; use super::util::org_line_ending;
use crate::context::parser_with_context; use crate::context::parser_with_context;
use crate::context::RefContext; use crate::context::RefContext;
@@ -48,8 +48,11 @@ where
), ),
))(remaining)?; ))(remaining)?;
let (remaining, post_blank) = let post_blank_begin = remaining;
maybe_consume_trailing_whitespace_if_not_exiting_mid_line(context, remaining)?; let (remaining, _first_line_break) = org_line_ending(remaining)?;
let (remaining, _additional_post_blank) =
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
let post_blank = get_consumed(post_blank_begin, remaining);
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
let mut value = Vec::with_capacity(remaining_lines.len() + 1); let mut value = Vec::with_capacity(remaining_lines.len() + 1);
value.push(Into::<&str>::into(first_line)); value.push(Into::<&str>::into(first_line));
@@ -63,7 +66,11 @@ where
affiliated_keywords, affiliated_keywords,
), ),
value, value,
post_blank: post_blank.map(Into::<&str>::into), post_blank: if post_blank.len() > 0 {
Some(Into::<&str>::into(post_blank))
} else {
None
},
}, },
)) ))
} }
@@ -72,8 +79,8 @@ where
fn fixed_width_area_line<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> { fn fixed_width_area_line<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
start_of_line(input)?; start_of_line(input)?;
let (remaining, _) = tuple((space0, tag(":")))(input)?; let (remaining, _) = tuple((space0, tag(":")))(input)?;
if let Ok((remain, _line_break)) = org_line_ending(remaining) { if let Ok((_remain, _line_break)) = org_line_ending(remaining) {
return Ok((remain, remaining.take(0))); return Ok((remaining, remaining.take(0)));
} }
let (remaining, _) = tag(" ")(remaining)?; let (remaining, _) = tag(" ")(remaining)?;
let (remaining, value) = recognize(many_till(anychar, peek(org_line_ending)))(remaining)?; let (remaining, value) = recognize(many_till(anychar, peek(org_line_ending)))(remaining)?;

View File

@@ -5,22 +5,21 @@ use nom::character::complete::anychar;
use nom::character::complete::line_ending; use nom::character::complete::line_ending;
use nom::character::complete::space0; use nom::character::complete::space0;
use nom::character::complete::space1; use nom::character::complete::space1;
use nom::combinator::consumed;
use nom::combinator::eof; use nom::combinator::eof;
use nom::combinator::not; use nom::combinator::not;
use nom::combinator::opt; use nom::combinator::opt;
use nom::combinator::peek; use nom::combinator::peek;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::combinator::verify; use nom::combinator::verify;
use nom::multi::many0;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::preceded;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::affiliated_keyword::parse_affiliated_keywords; use super::affiliated_keyword::parse_affiliated_keywords;
use super::org_source::OrgSource; use super::org_source::OrgSource;
use super::paragraph::empty_paragraph;
use super::util::in_section; use super::util::in_section;
use super::util::maybe_consume_trailing_whitespace_if_not_exiting; use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
use crate::context::bind_context;
use crate::context::parser_with_context; use crate::context::parser_with_context;
use crate::context::ContextElement; use crate::context::ContextElement;
use crate::context::ContextMatcher; use crate::context::ContextMatcher;
@@ -37,7 +36,6 @@ use crate::parser::util::start_of_line;
use crate::types::CenterBlock; use crate::types::CenterBlock;
use crate::types::Element; use crate::types::Element;
use crate::types::Keyword; use crate::types::Keyword;
use crate::types::Paragraph;
use crate::types::QuoteBlock; use crate::types::QuoteBlock;
use crate::types::SpecialBlock; use crate::types::SpecialBlock;
@@ -102,7 +100,7 @@ fn center_block<'b, 'g, 'r, 's, AK>(
where where
AK: IntoIterator<Item = Keyword<'s>>, AK: IntoIterator<Item = Keyword<'s>>,
{ {
let (remaining, (source, children)) = greater_block_body( let (remaining, body) = greater_block_body(
context, context,
input, input,
pre_affiliated_keywords_input, pre_affiliated_keywords_input,
@@ -112,12 +110,14 @@ where
Ok(( Ok((
remaining, remaining,
Element::CenterBlock(CenterBlock { Element::CenterBlock(CenterBlock {
source, source: body.source,
affiliated_keywords: parse_affiliated_keywords( affiliated_keywords: parse_affiliated_keywords(
context.get_global_settings(), context.get_global_settings(),
affiliated_keywords, affiliated_keywords,
), ),
children, children: body.children,
contents: body.contents,
post_blank: body.post_blank,
}), }),
)) ))
} }
@@ -135,7 +135,7 @@ fn quote_block<'b, 'g, 'r, 's, AK>(
where where
AK: IntoIterator<Item = Keyword<'s>>, AK: IntoIterator<Item = Keyword<'s>>,
{ {
let (remaining, (source, children)) = greater_block_body( let (remaining, body) = greater_block_body(
context, context,
input, input,
pre_affiliated_keywords_input, pre_affiliated_keywords_input,
@@ -145,12 +145,14 @@ where
Ok(( Ok((
remaining, remaining,
Element::QuoteBlock(QuoteBlock { Element::QuoteBlock(QuoteBlock {
source, source: body.source,
affiliated_keywords: parse_affiliated_keywords( affiliated_keywords: parse_affiliated_keywords(
context.get_global_settings(), context.get_global_settings(),
affiliated_keywords, affiliated_keywords,
), ),
children, children: body.children,
contents: body.contents,
post_blank: body.post_blank,
}), }),
)) ))
} }
@@ -196,7 +198,7 @@ where
AK: IntoIterator<Item = Keyword<'s>>, AK: IntoIterator<Item = Keyword<'s>>,
{ {
let (remaining, parameters) = opt(tuple((space1, parameters)))(input)?; let (remaining, parameters) = opt(tuple((space1, parameters)))(input)?;
let (remaining, (source, children)) = greater_block_body( let (remaining, body) = greater_block_body(
context, context,
remaining, remaining,
pre_affiliated_keywords_input, pre_affiliated_keywords_input,
@@ -206,18 +208,28 @@ where
Ok(( Ok((
remaining, remaining,
Element::SpecialBlock(SpecialBlock { Element::SpecialBlock(SpecialBlock {
source, source: body.source,
affiliated_keywords: parse_affiliated_keywords( affiliated_keywords: parse_affiliated_keywords(
context.get_global_settings(), context.get_global_settings(),
affiliated_keywords, affiliated_keywords,
), ),
children, children: body.children,
block_type: name, block_type: name,
parameters: parameters.map(|(_, parameters)| Into::<&str>::into(parameters)), parameters: parameters.map(|(_, parameters)| Into::<&str>::into(parameters)),
contents: body.contents,
post_blank: body.post_blank,
}), }),
)) ))
} }
#[derive(Debug)]
struct GreaterBlockBody<'s> {
source: &'s str,
children: Vec<Element<'s>>,
contents: Option<&'s str>,
post_blank: Option<&'s str>,
}
#[cfg_attr( #[cfg_attr(
feature = "tracing", feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context)) tracing::instrument(ret, level = "debug", skip(context))
@@ -228,7 +240,7 @@ fn greater_block_body<'c, 'b, 'g, 'r, 's>(
pre_affiliated_keywords_input: OrgSource<'s>, pre_affiliated_keywords_input: OrgSource<'s>,
name: &'c str, name: &'c str,
context_name: &'c str, context_name: &'c str,
) -> Res<OrgSource<'s>, (&'s str, Vec<Element<'s>>)> { ) -> Res<OrgSource<'s>, GreaterBlockBody<'s>> {
if in_section(context, context_name) { if in_section(context, context_name) {
return Err(nom::Err::Error(CustomError::Static( return Err(nom::Err::Error(CustomError::Static(
"Cannot nest objects of the same element", "Cannot nest objects of the same element",
@@ -250,28 +262,43 @@ fn greater_block_body<'c, 'b, 'g, 'r, 's>(
let element_matcher = parser_with_context!(element(true))(&parser_context); let element_matcher = parser_with_context!(element(true))(&parser_context);
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
not(exit_matcher)(remaining)?; not(exit_matcher)(remaining)?;
let (remaining, leading_blank_lines) = opt(consumed(tuple(( let contents_begin = remaining;
blank_line,
many0(preceded(not(exit_matcher), blank_line)), let blank_line_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
))))(remaining)?; class: ExitClass::Alpha,
let leading_blank_lines = exit_matcher: &leading_blank_lines_end,
leading_blank_lines.map(|(source, (first_line, _remaining_lines))| {
Element::Paragraph(Paragraph::of_text(source.into(), first_line.into()))
}); });
let blank_line_context = parser_context.with_additional_node(&blank_line_context);
let (remaining, leading_blank_lines) =
opt(bind_context!(empty_paragraph, &blank_line_context))(remaining)?;
let (remaining, (mut children, _exit_contents)) = let (remaining, (mut children, _exit_contents)) =
many_till(element_matcher, exit_matcher)(remaining)?; many_till(element_matcher, exit_matcher)(remaining)?;
if let Some(lines) = leading_blank_lines { if let Some(lines) = leading_blank_lines {
children.insert(0, lines); children.insert(0, Element::Paragraph(lines));
} }
let contents = get_consumed(contents_begin, remaining);
let (remaining, _end) = exit_with_name(&parser_context, remaining)?; let (remaining, _end) = exit_with_name(&parser_context, remaining)?;
// Not checking if parent exit matcher is causing exit because the greater_block_end matcher asserts we matched a full greater block // Not checking if parent exit matcher is causing exit because the greater_block_end matcher asserts we matched a full greater block
let (remaining, _trailing_ws) = let (remaining, post_blank) =
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(pre_affiliated_keywords_input, remaining); let source = get_consumed(pre_affiliated_keywords_input, remaining);
Ok((remaining, (Into::<&str>::into(source), children))) Ok((
remaining,
GreaterBlockBody {
source: Into::<&str>::into(source),
children,
contents: if contents.len() > 0 {
Some(Into::<&str>::into(contents))
} else {
None
},
post_blank: post_blank.map(Into::<&str>::into),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
@@ -307,3 +334,14 @@ fn _greater_block_end<'b, 'g, 'r, 's, 'c>(
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, source)) Ok((remaining, source))
} }
#[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(_context))
)]
pub(crate) fn leading_blank_lines_end<'b, 'g, 'r, 's, 'c>(
_context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(not(blank_line))(input)
}

View File

@@ -66,7 +66,8 @@ fn _heading<'b, 'g, 'r, 's>(
let (remaining, pre_headline) = headline(context, input, parent_star_count)?; let (remaining, pre_headline) = headline(context, input, parent_star_count)?;
let section_matcher = bind_context!(section, context); let section_matcher = bind_context!(section, context);
let heading_matcher = bind_context!(heading(pre_headline.star_count), context); let heading_matcher = bind_context!(heading(pre_headline.star_count), context);
let contents_begin = remaining; let (contents_begin, _) = opt(many0(blank_line))(remaining)?;
let maybe_post_blank = get_consumed(remaining, contents_begin);
let (remaining, maybe_section) = let (remaining, maybe_section) =
opt(map(section_matcher, DocumentElement::Section))(remaining)?; opt(map(section_matcher, DocumentElement::Section))(remaining)?;
let (remaining, _ws) = opt(tuple((start_of_line, many0(blank_line))))(remaining)?; let (remaining, _ws) = opt(tuple((start_of_line, many0(blank_line))))(remaining)?;
@@ -83,7 +84,8 @@ fn _heading<'b, 'g, 'r, 's>(
} }
children.insert(0, section); children.insert(0, section);
} }
let remaining = if children.is_empty() { let has_children = !children.is_empty();
let remaining = if !has_children {
// Support empty headings // Support empty headings
let (remain, _ws) = many0(blank_line)(remaining)?; let (remain, _ws) = many0(blank_line)(remaining)?;
remain remain
@@ -119,6 +121,11 @@ fn _heading<'b, 'g, 'r, 's>(
} else { } else {
None None
}, },
post_blank: if has_children {
None
} else {
Some(Into::<&str>::into(maybe_post_blank))
},
}, },
)) ))
} }

View File

@@ -101,7 +101,7 @@ pub(crate) fn empty_paragraph<'b, 'g, 'r, 's>(
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok(( Ok((
remaining, remaining,
Paragraph::of_text_full( Paragraph::of_text(
Into::<&str>::into(source), Into::<&str>::into(source),
Into::<&str>::into(first_line_with_spaces), Into::<&str>::into(first_line_with_spaces),
Some(Into::<&str>::into(first_line_with_spaces)), Some(Into::<&str>::into(first_line_with_spaces)),
@@ -116,7 +116,7 @@ pub(crate) fn empty_paragraph<'b, 'g, 'r, 's>(
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok(( Ok((
remaining, remaining,
Paragraph::of_text_full( Paragraph::of_text(
Into::<&str>::into(source), Into::<&str>::into(source),
Into::<&str>::into(first_line), Into::<&str>::into(first_line),
Some(Into::<&str>::into(first_line)), Some(Into::<&str>::into(first_line)),

View File

@@ -7,6 +7,7 @@ use nom::character::complete::multispace1;
use nom::character::complete::one_of; use nom::character::complete::one_of;
use nom::character::complete::space0; use nom::character::complete::space0;
use nom::character::complete::space1; use nom::character::complete::space1;
use nom::combinator::consumed;
use nom::combinator::eof; use nom::combinator::eof;
use nom::combinator::map; use nom::combinator::map;
use nom::combinator::not; use nom::combinator::not;
@@ -152,6 +153,7 @@ where
let mut children = Vec::new(); let mut children = Vec::new();
let mut first_item_indentation: Option<IndentationLevel> = None; let mut first_item_indentation: Option<IndentationLevel> = None;
let mut first_item_list_type: Option<PlainListType> = None; let mut first_item_list_type: Option<PlainListType> = None;
let contents_begin = remaining;
let mut remaining = remaining; let mut remaining = remaining;
// The final list item does not consume trailing blank lines (which instead get consumed by the list). We have three options here: // The final list item does not consume trailing blank lines (which instead get consumed by the list). We have three options here:
@@ -195,7 +197,8 @@ where
))); )));
} }
let (remaining, _trailing_ws) = let contents = get_consumed(contents_begin, remaining);
let (remaining, post_blank) =
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok(( Ok((
@@ -208,6 +211,8 @@ where
), ),
list_type: first_item_list_type.expect("Plain lists require at least one element."), list_type: first_item_list_type.expect("Plain lists require at least one element."),
children: children.into_iter().map(|(_start, item)| item).collect(), children: children.into_iter().map(|(_start, item)| item).collect(),
contents: Some(Into::<&str>::into(contents)),
post_blank: post_blank.map(Into::<&str>::into),
}, },
)) ))
} }
@@ -265,7 +270,7 @@ fn plain_list_item<'b, 'g, 'r, 's>(
let maybe_contentless_item: Res<OrgSource<'_>, ()> = let maybe_contentless_item: Res<OrgSource<'_>, ()> =
detect_contentless_item_contents(&parser_context, remaining); detect_contentless_item_contents(&parser_context, remaining);
if let Ok((_rem, _ws)) = maybe_contentless_item { if let Ok((_rem, _ws)) = maybe_contentless_item {
let (remaining, _trailing_ws) = if tuple(( let (remaining, post_blank) = if tuple((
blank_line, blank_line,
bind_context!(final_item_whitespace_cutoff, context), bind_context!(final_item_whitespace_cutoff, context),
))(remaining) ))(remaining)
@@ -291,6 +296,12 @@ fn plain_list_item<'b, 'g, 'r, 's>(
.unwrap_or(Vec::new()), .unwrap_or(Vec::new()),
pre_blank: 0, pre_blank: 0,
children: Vec::new(), children: Vec::new(),
contents: None,
post_blank: if post_blank.len() > 0 {
Some(Into::<&str>::into(post_blank))
} else {
None
},
}, },
), ),
)); ));
@@ -301,13 +312,13 @@ fn plain_list_item<'b, 'g, 'r, 's>(
.filter(|b| *b == b'\n') .filter(|b| *b == b'\n')
.count(); .count();
let (remaining, (children, _exit_contents)) = many_till( let (remaining, (contents, (children, _exit_contents))) = consumed(many_till(
include_input(bind_context!(element(true), &parser_context)), include_input(bind_context!(element(true), &parser_context)),
bind_context!(exit_matcher_parser, &parser_context), bind_context!(exit_matcher_parser, &parser_context),
)(remaining)?; ))(remaining)?;
// We have to use the parser_context here to include the whitespace cut-off // We have to use the parser_context here to include the whitespace cut-off
let (remaining, _trailing_ws) = let (remaining, post_blank) =
maybe_consume_trailing_whitespace_if_not_exiting(&final_whitespace_context, remaining)?; maybe_consume_trailing_whitespace_if_not_exiting(&final_whitespace_context, remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
@@ -329,6 +340,12 @@ fn plain_list_item<'b, 'g, 'r, 's>(
pre_blank: PlainListItemPreBlank::try_from(pre_blank) pre_blank: PlainListItemPreBlank::try_from(pre_blank)
.expect("pre-blank cannot be larger than 2."), .expect("pre-blank cannot be larger than 2."),
children: children.into_iter().map(|(_start, item)| item).collect(), children: children.into_iter().map(|(_start, item)| item).collect(),
contents: if contents.len() > 0 {
Some(contents.into())
} else {
None
},
post_blank: post_blank.map(Into::<&str>::into),
}, },
), ),
)); ));

View File

@@ -3,6 +3,7 @@ use nom::bytes::complete::is_not;
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
use nom::character::complete::line_ending; use nom::character::complete::line_ending;
use nom::character::complete::space0; use nom::character::complete::space0;
use nom::combinator::consumed;
use nom::combinator::not; use nom::combinator::not;
use nom::combinator::opt; use nom::combinator::opt;
use nom::combinator::peek; use nom::combinator::peek;
@@ -67,13 +68,13 @@ where
let org_mode_table_row_matcher = parser_with_context!(org_mode_table_row)(&parser_context); let org_mode_table_row_matcher = parser_with_context!(org_mode_table_row)(&parser_context);
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
let (remaining, (children, _exit_contents)) = let (remaining, (contents, (children, _exit_contents))) =
many_till(org_mode_table_row_matcher, exit_matcher)(remaining)?; consumed(many_till(org_mode_table_row_matcher, exit_matcher))(remaining)?;
let (remaining, formulas) = let (remaining, formulas) =
many0(parser_with_context!(table_formula_keyword)(context))(remaining)?; many0(parser_with_context!(table_formula_keyword)(context))(remaining)?;
let (remaining, _trailing_ws) = let (remaining, post_blank) =
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
@@ -87,6 +88,8 @@ where
), ),
formulas, formulas,
children, children,
contents: Into::<&str>::into(contents),
post_blank: post_blank.map(Into::<&str>::into),
}, },
)) ))
} }
@@ -150,6 +153,7 @@ fn org_mode_table_row_rule<'b, 'g, 'r, 's>(
TableRow { TableRow {
source: source.into(), source: source.into(),
children: Vec::new(), children: Vec::new(),
contents: None,
}, },
)) ))
} }
@@ -164,8 +168,8 @@ fn org_mode_table_row_regular<'b, 'g, 'r, 's>(
) -> Res<OrgSource<'s>, TableRow<'s>> { ) -> Res<OrgSource<'s>, TableRow<'s>> {
start_of_line(input)?; start_of_line(input)?;
let (remaining, _) = tuple((space0, tag("|")))(input)?; let (remaining, _) = tuple((space0, tag("|")))(input)?;
let (remaining, children) = let (remaining, (contents, children)) =
many1(parser_with_context!(org_mode_table_cell)(context))(remaining)?; consumed(many1(parser_with_context!(org_mode_table_cell)(context)))(remaining)?;
let (remaining, _tail) = recognize(tuple((space0, org_line_ending)))(remaining)?; let (remaining, _tail) = recognize(tuple((space0, org_line_ending)))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok(( Ok((
@@ -173,6 +177,11 @@ fn org_mode_table_row_regular<'b, 'g, 'r, 's>(
TableRow { TableRow {
source: source.into(), source: source.into(),
children, children,
contents: if contents.len() > 0 {
Some(Into::<&str>::into(contents))
} else {
None
},
}, },
)) ))
} }
@@ -194,12 +203,12 @@ fn org_mode_table_cell<'b, 'g, 'r, 's>(
parser_with_context!(table_cell_set_object)(&parser_context); parser_with_context!(table_cell_set_object)(&parser_context);
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
let (remaining, _) = space0(input)?; let (remaining, _) = space0(input)?;
let (remaining, (children, _exit_contents)) = verify( let (remaining, (contents, (children, _exit_contents))) = consumed(verify(
many_till(table_cell_set_object_matcher, exit_matcher), many_till(table_cell_set_object_matcher, exit_matcher),
|(children, exit_contents)| { |(children, exit_contents)| {
!children.is_empty() || Into::<&str>::into(exit_contents).ends_with('|') !children.is_empty() || Into::<&str>::into(exit_contents).ends_with('|')
}, },
)(remaining)?; ))(remaining)?;
let (remaining, _tail) = org_mode_table_cell_end(&parser_context, remaining)?; let (remaining, _tail) = org_mode_table_cell_end(&parser_context, remaining)?;
@@ -210,6 +219,7 @@ fn org_mode_table_cell<'b, 'g, 'r, 's>(
TableCell { TableCell {
source: source.into(), source: source.into(),
children, children,
contents: Into::<&str>::into(contents),
}, },
)) ))
} }

View File

@@ -46,7 +46,7 @@ pub(crate) fn target<'b, 'g, 'r, 's>(
))); )));
} }
let (remaining, _) = tag(">>")(remaining)?; let (remaining, _) = tag(">>")(remaining)?;
let (remaining, _trailing_whitespace) = let (remaining, post_blank) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?; maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
@@ -55,6 +55,7 @@ pub(crate) fn target<'b, 'g, 'r, 's>(
Target { Target {
source: source.into(), source: source.into(),
value: body.into(), value: body.into(),
post_blank: post_blank.map(Into::<&str>::into),
}, },
)) ))
} }

View File

@@ -72,13 +72,6 @@ fn element_trailing_whitespace<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, O
alt((eof, recognize(many0(blank_line))))(input) alt((eof, recognize(many0(blank_line))))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn element_trailing_whitespace_mid_line<'s>(
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
alt((eof, recognize(many0(blank_line))))(input)
}
#[cfg_attr( #[cfg_attr(
feature = "tracing", feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context)) tracing::instrument(ret, level = "debug", skip(context))
@@ -121,22 +114,6 @@ pub(crate) fn maybe_consume_trailing_whitespace_if_not_exiting<'b, 'g, 'r, 's>(
} }
} }
#[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
pub(crate) fn maybe_consume_trailing_whitespace_if_not_exiting_mid_line<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Option<OrgSource<'s>>> {
if context.should_consume_trailing_whitespace() && exit_matcher_parser(context, input).is_err()
{
Ok(opt(element_trailing_whitespace_mid_line)(input)?)
} else {
Ok((input, None))
}
}
#[cfg_attr( #[cfg_attr(
feature = "tracing", feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context)) tracing::instrument(ret, level = "debug", skip(context))

View File

@@ -1,6 +1,5 @@
use std::path::PathBuf; use std::path::PathBuf;
use super::remove_trailing::RemoveTrailing;
use super::Element; use super::Element;
use super::NodeProperty; use super::NodeProperty;
use super::Object; use super::Object;
@@ -18,6 +17,7 @@ pub struct Document<'s> {
pub path: Option<PathBuf>, pub path: Option<PathBuf>,
pub zeroth_section: Option<Section<'s>>, pub zeroth_section: Option<Section<'s>>,
pub children: Vec<Heading<'s>>, pub children: Vec<Heading<'s>>,
pub contents: &'s str,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -36,6 +36,7 @@ pub struct Heading<'s> {
pub deadline: Option<Timestamp<'s>>, pub deadline: Option<Timestamp<'s>>,
pub closed: Option<Timestamp<'s>>, pub closed: Option<Timestamp<'s>>,
pub contents: Option<&'s str>, pub contents: Option<&'s str>,
pub post_blank: Option<&'s str>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -63,35 +64,11 @@ impl<'s> StandardProperties<'s> for Document<'s> {
} }
fn get_contents<'b>(&'b self) -> Option<&'s str> { fn get_contents<'b>(&'b self) -> Option<&'s str> {
let post_blank = self.get_post_blank(); Some(self.contents)
let mut content_lines = self
.source
.split_inclusive('\n')
.remove_trailing(post_blank);
let first_line = content_lines.next();
let last_line = content_lines.last().or(first_line);
match (first_line, last_line) {
(None, None) => None,
(None, Some(_)) | (Some(_), None) => unreachable!(),
(Some(first_line), Some(last_line)) => {
let first_line_offset = first_line.as_ptr() as usize;
let last_line_offset = last_line.as_ptr() as usize + last_line.len();
let source_offset = self.source.as_ptr() as usize;
debug_assert!(super::lesser_element::is_slice_of(self.source, first_line));
debug_assert!(super::lesser_element::is_slice_of(self.source, last_line));
Some(
&self.source[(first_line_offset - source_offset)
..(last_line_offset - first_line_offset)],
)
}
}
} }
fn get_post_blank(&self) -> PostBlank { fn get_post_blank(&self) -> PostBlank {
self.into_iter() 0
.last()
.map(|child| child.get_post_blank())
.unwrap_or(0)
} }
} }
@@ -123,10 +100,11 @@ impl<'s> StandardProperties<'s> for Heading<'s> {
} }
fn get_post_blank(&self) -> PostBlank { fn get_post_blank(&self) -> PostBlank {
self.children self.post_blank
.last() .map(|text| text.lines().count())
.map(|child| child.get_post_blank())
.unwrap_or(0) .unwrap_or(0)
.try_into()
.expect("Too much post-blank to fit into a PostBlank.")
} }
} }

View File

@@ -13,6 +13,8 @@ pub struct PlainList<'s> {
pub affiliated_keywords: AffiliatedKeywords<'s>, pub affiliated_keywords: AffiliatedKeywords<'s>,
pub list_type: PlainListType, pub list_type: PlainListType,
pub children: Vec<PlainListItem<'s>>, pub children: Vec<PlainListItem<'s>>,
pub contents: Option<&'s str>, // TODO: Can contents ever be None?
pub post_blank: Option<&'s str>,
} }
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@@ -35,6 +37,8 @@ pub struct PlainListItem<'s> {
pub tag: Vec<Object<'s>>, pub tag: Vec<Object<'s>>,
pub pre_blank: PlainListItemPreBlank, pub pre_blank: PlainListItemPreBlank,
pub children: Vec<Element<'s>>, pub children: Vec<Element<'s>>,
pub contents: Option<&'s str>,
pub post_blank: Option<&'s str>,
} }
pub type PlainListItemCounter = u16; pub type PlainListItemCounter = u16;
@@ -52,6 +56,8 @@ pub struct CenterBlock<'s> {
pub source: &'s str, pub source: &'s str,
pub affiliated_keywords: AffiliatedKeywords<'s>, pub affiliated_keywords: AffiliatedKeywords<'s>,
pub children: Vec<Element<'s>>, pub children: Vec<Element<'s>>,
pub contents: Option<&'s str>,
pub post_blank: Option<&'s str>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -59,6 +65,8 @@ pub struct QuoteBlock<'s> {
pub source: &'s str, pub source: &'s str,
pub affiliated_keywords: AffiliatedKeywords<'s>, pub affiliated_keywords: AffiliatedKeywords<'s>,
pub children: Vec<Element<'s>>, pub children: Vec<Element<'s>>,
pub contents: Option<&'s str>,
pub post_blank: Option<&'s str>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -68,6 +76,8 @@ pub struct SpecialBlock<'s> {
pub block_type: &'s str, pub block_type: &'s str,
pub parameters: Option<&'s str>, pub parameters: Option<&'s str>,
pub children: Vec<Element<'s>>, pub children: Vec<Element<'s>>,
pub contents: Option<&'s str>,
pub post_blank: Option<&'s str>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -77,6 +87,8 @@ pub struct DynamicBlock<'s> {
pub block_name: &'s str, pub block_name: &'s str,
pub parameters: Option<&'s str>, pub parameters: Option<&'s str>,
pub children: Vec<Element<'s>>, pub children: Vec<Element<'s>>,
pub contents: Option<&'s str>,
pub post_blank: Option<&'s str>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -120,12 +132,15 @@ pub struct Table<'s> {
pub affiliated_keywords: AffiliatedKeywords<'s>, pub affiliated_keywords: AffiliatedKeywords<'s>,
pub formulas: Vec<Keyword<'s>>, pub formulas: Vec<Keyword<'s>>,
pub children: Vec<TableRow<'s>>, pub children: Vec<TableRow<'s>>,
pub contents: &'s str,
pub post_blank: Option<&'s str>,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct TableRow<'s> { pub struct TableRow<'s> {
pub source: &'s str, pub source: &'s str,
pub children: Vec<TableCell<'s>>, pub children: Vec<TableCell<'s>>,
pub contents: Option<&'s str>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -140,11 +155,15 @@ impl<'s> StandardProperties<'s> for PlainList<'s> {
} }
fn get_contents<'b>(&'b self) -> Option<&'s str> { fn get_contents<'b>(&'b self) -> Option<&'s str> {
todo!() self.contents
} }
fn get_post_blank(&self) -> PostBlank { fn get_post_blank(&self) -> PostBlank {
todo!() self.post_blank
.map(|text| text.lines().count())
.unwrap_or(0)
.try_into()
.expect("Too much post-blank to fit into a PostBlank.")
} }
} }
@@ -154,11 +173,15 @@ impl<'s> StandardProperties<'s> for PlainListItem<'s> {
} }
fn get_contents<'b>(&'b self) -> Option<&'s str> { fn get_contents<'b>(&'b self) -> Option<&'s str> {
todo!() self.contents
} }
fn get_post_blank(&self) -> PostBlank { fn get_post_blank(&self) -> PostBlank {
todo!() self.post_blank
.map(|text| text.lines().count())
.unwrap_or(0)
.try_into()
.expect("Too much post-blank to fit into a PostBlank.")
} }
} }
@@ -168,11 +191,15 @@ impl<'s> StandardProperties<'s> for CenterBlock<'s> {
} }
fn get_contents<'b>(&'b self) -> Option<&'s str> { fn get_contents<'b>(&'b self) -> Option<&'s str> {
todo!() self.contents
} }
fn get_post_blank(&self) -> PostBlank { fn get_post_blank(&self) -> PostBlank {
todo!() self.post_blank
.map(|text| text.lines().count())
.unwrap_or(0)
.try_into()
.expect("Too much post-blank to fit into a PostBlank.")
} }
} }
@@ -182,11 +209,15 @@ impl<'s> StandardProperties<'s> for QuoteBlock<'s> {
} }
fn get_contents<'b>(&'b self) -> Option<&'s str> { fn get_contents<'b>(&'b self) -> Option<&'s str> {
todo!() self.contents
} }
fn get_post_blank(&self) -> PostBlank { fn get_post_blank(&self) -> PostBlank {
todo!() self.post_blank
.map(|text| text.lines().count())
.unwrap_or(0)
.try_into()
.expect("Too much post-blank to fit into a PostBlank.")
} }
} }
@@ -196,11 +227,15 @@ impl<'s> StandardProperties<'s> for SpecialBlock<'s> {
} }
fn get_contents<'b>(&'b self) -> Option<&'s str> { fn get_contents<'b>(&'b self) -> Option<&'s str> {
todo!() self.contents
} }
fn get_post_blank(&self) -> PostBlank { fn get_post_blank(&self) -> PostBlank {
todo!() self.post_blank
.map(|text| text.lines().count())
.unwrap_or(0)
.try_into()
.expect("Too much post-blank to fit into a PostBlank.")
} }
} }
@@ -210,11 +245,15 @@ impl<'s> StandardProperties<'s> for DynamicBlock<'s> {
} }
fn get_contents<'b>(&'b self) -> Option<&'s str> { fn get_contents<'b>(&'b self) -> Option<&'s str> {
todo!() self.contents
} }
fn get_post_blank(&self) -> PostBlank { fn get_post_blank(&self) -> PostBlank {
todo!() self.post_blank
.map(|text| text.lines().count())
.unwrap_or(0)
.try_into()
.expect("Too much post-blank to fit into a PostBlank.")
} }
} }
@@ -292,11 +331,15 @@ impl<'s> StandardProperties<'s> for Table<'s> {
} }
fn get_contents<'b>(&'b self) -> Option<&'s str> { fn get_contents<'b>(&'b self) -> Option<&'s str> {
todo!() Some(self.contents)
} }
fn get_post_blank(&self) -> PostBlank { fn get_post_blank(&self) -> PostBlank {
todo!() self.post_blank
.map(|text| text.lines().count())
.unwrap_or(0)
.try_into()
.expect("Too much post-blank to fit into a PostBlank.")
} }
} }
@@ -306,11 +349,11 @@ impl<'s> StandardProperties<'s> for TableRow<'s> {
} }
fn get_contents<'b>(&'b self) -> Option<&'s str> { fn get_contents<'b>(&'b self) -> Option<&'s str> {
todo!() self.contents
} }
fn get_post_blank(&self) -> PostBlank { fn get_post_blank(&self) -> PostBlank {
todo!() 0
} }
} }

View File

@@ -42,6 +42,7 @@ pub struct Comment<'s> {
pub struct TableCell<'s> { pub struct TableCell<'s> {
pub source: &'s str, pub source: &'s str,
pub children: Vec<Object<'s>>, pub children: Vec<Object<'s>>,
pub contents: &'s str,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -203,21 +204,7 @@ impl<'s> Paragraph<'s> {
/// Generate a paragraph of the passed in text with no additional properties. /// 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. /// This is used for elements that support an "empty" content like greater blocks.
pub(crate) fn of_text(source: &'s str, body: &'s str) -> Self { pub(crate) fn of_text(
// TODO: This should be replaced with of_text_full.
Paragraph {
source,
contents: None,
post_blank: None,
affiliated_keywords: AffiliatedKeywords::default(),
children: vec![Object::PlainText(PlainText { source: body })],
}
}
/// 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_full(
source: &'s str, source: &'s str,
body: &'s str, body: &'s str,
contents: Option<&'s str>, contents: Option<&'s str>,
@@ -257,11 +244,11 @@ impl<'s> StandardProperties<'s> for TableCell<'s> {
} }
fn get_contents<'b>(&'b self) -> Option<&'s str> { fn get_contents<'b>(&'b self) -> Option<&'s str> {
todo!() Some(self.contents)
} }
fn get_post_blank(&self) -> PostBlank { fn get_post_blank(&self) -> PostBlank {
todo!() 0
} }
} }

View File

@@ -266,6 +266,7 @@ pub struct LineBreak<'s> {
pub struct Target<'s> { pub struct Target<'s> {
pub source: &'s str, pub source: &'s str,
pub value: &'s str, pub value: &'s str,
pub post_blank: Option<&'s str>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -923,11 +924,15 @@ impl<'s> StandardProperties<'s> for Target<'s> {
} }
fn get_contents<'b>(&'b self) -> Option<&'s str> { fn get_contents<'b>(&'b self) -> Option<&'s str> {
todo!() None
} }
fn get_post_blank(&self) -> PostBlank { fn get_post_blank(&self) -> PostBlank {
todo!() self.post_blank
.map(|post_blank| post_blank.chars().count())
.unwrap_or(0)
.try_into()
.expect("Too much post-blank to fit into a PostBlank.")
} }
} }
@@ -1009,11 +1014,13 @@ impl<'s> StandardProperties<'s> for PlainText<'s> {
} }
fn get_contents<'b>(&'b self) -> Option<&'s str> { fn get_contents<'b>(&'b self) -> Option<&'s str> {
todo!() // This field does not actually exist in emacs for plaintext
Some(self.source)
} }
fn get_post_blank(&self) -> PostBlank { fn get_post_blank(&self) -> PostBlank {
todo!() // This field does not actually exist in emacs for plaintext
0
} }
} }