Merge branch 'optional_value'
This commit is contained in:
		
						commit
						7da4e4a29b
					
				
							
								
								
									
										8
									
								
								org_mode_samples/affiliated_keyword/optional_value.org
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								org_mode_samples/affiliated_keyword/optional_value.org
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| #+CAPTION[foo]: *bar* | ||||
| #+CAPTION[*lorem* ipsum]: dolar | ||||
| 1. baz | ||||
| 
 | ||||
| 
 | ||||
| #+CAPTION[foo]: *bar* | ||||
| #+CAPTION[*lorem* ipsum]: dolar | ||||
| # Comments cannot have affiliated keywords so those become regular keywords. | ||||
| @ -416,14 +416,15 @@ where | ||||
| ///
 | ||||
| /// Org-mode seems to store these as a 3-deep list:
 | ||||
| ///   - Outer list with 1 element per #+caption keyword (or other parsed keyword).
 | ||||
| ///   - Middle list that seems to always have 1 element.
 | ||||
| ///   - Inner list of the objects from each #+caption keyword (or other parsed keyword).
 | ||||
| ///   - Middle list which has:
 | ||||
| ///     - first element is a list of objects representing the value after the colon.
 | ||||
| ///     - every additional element is a list of objects from inside the square brackets (the optional value).
 | ||||
| pub(crate) fn compare_property_list_of_list_of_list_of_ast_nodes< | ||||
|     'b, | ||||
|     's, | ||||
|     'x, | ||||
|     R, | ||||
|     RG: Fn(R) -> Option<&'b Vec<Vec<Object<'s>>>>, | ||||
|     RG: Fn(R) -> Option<&'b Vec<(Option<Vec<Object<'s>>>, Vec<Object<'s>>)>>, | ||||
| >( | ||||
|     source: &'s str, | ||||
|     emacs: &'b Token<'s>, | ||||
| @ -459,32 +460,76 @@ pub(crate) fn compare_property_list_of_list_of_list_of_ast_nodes< | ||||
|         (Some(value), Some(rust_value)) => (value, rust_value), | ||||
|     }; | ||||
| 
 | ||||
|     let mut full_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(rust_value.len()); | ||||
| 
 | ||||
|     // Iterate the outer lists
 | ||||
