Implement the new fields for bold, italic, underline, and strike-through.
This commit is contained in:
		
							parent
							
								
									3fb7cb82cd
								
							
						
					
					
						commit
						8fd9ff3848
					
				| @ -225,6 +225,8 @@ mod tests { | ||||
|         let input = OrgSource::new("foo *bar* baz"); | ||||
|         let radio_target_match = vec![Object::Bold(Bold { | ||||
|             source: "*bar*", | ||||
|             contents: "bar", | ||||
|             post_blank: Some(" "), | ||||
|             children: vec![Object::PlainText(PlainText { source: "bar" })], | ||||
|         })]; | ||||
|         let global_settings = GlobalSettings { | ||||
|  | ||||
| @ -3,11 +3,13 @@ use nom::bytes::complete::tag; | ||||
| use nom::character::complete::anychar; | ||||
| use nom::character::complete::multispace1; | ||||
| use nom::character::complete::one_of; | ||||
| use nom::character::complete::space0; | ||||
| use nom::character::complete::space1; | ||||
| use nom::combinator::all_consuming; | ||||
| use nom::combinator::consumed; | ||||
| use nom::combinator::map; | ||||
| use nom::combinator::map_parser; | ||||
| use nom::combinator::not; | ||||
| use nom::combinator::opt; | ||||
| use nom::combinator::peek; | ||||
| use nom::combinator::recognize; | ||||
| use nom::combinator::verify; | ||||
| @ -76,12 +78,14 @@ fn bold<'b, 'g, 'r, 's>( | ||||
|     context: RefContext<'b, 'g, 'r, 's>, | ||||
|     input: OrgSource<'s>, | ||||
| ) -> Res<OrgSource<'s>, Bold<'s>> { | ||||
|     let (remaining, children) = text_markup_object("*")(context, input)?; | ||||
|     let (remaining, (contents, children, post_blank)) = text_markup_object("*")(context, input)?; | ||||
|     let source = get_consumed(input, remaining); | ||||
|     Ok(( | ||||
|         remaining, | ||||
|         Bold { | ||||
|             source: source.into(), | ||||
|             contents: contents.into(), | ||||
|             post_blank: post_blank.map(Into::<&str>::into), | ||||
|             children, | ||||
|         }, | ||||
|     )) | ||||
| @ -95,12 +99,14 @@ fn italic<'b, 'g, 'r, 's>( | ||||
|     context: RefContext<'b, 'g, 'r, 's>, | ||||
|     input: OrgSource<'s>, | ||||
| ) -> Res<OrgSource<'s>, Italic<'s>> { | ||||
|     let (remaining, children) = text_markup_object("/")(context, input)?; | ||||
|     let (remaining, (contents, children, post_blank)) = text_markup_object("/")(context, input)?; | ||||
|     let source = get_consumed(input, remaining); | ||||
|     Ok(( | ||||
|         remaining, | ||||
|         Italic { | ||||
|             source: source.into(), | ||||
|             contents: contents.into(), | ||||
|             post_blank: post_blank.map(Into::<&str>::into), | ||||
|             children, | ||||
|         }, | ||||
|     )) | ||||
| @ -114,12 +120,14 @@ fn underline<'b, 'g, 'r, 's>( | ||||
|     context: RefContext<'b, 'g, 'r, 's>, | ||||
|     input: OrgSource<'s>, | ||||
| ) -> Res<OrgSource<'s>, Underline<'s>> { | ||||
|     let (remaining, children) = text_markup_object("_")(context, input)?; | ||||
|     let (remaining, (contents, children, post_blank)) = text_markup_object("_")(context, input)?; | ||||
|     let source = get_consumed(input, remaining); | ||||
|     Ok(( | ||||
|         remaining, | ||||
|         Underline { | ||||
|             source: source.into(), | ||||
|             contents: contents.into(), | ||||
|             post_blank: post_blank.map(Into::<&str>::into), | ||||
|             children, | ||||
|         }, | ||||
|     )) | ||||
| @ -133,12 +141,14 @@ fn strike_through<'b, 'g, 'r, 's>( | ||||
|     context: RefContext<'b, 'g, 'r, 's>, | ||||
|     input: OrgSource<'s>, | ||||
| ) -> Res<OrgSource<'s>, StrikeThrough<'s>> { | ||||
|     let (remaining, children) = text_markup_object("+")(context, input)?; | ||||
|     let (remaining, (contents, children, post_blank)) = text_markup_object("+")(context, input)?; | ||||
|     let source = get_consumed(input, remaining); | ||||
|     Ok(( | ||||
|         remaining, | ||||
|         StrikeThrough { | ||||
|             source: source.into(), | ||||
|             contents: contents.into(), | ||||
|             post_blank: post_blank.map(Into::<&str>::into), | ||||
|             children, | ||||
|         }, | ||||
|     )) | ||||
| @ -187,8 +197,10 @@ fn text_markup_object( | ||||
| ) -> impl for<'b, 'g, 'r, 's> Fn( | ||||
|     RefContext<'b, 'g, 'r, 's>, | ||||
|     OrgSource<'s>, | ||||
| ) -> Res<OrgSource<'s>, Vec<Object<'s>>> | ||||
|        + '_ { | ||||
| ) -> Res< | ||||
|     OrgSource<'s>, | ||||
|     (OrgSource<'s>, Vec<Object<'s>>, Option<OrgSource<'s>>), | ||||
| > + '_ { | ||||
|     move |context, input: OrgSource<'_>| _text_markup_object(context, input, marker_symbol) | ||||
| } | ||||
| 
 | ||||
| @ -200,7 +212,7 @@ fn _text_markup_object<'b, 'g, 'r, 's, 'c>( | ||||
|     context: RefContext<'b, 'g, 'r, 's>, | ||||
|     input: OrgSource<'s>, | ||||
|     marker_symbol: &'c str, | ||||
| ) -> Res<OrgSource<'s>, Vec<Object<'s>>> { | ||||
| ) -> Res<OrgSource<'s>, (OrgSource<'s>, Vec<Object<'s>>, Option<OrgSource<'s>>)> { | ||||
|     let (remaining, _) = pre(context, input)?; | ||||
|     let (remaining, open) = tag(marker_symbol)(remaining)?; | ||||
|     let (remaining, _peek_not_whitespace) = | ||||
| @ -215,7 +227,7 @@ fn _text_markup_object<'b, 'g, 'r, 's, 'c>( | ||||
|     let initial_context = ContextElement::document_context(); | ||||
|     let initial_context = Context::new(context.get_global_settings(), List::new(&initial_context)); | ||||
| 
 | ||||
|     let (remaining, children) = map_parser( | ||||
|     let (remaining, (contents, children)) = consumed(map_parser( | ||||
|         verify( | ||||
|             parser_with_context!(text_until_exit)(&parser_context), | ||||
|             |text| text.len() > 0, | ||||
| @ -225,7 +237,7 @@ fn _text_markup_object<'b, 'g, 'r, 's, 'c>( | ||||
|                 &initial_context, | ||||
|             )))(i) | ||||
|         }), | ||||
|     )(remaining)?; | ||||
|     ))(remaining)?; | ||||
| 
 | ||||
