Merge branch 'plain_list_item_properties'
All checks were successful
rustfmt Build rustfmt 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

This commit is contained in:
Tom Alexander 2023-09-29 19:30:32 -04:00
commit 5e127fec11
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
6 changed files with 133 additions and 24 deletions

View File

@ -9,6 +9,7 @@ use super::sexp::unquote;
use super::sexp::Token; use super::sexp::Token;
use super::util::compare_standard_properties; use super::util::compare_standard_properties;
use super::util::get_property; use super::util::get_property;
use super::util::get_property_quoted_string;
use super::util::get_property_unquoted_atom; use super::util::get_property_unquoted_atom;
use crate::types::AngleLink; use crate::types::AngleLink;
use crate::types::BabelCall; use crate::types::BabelCall;
@ -51,6 +52,8 @@ use crate::types::Paragraph;
use crate::types::PlainLink; use crate::types::PlainLink;
use crate::types::PlainList; use crate::types::PlainList;
use crate::types::PlainListItem; use crate::types::PlainListItem;
use crate::types::PlainListItemCounter;
use crate::types::PlainListItemPreBlank;
use crate::types::PlainListType; use crate::types::PlainListType;
use crate::types::PlainText; use crate::types::PlainText;
use crate::types::Planning; use crate::types::Planning;
@ -753,6 +756,7 @@ fn compare_plain_list<'s>(
let mut this_status = DiffStatus::Good; let mut this_status = DiffStatus::Good;
let mut message = None; let mut message = None;
// Compare type
// :type is an unquoted atom of either descriptive, ordered, or unordered // :type is an unquoted atom of either descriptive, ordered, or unordered
let list_type = get_property_unquoted_atom(emacs, ":type")?; let list_type = get_property_unquoted_atom(emacs, ":type")?;
match (list_type, &rust.list_type) { match (list_type, &rust.list_type) {
@ -830,7 +834,40 @@ fn compare_plain_list_item<'s>(
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
child_status.push(artificial_diff_scope("contents", contents_status)?); child_status.push(artificial_diff_scope("contents", contents_status)?);
// TODO: Compare :bullet :counter :pre-blank // Compare bullet
let bullet = get_property_quoted_string(emacs, ":bullet")?
.ok_or("Plain list items must have a :bullet.")?;
if bullet != rust.bullet {
this_status = DiffStatus::Bad;
message = Some(format!(
"Bullet mismatch (emacs != rust) {:?} != {:?}",
bullet, rust.bullet
));
}
// Compare counter
let counter = get_property_unquoted_atom(emacs, ":counter")?;
let counter: Option<PlainListItemCounter> = counter
.map(|val| val.parse())
.map_or(Ok(None), |r| r.map(Some))?;
match (counter, rust.counter) {
(None, None) => {}
(None, Some(_)) | (Some(_), None) => {
this_status = DiffStatus::Bad;
message = Some(format!(
"Counter mismatch (emacs != rust) {:?} != {:?}",
counter, rust.counter
));
}
(Some(e), Some(r)) if e != r => {
this_status = DiffStatus::Bad;
message = Some(format!(
"Counter mismatch (emacs != rust) {:?} != {:?}",
counter, rust.counter
));
}
(Some(_), Some(_)) => {}
};
// Compare checkbox // Compare checkbox
let checkbox = get_property(emacs, ":checkbox")? let checkbox = get_property(emacs, ":checkbox")?
@ -851,6 +888,20 @@ fn compare_plain_list_item<'s>(
} }
}; };
// Compare pre-blank
// :pre-blank appears to count the line breaks between "::" and the contents in a descriptive list. Oddly enough it does not count the spaces so I'm not quite sure what the value is.
let pre_blank = get_property_unquoted_atom(emacs, ":pre-blank")?;
let pre_blank: Option<PlainListItemPreBlank> = pre_blank
.map(|val| val.parse())
.map_or(Ok(None), |r| r.map(Some))?;
if pre_blank.unwrap_or(0) != rust.pre_blank {
this_status = DiffStatus::Bad;
message = Some(format!(
"Pre-blank mismatch (emacs != rust) {:?} != {:?}",
pre_blank, rust.pre_blank
));
}
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,
name: rust.get_elisp_name(), name: rust.get_elisp_name(),

View File

