diff --git a/espanso-match/src/lib.rs b/espanso-match/src/lib.rs index 86851e4..8480dcb 100644 --- a/espanso-match/src/lib.rs +++ b/espanso-match/src/lib.rs @@ -24,7 +24,9 @@ extern crate lazy_static; pub mod event; pub mod rolling; +pub mod regex; +// TODO: instead of returning Vec, return a Vec of structs which contain Id, trigger string and variables(if any) pub trait Matcher<'a, State, Id> where Id: Clone { fn process(&'a self, prev_state: Option<&State>, event: Event) -> (State, Vec); } \ No newline at end of file diff --git a/espanso-match/src/regex/mod.rs b/espanso-match/src/regex/mod.rs new file mode 100644 index 0000000..dc5a166 --- /dev/null +++ b/espanso-match/src/regex/mod.rs @@ -0,0 +1,125 @@ +/* + * This file is part of espanso. + * + * Copyright (C) 2019-202 case_insensitive: (), preserve_case_markers: (), left_word: (), right_word: ()1 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 . + */ + +use log::error; +use regex::{Regex, RegexSet}; + +use crate::event::Event; +use crate::Matcher; + +#[derive(Debug)] +pub struct RegexMatch { + pub id: Id, + pub regex: String, +} + +impl RegexMatch { + pub fn new(id: Id, regex: &str) -> Self { + Self { + id, + regex: regex.to_string(), + } + } +} + +#[derive(Clone)] +pub struct RegexMatcherState { + buffer: String, +} + +impl Default for RegexMatcherState { + fn default() -> Self { + Self { + buffer: String::new(), + } + } +} + +pub struct RegexMatcher { + ids: Vec, + // The RegexSet is used to efficiently determine which regexes match + regex_set: RegexSet, + + // The single regexes are then used to find the captures + regexes: Vec, +} + +impl<'a, Id> Matcher<'a, RegexMatcherState, Id> for RegexMatcher +where + Id: Clone, +{ + fn process( + &'a self, + prev_state: Option<&RegexMatcherState>, + event: Event, + ) -> (RegexMatcherState, Vec) { + let mut buffer = if let Some(prev_state) = prev_state { + prev_state.buffer.clone() + } else { + "".to_string() + }; + + if let Event::Key { key: _, chars } = event { + if let Some(chars) = chars { + buffer.push_str(&chars); + } + } + + // Find matches + if self.regex_set.is_match(&buffer) { + for index in self.regex_set.matches(&buffer) { + if let (Some(id), Some(regex)) = (self.ids.get(index), self.regexes.get(index)) { + // TODO: find complete match and captures + } else { + error!("received inconsistent index from regex set with index: {}", index); + } + } + } + + let current_state = RegexMatcherState { buffer }; + (current_state, Vec::new()) + } +} + +impl RegexMatcher { + pub fn new(matches: &[RegexMatch]) -> Self { + let mut ids = Vec::new(); + let mut regexes = Vec::new(); + let mut good_regexes = Vec::new(); + + for m in matches { + match Regex::new(&m.regex) { + Ok(regex) => { + ids.push(m.id.clone()); + good_regexes.push(&m.regex); + regexes.push(regex); + } + Err(err) => { + error!("unable to compile regex: '{}', error: {:?}", m.regex, err); + } + } + } + + let regex_set = RegexSet::new(&good_regexes).expect("unable to build regex set"); + + Self { ids, regex_set, regexes } + } +} + +// TODO: test \ No newline at end of file diff --git a/espanso-match/src/rolling/mod.rs b/espanso-match/src/rolling/mod.rs index e69450c..9a8d92e 100644 --- a/espanso-match/src/rolling/mod.rs +++ b/espanso-match/src/rolling/mod.rs @@ -32,8 +32,8 @@ pub enum RollingItem { #[derive(Debug, PartialEq)] pub struct RollingMatch { - id: Id, - items: Vec, + pub id: Id, + pub items: Vec, } impl RollingMatch {