Merge remote-tracking branch 'input/main' into wrapped_input
This commit is contained in:
		
						commit
						65b87bd65d
					
				
							
								
								
									
										180
									
								
								src/input/wrapped_input.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								src/input/wrapped_input.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,180 @@ | ||||
| use std::ops::RangeBounds; | ||||
| 
 | ||||
| use nom::{Compare, InputTake, Slice}; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| struct WrappedInput<'s> { | ||||
|     full_source: &'s str, | ||||
|     start: usize, | ||||
|     end: usize, //exclusive
 | ||||
|     preceding_line_break: Option<usize>, | ||||
| } | ||||
| 
 | ||||
| impl<'s> WrappedInput<'s> { | ||||
|     /// Returns a wrapped string that keeps track of values we need for parsing org-mode.
 | ||||
|     ///
 | ||||
|     /// Only call this on the full original string. Calling this on a substring can result in invalid values.
 | ||||
|     pub fn new(input: &'s str) -> Self { | ||||
|         WrappedInput { | ||||
|             full_source: input, | ||||
|             start: 0, | ||||
|             end: input.len(), | ||||
|             preceding_line_break: None, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Get the text since the line break preceding the start of this WrappedInput.
 | ||||
|     pub fn text_since_line_break(&self) -> &'s str { | ||||
|         let start = self.preceding_line_break.unwrap_or(0); | ||||
|         &self.full_source[start..self.start] | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'s> InputTake for WrappedInput<'s> { | ||||
|     fn take(&self, count: usize) -> Self { | ||||
|         self.slice(..count) | ||||
|     } | ||||
| 
 | ||||
|     fn take_split(&self, count: usize) -> (Self, Self) { | ||||
|         (self.slice(count..), self.slice(..count)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'s, O: Into<&'s str>> Compare<O> for WrappedInput<'s> { | ||||
|     fn compare(&self, t: O) -> nom::CompareResult { | ||||
|         (&self.full_source[self.start..self.end]).compare(t.into()) | ||||
|     } | ||||
| 
 | ||||
|     fn compare_no_case(&self, t: O) -> nom::CompareResult { | ||||
|         (&self.full_source[self.start..self.end]).compare_no_case(t.into()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'s> From<&'s str> for WrappedInput<'s> { | ||||
|     fn from(value: &'s str) -> Self { | ||||
|         WrappedInput::new(value) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'s> From<&WrappedInput<'s>> for &'s str { | ||||
|     fn from(value: &WrappedInput<'s>) -> Self { | ||||
|         &value.full_source[value.start..value.end] | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'s, R> Slice<R> for WrappedInput<'s> | ||||
| where | ||||
|     R: RangeBounds<usize>, | ||||
| { | ||||
|     fn slice(&self, range: R) -> Self { | ||||
|         let new_start = match range.start_bound() { | ||||
|             std::ops::Bound::Included(idx) => self.start + idx, | ||||
|             std::ops::Bound::Excluded(idx) => self.start + idx - 1, | ||||
|             std::ops::Bound::Unbounded => self.start, | ||||
|         }; | ||||
|         let new_end = match range.end_bound() { | ||||
|             std::ops::Bound::Included(idx) => self.start + idx + 1, | ||||
|             std::ops::Bound::Excluded(idx) => self.start + idx, | ||||
|             std::ops::Bound::Unbounded => self.end, | ||||
|         }; | ||||
|         if new_start < self.start { | ||||
|             panic!("Attempted to extend before the start of the WrappedInput.") | ||||
|         } | ||||
|         if new_end > self.end { | ||||
|             panic!("Attempted to extend past the end of the WrappedInput.") | ||||
|         } | ||||
| 
 | ||||
|         let skipped_text = &self.full_source[self.start..new_start]; | ||||
|         let last_line_feed = skipped_text | ||||
|             .rfind('\n') | ||||
|             .map(|idx| idx + self.preceding_line_break.unwrap_or(0) + 1); | ||||
| 
 | ||||
|         // TODO: calculate updated values for WrappedInput
 | ||||
|         WrappedInput { | ||||
|             full_source: self.full_source, | ||||
|             start: new_start, | ||||
|             end: new_end, | ||||
|             preceding_line_break: last_line_feed, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'s> std::fmt::Display for WrappedInput<'s> { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||||
|         Into::<&str>::into(self).fmt(f) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn range() { | ||||
|         let input = WrappedInput::new("foo bar baz"); | ||||
|         let output = input.slice(4..7); | ||||
|         assert_eq!(output.to_string(), "bar"); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn range_to() { | ||||
|         let input = WrappedInput::new("foo bar baz"); | ||||
|         let output = input.slice(..7); | ||||
|         assert_eq!(output.to_string(), "foo bar"); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn range_from() { | ||||
|         let input = WrappedInput::new("foo bar baz"); | ||||
|         let output = input.slice(4..); | ||||
|         assert_eq!(output.to_string(), "bar baz"); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn full_range() { | ||||
|         let input = WrappedInput::new("foo bar baz"); | ||||
|         let output = input.slice(..); | ||||
|         assert_eq!(output.to_string(), "foo bar baz"); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn nested_range() { | ||||
|         let input = WrappedInput::new("lorem foo bar baz ipsum"); | ||||
|         let first_cut = input.slice(6..17); | ||||
|         let output = first_cut.slice(4..7); | ||||
|         assert_eq!(first_cut.to_string(), "foo bar baz"); | ||||
|         assert_eq!(output.to_string(), "bar"); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     #[should_panic] | ||||
|     fn out_of_bounds() { | ||||
|         let input = WrappedInput::new("lorem foo bar baz ipsum"); | ||||
|         input.slice(6..30); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     #[should_panic] | ||||
|     fn out_of_nested_bounds() { | ||||
|         let input = WrappedInput::new("lorem foo bar baz ipsum"); | ||||
|         let first_cut = input.slice(6..17); | ||||
|         first_cut.slice(4..14); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn line_break() { | ||||
|         let input = WrappedInput::new("lorem\nfoo\nbar\nbaz\nipsum"); | ||||
|         assert_eq!(input.slice(5..).preceding_line_break, None); | ||||
|         assert_eq!(input.slice(6..).preceding_line_break, Some(6)); | ||||
|         assert_eq!(input.slice(6..).slice(10..).preceding_line_break, Some(14)); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn text_since_line_break() { | ||||
|         let input = WrappedInput::new("lorem\nfoo\nbar\nbaz\nipsum"); | ||||
|         assert_eq!(input.text_since_line_break(), ""); | ||||
|         assert_eq!(input.slice(5..).text_since_line_break(), "lorem"); | ||||
|         assert_eq!(input.slice(6..).text_since_line_break(), ""); | ||||
|         assert_eq!(input.slice(6..).slice(10..).text_since_line_break(), "ba"); | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Tom Alexander
						Tom Alexander