Add support for diffing description lists.
This commit is contained in:
parent
2682779534
commit
f6c895319f
@ -5,3 +5,4 @@
|
||||
:: ipsum
|
||||
-
|
||||
lorem :: ipsum
|
||||
- dolar *bold* foo :: ipsum
|
||||
|
@ -182,6 +182,23 @@ impl<'s> DiffResult<'s> {
|
||||
}
|
||||
}
|
||||
|
||||
fn artificial_diff_scope<'s>(
|
||||
name: String,
|
||||
message: Option<String>,
|
||||
children: Vec<DiffResult<'s>>,
|
||||
emacs_token: &'s Token<'s>,
|
||||
rust_source: &'s str,
|
||||
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
|
||||
Ok(DiffResult {
|
||||
status: DiffStatus::Good,
|
||||
name,
|
||||
message,
|
||||
children,
|
||||
rust_source,
|
||||
emacs_token,
|
||||
})
|
||||
}
|
||||
|
||||
fn compare_element<'s>(
|
||||
source: &'s str,
|
||||
emacs: &'s Token<'s>,
|
||||
@ -385,18 +402,10 @@ fn compare_heading<'s>(
|
||||
}
|
||||
|
||||
// Compare todo-keyword
|
||||
let todo_keyword = {
|
||||
let children = emacs.as_list()?;
|
||||
let attributes_child = children
|
||||
.iter()
|
||||
.nth(1)
|
||||
.ok_or("Should have an attributes child.")?;
|
||||
let attributes_map = attributes_child.as_map()?;
|
||||
let todo_keyword = attributes_map
|
||||
.get(":todo-keyword")
|
||||
.ok_or("Missing :todo-keyword attribute.");
|
||||
todo_keyword?.as_atom()?
|
||||
};
|
||||
let todo_keyword = get_property(emacs, ":todo-keyword")?
|
||||
.map(Token::as_atom)
|
||||
.map_or(Ok(None), |r| r.map(Some))?
|
||||
.unwrap_or("nil");
|
||||
match (todo_keyword, rust.todo_keyword, unquote(todo_keyword)) {
|
||||
("nil", None, _) => {}
|
||||
(_, Some(rust_todo), Ok(emacs_todo)) if emacs_todo == rust_todo => {}
|
||||
@ -410,18 +419,7 @@ fn compare_heading<'s>(
|
||||
};
|
||||
|
||||
// Compare title
|
||||
let title = {
|
||||
let children = emacs.as_list()?;
|
||||
let attributes_child = children
|
||||
.iter()
|
||||
.nth(1)
|
||||
.ok_or("Should have an attributes child.")?;
|
||||
let attributes_map = attributes_child.as_map()?;
|
||||
let title = attributes_map
|
||||
.get(":title")
|
||||
.ok_or("Missing :title attribute.");
|
||||
*title?
|
||||
};
|
||||
let title = get_property(emacs, ":title")?.ok_or("Missing :title attribute.")?;
|
||||
for (emacs_child, rust_child) in title.as_list()?.iter().zip(rust.title.iter()) {
|
||||
child_status.push(compare_object(source, emacs_child, rust_child)?);
|
||||
}
|
||||
@ -453,19 +451,12 @@ fn compare_heading<'s>(
|
||||
fn get_tags_from_heading<'s>(
|
||||
emacs: &'s Token<'s>,
|
||||
) -> Result<HashSet<String>, Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let attributes_child = children
|
||||
.iter()
|
||||
.nth(1)
|
||||
.ok_or("Should have an attributes child.")?;
|
||||
let attributes_map = attributes_child.as_map()?;
|
||||
let tags = attributes_map
|
||||
.get(":tags")
|
||||
.ok_or("Missing :tags attribute.")?;
|
||||
let tags = match get_property(emacs, ":tags")? {
|
||||
Some(prop) => prop,
|
||||
None => return Ok(HashSet::new()),
|
||||
};
|
||||
|
||||
match tags.as_atom() {
|
||||
Ok("nil") => {
|
||||
return Ok(HashSet::new());
|
||||
}
|
||||
Ok(val) => panic!("Unexpected value for tags: {:?}", val),
|
||||
Err(_) => {}
|
||||
};
|
||||
@ -483,6 +474,26 @@ fn get_tags_from_heading<'s>(
|
||||
Ok(tags)
|
||||
}
|
||||
|
||||
fn get_property<'s, 'x>(
|
||||
emacs: &'s Token<'s>,
|
||||
key: &'x str,
|
||||
) -> Result<Option<&'s Token<'s>>, Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let attributes_child = children
|
||||
.iter()
|
||||
.nth(1)
|
||||
.ok_or("Should have an attributes child.")?;
|
||||
let attributes_map = attributes_child.as_map()?;
|
||||
let prop = attributes_map
|
||||
.get(key)
|
||||
.ok_or(format!("Missing {} attribute.", key))?;
|
||||
match prop.as_atom() {
|
||||
Ok("nil") => return Ok(None),
|
||||
_ => {}
|
||||
};
|
||||
Ok(Some(*prop))
|
||||
}
|
||||
|
||||
fn compare_paragraph<'s>(
|
||||
source: &'s str,
|
||||
emacs: &'s Token<'s>,
|
||||
@ -553,6 +564,7 @@ fn compare_plain_list_item<'s>(
|
||||
let children = emacs.as_list()?;
|
||||
let mut child_status = Vec::new();
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
let emacs_name = "item";
|
||||
if assert_name(emacs, emacs_name).is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
@ -562,14 +574,50 @@ fn compare_plain_list_item<'s>(
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
||||
child_status.push(compare_element(source, emacs_child, rust_child)?);
|
||||
// Compare tag
|
||||
let tag = get_property(emacs, ":tag")?;
|
||||
match (tag, rust.tag.is_empty()) {
|
||||
(None, true) => {}
|
||||
(None, false) | (Some(_), true) => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some("Mismatched tags".to_owned());
|
||||
}
|
||||
(Some(tag), false) => {
|
||||
let tag_status = tag
|
||||
.as_list()?
|
||||
.iter()
|
||||
.zip(rust.tag.iter())
|
||||
.map(|(emacs_child, rust_child)| compare_object(source, emacs_child, rust_child))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
child_status.push(artificial_diff_scope(
|
||||
"tag".to_owned(),
|
||||
None,
|
||||
tag_status,
|
||||
tag,
|
||||
rust.get_source(),
|
||||
)?);
|
||||
}
|
||||
};
|
||||
|
||||
// Compare contents
|
||||
let contents_status = children
|
||||
.iter()
|
||||
.skip(2)
|
||||
.zip(rust.children.iter())
|
||||
.map(|(emacs_child, rust_child)| compare_element(source, emacs_child, rust_child))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
child_status.push(artificial_diff_scope(
|
||||
"contents".to_owned(),
|
||||
None,
|
||||
contents_status,
|
||||
emacs,
|
||||
rust.get_source(),
|
||||
)?);
|
||||
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
name: emacs_name.to_owned(),
|
||||
message: None,
|
||||
message,
|
||||
children: child_status,
|
||||
rust_source: rust.get_source(),
|
||||
emacs_token: emacs,
|
||||
@ -1167,6 +1215,7 @@ fn compare_plain_text<'s>(
|
||||
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
let rust_source = rust.get_source();
|
||||
let text = emacs.as_text()?;
|
||||
let start_ind: usize = text
|
||||
.properties
|
||||
@ -1181,20 +1230,20 @@ fn compare_plain_text<'s>(
|
||||
.as_atom()?
|
||||
.parse()?;
|
||||
let emacs_text_length = end_ind - start_ind;
|
||||
if rust.source.len() != emacs_text_length {
|
||||
if rust_source.len() != emacs_text_length {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"(emacs len != rust len) {:?} != {:?}",
|
||||
emacs_text_length,
|
||||
rust.source.len()
|
||||
rust_source.len()
|
||||
));
|
||||
}
|
||||
let unquoted_text = unquote(text.text)?;
|
||||
if unquoted_text != rust.source {
|
||||
if unquoted_text != rust_source {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"(emacs != rust) {:?} != {:?}",
|
||||
unquoted_text, rust.source
|
||||
unquoted_text, rust_source
|
||||
));
|
||||
}
|
||||
|
||||
@ -1203,7 +1252,7 @@ fn compare_plain_text<'s>(
|
||||
name: "plain-text".to_owned(),
|
||||
message,
|
||||
children: Vec::new(),
|
||||
rust_source: rust.get_source(),
|
||||
rust_source,
|
||||
emacs_token: emacs,
|
||||
})
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::element::Element;
|
||||
use super::lesser_element::TableCell;
|
||||
use super::source::Source;
|
||||
use super::Object;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PlainList<'s> {
|
||||
@ -13,6 +14,7 @@ pub struct PlainListItem<'s> {
|
||||
pub source: &'s str,
|
||||
pub indentation: usize,
|
||||
pub bullet: &'s str,
|
||||
pub tag: Vec<Object<'s>>,
|
||||
pub children: Vec<Element<'s>>,
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,12 @@
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::character::complete::anychar;
|
||||
use nom::character::complete::digit1;
|
||||
use nom::character::complete::line_ending;
|
||||
use nom::character::complete::multispace1;
|
||||
use nom::character::complete::one_of;
|
||||
use nom::character::complete::space0;
|
||||
use nom::character::complete::space1;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::opt;
|
||||
use nom::combinator::peek;
|
||||
use nom::combinator::recognize;
|
||||
use nom::combinator::verify;
|
||||
use nom::multi::many1;
|
||||
@ -18,10 +15,12 @@ use nom::sequence::tuple;
|
||||
|
||||
use super::greater_element::PlainList;
|
||||
use super::greater_element::PlainListItem;
|
||||
use super::object_parser::standard_set_object;
|
||||
use super::org_source::OrgSource;
|
||||
use super::parser_with_context::parser_with_context;
|
||||
use super::util::non_whitespace_character;
|
||||
use super::Context;
|
||||
use super::Object;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
@ -151,6 +150,7 @@ pub fn plain_list_item<'r, 's>(
|
||||
source: source.into(),
|
||||
indentation: indent_level,
|
||||
bullet: bull.into(),
|
||||
tag: Vec::new(),
|
||||
children: Vec::new(),
|
||||
},
|
||||
));
|
||||
@ -158,7 +158,11 @@ pub fn plain_list_item<'r, 's>(
|
||||
Err(_) => {}
|
||||
};
|
||||
|
||||
let (remaining, _maybe_tag) = opt(tuple((space1, item_tag, tag(" ::"))))(remaining)?;
|
||||
let (remaining, maybe_tag) = opt(tuple((
|
||||
space1,
|
||||
parser_with_context!(item_tag)(context),
|
||||
tag(" ::"),
|
||||
)))(remaining)?;
|
||||
let (remaining, _ws) = alt((space1, line_ending))(remaining)?;
|
||||
let exit_matcher = plain_list_item_end(indent_level);
|
||||
let parser_context = context
|
||||
@ -183,6 +187,9 @@ pub fn plain_list_item<'r, 's>(
|
||||
source: source.into(),
|
||||
indentation: indent_level,
|
||||
bullet: bull.into(),
|
||||
tag: maybe_tag
|
||||
.map(|(_ws, item_tag, _divider)| item_tag)
|
||||
.unwrap_or(Vec::new()), // TODO
|
||||
children,
|
||||
},
|
||||
));
|
||||
@ -266,15 +273,36 @@ fn _line_indented_lte<'r, 's>(
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn item_tag<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
recognize(many_till(
|
||||
anychar,
|
||||
peek(alt((
|
||||
fn item_tag<'r, 's>(
|
||||
context: Context<'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
|
||||
let parser_context =
|
||||
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||
class: ExitClass::Gamma,
|
||||
exit_matcher: &item_tag_end,
|
||||
}));
|
||||
let (remaining, (children, _exit_contents)) = verify(
|
||||
many_till(
|
||||
// TODO: Should this be using a different set like the minimal set?
|
||||
parser_with_context!(standard_set_object)(&parser_context),
|
||||
parser_with_context!(exit_matcher_parser)(&parser_context),
|
||||
),
|
||||
|(children, _exit_contents)| !children.is_empty(),
|
||||
)(input)?;
|
||||
Ok((remaining, children))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn item_tag_end<'r, 's>(
|
||||
_context: Context<'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
recognize(alt((
|
||||
line_ending,
|
||||
tag(" :: "),
|
||||
recognize(tuple((tag(" ::"), alt((line_ending, eof))))),
|
||||
))),
|
||||
))(input)
|
||||
)))(input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
Loading…
Reference in New Issue
Block a user