feat(match): provide word separators in the output of Rolling Match result
This commit is contained in:
		
							parent
							
								
									dd2cc9de17
								
							
						
					
					
						commit
						97b789e946
					
				|  | @ -30,6 +30,8 @@ mod util; | ||||||
| pub struct MatchResult<Id> { | pub struct MatchResult<Id> { | ||||||
|   pub id: Id, |   pub id: Id, | ||||||
|   pub trigger: String, |   pub trigger: String, | ||||||
|  |   pub left_separator: Option<String>, | ||||||
|  |   pub right_separator: Option<String>, | ||||||
|   pub vars: HashMap<String, String>, |   pub vars: HashMap<String, String>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -38,6 +40,8 @@ impl<Id: Default> Default for MatchResult<Id> { | ||||||
|     Self { |     Self { | ||||||
|       id: Id::default(), |       id: Id::default(), | ||||||
|       trigger: "".to_string(), |       trigger: "".to_string(), | ||||||
|  |       left_separator: None, | ||||||
|  |       right_separator: None, | ||||||
|       vars: HashMap::new(), |       vars: HashMap::new(), | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -102,6 +102,8 @@ where | ||||||
|               let result = MatchResult { |               let result = MatchResult { | ||||||
|                 id: (*id).clone(), |                 id: (*id).clone(), | ||||||
|                 trigger: full_match.to_string(), |                 trigger: full_match.to_string(), | ||||||
|  |                 left_separator: None, | ||||||
|  |                 right_separator: None, | ||||||
|                 vars: variables, |                 vars: variables, | ||||||
|               }; |               }; | ||||||
| 
 | 
 | ||||||
|  | @ -169,6 +171,8 @@ mod tests { | ||||||
|     MatchResult { |     MatchResult { | ||||||
|       id, |       id, | ||||||
|       trigger: trigger.to_string(), |       trigger: trigger.to_string(), | ||||||
|  |       left_separator: None, | ||||||
|  |       right_separator: None, | ||||||
|       vars, |       vars, | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -31,6 +31,8 @@ use crate::{ | ||||||
| }; | }; | ||||||
| use unicase::UniCase; | use unicase::UniCase; | ||||||
| 
 | 
 | ||||||
|  | pub(crate) type IsWordSeparator = bool; | ||||||
|  | 
 | ||||||
| #[derive(Clone)] | #[derive(Clone)] | ||||||
| pub struct RollingMatcherState<'a, Id> { | pub struct RollingMatcherState<'a, Id> { | ||||||
|   paths: Vec<RollingMatcherStatePath<'a, Id>>, |   paths: Vec<RollingMatcherStatePath<'a, Id>>, | ||||||
|  | @ -45,7 +47,7 @@ impl<'a, Id> Default for RollingMatcherState<'a, Id> { | ||||||
| #[derive(Clone)] | #[derive(Clone)] | ||||||
| struct RollingMatcherStatePath<'a, Id> { | struct RollingMatcherStatePath<'a, Id> { | ||||||
|   node: &'a MatcherTreeNode<Id>, |   node: &'a MatcherTreeNode<Id>, | ||||||
|   events: Vec<Event>, |   events: Vec<(Event, IsWordSeparator)>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub struct RollingMatcherOptions { | pub struct RollingMatcherOptions { | ||||||
|  | @ -87,9 +89,9 @@ where | ||||||
|           self |           self | ||||||
|             .find_refs(node_path.node, &event, true) |             .find_refs(node_path.node, &event, true) | ||||||
|             .into_iter() |             .into_iter() | ||||||
|             .map(|node_ref| { |             .map(|(node_ref, is_word_separator)| { | ||||||
|               let mut new_events = node_path.events.clone(); |               let mut new_events = node_path.events.clone(); | ||||||
|               new_events.push(event.clone()); |               new_events.push((event.clone(), is_word_separator)); | ||||||
|               (node_ref, new_events) |               (node_ref, new_events) | ||||||
|             }), |             }), | ||||||
|         ); |         ); | ||||||
|  | @ -101,7 +103,7 @@ where | ||||||
|     next_refs.extend( |     next_refs.extend( | ||||||
|       root_refs |       root_refs | ||||||
|         .into_iter() |         .into_iter() | ||||||
|         .map(|node_ref| (node_ref, vec![event.clone()])), |         .map(|(node_ref, is_word_separator)| (node_ref, vec![(event.clone(), is_word_separator)])), | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     let mut next_paths = Vec::new(); |     let mut next_paths = Vec::new(); | ||||||
|  | @ -109,12 +111,14 @@ where | ||||||
|     for (node_ref, events) in next_refs { |     for (node_ref, events) in next_refs { | ||||||
|       match node_ref { |       match node_ref { | ||||||
|         MatcherTreeRef::Matches(matches) => { |         MatcherTreeRef::Matches(matches) => { | ||||||
|           let trigger = extract_string_from_events(&events); |           let (trigger, left_separator, right_separator) = extract_string_from_events(&events); | ||||||
|           let results = matches |           let results = matches | ||||||
|             .iter() |             .iter() | ||||||
|             .map(|id| MatchResult { |             .map(|id| MatchResult { | ||||||
|               id: id.clone(), |               id: id.clone(), | ||||||
|               trigger: trigger.clone(), |               trigger: trigger.clone(), | ||||||
|  |               left_separator: left_separator.clone(), | ||||||
|  |               right_separator: right_separator.clone(), | ||||||
|               vars: HashMap::new(), |               vars: HashMap::new(), | ||||||
|             }) |             }) | ||||||
|             .collect(); |             .collect(); | ||||||
|  | @ -152,19 +156,19 @@ impl<Id: Clone> RollingMatcher<Id> { | ||||||
|     node: &'a MatcherTreeNode<Id>, |     node: &'a MatcherTreeNode<Id>, | ||||||
|     event: &Event, |     event: &Event, | ||||||
|     has_previous_state: bool, |     has_previous_state: bool, | ||||||
|   ) -> Vec<&'a MatcherTreeRef<Id>> { |   ) -> Vec<(&'a MatcherTreeRef<Id>, IsWordSeparator)> { | ||||||
|     let mut refs = Vec::new(); |     let mut refs = Vec::new(); | ||||||
| 
 | 
 | ||||||
|     if let Event::Key { key, chars } = event { |     if let Event::Key { key, chars } = event { | ||||||
|       // Key matching
 |       // Key matching
 | ||||||
|       if let Some((_, node_ref)) = node.keys.iter().find(|(_key, _)| _key == key) { |       if let Some((_, node_ref)) = node.keys.iter().find(|(_key, _)| _key == key) { | ||||||
|         refs.push(node_ref); |         refs.push((node_ref, false)); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       if let Some(char) = chars { |       if let Some(char) = chars { | ||||||
|         // Char matching
 |         // Char matching
 | ||||||
|         if let Some((_, node_ref)) = node.chars.iter().find(|(_char, _)| _char == char) { |         if let Some((_, node_ref)) = node.chars.iter().find(|(_char, _)| _char == char) { | ||||||
|           refs.push(node_ref); |           refs.push((node_ref, false)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Char case-insensitive
 |         // Char case-insensitive
 | ||||||
|  | @ -174,14 +178,14 @@ impl<Id: Clone> RollingMatcher<Id> { | ||||||
|           .iter() |           .iter() | ||||||
|           .find(|(_char, _)| *_char == insensitive_char) |           .find(|(_char, _)| *_char == insensitive_char) | ||||||
|         { |         { | ||||||
|           refs.push(node_ref); |           refs.push((node_ref, false)); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if self.is_word_separator(event) { |     if self.is_word_separator(event) { | ||||||
|       if let Some(node_ref) = node.word_separators.as_ref() { |       if let Some(node_ref) = node.word_separators.as_ref() { | ||||||
|         refs.push(node_ref) |         refs.push((node_ref, true)) | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -226,6 +230,16 @@ mod tests { | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   fn match_result_with_sep<Id: Default>(id: Id, trigger: &str, left: Option<&str>, right: Option<&str>) -> MatchResult<Id> { | ||||||
|  |     MatchResult { | ||||||
|  |       id, | ||||||
|  |       trigger: trigger.to_string(), | ||||||
|  |       left_separator: left.map(str::to_owned), | ||||||
|  |       right_separator: right.map(str::to_owned), | ||||||
|  |       ..Default::default() | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   #[test] |   #[test] | ||||||
|   fn matcher_process_simple_strings() { |   fn matcher_process_simple_strings() { | ||||||
|     let matcher = RollingMatcher::new( |     let matcher = RollingMatcher::new( | ||||||
|  | @ -281,11 +295,11 @@ mod tests { | ||||||
|     // Word matches are also triggered when there is no left separator but it's a new state
 |     // Word matches are also triggered when there is no left separator but it's a new state
 | ||||||
|     assert_eq!( |     assert_eq!( | ||||||
|       get_matches_after_str("hi,", &matcher), |       get_matches_after_str("hi,", &matcher), | ||||||
|       vec![match_result(1, "hi,")] |       vec![match_result_with_sep(1, "hi,", None, Some(","))] | ||||||
|     ); |     ); | ||||||
|     assert_eq!( |     assert_eq!( | ||||||
|       get_matches_after_str(".hi,", &matcher), |       get_matches_after_str(".hi,", &matcher), | ||||||
|       vec![match_result(1, ".hi,")] |       vec![match_result_with_sep(1, ".hi,", Some("."), Some(","))] | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -19,19 +19,33 @@ | ||||||
| 
 | 
 | ||||||
| use crate::Event; | use crate::Event; | ||||||
| 
 | 
 | ||||||
| // TODO: test
 | use super::matcher::IsWordSeparator; | ||||||
| pub(crate) fn extract_string_from_events(events: &[Event]) -> String { | 
 | ||||||
|  | pub(crate) fn extract_string_from_events( | ||||||
|  |   events: &[(Event, IsWordSeparator)], | ||||||
|  | ) -> (String, Option<String>, Option<String>) { | ||||||
|   let mut string = String::new(); |   let mut string = String::new(); | ||||||
| 
 | 
 | ||||||
|   for event in events { |   let mut left_separator = None; | ||||||
|  |   let mut right_separator = None; | ||||||
|  | 
 | ||||||
|  |   for (i, (event, is_word_separator)) in events.iter().enumerate() { | ||||||
|     if let Event::Key { key: _, chars } = event { |     if let Event::Key { key: _, chars } = event { | ||||||
|       if let Some(chars) = chars { |       if let Some(chars) = chars { | ||||||
|         string.push_str(chars); |         string.push_str(chars); | ||||||
|  | 
 | ||||||
|  |         if *is_word_separator { | ||||||
|  |           if i == 0 { | ||||||
|  |             left_separator = Some(chars.clone()); | ||||||
|  |           } else if i == (events.len() - 1) { | ||||||
|  |             right_separator = Some(chars.clone()); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   string |   (string, left_separator, right_separator) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
|  | @ -43,28 +57,84 @@ mod tests { | ||||||
|   fn extract_string_from_events_all_chars() { |   fn extract_string_from_events_all_chars() { | ||||||
|     assert_eq!( |     assert_eq!( | ||||||
|       extract_string_from_events(&[ |       extract_string_from_events(&[ | ||||||
|         Event::Key { |         ( | ||||||
|           key: Key::Other, |           Event::Key { | ||||||
|           chars: Some("h".to_string()) |             key: Key::Other, | ||||||
|         }, |             chars: Some("h".to_string()) | ||||||
|         Event::Key { |           }, | ||||||
|           key: Key::Other, |           false | ||||||
|           chars: Some("e".to_string()) |         ), | ||||||
|         }, |         ( | ||||||
|         Event::Key { |           Event::Key { | ||||||
|           key: Key::Other, |             key: Key::Other, | ||||||
|           chars: Some("l".to_string()) |             chars: Some("e".to_string()) | ||||||
|         }, |           }, | ||||||
|         Event::Key { |           false | ||||||
|           key: Key::Other, |         ), | ||||||
|           chars: Some("l".to_string()) |         ( | ||||||
|         }, |           Event::Key { | ||||||
|         Event::Key { |             key: Key::Other, | ||||||
|           key: Key::Other, |             chars: Some("l".to_string()) | ||||||
|           chars: Some("o".to_string()) |           }, | ||||||
|         }, |           false | ||||||
|  |         ), | ||||||
|  |         ( | ||||||
|  |           Event::Key { | ||||||
|  |             key: Key::Other, | ||||||
|  |             chars: Some("l".to_string()) | ||||||
|  |           }, | ||||||
|  |           false | ||||||
|  |         ), | ||||||
|  |         ( | ||||||
|  |           Event::Key { | ||||||
|  |             key: Key::Other, | ||||||
|  |             chars: Some("o".to_string()) | ||||||
|  |           }, | ||||||
|  |           false | ||||||
|  |         ), | ||||||
|       ]), |       ]), | ||||||
|       "hello" |       ("hello".to_string(), None, None) | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   #[test] | ||||||
|  |   fn extract_string_from_events_word_separators() { | ||||||
|  |     assert_eq!( | ||||||
|  |       extract_string_from_events(&[ | ||||||
|  |         ( | ||||||
|  |           Event::Key { | ||||||
|  |             key: Key::Other, | ||||||
|  |             chars: Some(".".to_string()) | ||||||
|  |           }, | ||||||
|  |           true | ||||||
|  |         ), | ||||||
|  |         ( | ||||||
|  |           Event::Key { | ||||||
|  |             key: Key::Other, | ||||||
|  |             chars: Some("h".to_string()) | ||||||
|  |           }, | ||||||
|  |           false | ||||||
|  |         ), | ||||||
|  |         ( | ||||||
|  |           Event::Key { | ||||||
|  |             key: Key::Other, | ||||||
|  |             chars: Some("i".to_string()) | ||||||
|  |           }, | ||||||
|  |           false | ||||||
|  |         ), | ||||||
|  |         ( | ||||||
|  |           Event::Key { | ||||||
|  |             key: Key::Other, | ||||||
|  |             chars: Some(",".to_string()) | ||||||
|  |           }, | ||||||
|  |           true | ||||||
|  |         ), | ||||||
|  |       ]), | ||||||
|  |       ( | ||||||
|  |         ".hi,".to_string(), | ||||||
|  |         Some(".".to_string()), | ||||||
|  |         Some(",".to_string()) | ||||||
|  |       ), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -72,20 +142,29 @@ mod tests { | ||||||
|   fn extract_string_from_events_no_chars() { |   fn extract_string_from_events_no_chars() { | ||||||
|     assert_eq!( |     assert_eq!( | ||||||
|       extract_string_from_events(&[ |       extract_string_from_events(&[ | ||||||
|         Event::Key { |         ( | ||||||
|           key: Key::ArrowUp, |           Event::Key { | ||||||
|           chars: None |             key: Key::ArrowUp, | ||||||
|         }, |             chars: None | ||||||
|         Event::Key { |           }, | ||||||
|           key: Key::ArrowUp, |           false | ||||||
|           chars: None |         ), | ||||||
|         }, |         ( | ||||||
|         Event::Key { |           Event::Key { | ||||||
|           key: Key::ArrowUp, |             key: Key::ArrowUp, | ||||||
|           chars: None |             chars: None | ||||||
|         }, |           }, | ||||||
|  |           false | ||||||
|  |         ), | ||||||
|  |         ( | ||||||
|  |           Event::Key { | ||||||
|  |             key: Key::ArrowUp, | ||||||
|  |             chars: None | ||||||
|  |           }, | ||||||
|  |           false | ||||||
|  |         ), | ||||||
|       ]), |       ]), | ||||||
|       "" |       ("".to_string(), None, None) | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -93,32 +172,50 @@ mod tests { | ||||||
|   fn extract_string_from_events_mixed() { |   fn extract_string_from_events_mixed() { | ||||||
|     assert_eq!( |     assert_eq!( | ||||||
|       extract_string_from_events(&[ |       extract_string_from_events(&[ | ||||||
|         Event::Key { |         ( | ||||||
|           key: Key::Other, |           Event::Key { | ||||||
|           chars: Some("h".to_string()) |             key: Key::Other, | ||||||
|         }, |             chars: Some("h".to_string()) | ||||||
|         Event::Key { |           }, | ||||||
|           key: Key::Other, |           false | ||||||
|           chars: Some("e".to_string()) |         ), | ||||||
|         }, |         ( | ||||||
|         Event::Key { |           Event::Key { | ||||||
|           key: Key::Other, |             key: Key::Other, | ||||||
|           chars: Some("l".to_string()) |             chars: Some("e".to_string()) | ||||||
|         }, |           }, | ||||||
|         Event::Key { |           false | ||||||
|           key: Key::Other, |         ), | ||||||
|           chars: Some("l".to_string()) |         ( | ||||||
|         }, |           Event::Key { | ||||||
|         Event::Key { |             key: Key::Other, | ||||||
|           key: Key::Other, |             chars: Some("l".to_string()) | ||||||
|           chars: Some("o".to_string()) |           }, | ||||||
|         }, |           false | ||||||
|         Event::Key { |         ), | ||||||
|           key: Key::ArrowUp, |         ( | ||||||
|           chars: None |           Event::Key { | ||||||
|         }, |             key: Key::Other, | ||||||
|  |             chars: Some("l".to_string()) | ||||||
|  |           }, | ||||||
|  |           false | ||||||
|  |         ), | ||||||
|  |         ( | ||||||
|  |           Event::Key { | ||||||
|  |             key: Key::Other, | ||||||
|  |             chars: Some("o".to_string()) | ||||||
|  |           }, | ||||||
|  |           false | ||||||
|  |         ), | ||||||
|  |         ( | ||||||
|  |           Event::Key { | ||||||
|  |             key: Key::ArrowUp, | ||||||
|  |             chars: None | ||||||
|  |           }, | ||||||
|  |           false | ||||||
|  |         ), | ||||||
|       ]), |       ]), | ||||||
|       "hello" |       ("hello".to_string(), None, None) | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user