|     { | ||||
|         #[cfg(feature = "tracing")] | ||||
| @ -240,9 +252,9 @@ fn _text_markup_object<'b, 'g, 'r, 's, 'c>( | ||||
|     } | ||||
| 
 | ||||
|     let (remaining, _close) = text_markup_end_specialized(context, remaining)?; | ||||
|     let (remaining, _trailing_whitespace) = | ||||
|     let (remaining, post_blank) = | ||||
|         maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?; | ||||
|     Ok((remaining, children)) | ||||
|     Ok((remaining, (contents, children, post_blank))) | ||||
| } | ||||
| 
 | ||||
| fn text_markup_string( | ||||
| @ -382,13 +394,15 @@ impl<'x> RematchObject<'x> for Bold<'x> { | ||||
|         _context: RefContext<'b, 'g, 'r, 's>, | ||||
|         input: OrgSource<'s>, | ||||
|     ) -> Res<OrgSource<'s>, Object<'s>> { | ||||
|         let (remaining, children) = | ||||
|         let (remaining, (contents, children, post_blank)) = | ||||
|             _rematch_text_markup_object(_context, input, "*", &self.children)?; | ||||
|         let source = get_consumed(input, remaining); | ||||
|         Ok(( | ||||
|             remaining, | ||||
|             Object::Bold(Bold { | ||||
|                 source: source.into(), | ||||
|                 contents: contents.into(), | ||||
|                 post_blank: post_blank.map(Into::<&str>::into), | ||||
|                 children, | ||||
|             }), | ||||
|         )) | ||||
| @ -405,13 +419,15 @@ impl<'x> RematchObject<'x> for Italic<'x> { | ||||
|         _context: RefContext<'b, 'g, 'r, 's>, | ||||
|         input: OrgSource<'s>, | ||||
|     ) -> Res<OrgSource<'s>, Object<'s>> { | ||||
|         let (remaining, children) = | ||||
|         let (remaining, (contents, children, post_blank)) = | ||||
|             _rematch_text_markup_object(_context, input, "/", &self.children)?; | ||||
|         let source = get_consumed(input, remaining); | ||||
|         Ok(( | ||||
|             remaining, | ||||
|             Object::Italic(Italic { | ||||
|                 source: source.into(), | ||||
|                 contents: contents.into(), | ||||
|                 post_blank: post_blank.map(Into::<&str>::into), | ||||
|                 children, | ||||
|             }), | ||||
|         )) | ||||
| @ -428,13 +444,15 @@ impl<'x> RematchObject<'x> for Underline<'x> { | ||||
|         _context: RefContext<'b, 'g, 'r, 's>, | ||||
|         input: OrgSource<'s>, | ||||
|     ) -> Res<OrgSource<'s>, Object<'s>> { | ||||
|         let (remaining, children) = | ||||
|         let (remaining, (contents, children, post_blank)) = | ||||
|             _rematch_text_markup_object(_context, input, "_", &self.children)?; | ||||
|         let source = get_consumed(input, remaining); | ||||
|         Ok(( | ||||
|             remaining, | ||||
|             Object::Underline(Underline { | ||||
|                 source: source.into(), | ||||
|                 contents: contents.into(), | ||||
|                 post_blank: post_blank.map(Into::<&str>::into), | ||||
|                 children, | ||||
|             }), | ||||
|         )) | ||||
| @ -451,13 +469,15 @@ impl<'x> RematchObject<'x> for StrikeThrough<'x> { | ||||
|         _context: RefContext<'b, 'g, 'r, 's>, | ||||
|         input: OrgSource<'s>, | ||||
|     ) -> Res<OrgSource<'s>, Object<'s>> { | ||||
|         let (remaining, children) = | ||||
|         let (remaining, (contents, children, post_blank)) = | ||||
|             _rematch_text_markup_object(_context, input, "+", &self.children)?; | ||||
|         let source = get_consumed(input, remaining); | ||||
|         Ok(( | ||||
|             remaining, | ||||
|             Object::StrikeThrough(StrikeThrough { | ||||
|                 source: source.into(), | ||||
|                 contents: contents.into(), | ||||
|                 post_blank: post_blank.map(Into::<&str>::into), | ||||
|                 children, | ||||
|             }), | ||||
|         )) | ||||
| @ -473,7 +493,7 @@ fn _rematch_text_markup_object<'b, 'g, 'r, 's, 'x>( | ||||
|     input: OrgSource<'s>, | ||||
|     marker_symbol: &'static str, | ||||
|     original_match_children: &'x Vec<Object<'x>>, | ||||
| ) -> Res<OrgSource<'s>, Vec<Object<'s>>> { | ||||
| ) -> Res<OrgSource<'s>, (OrgSource<'s>, Vec<Object<'s>>, Option<OrgSource<'s>>)> { | ||||
|     let (remaining, _) = pre(context, input)?; | ||||
|     let (remaining, open) = tag(marker_symbol)(remaining)?; | ||||
|     let (remaining, _peek_not_whitespace) = peek(not(multispace1))(remaining)?; | ||||
| @ -484,6 +504,7 @@ fn _rematch_text_markup_object<'b, 'g, 'r, 's, 'x>( | ||||
|     }); | ||||
|     let parser_context = context.with_additional_node(&parser_context); | ||||
| 
 | ||||