|     for (value, rust_value) in value.iter().zip(rust_value.iter()) { | ||||
|         // Assert the middle list is a length of 1 because I've never seen it any other way.
 | ||||
|         let value = value.as_list()?; | ||||
|         if value.len() != 1 { | ||||
|     for (value, (rust_optional, rust_value)) in value.iter().zip(rust_value.iter()) { | ||||
|         let mut middle_value = value.as_list()?.iter(); | ||||
|         // First element of middle list is the mandatory value (the value past the colon).
 | ||||
|         let mandatory_value = middle_value.next(); | ||||
|         let mandatory_value = match mandatory_value { | ||||
|             Some(mandatory_value) => mandatory_value, | ||||
|             None => { | ||||
|                 let this_status = DiffStatus::Bad; | ||||
|                 let message = Some(format!( | ||||
|                     "{} mismatch (emacs != rust) {:?} != {:?}", | ||||
|                     emacs_field, value, rust_value | ||||
|                 )); | ||||
|                 return Ok(ComparePropertiesResult::SelfChange(this_status, message)); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         // Compare optional value
 | ||||
|         if let Some(rust_optional) = rust_optional { | ||||
|             let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(rust_value.len()); | ||||
|             if rust_optional.len() != middle_value.len() { | ||||
|                 let this_status = DiffStatus::Bad; | ||||
|                 let message = Some(format!( | ||||
|                     "{} optional value length mismatch (emacs != rust) {} != {} | {:?}", | ||||
|                     emacs_field, | ||||
|                     middle_value.len(), | ||||
|                     rust_optional.len(), | ||||
|                     rust_optional | ||||
|                 )); | ||||
|                 return Ok(ComparePropertiesResult::SelfChange(this_status, message)); | ||||
|             } | ||||
|             for (e, r) in middle_value.zip(rust_optional) { | ||||
|                 child_status.push(compare_ast_node(source, e, r.into())?); | ||||
|             } | ||||
|             if !child_status.is_empty() { | ||||
|                 let diff_scope = artificial_diff_scope("optional value", child_status)?; | ||||
|                 full_status.push(diff_scope); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Compare mandatory value
 | ||||
|         let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(rust_value.len()); | ||||
|         let mandatory_value = mandatory_value.as_list()?; | ||||
|         if rust_value.len() != mandatory_value.len() { | ||||
|             let this_status = DiffStatus::Bad; | ||||
|             let message = Some(format!( | ||||
|                 "{} mismatch (emacs != rust) {:?} != {:?}", | ||||
|                 emacs_field, value, rust_value | ||||
|                 "{} mandatory value length mismatch (emacs != rust) {} != {} | {:?}", | ||||
|                 emacs_field, | ||||
|                 mandatory_value.len(), | ||||
|                 rust_value.len(), | ||||
|                 rust_value | ||||
|             )); | ||||
|             return Ok(ComparePropertiesResult::SelfChange(this_status, message)); | ||||
|         } | ||||
|         // Drill past the middle list to the inner list.
 | ||||
|         let value = value | ||||
|             .first() | ||||
|             .expect("The above if-statement asserts this exists."); | ||||
|         let value = value.as_list()?; | ||||
|         // Compare inner lists
 | ||||
|         let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(rust_value.len()); | ||||
|         for (e, r) in value.iter().zip(rust_value) { | ||||
|         for (e, r) in mandatory_value.iter().zip(rust_value) { | ||||
|             child_status.push(compare_ast_node(source, e, r.into())?); | ||||
|         } | ||||
|         let diff_scope = artificial_owned_diff_scope(emacs_field, child_status)?; | ||||
|         return Ok(ComparePropertiesResult::DiffEntry(diff_scope)); | ||||
|         if !child_status.is_empty() { | ||||
|             let diff_scope = artificial_diff_scope("mandatory value", child_status)?; | ||||
|             full_status.push(diff_scope); | ||||
|         } | ||||
|     } | ||||
|     if full_status.is_empty() { | ||||
|         Ok(ComparePropertiesResult::NoChange) | ||||
|     } else { | ||||
|         let diff_scope = artificial_owned_diff_scope(emacs_field, full_status)?; | ||||
|         Ok(ComparePropertiesResult::DiffEntry(diff_scope)) | ||||
|     } | ||||
|     Ok(ComparePropertiesResult::NoChange) | ||||
| } | ||||
| 
 | ||||
| pub(crate) fn compare_property_number_lines< | ||||
|  | ||||
| @ -1,9 +1,21 @@ | ||||
| use std::collections::BTreeMap; | ||||
| 
 | ||||
| use nom::bytes::complete::tag; | ||||
| use nom::bytes::complete::take_until; | ||||
| use nom::character::complete::anychar; | ||||
| use nom::combinator::all_consuming; | ||||
| use nom::combinator::eof; | ||||
| use nom::combinator::map; | ||||
| use nom::combinator::map_parser; | ||||
| use nom::combinator::opt; | ||||
| use nom::combinator::peek; | ||||
| use nom::combinator::recognize; | ||||
| use nom::multi::many0; | ||||
| use nom::multi::many_till; | ||||
| use nom::sequence::tuple; | ||||
| 
 | ||||
| use super::object_parser::standard_set_object; | ||||
| use super::util::confine_context; | ||||
| use crate::context::parser_with_context; | ||||
| use crate::context::Context; | ||||
| use crate::context::ContextElement; | ||||
| @ -45,6 +57,25 @@ pub(crate) fn parse_affiliated_keywords<'g, 's>( | ||||
|             let initial_context = ContextElement::document_context(); | ||||
|             let initial_context = Context::new(global_settings, List::new(&initial_context)); | ||||
| 
 | ||||
|             let (_remaining, optional_objects) = opt(all_consuming(map( | ||||
|                 tuple(( | ||||
|                     take_until("["), | ||||
|                     tag("["), | ||||
|                     map_parser( | ||||
|                         recognize(many_till(anychar, peek(tuple((tag("]"), eof))))), | ||||
|                         confine_context(|i| { | ||||
|                             all_consuming(many0(parser_with_context!(standard_set_object)( | ||||
|                                 &initial_context, | ||||
|                             )))(i) | ||||
|                         }), | ||||
|                     ), | ||||
|                     tag("]"), | ||||
|                     eof, | ||||
|                 )), | ||||
|                 |(_, _, objects, _, _)| objects, | ||||
|             )))(kw.key.into()) | ||||
|             .expect("Object parser should always succeed."); | ||||
| 
 | ||||
|             // TODO: This should be omitting footnote references
 | ||||
|             let (_remaining, objects) = all_consuming(many0(parser_with_context!( | ||||
|                 standard_set_object | ||||
| @ -55,7 +86,7 @@ pub(crate) fn parse_affiliated_keywords<'g, 's>( | ||||
|             }); | ||||
|             match list_of_lists { | ||||
|                 AffiliatedKeywordValue::ListOfListsOfObjects(list_of_lists) => { | ||||
|                     list_of_lists.push(objects); | ||||
|                     list_of_lists.push((optional_objects, objects)); | ||||
|                 } | ||||
|                 _ => panic!("Invalid AffiliatedKeywordValue type."), | ||||
|             } | ||||
| @ -75,12 +106,16 @@ pub(crate) fn parse_affiliated_keywords<'g, 's>( | ||||
| } | ||||
| 
 | ||||
| fn translate_name<'g, 's>(global_settings: &'g GlobalSettings<'g, 's>, name: &'s str) -> String { | ||||
|     let name_until_optval = name | ||||
|         .split_once("[") | ||||
|         .map(|(before, _after)| before) | ||||
|         .unwrap_or(name); | ||||
|     for (src, dst) in global_settings.element_keyword_translation_alist { | ||||
|         if name.eq_ignore_ascii_case(src) { | ||||
|         if name_until_optval.eq_ignore_ascii_case(src) { | ||||
|             return dst.to_lowercase(); | ||||
|         } | ||||
|     } | ||||
|     name.to_lowercase() | ||||
|     name_until_optval.to_lowercase() | ||||
| } | ||||
| 
 | ||||
| fn is_single_string_keyword<'g, 's>( | ||||
|  | ||||
| @ -6,7 +6,7 @@ use super::Object; | ||||
| pub enum AffiliatedKeywordValue<'s> { | ||||
|     SingleString(&'s str), | ||||
|     ListOfStrings(Vec<&'s str>), | ||||
|     ListOfListsOfObjects(Vec<Vec<Object<'s>>>), | ||||
|     ListOfListsOfObjects(Vec<(Option<Vec<Object<'s>>>, Vec<Object<'s>>)>), | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Tom Alexander
						Tom Alexander