Compare heading todo keywords.
This only handles the default case where the only valid TODO keywords are TODO and DONE.
This commit is contained in:
		
							parent
							
								
									9cc5e63c1b
								
							
						
					
					
						commit
						3e143796f7
					
				| @ -2,6 +2,7 @@ use std::collections::HashSet; | ||||
| 
 | ||||
| use super::util::assert_bounds; | ||||
| use super::util::assert_name; | ||||
| use crate::parser::sexp::unquote; | ||||
| use crate::parser::sexp::Token; | ||||
| use crate::parser::AngleLink; | ||||
| use crate::parser::Bold; | ||||
| @ -60,7 +61,6 @@ use crate::parser::Timestamp; | ||||
| use crate::parser::Underline; | ||||
| use crate::parser::Verbatim; | ||||
| use crate::parser::VerseBlock; | ||||
| use crate::parser::sexp::unquote; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct DiffResult { | ||||
| @ -336,6 +336,45 @@ fn compare_heading<'s>( | ||||
|         this_status = DiffStatus::Bad; | ||||
|     } | ||||
| 
 | ||||
|     // Compare tags
 | ||||
|     let emacs_tags = get_tags_from_heading(emacs)?; | ||||
|     let emacs_tags: HashSet<_> = emacs_tags.iter().map(|val| val.as_str()).collect(); | ||||
|     let rust_tags: HashSet<&str> = rust.tags.iter().map(|val| *val).collect(); | ||||
|     let difference: Vec<&str> = emacs_tags | ||||
|         .symmetric_difference(&rust_tags) | ||||
|         .map(|val| *val) | ||||
|         .collect(); | ||||
|     if !difference.is_empty() { | ||||
|         this_status = DiffStatus::Bad; | ||||
|         message = Some(format!("Mismatched tags: {}", difference.join(", "))); | ||||
|     } | ||||
| 
 | ||||
