skimmer/engine/
regexp.rs

1use std::fmt::{Display, Error, Formatter};
2use std::sync::Arc;
3
4use regex::Regex;
5
6use crate::engine::util::regex_match;
7use crate::item::RankBuilder;
8use crate::{CaseMatching, MatchEngine};
9use crate::{MatchRange, MatchResult, SkimItem};
10use std::cmp::min;
11
12//------------------------------------------------------------------------------
13// Regular Expression engine
14#[derive(Debug)]
15pub struct RegexEngine {
16    query_regex: Option<Regex>,
17    rank_builder: Arc<RankBuilder>,
18}
19
20impl RegexEngine {
21    pub fn builder(query: &str, case: CaseMatching) -> Self {
22        let mut query_builder = String::new();
23
24        match case {
25            CaseMatching::Respect => {}
26            CaseMatching::Ignore => query_builder.push_str("(?i)"),
27            CaseMatching::Smart => {}
28        }
29
30        query_builder.push_str(query);
31
32        RegexEngine {
33            query_regex: Regex::new(&query_builder).ok(),
34            rank_builder: Default::default(),
35        }
36    }
37
38    pub fn rank_builder(mut self, rank_builder: Arc<RankBuilder>) -> Self {
39        self.rank_builder = rank_builder;
40        self
41    }
42
43    pub fn build(self) -> Self {
44        self
45    }
46}
47
48impl MatchEngine for RegexEngine {
49    fn match_item(&self, item: Arc<dyn SkimItem>) -> Option<MatchResult> {
50        let mut matched_result = None;
51        let item_text = item.text();
52        let default_range = [(0, item_text.len())];
53        for &(start, end) in item.get_matching_ranges().unwrap_or(&default_range) {
54            let start = min(start, item_text.len());
55            let end = min(end, item_text.len());
56            if self.query_regex.is_none() {
57                matched_result = Some((0, 0));
58                break;
59            }
60
61            matched_result =
62                regex_match(&item_text[start..end], &self.query_regex).map(|(s, e)| (s + start, e + start));
63
64            if matched_result.is_some() {
65                break;
66            }
67        }
68
69        let (begin, end) = matched_result?;
70        let score = (end - begin) as i32;
71        let item_len = item_text.len();
72
73        Some(MatchResult {
74            rank: self.rank_builder.build_rank(score, begin, end, item_len),
75            matched_range: MatchRange::ByteRange(begin, end),
76        })
77    }
78}
79
80impl Display for RegexEngine {
81    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
82        write!(
83            f,
84            "(Regex: {})",
85            self.query_regex
86                .as_ref()
87                .map_or("".to_string(), |re| re.as_str().to_string())
88        )
89    }
90}