Merge branch 'plain_list_properties'
This commit is contained in:
commit
d7a36c8aca
@ -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_unquoted_atom;
|
||||||
use crate::types::AngleLink;
|
use crate::types::AngleLink;
|
||||||
use crate::types::BabelCall;
|
use crate::types::BabelCall;
|
||||||
use crate::types::Bold;
|
use crate::types::Bold;
|
||||||
@ -50,6 +51,7 @@ 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::PlainListType;
|
||||||
use crate::types::PlainText;
|
use crate::types::PlainText;
|
||||||
use crate::types::Planning;
|
use crate::types::Planning;
|
||||||
use crate::types::PriorityCookie;
|
use crate::types::PriorityCookie;
|
||||||
@ -748,11 +750,24 @@ fn compare_plain_list<'s>(
|
|||||||
) -> Result<DiffEntry<'s>, Box<dyn std::error::Error>> {
|
) -> Result<DiffEntry<'s>, Box<dyn std::error::Error>> {
|
||||||
let children = emacs.as_list()?;
|
let children = emacs.as_list()?;
|
||||||
let mut child_status = Vec::new();
|
let mut child_status = Vec::new();
|
||||||
let this_status = DiffStatus::Good;
|
let mut this_status = DiffStatus::Good;
|
||||||
let message = None;
|
let mut message = None;
|
||||||
// TODO: 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")?;
|
||||||
|
match (list_type, &rust.list_type) {
|
||||||
|
(None, _) => panic!("Emacs returned a list with no type."),
|
||||||
|
(Some("unordered"), PlainListType::Unordered) => {}
|
||||||
|
(Some("ordered"), PlainListType::Ordered) => {}
|
||||||
|
(Some("descriptive"), PlainListType::Descriptive) => {}
|
||||||
|
_ => {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"List type mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
list_type, rust.list_type
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
||||||
child_status.push(compare_plain_list_item(source, emacs_child, rust_child)?);
|
child_status.push(compare_plain_list_item(source, emacs_child, rust_child)?);
|
||||||
|
@ -190,3 +190,15 @@ pub(crate) fn get_property<'s, 'x>(
|
|||||||
};
|
};
|
||||||
Ok(Some(*prop))
|
Ok(Some(*prop))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a named property containing an unquoted atom from the emacs token.
|
||||||
|
///
|
||||||
|
/// Returns None if key is not found.
|
||||||
|
pub(crate) fn get_property_unquoted_atom<'s, 'x>(
|
||||||
|
emacs: &'s Token<'s>,
|
||||||
|
key: &'x str,
|
||||||
|
) -> Result<Option<&'s str>, Box<dyn std::error::Error>> {
|
||||||
|
Ok(get_property(emacs, key)?
|
||||||
|
.map(Token::as_atom)
|
||||||
|
.map_or(Ok(None), |r| r.map(Some))?)
|
||||||
|
}
|
||||||
|
@ -44,6 +44,7 @@ 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::PlainListType;
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn detect_plain_list<'b, 'g, 'r, 's>(
|
pub(crate) fn detect_plain_list<'b, 'g, 'r, 's>(
|
||||||
@ -90,6 +91,7 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>(
|
|||||||
// children stores tuple of (input string, parsed object) so we can re-parse the final item
|
// children stores tuple of (input string, parsed object) so we can re-parse the final item
|
||||||
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 remaining = input;
|
let mut remaining = input;
|
||||||
|
|
||||||
// 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:
|
||||||
@ -102,8 +104,15 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>(
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
let list_item = parser_with_context!(plain_list_item)(&parser_context)(remaining);
|
let list_item = parser_with_context!(plain_list_item)(&parser_context)(remaining);
|
||||||
|
match (&first_item_list_type, &list_item) {
|
||||||
|
(None, Ok((_remain, (list_type, _item)))) => {
|
||||||
|
let _ = first_item_list_type.insert(*list_type);
|
||||||
|
}
|
||||||
|
(None, Err(_)) => {}
|
||||||
|
(Some(_), _) => {}
|
||||||
|
};
|
||||||
match list_item {
|
match list_item {
|
||||||
Ok((remain, item))
|
Ok((remain, (_list_type, item)))
|
||||||
if item.indentation == *first_item_indentation.get_or_insert(item.indentation) =>
|
if item.indentation == *first_item_indentation.get_or_insert(item.indentation) =>
|
||||||
{
|
{
|
||||||
children.push((remaining, item));
|
children.push((remaining, item));
|
||||||
@ -130,7 +139,7 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>(
|
|||||||
};
|
};
|
||||||
let final_item_context = ContextElement::ConsumeTrailingWhitespace(false);
|
let final_item_context = ContextElement::ConsumeTrailingWhitespace(false);
|
||||||
let final_item_context = parser_context.with_additional_node(&final_item_context);
|
let final_item_context = parser_context.with_additional_node(&final_item_context);
|
||||||
let (remaining, reparsed_final_item) =
|
let (remaining, (_, reparsed_final_item)) =
|
||||||
parser_with_context!(plain_list_item)(&final_item_context)(final_child_start)?;
|
parser_with_context!(plain_list_item)(&final_item_context)(final_child_start)?;
|
||||||
children.push((final_child_start, reparsed_final_item));
|
children.push((final_child_start, reparsed_final_item));
|
||||||
|
|
||||||
@ -139,6 +148,7 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>(
|
|||||||
remaining,
|
remaining,
|
||||||
PlainList {
|
PlainList {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
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(),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
@ -148,7 +158,7 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>(
|
|||||||
fn plain_list_item<'b, 'g, 'r, 's>(
|
fn plain_list_item<'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>, PlainListItem<'s>> {
|
) -> Res<OrgSource<'s>, (PlainListType, PlainListItem<'s>)> {
|
||||||
start_of_line(input)?;
|
start_of_line(input)?;
|
||||||
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(
|
||||||
@ -170,6 +180,12 @@ fn plain_list_item<'b, 'g, 'r, 's>(
|
|||||||
} else {
|
} else {
|
||||||
(remaining, None)
|
(remaining, None)
|
||||||
};
|
};
|
||||||
|
let list_type = match (&maybe_tag, bullet_type) {
|
||||||
|
(None, BulletType::Ordered) => PlainListType::Ordered,
|
||||||
|
(None, BulletType::Unordered) => PlainListType::Unordered,
|
||||||
|
(Some(_), BulletType::Ordered) => unreachable!(),
|
||||||
|
(Some(_), BulletType::Unordered) => PlainListType::Descriptive,
|
||||||
|
};
|
||||||
|
|
||||||
let exit_matcher = plain_list_item_end(indent_level);
|
let exit_matcher = plain_list_item_end(indent_level);
|
||||||
let contexts = [
|
let contexts = [
|
||||||
@ -195,16 +211,19 @@ fn plain_list_item<'b, 'g, 'r, 's>(
|
|||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
return Ok((
|
return Ok((
|
||||||
remaining,
|
remaining,
|
||||||
PlainListItem {
|
(
|
||||||
source: source.into(),
|
list_type,
|
||||||
indentation: indent_level,
|
PlainListItem {
|
||||||
bullet: bull.into(),
|
source: source.into(),
|
||||||
checkbox: None,
|
indentation: indent_level,
|
||||||
tag: maybe_tag
|
bullet: bull.into(),
|
||||||
.map(|(_ws, item_tag)| item_tag)
|
checkbox: None,
|
||||||
.unwrap_or(Vec::new()),
|
tag: maybe_tag
|
||||||
children: Vec::new(),
|
.map(|(_ws, item_tag)| item_tag)
|
||||||
},
|
.unwrap_or(Vec::new()),
|
||||||
|
children: Vec::new(),
|
||||||
|
},
|
||||||
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Err(_) => {}
|
Err(_) => {}
|
||||||
@ -235,17 +254,21 @@ fn plain_list_item<'b, 'g, 'r, 's>(
|
|||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
return Ok((
|
return Ok((
|
||||||
remaining,
|
remaining,
|
||||||
PlainListItem {
|
(
|
||||||
source: source.into(),
|
list_type,
|
||||||
indentation: indent_level,
|
PlainListItem {
|
||||||
bullet: bull.into(),
|
source: source.into(),
|
||||||
checkbox: maybe_checkbox
|
indentation: indent_level,
|
||||||
.map(|(_, (checkbox_type, source))| (checkbox_type, Into::<&str>::into(source))),
|
bullet: bull.into(),
|
||||||
tag: maybe_tag
|
checkbox: maybe_checkbox.map(|(_, (checkbox_type, source))| {
|
||||||
.map(|(_ws, item_tag)| item_tag)
|
(checkbox_type, Into::<&str>::into(source))
|
||||||
.unwrap_or(Vec::new()),
|
}),
|
||||||
children: children.into_iter().map(|(_start, item)| item).collect(),
|
tag: maybe_tag
|
||||||
},
|
.map(|(_ws, item_tag)| item_tag)
|
||||||
|
.unwrap_or(Vec::new()),
|
||||||
|
children: children.into_iter().map(|(_start, item)| item).collect(),
|
||||||
|
},
|
||||||
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -454,7 +477,7 @@ mod tests {
|
|||||||
let initial_context = ContextElement::document_context();
|
let initial_context = ContextElement::document_context();
|
||||||
let initial_context = Context::new(&global_settings, List::new(&initial_context));
|
let initial_context = Context::new(&global_settings, List::new(&initial_context));
|
||||||
let plain_list_item_matcher = parser_with_context!(plain_list_item)(&initial_context);
|
let plain_list_item_matcher = parser_with_context!(plain_list_item)(&initial_context);
|
||||||
let (remaining, result) = plain_list_item_matcher(input).unwrap();
|
let (remaining, (_, result)) = plain_list_item_matcher(input).unwrap();
|
||||||
assert_eq!(Into::<&str>::into(remaining), "");
|
assert_eq!(Into::<&str>::into(remaining), "");
|
||||||
assert_eq!(result.get_standard_properties().get_source(), "1.");
|
assert_eq!(result.get_standard_properties().get_source(), "1.");
|
||||||
}
|
}
|
||||||
@ -466,7 +489,7 @@ mod tests {
|
|||||||
let initial_context = ContextElement::document_context();
|
let initial_context = ContextElement::document_context();
|
||||||
let initial_context = Context::new(&global_settings, List::new(&initial_context));
|
let initial_context = Context::new(&global_settings, List::new(&initial_context));
|
||||||
let plain_list_item_matcher = parser_with_context!(plain_list_item)(&initial_context);
|
let plain_list_item_matcher = parser_with_context!(plain_list_item)(&initial_context);
|
||||||
let (remaining, result) = plain_list_item_matcher(input).unwrap();
|
let (remaining, (_, result)) = plain_list_item_matcher(input).unwrap();
|
||||||
assert_eq!(Into::<&str>::into(remaining), "");
|
assert_eq!(Into::<&str>::into(remaining), "");
|
||||||
assert_eq!(result.get_standard_properties().get_source(), "1. foo");
|
assert_eq!(result.get_standard_properties().get_source(), "1. foo");
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,17 @@ use super::StandardProperties;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PlainList<'s> {
|
pub struct PlainList<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
|
pub list_type: PlainListType,
|
||||||
pub children: Vec<PlainListItem<'s>>,
|
pub children: Vec<PlainListItem<'s>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum PlainListType {
|
||||||
|
Unordered,
|
||||||
|
Ordered,
|
||||||
|
Descriptive,
|
||||||
|
}
|
||||||
|
|
||||||
/// The width that something is indented. For example, a single tab character could be a value of 4 or 8.
|
/// The width that something is indented. For example, a single tab character could be a value of 4 or 8.
|
||||||
pub type IndentationLevel = u16;
|
pub type IndentationLevel = u16;
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ 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::PlainListType;
|
||||||
pub use greater_element::PropertyDrawer;
|
pub use greater_element::PropertyDrawer;
|
||||||
pub use greater_element::Table;
|
pub use greater_element::Table;
|
||||||
pub use greater_element::TableRow;
|
pub use greater_element::TableRow;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user