skim/helper/
selector.rs

1use std::collections::HashSet;
2
3use regex::Regex;
4
5use crate::{Selector, SkimItem};
6
7/// Default implementation of the selector trait for pre-selecting items
8#[derive(Debug, Default)]
9pub struct DefaultSkimSelector {
10    first_n: usize,
11    regex: Option<Regex>,
12    preset: Option<HashSet<String>>,
13}
14
15impl DefaultSkimSelector {
16    /// Selects the first N items
17    pub fn first_n(mut self, first_n: usize) -> Self {
18        trace!("select first_n: {first_n}");
19        self.first_n = first_n;
20        self
21    }
22
23    /// Selects items whose text matches any of the preset strings
24    pub fn preset(mut self, preset: impl IntoIterator<Item = String>) -> Self {
25        if self.preset.is_none() {
26            self.preset = Some(HashSet::new())
27        }
28
29        if let Some(set) = self.preset.as_mut() {
30            set.extend(preset)
31        }
32        self
33    }
34
35    /// Selects items whose text matches the given regex pattern
36    pub fn regex(mut self, regex: &str) -> Self {
37        trace!("select regex: {regex}");
38        if !regex.is_empty() {
39            self.regex = Regex::new(regex).ok();
40        }
41        self
42    }
43}
44
45impl Selector for DefaultSkimSelector {
46    fn should_select(&self, index: usize, item: &dyn SkimItem) -> bool {
47        if self.first_n > index {
48            return true;
49        }
50
51        if self.preset.is_some()
52            && self
53                .preset
54                .as_ref()
55                .map(|preset| preset.contains(item.text().as_ref()))
56                .unwrap_or(false)
57        {
58            return true;
59        }
60
61        if self.regex.is_some() && self.regex.as_ref().map(|re| re.is_match(&item.text())).unwrap_or(false) {
62            return true;
63        }
64
65        false
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    pub fn test_first_n() {
75        let selector = DefaultSkimSelector::default().first_n(10);
76        assert!(selector.should_select(0, &"item"));
77        assert!(selector.should_select(1, &"item"));
78        assert!(selector.should_select(2, &"item"));
79        assert!(selector.should_select(9, &"item"));
80        assert!(!selector.should_select(10, &"item"));
81    }
82
83    #[test]
84    pub fn test_preset() {
85        let selector = DefaultSkimSelector::default().preset(vec!["a".to_string(), "b".to_string(), "c".to_string()]);
86        assert!(selector.should_select(0, &"a"));
87        assert!(selector.should_select(0, &"b"));
88        assert!(selector.should_select(0, &"c"));
89        assert!(!selector.should_select(0, &"d"));
90    }
91
92    #[test]
93    pub fn test_regex() {
94        let selector = DefaultSkimSelector::default().regex("^[0-9]");
95        assert!(selector.should_select(0, &"1"));
96        assert!(selector.should_select(0, &"2"));
97        assert!(selector.should_select(0, &"3"));
98        assert!(selector.should_select(0, &"1a"));
99        assert!(!selector.should_select(0, &"a"));
100    }
101
102    #[test]
103    pub fn test_all_together() {
104        let selector = DefaultSkimSelector::default()
105            .first_n(1)
106            .regex("b")
107            .preset(vec!["c".to_string()]);
108        assert!(selector.should_select(0, &"a"));
109        assert!(selector.should_select(1, &"b"));
110        assert!(selector.should_select(2, &"c"));
111        assert!(!selector.should_select(3, &"d"));
112    }
113}