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(( | ||||
|             line_ending, | ||||
|             tag(" :: "), | ||||
|             recognize(tuple((tag(" ::"), alt((line_ending, eof))))), | ||||
|         ))), | ||||
|     ))(input) | ||||
| 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) | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Tom Alexander
						Tom Alexander