|     let contents_begin = remaining; | ||||
|     let (remaining, children) = | ||||
|         // TODO: This doesn't really check the exit matcher between each object. I think it may be possible to construct an org document that parses incorrectly with the current code.
 | ||||
|         rematch_target(&parser_context, original_match_children, remaining)?; | ||||
| @ -499,8 +520,10 @@ fn _rematch_text_markup_object<'b, 'g, 'r, 's, 'x>( | ||||
|             ))); | ||||
|         } | ||||
|     } | ||||
|     let contents_end = remaining; | ||||
|     let contents = contents_begin.get_until(contents_end); | ||||
| 
 | ||||
|     let (remaining, _close) = text_markup_end_specialized(context, remaining)?; | ||||
|     let (remaining, _trailing_whitespace) = space0(remaining)?; | ||||
|     Ok((remaining, children)) | ||||
|     let (remaining, post_blank) = opt(space1)(remaining)?; | ||||
|     Ok((remaining, (contents, children, post_blank))) | ||||
| } | ||||
|  | ||||
| @ -81,14 +81,21 @@ pub(crate) fn maybe_consume_object_trailing_whitespace_if_not_exiting<'b, 'g, 'r | ||||
|     input: OrgSource<'s>, | ||||
| ) -> Res<OrgSource<'s>, Option<OrgSource<'s>>> { | ||||
|     // We have to check exit matcher after each character because description list tags need to end with a space unconsumed (" ::").
 | ||||
|     let (remaining, _) = many_till( | ||||
|     let (remaining, post_blank) = recognize(many_till( | ||||
|         one_of(" \t"), | ||||
|         alt(( | ||||
|             peek(recognize(none_of(" \t"))), | ||||
|             parser_with_context!(exit_matcher_parser)(context), | ||||
|         )), | ||||
|     )(input)?; | ||||
|     Ok((remaining, None)) | ||||
|     ))(input)?; | ||||
|     Ok(( | ||||
|         remaining, | ||||
|         if post_blank.len() == 0 { | ||||
|             None | ||||
|         } else { | ||||
|             Some(post_blank) | ||||
|         }, | ||||
|     )) | ||||
| } | ||||
| 
 | ||||
