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