@ -1,5 +1,6 @@
use super::elisp_fact::GetElispFact; use super::elisp_fact::GetElispFact;
use super::sexp::Token; use super::sexp::Token;
use crate::compare::sexp::unquote;
use crate::types::GetStandardProperties; use crate::types::GetStandardProperties;
use crate::types::StandardProperties; use crate::types::StandardProperties;
@ -202,3 +203,17 @@ pub(crate) fn get_property_unquoted_atom<'s, 'x>(
.map(Token::as_atom) .map(Token::as_atom)
.map_or(Ok(None), |r| r.map(Some))?) .map_or(Ok(None), |r| r.map(Some))?)
} }
/// Get a named property containing an quoted string from the emacs token.
///
/// Returns None if key is not found.
pub(crate) fn get_property_quoted_string<'s, 'x>(
emacs: &'s Token<'s>,
key: &'x str,
) -> Result<Option<String>, Box<dyn std::error::Error>> {
Ok(get_property(emacs, key)?
.map(Token::as_atom)
.map_or(Ok(None), |r| r.map(Some))?
.map(unquote)
.map_or(Ok(None), |r| r.map(Some))?)
}

View File

@ -78,6 +78,12 @@ impl<'s> OrgSource<'s> {
self.slice(..(other.start - self.start)) self.slice(..(other.start - self.start))
} }
pub(crate) fn get_until_end_of(&self, other: OrgSource<'s>) -> OrgSource<'s> {
debug_assert!(other.start >= self.start);
debug_assert!(other.end <= self.end);
self.slice(..(other.end - self.start))
}
pub(crate) fn get_start_of_line(&self) -> OrgSource<'s> { pub(crate) fn get_start_of_line(&self) -> OrgSource<'s> {
let skipped_text = self.text_since_line_break(); let skipped_text = self.text_since_line_break();
let mut bracket_depth = self.bracket_depth; let mut bracket_depth = self.bracket_depth;

View File

@ -44,6 +44,8 @@ use crate::types::IndentationLevel;
use crate::types::Object; use crate::types::Object;
use crate::types::PlainList; use crate::types::PlainList;
use crate::types::PlainListItem; use crate::types::PlainListItem;
use crate::types::PlainListItemCounter;
use crate::types::PlainListItemPreBlank;
use crate::types::PlainListType; use crate::types::PlainListType;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
@ -54,12 +56,12 @@ pub(crate) fn detect_plain_list<'b, 'g, 'r, 's>(
if verify( if verify(
tuple(( tuple((
start_of_line, start_of_line,
space0, parser_with_context!(indentation_level)(context),
parser_with_context!(bullet)(context), parser_with_context!(bullet)(context),
alt((space1, line_ending, eof)), alt((space1, line_ending, eof)),
)), )),
|(_start, indent, (_bullet_type, bull), _after_whitespace)| { |(_start, (indent_level, _), (_bullet_type, bull), _after_whitespace)| {
Into::<&str>::into(bull) != "*" || indent.len() > 0 !Into::<&str>::into(bull).starts_with("*") || *indent_level > 0
}, },
)(input) )(input)
.is_ok() .is_ok()
@ -163,11 +165,12 @@ fn plain_list_item<'b, 'g, 'r, 's>(
let (remaining, (indent_level, _leading_whitespace)) = indentation_level(context, input)?; let (remaining, (indent_level, _leading_whitespace)) = indentation_level(context, input)?;
let (remaining, (bullet_type, bull)) = verify( let (remaining, (bullet_type, bull)) = verify(
parser_with_context!(bullet)(context), parser_with_context!(bullet)(context),
|(_bullet_type, bull)| Into::<&str>::into(bull) != "*" || indent_level > 0, |(_bullet_type, bull)| !Into::<&str>::into(bull).starts_with("*") || indent_level > 0,
)(remaining)?; )(remaining)?;
let (remaining, _maybe_counter_set) = let (remaining, maybe_counter_set) =
opt(tuple((space1, tag("[@"), counter_set_value, tag("]"))))(remaining)?; opt(tuple((space1, tag("[@"), counter_set_value, tag("]"))))(remaining)?;
let maybe_counter_set = maybe_counter_set.map(|(_, _, val, _)| val);
let (remaining, maybe_checkbox) = opt(tuple((space1, item_checkbox)))(remaining)?; let (remaining, maybe_checkbox) = opt(tuple((space1, item_checkbox)))(remaining)?;
@ -213,10 +216,12 @@ fn plain_list_item<'b, 'g, 'r, 's>(
source: source.into(), source: source.into(),
indentation: indent_level, indentation: indent_level,
bullet: bull.into(), bullet: bull.into(),
counter: maybe_counter_set,
checkbox: None, checkbox: None,
tag: maybe_tag tag: maybe_tag
.map(|(_ws, item_tag)| item_tag) .map(|(_ws, item_tag)| item_tag)
.unwrap_or(Vec::new()), .unwrap_or(Vec::new()),
pre_blank: 0,
children: Vec::new(), children: Vec::new(),
}, },
), ),
@ -224,7 +229,11 @@ fn plain_list_item<'b, 'g, 'r, 's>(
} }
Err(_) => {} Err(_) => {}
}; };
let (remaining, _ws) = item_tag_post_gap(&parser_context, remaining)?; let (remaining, pre_blank) = item_tag_post_gap(&parser_context, remaining)?;
let pre_blank = Into::<&str>::into(pre_blank)
.bytes()
.filter(|b| *b == b'\n')
.count();
let (mut remaining, (mut children, _exit_contents)) = many_till( let (mut remaining, (mut children, _exit_contents)) = many_till(
include_input(parser_with_context!(element(true))(&parser_context)), include_input(parser_with_context!(element(true))(&parser_context)),
@ -256,12 +265,15 @@ fn plain_list_item<'b, 'g, 'r, 's>(
source: source.into(), source: source.into(),
indentation: indent_level, indentation: indent_level,
bullet: bull.into(), bullet: bull.into(),
counter: maybe_counter_set,
checkbox: maybe_checkbox.map(|(_, (checkbox_type, source))| { checkbox: maybe_checkbox.map(|(_, (checkbox_type, source))| {
(checkbox_type, Into::<&str>::into(source)) (checkbox_type, Into::<&str>::into(source))
}), }),
tag: maybe_tag tag: maybe_tag
.map(|(_ws, item_tag)| item_tag) .map(|(_ws, item_tag)| item_tag)
.unwrap_or(Vec::new()), .unwrap_or(Vec::new()),
pre_blank: PlainListItemPreBlank::try_from(pre_blank)
.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(),
}, },
), ),
@ -279,18 +291,23 @@ fn bullet<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, (BulletType, OrgSource<'s>)> { ) -> Res<OrgSource<'s>, (BulletType, OrgSource<'s>)> {
alt(( let (remaining, ((bullet_type, _without_space), peek_trailing_space)) = tuple((
map(tag("*"), |bull| (BulletType::Unordered, bull)), alt((
map(tag("-"), |bull| (BulletType::Unordered, bull)), map(tag("*"), |bull| (BulletType::Unordered, bull)),
map(tag("+"), |bull| (BulletType::Unordered, bull)), map(tag("-"), |bull| (BulletType::Unordered, bull)),
map( map(tag("+"), |bull| (BulletType::Unordered, bull)),
recognize(tuple(( map(
parser_with_context!(counter)(context), recognize(tuple((
alt((tag("."), tag(")"))), parser_with_context!(counter)(context),
))), alt((tag("."), tag(")"))),
|bull| (BulletType::Ordered, bull), ))),
), |bull| (BulletType::Ordered, bull),
))(input) ),
)),
peek(space0),
))(input)?;
let with_space = input.get_until_end_of(peek_trailing_space);
Ok((remaining, (bullet_type, with_space)))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
@ -311,12 +328,25 @@ fn counter<'b, 'g, 'r, 's>(
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn counter_set_value<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> { fn counter_set_value<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PlainListItemCounter> {
alt(( alt((
recognize(one_of( map(
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", one_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"),
)), |letter| {
digit1, let num = match letter {
'a'..='z' => (letter as u32) - ('a' as u32) + 1,
'A'..='Z' => (letter as u32) - ('A' as u32) + 1,
_ => unreachable!(),
};
PlainListItemCounter::try_from(num)
.expect("Counter set value should be between 1 and 26 inclusive.")
},
),
map(digit1, |num: OrgSource<'_>| {
Into::<&str>::into(num)
.parse()
.expect("digit1 must parse to a number.")
}),
))(input) ))(input)
} }

View File

@ -26,11 +26,16 @@ pub struct PlainListItem<'s> {
pub source: &'s str, pub source: &'s str,
pub indentation: IndentationLevel, pub indentation: IndentationLevel,
pub bullet: &'s str, pub bullet: &'s str,
pub counter: Option<PlainListItemCounter>,
pub checkbox: Option<(CheckboxType, &'s str)>, pub checkbox: Option<(CheckboxType, &'s str)>,
pub tag: Vec<Object<'s>>, pub tag: Vec<Object<'s>>,
pub pre_blank: PlainListItemPreBlank,
pub children: Vec<Element<'s>>, pub children: Vec<Element<'s>>,
} }
pub type PlainListItemCounter = u16;
pub type PlainListItemPreBlank = u8;
#[derive(Debug)] #[derive(Debug)]
pub enum CheckboxType { pub enum CheckboxType {
On, On,

View File

@ -24,6 +24,8 @@ pub use greater_element::IndentationLevel;
pub use greater_element::NodeProperty; pub use greater_element::NodeProperty;
pub use greater_element::PlainList; pub use greater_element::PlainList;
pub use greater_element::PlainListItem; pub use greater_element::PlainListItem;
pub use greater_element::PlainListItemCounter;
pub use greater_element::PlainListItemPreBlank;
pub use greater_element::PlainListType; pub use greater_element::PlainListType;
pub use greater_element::PropertyDrawer; pub use greater_element::PropertyDrawer;
pub use greater_element::Table; pub use greater_element::Table;