Skip to main content

nucleo/
pattern.rs

1pub use nucleo_matcher::pattern::{Atom, AtomKind, CaseMatching, Normalization, Pattern};
2use nucleo_matcher::{Matcher, Utf32String};
3
4#[cfg(test)]
5mod tests;
6
7#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Default)]
8pub(crate) enum Status {
9    #[default]
10    Unchanged,
11    Update,
12    Rescore,
13}
14
15#[derive(Debug)]
16pub struct MultiPattern {
17    cols: Vec<(Pattern, Status)>,
18}
19
20impl Clone for MultiPattern {
21    fn clone(&self) -> Self {
22        Self {
23            cols: self.cols.clone(),
24        }
25    }
26
27    fn clone_from(&mut self, source: &Self) {
28        self.cols.clone_from(&source.cols)
29    }
30}
31
32impl MultiPattern {
33    /// Creates a multi pattern with `columns` empty column patterns.
34    pub fn new(columns: usize) -> Self {
35        Self {
36            cols: vec![Default::default(); columns],
37        }
38    }
39
40    /// Reparses a column. By specifying `append` the caller promises that text passed
41    /// to the previous `reparse` invocation is a prefix of `new_text`. This enables
42    /// additional optimizations but can lead to missing matches if an incorrect value
43    /// is passed.
44    pub fn reparse(
45        &mut self,
46        column: usize,
47        new_text: &str,
48        case_matching: CaseMatching,
49        normalization: Normalization,
50        append: bool,
51    ) {
52        let old_status = self.cols[column].1;
53        if append
54            && old_status != Status::Rescore
55            && self.cols[column]
56                .0
57                .atoms
58                .last()
59                .map_or(true, |last| !last.negative)
60        {
61            self.cols[column].1 = Status::Update;
62        } else {
63            self.cols[column].1 = Status::Rescore;
64        }
65        self.cols[column]
66            .0
67            .reparse(new_text, case_matching, normalization);
68    }
69
70    pub fn column_pattern(&self, column: usize) -> &Pattern {
71        &self.cols[column].0
72    }
73
74    pub(crate) fn status(&self) -> Status {
75        self.cols
76            .iter()
77            .map(|&(_, status)| status)
78            .max()
79            .unwrap_or(Status::Unchanged)
80    }
81
82    pub(crate) fn reset_status(&mut self) {
83        for (_, status) in &mut self.cols {
84            *status = Status::Unchanged
85        }
86    }
87
88    pub fn score(&self, haystack: &[Utf32String], matcher: &mut Matcher) -> Option<u32> {
89        // TODO: wheight columns?
90        let mut score = 0;
91        for ((pattern, _), haystack) in self.cols.iter().zip(haystack) {
92            score += pattern.score(haystack.slice(..), matcher)?
93        }
94        Some(score)
95    }
96
97    pub fn is_empty(&self) -> bool {
98        self.cols.iter().all(|(pat, _)| pat.atoms.is_empty())
99    }
100}