Compare commits

...

6 Commits

Author SHA1 Message Date
Tom Alexander
444d6758aa
Handle leading blank lines in greater blocks.
Some checks failed
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
rust-foreign-document-test Build rust-foreign-document-test has failed
2023-09-15 21:03:35 -04:00
Tom Alexander
6c7203410e
Add a test showing we're not handling leading blank lines in greater blocks. 2023-09-15 17:02:41 -04:00
Tom Alexander
bfe67b1f75
Parse plain list item checkboxes. 2023-09-15 16:09:57 -04:00
Tom Alexander
fd41ad9c29
Pretend dos line endings do not exist. 2023-09-15 14:13:17 -04:00
Tom Alexander
7f751d4f28
Allow no digit in repeater in timestamp. 2023-09-15 13:12:54 -04:00
Tom Alexander
52a4dab67c
Use the timestamp parser in planning.
Previously we did not support inactive timestamps in planning. This fixes that.
2023-09-15 12:45:19 -04:00
9 changed files with 95 additions and 34 deletions

View File

@ -0,0 +1,5 @@
#+begin_quote
foo
#+end_quote

View File

@ -14,7 +14,9 @@ use crate::LocalFileAccessInterface;
pub fn run_anonymous_compare<P: AsRef<str>>(
org_contents: P,
) -> Result<(), Box<dyn std::error::Error>> {
let org_contents = org_contents.as_ref();
// TODO: This is a work-around to pretend that dos line endings do not exist. It would be better to handle the difference in line endings.
let org_contents = org_contents.as_ref().replace("\r\n", "\n");
let org_contents = org_contents.as_str();
eprintln!("Using emacs version: {}", get_emacs_version()?.trim());
eprintln!("Using org-mode version: {}", get_org_mode_version()?.trim());
let rust_parsed = parse(org_contents)?;
@ -44,6 +46,8 @@ pub fn run_compare_on_file<P: AsRef<Path>>(org_path: P) -> Result<(), Box<dyn st
.parent()
.ok_or("Should be contained inside a directory.")?;
let org_contents = std::fs::read_to_string(org_path)?;
// TODO: This is a work-around to pretend that dos line endings do not exist. It would be better to handle the difference in line endings.
let org_contents = org_contents.replace("\r\n", "\n");
let org_contents = org_contents.as_str();
let file_access_interface = LocalFileAccessInterface {
working_directory: Some(parent_directory.to_path_buf()),

View File

@ -8,6 +8,7 @@ use super::util::assert_name;
use super::util::get_property;
use crate::types::AngleLink;
use crate::types::Bold;
use crate::types::CheckboxType;
use crate::types::Citation;
use crate::types::CitationReference;
use crate::types::Clock;
@ -799,7 +800,26 @@ fn compare_plain_list_item<'s>(
contents_status,
)?);
// TODO: compare :bullet :checkbox :counter :pre-blank
// TODO: compare :bullet :counter :pre-blank
// Compare checkbox
let checkbox = get_property(emacs, ":checkbox")?
.map(Token::as_atom)
.map_or(Ok(None), |r| r.map(Some))?
.unwrap_or("nil");
match (checkbox, &rust.checkbox) {
("nil", None) => {}
("off", Some((CheckboxType::Off, _))) => {}
("trans", Some((CheckboxType::Trans, _))) => {}
("on", Some((CheckboxType::On, _))) => {}
_ => {
this_status = DiffStatus::Bad;
message = Some(format!(
"Checkbox mismatch (emacs != rust) {:?} != {:?}",
checkbox, rust.checkbox
));
}
};
Ok(DiffResult {
status: this_status,

View File

@ -4,11 +4,14 @@ use nom::bytes::complete::tag_no_case;
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::not;
use nom::combinator::opt;
use nom::combinator::verify;
use nom::multi::many0;
use nom::multi::many_till;
use nom::sequence::preceded;
use nom::sequence::tuple;
use super::org_source::OrgSource;
@ -80,25 +83,23 @@ pub(crate) fn greater_block<'b, 'g, 'r, 's>(
let element_matcher = parser_with_context!(element(true))(&parser_context);
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
// Check for a completely empty block
let (remaining, children) = match tuple((
not(exit_matcher),
not(exit_matcher)(remaining)?;
let (remaining, leading_blank_lines) = opt(consumed(tuple((
blank_line,
many_till(blank_line, exit_matcher),
))(remaining)
{
Ok((remain, (_not_immediate_exit, first_line, (_trailing_whitespace, _exit_contents)))) => {
many0(preceded(not(exit_matcher), blank_line)),
))))(remaining)?;
let leading_blank_lines =
leading_blank_lines.map(|(source, (first_line, _remaining_lines))| {
let mut element = Element::Paragraph(Paragraph::of_text(first_line.into()));
let source = get_consumed(remaining, remain);
element.set_source(source.into());
(remain, vec![element])
}
Err(_) => {
let (remaining, (children, _exit_contents)) =
many_till(element_matcher, exit_matcher)(remaining)?;
(remaining, children)
}
};
element
});
let (remaining, (mut children, _exit_contents)) =
many_till(element_matcher, exit_matcher)(remaining)?;
if let Some(lines) = leading_blank_lines {
children.insert(0, lines);
}
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

View File

@ -7,6 +7,7 @@ use nom::character::complete::one_of;
use nom::character::complete::space0;
use nom::character::complete::space1;
use nom::combinator::eof;
use nom::combinator::map;
use nom::combinator::not;
use nom::combinator::opt;
use nom::combinator::peek;
@ -35,7 +36,9 @@ use crate::parser::util::blank_line;
use crate::parser::util::exit_matcher_parser;
use crate::parser::util::get_consumed;
use crate::parser::util::maybe_consume_trailing_whitespace_if_not_exiting;
use crate::parser::util::org_space;
use crate::parser::util::start_of_line;
use crate::types::CheckboxType;
use crate::types::Object;
use crate::types::PlainList;
use crate::types::PlainListItem;
@ -160,7 +163,7 @@ fn plain_list_item<'b, 'g, 'r, 's>(
tag("]"),
)))(remaining)?;
// TODO: parse checkbox
let (remaining, maybe_checkbox) = opt(tuple((space1, item_checkbox)))(remaining)?;
let (remaining, maybe_tag) =
opt(tuple((space1, parser_with_context!(item_tag)(context))))(remaining)?;
@ -178,6 +181,7 @@ fn plain_list_item<'b, 'g, 'r, 's>(
source: source.into(),
indentation: indent_level,
bullet: bull.into(),
checkbox: None,
tag: maybe_tag
.map(|(_ws, item_tag)| item_tag)
.unwrap_or(Vec::new()),
@ -227,6 +231,8 @@ fn plain_list_item<'b, 'g, 'r, 's>(
source: source.into(),
indentation: indent_level,
bullet: bull.into(),
checkbox: maybe_checkbox
.map(|(_, (checkbox_type, source))| (checkbox_type, Into::<&str>::into(source))),
tag: maybe_tag
.map(|(_ws, item_tag)| item_tag)
.unwrap_or(Vec::new()),
@ -389,6 +395,18 @@ fn item_tag_post_gap<'b, 'g, 'r, 's>(
)(input)
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn item_checkbox<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, (CheckboxType, OrgSource<'s>)> {
alt((
map(
recognize(tuple((tag("["), org_space, tag("]")))),
|capture| (CheckboxType::Off, capture),
),
map(tag("[-]"), |capture| (CheckboxType::Trans, capture)),
map(tag("[X]"), |capture| (CheckboxType::On, capture)),
))(input)
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn detect_contentless_item_contents<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,

View File

@ -1,16 +1,16 @@
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;
use nom::character::complete::space1;
use nom::combinator::eof;
use nom::multi::separated_list1;
use nom::multi::many1;
use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::timestamp::timestamp;
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
use super::util::org_line_ending;
use super::util::org_spaces0;
use super::util::org_spaces1;
use crate::context::parser_with_context;
use crate::context::RefContext;
use crate::error::Res;
use crate::parser::util::get_consumed;
@ -23,9 +23,10 @@ pub(crate) fn planning<'b, 'g, 'r, 's>(
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Planning<'s>> {
start_of_line(input)?;
let (remaining, _leading_whitespace) = space0(input)?;
let (remaining, _planning_parameters) = separated_list1(space1, planning_parameter)(remaining)?;
let (remaining, _trailing_ws) = tuple((space0, alt((line_ending, eof))))(remaining)?;
let (remaining, _leading_whitespace) = org_spaces0(input)?;
let (remaining, _planning_parameters) =
many1(parser_with_context!(planning_parameter)(context))(remaining)?;
let (remaining, _trailing_ws) = tuple((org_spaces0, org_line_ending))(remaining)?;
let (remaining, _trailing_ws) =
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
@ -40,15 +41,17 @@ pub(crate) fn planning<'b, 'g, 'r, 's>(
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn planning_parameter<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
fn planning_parameter<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _planning_type) = alt((
tag_no_case("DEADLINE"),
tag_no_case("SCHEDULED"),
tag_no_case("CLOSED"),
))(input)?;
let (remaining, _gap) = tuple((tag(":"), space1))(remaining)?;
// TODO: Make this invoke the real timestamp parser.
let (remaining, _timestamp) = tuple((tag("<"), is_not("\r\n>"), tag(">")))(remaining)?;
let (remaining, _gap) = tuple((tag(":"), org_spaces1))(remaining)?;
let (remaining, _timestamp) = timestamp(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((remaining, source))
}

View File

@ -1,6 +1,7 @@
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::anychar;
use nom::character::complete::digit0;
use nom::character::complete::digit1;
use nom::character::complete::one_of;
use nom::character::complete::space1;
@ -414,7 +415,7 @@ fn repeater<'b, 'g, 'r, 's>(
// ++ for catch-up type
// .+ for restart type
let (remaining, _mark) = alt((tag("++"), tag("+"), tag(".+")))(input)?;
let (remaining, _value) = digit1(remaining)?;
let (remaining, _value) = digit0(remaining)?;
// h = hour, d = day, w = week, m = month, y = year
let (remaining, _unit) = recognize(one_of("hdwmy"))(remaining)?;
let source = get_consumed(input, remaining);
@ -429,7 +430,7 @@ fn warning_delay<'b, 'g, 'r, 's>(
// - for all type
// -- for first type
let (remaining, _mark) = alt((tag("--"), tag("-")))(input)?;
let (remaining, _value) = digit1(remaining)?;
let (remaining, _value) = digit0(remaining)?;
// h = hour, d = day, w = week, m = month, y = year
let (remaining, _unit) = recognize(one_of("hdwmy"))(remaining)?;
let source = get_consumed(input, remaining);

View File

@ -15,10 +15,18 @@ pub struct PlainListItem<'s> {
pub source: &'s str,
pub indentation: usize,
pub bullet: &'s str,
pub checkbox: Option<(CheckboxType, &'s str)>,
pub tag: Vec<Object<'s>>,
pub children: Vec<Element<'s>>,
}
#[derive(Debug)]
pub enum CheckboxType {
On,
Trans,
Off,
}
#[derive(Debug)]
pub struct GreaterBlock<'s> {
pub source: &'s str,

View File

@ -11,6 +11,7 @@ pub use document::PriorityCookie;
pub use document::Section;
pub use document::TodoKeywordType;
pub use element::Element;
pub use greater_element::CheckboxType;
pub use greater_element::Drawer;
pub use greater_element::DynamicBlock;
pub use greater_element::FootnoteDefinition;