| #[cfg_attr(
 | ||||
|  | ||||
| @ -43,24 +43,32 @@ pub enum Object<'s> { | ||||
| #[derive(Debug)] | ||||
| pub struct Bold<'s> { | ||||
|     pub source: &'s str, | ||||
|     pub contents: &'s str, | ||||
|     pub post_blank: Option<&'s str>, | ||||
|     pub children: Vec<Object<'s>>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct Italic<'s> { | ||||
|     pub source: &'s str, | ||||
|     pub contents: &'s str, | ||||
|     pub post_blank: Option<&'s str>, | ||||
|     pub children: Vec<Object<'s>>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct Underline<'s> { | ||||
|     pub source: &'s str, | ||||
|     pub contents: &'s str, | ||||
|     pub post_blank: Option<&'s str>, | ||||
|     pub children: Vec<Object<'s>>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct StrikeThrough<'s> { | ||||
|     pub source: &'s str, | ||||
|     pub contents: &'s str, | ||||
|     pub post_blank: Option<&'s str>, | ||||
|     pub children: Vec<Object<'s>>, | ||||
| } | ||||
| 
 | ||||
| @ -523,11 +531,15 @@ impl<'s> StandardProperties<'s> for Bold<'s> { | ||||
|     } | ||||
| 
 | ||||
|     fn get_contents<'b>(&'b self) -> Option<&'s str> { | ||||
|         todo!() | ||||
|         Some(self.contents) | ||||
|     } | ||||
| 
 | ||||
|     fn get_post_blank(&self) -> PostBlank { | ||||
|         todo!() | ||||
|         self.post_blank | ||||
|             .map(|post_blank| post_blank.chars().count()) | ||||
|             .unwrap_or(0) | ||||
|             .try_into() | ||||
|             .expect("Too much post-blank to fit into a PostBlank.") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -537,11 +549,15 @@ impl<'s> StandardProperties<'s> for Italic<'s> { | ||||
|     } | ||||
| 
 | ||||
|     fn get_contents<'b>(&'b self) -> Option<&'s str> { | ||||
|         todo!() | ||||
|         Some(self.contents) | ||||
|     } | ||||
| 
 | ||||
|     fn get_post_blank(&self) -> PostBlank { | ||||
|         todo!() | ||||
|         self.post_blank | ||||
|             .map(|post_blank| post_blank.chars().count()) | ||||
|             .unwrap_or(0) | ||||
|             .try_into() | ||||
|             .expect("Too much post-blank to fit into a PostBlank.") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -551,11 +567,15 @@ impl<'s> StandardProperties<'s> for Underline<'s> { | ||||
|     } | ||||
| 
 | ||||
|     fn get_contents<'b>(&'b self) -> Option<&'s str> { | ||||
|         todo!() | ||||
|         Some(self.contents) | ||||
|     } | ||||
| 
 | ||||
|     fn get_post_blank(&self) -> PostBlank { | ||||
|         todo!() | ||||
|         self.post_blank | ||||
|             .map(|post_blank| post_blank.chars().count()) | ||||
|             .unwrap_or(0) | ||||
|             .try_into() | ||||
|             .expect("Too much post-blank to fit into a PostBlank.") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -565,11 +585,15 @@ impl<'s> StandardProperties<'s> for StrikeThrough<'s> { | ||||
|     } | ||||
| 
 | ||||
|     fn get_contents<'b>(&'b self) -> Option<&'s str> { | ||||
|         todo!() | ||||
|         Some(self.contents) | ||||
|     } | ||||
| 
 | ||||
|     fn get_post_blank(&self) -> PostBlank { | ||||
|         todo!() | ||||
|         self.post_blank | ||||
|             .map(|post_blank| post_blank.chars().count()) | ||||
|             .unwrap_or(0) | ||||
|             .try_into() | ||||
|             .expect("Too much post-blank to fit into a PostBlank.") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Tom Alexander
						Tom Alexander