Finalize first draft of RollingMatcher
This commit is contained in:
parent
caf72c9aef
commit
b2f28bb739
|
@ -26,7 +26,7 @@ pub enum Event {
|
|||
VirtualSeparator,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Key {
|
||||
// Modifiers
|
||||
Alt,
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::any::Any;
|
||||
use event::Event;
|
||||
|
||||
#[macro_use]
|
||||
|
@ -27,5 +26,5 @@ pub mod event;
|
|||
pub mod rolling;
|
||||
|
||||
pub trait Matcher<'a, State, Id> where Id: Clone {
|
||||
fn process(&'a self, prev_state: Option<&'a State>, event: Event) -> (State, Vec<Id>);
|
||||
fn process(&'a self, prev_state: Option<&State>, event: Event) -> (State, Vec<Id>);
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
use crate::event::Key;
|
||||
|
||||
/*
|
||||
* This file is part of espanso.
|
||||
*
|
||||
* Copyright (C) 2019-2021 Federico Terzi
|
||||
*
|
||||
* espanso is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* espanso is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
pub enum RollingItem {
|
||||
WordSeparator,
|
||||
Key(Key),
|
||||
Char(String),
|
||||
CharInsensitive(String),
|
||||
}
|
|
@ -17,19 +17,35 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use unicase::UniCase;
|
||||
use crate::Matcher;
|
||||
use super::{
|
||||
tree::{MatcherTreeNode, MatcherTreeRef},
|
||||
RollingMatch,
|
||||
};
|
||||
use crate::event::{Event, Key};
|
||||
use super::{RollingItem, RollingMatch, tree::{MatcherTreeNode, MatcherTreeRef}};
|
||||
use crate::Matcher;
|
||||
use unicase::UniCase;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RollingMatcherState<'a, Id> {
|
||||
nodes: Vec<&'a MatcherTreeNode<Id>>,
|
||||
}
|
||||
|
||||
impl <'a, Id> Default for RollingMatcherState<'a, Id> {
|
||||
impl<'a, Id> Default for RollingMatcherState<'a, Id> {
|
||||
fn default() -> Self {
|
||||
Self { nodes: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RollingMatcherOptions {
|
||||
char_word_separators: Vec<String>,
|
||||
key_word_separators: Vec<Key>,
|
||||
}
|
||||
|
||||
impl Default for RollingMatcherOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
nodes: Vec::new(),
|
||||
char_word_separators: Vec::new(),
|
||||
key_word_separators: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,11 +57,13 @@ pub struct RollingMatcher<Id> {
|
|||
root: MatcherTreeNode<Id>,
|
||||
}
|
||||
|
||||
|
||||
impl <'a, Id> Matcher<'a, RollingMatcherState<'a, Id>, Id> for RollingMatcher<Id> where Id: Clone {
|
||||
impl<'a, Id> Matcher<'a, RollingMatcherState<'a, Id>, Id> for RollingMatcher<Id>
|
||||
where
|
||||
Id: Clone,
|
||||
{
|
||||
fn process(
|
||||
&'a self,
|
||||
prev_state: Option<&'a RollingMatcherState<'a, Id>>,
|
||||
prev_state: Option<&RollingMatcherState<'a, Id>>,
|
||||
event: Event,
|
||||
) -> (RollingMatcherState<'a, Id>, Vec<Id>) {
|
||||
let mut next_refs = Vec::new();
|
||||
|
@ -68,31 +86,34 @@ impl <'a, Id> Matcher<'a, RollingMatcherState<'a, Id>, Id> for RollingMatcher<Id
|
|||
MatcherTreeRef::Matches(matches) => {
|
||||
// Reset the state and return the matches
|
||||
return (RollingMatcherState::default(), matches.clone());
|
||||
},
|
||||
}
|
||||
MatcherTreeRef::Node(node) => {
|
||||
next_nodes.push(node.as_ref());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let current_state = RollingMatcherState {
|
||||
nodes: next_nodes,
|
||||
};
|
||||
let current_state = RollingMatcherState { nodes: next_nodes };
|
||||
|
||||
(current_state, Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl <Id> RollingMatcher<Id> {
|
||||
pub fn new(matches: &[RollingMatch<Id>]) -> Self {
|
||||
todo!()
|
||||
// Self {
|
||||
|
||||
// }
|
||||
impl<Id: Clone> RollingMatcher<Id> {
|
||||
pub fn new(matches: &[RollingMatch<Id>], opt: RollingMatcherOptions) -> Self {
|
||||
let root = MatcherTreeNode::from_matches(matches);
|
||||
Self {
|
||||
root,
|
||||
char_word_separators: opt.char_word_separators,
|
||||
key_word_separators: opt.key_word_separators,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
fn find_refs<'a>(&'a self, node: &'a MatcherTreeNode<Id>, event: &Event) -> Vec<&'a MatcherTreeRef<Id>> {
|
||||
fn find_refs<'a>(
|
||||
&'a self,
|
||||
node: &'a MatcherTreeNode<Id>,
|
||||
event: &Event,
|
||||
) -> Vec<&'a MatcherTreeRef<Id>> {
|
||||
let mut refs = Vec::new();
|
||||
|
||||
if let Event::Key { key, chars } = event {
|
||||
|
@ -140,7 +161,6 @@ impl <Id> RollingMatcher<Id> {
|
|||
}
|
||||
}
|
||||
Event::VirtualSeparator => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -148,38 +168,107 @@ impl <Id> RollingMatcher<Id> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::rolling::{StringMatchOptions};
|
||||
|
||||
fn get_matches_after_str<Id: Clone>(string: &str, matcher: &RollingMatcher<Id>) -> Vec<Id> {
|
||||
let mut prev_state = None;
|
||||
let mut matches = Vec::new();
|
||||
|
||||
for c in string.chars() {
|
||||
let (state, _matches) = matcher.process(
|
||||
prev_state.as_ref(),
|
||||
Event::Key {
|
||||
key: Key::Other,
|
||||
chars: Some(c.to_string()),
|
||||
},
|
||||
);
|
||||
|
||||
prev_state = Some(state);
|
||||
matches = _matches;
|
||||
}
|
||||
|
||||
matches
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() { // TODO: convert to actual test
|
||||
let root = MatcherTreeNode {
|
||||
chars: vec![
|
||||
("h".to_string(), MatcherTreeRef::Node(Box::new(MatcherTreeNode {
|
||||
chars: vec![
|
||||
("i".to_string(), MatcherTreeRef::Matches(vec![1])),
|
||||
fn matcher_process_simple_strings() {
|
||||
let matcher = RollingMatcher::new(
|
||||
&[
|
||||
RollingMatch::from_string(1, "hi", &StringMatchOptions::default()),
|
||||
RollingMatch::from_string(2, "hey", &StringMatchOptions::default()),
|
||||
RollingMatch::from_string(3, "my", &StringMatchOptions::default()),
|
||||
RollingMatch::from_string(4, "myself", &StringMatchOptions::default()),
|
||||
RollingMatch::from_string(5, "hi", &StringMatchOptions::default()),
|
||||
],
|
||||
RollingMatcherOptions {
|
||||
..Default::default()
|
||||
}))
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(get_matches_after_str("hi", &matcher), vec![1, 5]);
|
||||
assert_eq!(get_matches_after_str("my", &matcher), vec![3]);
|
||||
assert_eq!(get_matches_after_str("invalid", &matcher), vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matcher_process_word_matches() {
|
||||
let matcher = RollingMatcher::new(
|
||||
&[
|
||||
RollingMatch::from_string(
|
||||
1,
|
||||
"hi",
|
||||
&StringMatchOptions {
|
||||
left_word: true,
|
||||
right_word: true,
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
RollingMatch::from_string(2, "hey", &StringMatchOptions::default()),
|
||||
],
|
||||
RollingMatcherOptions {
|
||||
char_word_separators: vec![".".to_string(), ",".to_string()],
|
||||
..Default::default()
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
let matcher = RollingMatcher {
|
||||
char_word_separators: vec![".".to_owned()],
|
||||
key_word_separators: vec![Key::ArrowUp],
|
||||
root,
|
||||
};
|
||||
assert_eq!(get_matches_after_str("hi", &matcher), vec![]);
|
||||
assert_eq!(get_matches_after_str(".hi,", &matcher), vec![1]);
|
||||
}
|
||||
|
||||
let (state, matches) = matcher.process(None, Event::Key {
|
||||
key: Key::Other,
|
||||
chars: Some("h".to_string()),
|
||||
});
|
||||
assert_eq!(matches.len(), 0);
|
||||
#[test]
|
||||
fn matcher_process_case_insensitive() {
|
||||
let matcher = RollingMatcher::new(
|
||||
&[
|
||||
RollingMatch::from_string(
|
||||
1,
|
||||
"hi",
|
||||
&StringMatchOptions {
|
||||
case_insensitive: true,
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
RollingMatch::from_string(2, "hey", &StringMatchOptions::default()),
|
||||
RollingMatch::from_string(
|
||||
3,
|
||||
"arty",
|
||||
&StringMatchOptions {
|
||||
case_insensitive: true,
|
||||
preserve_case_markers: true,
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
],
|
||||
RollingMatcherOptions {
|
||||
char_word_separators: vec![".".to_string(), ",".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
let (state, matches) = matcher.process(Some(&state), Event::Key {
|
||||
key: Key::Other,
|
||||
chars: Some("i".to_string()),
|
||||
});
|
||||
assert_eq!(matches, vec![1]);
|
||||
assert_eq!(get_matches_after_str("hi", &matcher), vec![1]);
|
||||
assert_eq!(get_matches_after_str("Hi", &matcher), vec![1]);
|
||||
assert_eq!(get_matches_after_str("HI", &matcher), vec![1]);
|
||||
assert_eq!(get_matches_after_str("arty", &matcher), vec![3]);
|
||||
assert_eq!(get_matches_after_str("arTY", &matcher), vec![3]);
|
||||
assert_eq!(get_matches_after_str("ARTY", &matcher), vec![]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ use crate::event::Key;
|
|||
mod matcher;
|
||||
mod tree;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum RollingItem {
|
||||
WordSeparator,
|
||||
Key(Key),
|
||||
|
@ -68,6 +68,13 @@ impl<Id> RollingMatch<Id> {
|
|||
|
||||
Self { id, items }
|
||||
}
|
||||
|
||||
pub fn from_items(id: Id, items: &[RollingItem]) -> Self {
|
||||
Self {
|
||||
id,
|
||||
items: items.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StringMatchOptions {
|
||||
|
|
|
@ -21,15 +21,15 @@ use unicase::UniCase;
|
|||
|
||||
use crate::event::Key;
|
||||
|
||||
use super::RollingItem;
|
||||
use super::{RollingItem, RollingMatch};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) enum MatcherTreeRef<Id> {
|
||||
Matches(Vec<Id>),
|
||||
Node(Box<MatcherTreeNode<Id>>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) struct MatcherTreeNode<Id> {
|
||||
pub word_separators: Option<MatcherTreeRef<Id>>,
|
||||
pub keys: Vec<(Key, MatcherTreeRef<Id>)>,
|
||||
|
@ -37,7 +37,7 @@ pub(crate) struct MatcherTreeNode<Id> {
|
|||
pub chars_insensitive: Vec<(UniCase<String>, MatcherTreeRef<Id>)>,
|
||||
}
|
||||
|
||||
impl <Id> Default for MatcherTreeNode<Id> {
|
||||
impl<Id> Default for MatcherTreeNode<Id> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
word_separators: None,
|
||||
|
@ -48,10 +48,312 @@ impl <Id> Default for MatcherTreeNode<Id> {
|
|||
}
|
||||
}
|
||||
|
||||
impl <Id> MatcherTreeNode<Id> {
|
||||
// TODO: test
|
||||
pub fn from_items(items: &[RollingItem]) -> MatcherTreeNode<Id> {
|
||||
// TODO: implement the tree building algorithm
|
||||
todo!()
|
||||
impl<Id> MatcherTreeNode<Id>
|
||||
where
|
||||
Id: Clone,
|
||||
{
|
||||
pub fn from_matches(matches: &[RollingMatch<Id>]) -> MatcherTreeNode<Id> {
|
||||
let mut root = MatcherTreeNode::default();
|
||||
for m in matches {
|
||||
insert_items_recursively(m.id.clone(), &mut root, &m.items);
|
||||
}
|
||||
root
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_items_recursively<Id>(id: Id, node: &mut MatcherTreeNode<Id>, items: &[RollingItem]) {
|
||||
if items.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
if items.len() == 1 {
|
||||
let item = items.get(0).unwrap();
|
||||
match item {
|
||||
RollingItem::WordSeparator => {
|
||||
let mut new_matches = Vec::new();
|
||||
if let Some(node_ref) = node.word_separators.take() {
|
||||
if let MatcherTreeRef::Matches(matches) = node_ref {
|
||||
new_matches.extend(matches);
|
||||
}
|
||||
}
|
||||
new_matches.push(id);
|
||||
node.word_separators = Some(MatcherTreeRef::Matches(new_matches))
|
||||
}
|
||||
RollingItem::Key(key) => {
|
||||
if let Some(entry) = node.keys.iter_mut().find(|(_key, _)| _key == key) {
|
||||
if let MatcherTreeRef::Matches(matches) = &mut entry.1 {
|
||||
matches.push(id)
|
||||
} else {
|
||||
entry.1 = MatcherTreeRef::Matches(vec![id])
|
||||
};
|
||||
} else {
|
||||
node
|
||||
.keys
|
||||
.push((key.clone(), MatcherTreeRef::Matches(vec![id])));
|
||||
}
|
||||
}
|
||||
RollingItem::Char(c) => {
|
||||
if let Some(entry) = node.chars.iter_mut().find(|(_c, _)| _c == c) {
|
||||
if let MatcherTreeRef::Matches(matches) = &mut entry.1 {
|
||||
matches.push(id)
|
||||
} else {
|
||||
entry.1 = MatcherTreeRef::Matches(vec![id])
|
||||
};
|
||||
} else {
|
||||
node
|
||||
.chars
|
||||
.push((c.clone(), MatcherTreeRef::Matches(vec![id])));
|
||||
}
|
||||
}
|
||||
RollingItem::CharInsensitive(c) => {
|
||||
let uni_char = UniCase::new(c.clone());
|
||||
if let Some(entry) = node
|
||||
.chars_insensitive
|
||||
.iter_mut()
|
||||
.find(|(_c, _)| _c == &uni_char)
|
||||
{
|
||||
if let MatcherTreeRef::Matches(matches) = &mut entry.1 {
|
||||
matches.push(id)
|
||||
} else {
|
||||
entry.1 = MatcherTreeRef::Matches(vec![id])
|
||||
};
|
||||
} else {
|
||||
node
|
||||
.chars_insensitive
|
||||
.push((uni_char, MatcherTreeRef::Matches(vec![id])));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let item = items.get(0).unwrap();
|
||||
match item {
|
||||
RollingItem::WordSeparator => match node.word_separators.as_mut() {
|
||||
Some(MatcherTreeRef::Node(next_node)) => {
|
||||
insert_items_recursively(id, next_node.as_mut(), &items[1..])
|
||||
}
|
||||
None => {
|
||||
let mut next_node = Box::new(MatcherTreeNode::default());
|
||||
insert_items_recursively(id, next_node.as_mut(), &items[1..]);
|
||||
node.word_separators = Some(MatcherTreeRef::Node(next_node));
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
RollingItem::Key(key) => {
|
||||
if let Some(entry) = node.keys.iter_mut().find(|(_key, _)| _key == key) {
|
||||
if let MatcherTreeRef::Node(next_node) = &mut entry.1 {
|
||||
insert_items_recursively(id, next_node, &items[1..])
|
||||
}
|
||||
} else {
|
||||
let mut next_node = Box::new(MatcherTreeNode::default());
|
||||
insert_items_recursively(id, next_node.as_mut(), &items[1..]);
|
||||
node
|
||||
.keys
|
||||
.push((key.clone(), MatcherTreeRef::Node(next_node)));
|
||||
}
|
||||
}
|
||||
RollingItem::Char(c) => {
|
||||
if let Some(entry) = node.chars.iter_mut().find(|(_c, _)| _c == c) {
|
||||
if let MatcherTreeRef::Node(next_node) = &mut entry.1 {
|
||||
insert_items_recursively(id, next_node, &items[1..])
|
||||
}
|
||||
} else {
|
||||
let mut next_node = Box::new(MatcherTreeNode::default());
|
||||
insert_items_recursively(id, next_node.as_mut(), &items[1..]);
|
||||
node
|
||||
.chars
|
||||
.push((c.clone(), MatcherTreeRef::Node(next_node)));
|
||||
}
|
||||
}
|
||||
RollingItem::CharInsensitive(c) => {
|
||||
let uni_char = UniCase::new(c.clone());
|
||||
if let Some(entry) = node
|
||||
.chars_insensitive
|
||||
.iter_mut()
|
||||
.find(|(_c, _)| _c == &uni_char)
|
||||
{
|
||||
if let MatcherTreeRef::Node(next_node) = &mut entry.1 {
|
||||
insert_items_recursively(id, next_node, &items[1..])
|
||||
}
|
||||
} else {
|
||||
let mut next_node = Box::new(MatcherTreeNode::default());
|
||||
insert_items_recursively(id, next_node.as_mut(), &items[1..]);
|
||||
node
|
||||
.chars_insensitive
|
||||
.push((uni_char, MatcherTreeRef::Node(next_node)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::rolling::StringMatchOptions;
|
||||
|
||||
#[test]
|
||||
fn generate_tree_from_items_simple_strings() {
|
||||
let root = MatcherTreeNode::from_matches(&[
|
||||
RollingMatch::from_string(1, "hi", &StringMatchOptions::default()),
|
||||
RollingMatch::from_string(2, "hey", &StringMatchOptions::default()),
|
||||
RollingMatch::from_string(3, "my", &StringMatchOptions::default()),
|
||||
RollingMatch::from_string(4, "myself", &StringMatchOptions::default()),
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
root,
|
||||
MatcherTreeNode {
|
||||
chars: vec![
|
||||
(
|
||||
"h".to_string(),
|
||||
MatcherTreeRef::Node(Box::new(MatcherTreeNode {
|
||||
chars: vec![
|
||||
("i".to_string(), MatcherTreeRef::Matches(vec![1])),
|
||||
(
|
||||
"e".to_string(),
|
||||
MatcherTreeRef::Node(Box::new(MatcherTreeNode {
|
||||
chars: vec![("y".to_string(), MatcherTreeRef::Matches(vec![2]))],
|
||||
..Default::default()
|
||||
}))
|
||||
),
|
||||
],
|
||||
..Default::default()
|
||||
}))
|
||||
),
|
||||
(
|
||||
"m".to_string(),
|
||||
MatcherTreeRef::Node(Box::new(MatcherTreeNode {
|
||||
chars: vec![("y".to_string(), MatcherTreeRef::Matches(vec![3])),],
|
||||
..Default::default()
|
||||
}))
|
||||
)
|
||||
],
|
||||
..Default::default()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_tree_from_items_keys() {
|
||||
let root = MatcherTreeNode::from_matches(&[
|
||||
RollingMatch::from_items(
|
||||
1,
|
||||
&[
|
||||
RollingItem::Key(Key::ArrowUp),
|
||||
RollingItem::Key(Key::ArrowLeft),
|
||||
],
|
||||
),
|
||||
RollingMatch::from_items(
|
||||
2,
|
||||
&[
|
||||
RollingItem::Key(Key::ArrowUp),
|
||||
RollingItem::Key(Key::ArrowRight),
|
||||
],
|
||||
),
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
root,
|
||||
MatcherTreeNode {
|
||||
keys: vec![(
|
||||
Key::ArrowUp,
|
||||
MatcherTreeRef::Node(Box::new(MatcherTreeNode {
|
||||
keys: vec![
|
||||
(Key::ArrowLeft, MatcherTreeRef::Matches(vec![1])),
|
||||
(Key::ArrowRight, MatcherTreeRef::Matches(vec![2])),
|
||||
],
|
||||
..Default::default()
|
||||
}))
|
||||
),],
|
||||
..Default::default()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_tree_from_items_mixed() {
|
||||
let root = MatcherTreeNode::from_matches(&[
|
||||
RollingMatch::from_items(
|
||||
1,
|
||||
&[
|
||||
RollingItem::Key(Key::ArrowUp),
|
||||
RollingItem::Key(Key::ArrowLeft),
|
||||
],
|
||||
),
|
||||
RollingMatch::from_string(
|
||||
2,
|
||||
"my",
|
||||
&StringMatchOptions {
|
||||
left_word: true,
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
RollingMatch::from_string(
|
||||
3,
|
||||
"hi",
|
||||
&StringMatchOptions {
|
||||
left_word: true,
|
||||
right_word: true,
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
RollingMatch::from_string(
|
||||
4,
|
||||
"no",
|
||||
&StringMatchOptions {
|
||||
case_insensitive: true,
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
root,
|
||||
MatcherTreeNode {
|
||||
keys: vec![(
|
||||
Key::ArrowUp,
|
||||
MatcherTreeRef::Node(Box::new(MatcherTreeNode {
|
||||
keys: vec![(Key::ArrowLeft, MatcherTreeRef::Matches(vec![1])),],
|
||||
..Default::default()
|
||||
}))
|
||||
),],
|
||||
word_separators: Some(MatcherTreeRef::Node(Box::new(MatcherTreeNode {
|
||||
chars: vec![
|
||||
(
|
||||
"m".to_string(),
|
||||
MatcherTreeRef::Node(Box::new(MatcherTreeNode {
|
||||
chars: vec![("y".to_string(), MatcherTreeRef::Matches(vec![2])),],
|
||||
..Default::default()
|
||||
}))
|
||||
),
|
||||
(
|
||||
"h".to_string(),
|
||||
MatcherTreeRef::Node(Box::new(MatcherTreeNode {
|
||||
chars: vec![(
|
||||
"i".to_string(),
|
||||
MatcherTreeRef::Node(Box::new(MatcherTreeNode {
|
||||
word_separators: Some(MatcherTreeRef::Matches(vec![3])),
|
||||
..Default::default()
|
||||
}))
|
||||
),],
|
||||
..Default::default()
|
||||
}))
|
||||
),
|
||||
],
|
||||
..Default::default()
|
||||
}))),
|
||||
chars_insensitive: vec![(
|
||||
UniCase::new("n".to_string()),
|
||||
MatcherTreeRef::Node(Box::new(MatcherTreeNode {
|
||||
chars_insensitive: vec![(
|
||||
UniCase::new("o".to_string()),
|
||||
MatcherTreeRef::Matches(vec![4])
|
||||
),],
|
||||
..Default::default()
|
||||
}))
|
||||
),],
|
||||
..Default::default()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user