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 {
|
Event::Key {
|
||||||
key: Key::Other,
|
key: Key::Other,
|
||||||
chars: Some("h".to_string())
|
chars: Some("h".to_string())
|
||||||
},
|
},
|
||||||
|
false
|
||||||
|
),
|
||||||
|
(
|
||||||
Event::Key {
|
Event::Key {
|
||||||
key: Key::Other,
|
key: Key::Other,
|
||||||
chars: Some("e".to_string())
|
chars: Some("e".to_string())
|
||||||
},
|
},
|
||||||
|
false
|
||||||
|
),
|
||||||
|
(
|
||||||
Event::Key {
|
Event::Key {
|
||||||
key: Key::Other,
|
key: Key::Other,
|
||||||
chars: Some("l".to_string())
|
chars: Some("l".to_string())
|
||||||
},
|
},
|
||||||
|
false
|
||||||
|
),
|
||||||
|
(
|
||||||
Event::Key {
|
Event::Key {
|
||||||
key: Key::Other,
|
key: Key::Other,
|
||||||
chars: Some("l".to_string())
|
chars: Some("l".to_string())
|
||||||
},
|
},
|
||||||
|
false
|
||||||
|
),
|
||||||
|
(
|
||||||
Event::Key {
|
Event::Key {
|
||||||
key: Key::Other,
|
key: Key::Other,
|
||||||
chars: Some("o".to_string())
|
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 {
|
Event::Key {
|
||||||
key: Key::ArrowUp,
|
key: Key::ArrowUp,
|
||||||
chars: None
|
chars: None
|
||||||
},
|
},
|
||||||
|
false
|
||||||
|
),
|
||||||
|
(
|
||||||
Event::Key {
|
Event::Key {
|
||||||
key: Key::ArrowUp,
|
key: Key::ArrowUp,
|
||||||
chars: None
|
chars: None
|
||||||
},
|
},
|
||||||
|
false
|
||||||
|
),
|
||||||
|
(
|
||||||
Event::Key {
|
Event::Key {
|
||||||
key: Key::ArrowUp,
|
key: Key::ArrowUp,
|
||||||
chars: None
|
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 {
|
Event::Key {
|
||||||
key: Key::Other,
|
key: Key::Other,
|
||||||
chars: Some("h".to_string())
|
chars: Some("h".to_string())
|
||||||
},
|
},
|
||||||
|
false
|
||||||
|
),
|
||||||
|
(
|
||||||
Event::Key {
|
Event::Key {
|
||||||
key: Key::Other,
|
key: Key::Other,
|
||||||
chars: Some("e".to_string())
|
chars: Some("e".to_string())
|
||||||
},
|
},
|
||||||
|
false
|
||||||
|
),
|
||||||
|
(
|
||||||
Event::Key {
|
Event::Key {
|
||||||
key: Key::Other,
|
key: Key::Other,
|
||||||
chars: Some("l".to_string())
|
chars: Some("l".to_string())
|
||||||
},
|
},
|
||||||
|
false
|
||||||
|
),
|
||||||
|
(
|
||||||
Event::Key {
|
Event::Key {
|
||||||
key: Key::Other,
|
key: Key::Other,
|
||||||
chars: Some("l".to_string())
|
chars: Some("l".to_string())
|
||||||
},
|
},
|
||||||
|
false
|
||||||
|
),
|
||||||
|
(
|
||||||
Event::Key {
|
Event::Key {
|
||||||
key: Key::Other,
|
key: Key::Other,
|
||||||
chars: Some("o".to_string())
|
chars: Some("o".to_string())
|
||||||
},
|
},
|
||||||
|
false
|
||||||
|
),
|
||||||
|
(
|
||||||
Event::Key {
|
Event::Key {
|
||||||
key: Key::ArrowUp,
|
key: Key::ArrowUp,
|
||||||
chars: None
|
chars: None
|
||||||
},
|
},
|
||||||
|
false
|
||||||
|
),
|
||||||
]),
|
]),
|
||||||
"hello"
|
("hello".to_string(), None, None)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user