|     // 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()? | ||||
|     }; | ||||
|     match (todo_keyword, rust.todo_keyword, unquote(todo_keyword)) { | ||||
|         ("nil", None, _) => {} | ||||
|         (_, Some(rust_todo), Ok(emacs_todo)) if emacs_todo == rust_todo => {} | ||||
|         (emacs_todo, rust_todo, _) => { | ||||
|             this_status = DiffStatus::Bad; | ||||
|             message = Some(format!( | ||||
|                 "(emacs != rust) {:?} != {:?}", | ||||
|                 emacs_todo, rust_todo | ||||
|             )); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     // Compare title
 | ||||
|     let title = { | ||||
|         let children = emacs.as_list()?; | ||||
|         let attributes_child = children | ||||
| @ -351,20 +390,10 @@ fn compare_heading<'s>( | ||||
|     for (emacs_child, rust_child) in title.as_list()?.iter().zip(rust.title.iter()) { | ||||
|         child_status.push(compare_object(source, emacs_child, rust_child)?); | ||||
|     } | ||||
|     let emacs_tags = get_tags_from_heading(emacs)?; | ||||
|     let emacs_tags: HashSet<_> = emacs_tags.iter().map(|val| val.as_str()).collect(); | ||||
|     let rust_tags: HashSet<&str> = rust.tags.iter().map(|val| *val).collect(); | ||||
|     let difference: Vec<&str> = emacs_tags | ||||
|         .symmetric_difference(&rust_tags) | ||||
|         .map(|val| *val) | ||||
|         .collect(); | ||||
|     if !difference.is_empty() { | ||||
|         this_status = DiffStatus::Bad; | ||||
|         message = Some(format!("Mismatched tags: {}", difference.join(", "))); | ||||
|     } | ||||
| 
 | ||||
|     // TODO: Compare todo-keyword, level, priority
 | ||||
|     // TODO: Compare todo-type, level, priority
 | ||||
| 
 | ||||
|     // Compare section
 | ||||
|     for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) { | ||||
|         match rust_child { | ||||
|             DocumentElement::Heading(rust_heading) => { | ||||
| @ -405,8 +434,14 @@ fn get_tags_from_heading<'s>( | ||||
|     }; | ||||
|     let tags = { | ||||
|         let tags = tags.as_list()?; | ||||
|         let strings = tags.iter().map(Token::as_atom).collect::<Result<Vec<&str>, _>>()?; | ||||
|         strings.into_iter().map(unquote).collect::<Result<HashSet<String>, _>>()? | ||||
|         let strings = tags | ||||
|             .iter() | ||||
|             .map(Token::as_atom) | ||||
|             .collect::<Result<Vec<&str>, _>>()?; | ||||
|         strings | ||||
|             .into_iter() | ||||
|             .map(unquote) | ||||
|             .collect::<Result<HashSet<String>, _>>()? | ||||
|     }; | ||||
|     Ok(tags) | ||||
| } | ||||
|  | ||||
| @ -53,6 +53,8 @@ pub struct Document<'s> { | ||||
| pub struct Heading<'s> { | ||||
|     pub source: &'s str, | ||||
|     pub stars: usize, | ||||
|     pub todo_keyword: Option<&'s str>, | ||||
|     // TODO: add todo-type enum
 | ||||
|     pub title: Vec<Object<'s>>, | ||||
|     pub tags: Vec<&'s str>, | ||||
|     pub children: Vec<DocumentElement<'s>>, | ||||
| @ -271,7 +273,8 @@ fn heading<'r, 's>( | ||||
|     input: OrgSource<'s>, | ||||
| ) -> Res<OrgSource<'s>, Heading<'s>> { | ||||
|     not(|i| context.check_exit_matcher(i))(input)?; | ||||
|     let (remaining, (star_count, _ws, title, heading_tags)) = headline(context, input)?; | ||||
|     let (remaining, (star_count, _ws, maybe_todo_keyword, title, heading_tags)) = | ||||
|         headline(context, input)?; | ||||
|     let section_matcher = parser_with_context!(section)(context); | ||||
|     let heading_matcher = parser_with_context!(heading)(context); | ||||
|     let (remaining, children) = many0(alt(( | ||||
| @ -287,6 +290,8 @@ fn heading<'r, 's>( | ||||
|         Heading { | ||||
|             source: source.into(), | ||||
|             stars: star_count, | ||||
|             todo_keyword: maybe_todo_keyword | ||||
|                 .map(|(todo_keyword, _ws)| Into::<&str>::into(todo_keyword)), | ||||
|             title, | ||||
|             tags: heading_tags, | ||||
|             children, | ||||
| @ -298,7 +303,16 @@ fn heading<'r, 's>( | ||||
| fn headline<'r, 's>( | ||||
|     context: Context<'r, 's>, | ||||
|     input: OrgSource<'s>, | ||||
| ) -> Res<OrgSource<'s>, (usize, OrgSource<'s>, Vec<Object<'s>>, Vec<&'s str>)> { | ||||
| ) -> Res< | ||||
|     OrgSource<'s>, | ||||
|     ( | ||||
|         usize, | ||||
|         OrgSource<'s>, | ||||
|         Option<(OrgSource<'s>, OrgSource<'s>)>, | ||||
|         Vec<Object<'s>>, | ||||
|         Vec<&'s str>, | ||||
|     ), | ||||
| > { | ||||
|     let parser_context = | ||||
|         context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { | ||||
|             class: ExitClass::Document, | ||||
| @ -306,10 +320,14 @@ fn headline<'r, 's>( | ||||
|         })); | ||||
|     let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context); | ||||
| 
 | ||||
|     let (remaining, (_sol, star_count, ws, title, maybe_tags, _ws, _line_ending)) = tuple(( | ||||
|     let ( | ||||
|         remaining, | ||||
|         (_sol, star_count, ws, maybe_todo_keyword, title, maybe_tags, _ws, _line_ending), | ||||
|     ) = tuple(( | ||||
|         start_of_line, | ||||
|         many1_count(tag("*")), | ||||
|         space1, | ||||
|         opt(tuple((heading_keyword, space1))), | ||||
|         many1(standard_set_object_matcher), | ||||
|         opt(tuple((space0, tags))), | ||||
|         space0, | ||||
| @ -320,6 +338,7 @@ fn headline<'r, 's>( | ||||
|         ( | ||||
|             star_count, | ||||
|             ws, | ||||
|             maybe_todo_keyword, | ||||
|             title, | ||||
|             maybe_tags | ||||
|                 .map(|(_ws, tags)| { | ||||
| @ -357,6 +376,12 @@ fn single_tag<'r, 's>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> | ||||
|     })))(input) | ||||
| } | ||||
| 
 | ||||
| #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] | ||||
| fn heading_keyword<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> { | ||||
|     // TODO: This should take into account the value of "#+TODO:" ref https://orgmode.org/manual/Per_002dfile-keywords.html and possibly the configurable variable org-todo-keywords ref https://orgmode.org/manual/Workflow-states.html. Case is significant.
 | ||||
|     alt((tag("TODO"), tag("DONE")))(input) | ||||
| } | ||||
| 
 | ||||
| impl<'s> Document<'s> { | ||||
|     pub fn iter_tokens<'r>(&'r self) -> impl Iterator<Item = Token<'r, 's>> { | ||||
|         AllTokensIterator::new(Token::Document(self)) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Tom Alexander
						Tom Alexander