skim/
item.rs

1///! An item is line of text that read from `find` command or stdin together with
2///! the internal states, such as selected or not
3use std::cmp::min;
4use std::default::Default;
5use std::ops::Deref;
6use std::sync::atomic::{AtomicUsize, Ordering};
7use std::sync::Arc;
8
9use crate::spinlock::{SpinLock, SpinLockGuard};
10use crate::{MatchRange, Rank, SkimItem};
11
12//------------------------------------------------------------------------------
13
14#[derive(Debug)]
15pub struct RankBuilder {
16    criterion: Vec<RankCriteria>,
17}
18
19impl Default for RankBuilder {
20    fn default() -> Self {
21        Self {
22            criterion: vec![RankCriteria::Score, RankCriteria::Begin, RankCriteria::End],
23        }
24    }
25}
26
27impl RankBuilder {
28    pub fn new(mut criterion: Vec<RankCriteria>) -> Self {
29        if !criterion.contains(&RankCriteria::Score) && !criterion.contains(&RankCriteria::NegScore) {
30            criterion.insert(0, RankCriteria::Score);
31        }
32
33        criterion.dedup();
34        Self { criterion }
35    }
36
37    /// score: the greater the better
38    pub fn build_rank(&self, score: i32, begin: usize, end: usize, length: usize) -> Rank {
39        let mut rank = [0; 4];
40        let begin = begin as i32;
41        let end = end as i32;
42        let length = length as i32;
43
44        for (index, criteria) in self.criterion.iter().take(4).enumerate() {
45            let value = match criteria {
46                RankCriteria::Score => -score,
47                RankCriteria::Begin => begin,
48                RankCriteria::End => end,
49                RankCriteria::NegScore => score,
50                RankCriteria::NegBegin => -begin,
51                RankCriteria::NegEnd => -end,
52                RankCriteria::Length => length,
53                RankCriteria::NegLength => -length,
54            };
55
56            rank[index] = value;
57        }
58
59        rank
60    }
61}
62
63//------------------------------------------------------------------------------
64#[derive(Clone)]
65pub struct MatchedItem {
66    pub item: Arc<dyn SkimItem>,
67    pub rank: Rank,
68    pub matched_range: Option<MatchRange>, // range of chars that matched the pattern
69    pub item_idx: u32,
70}
71
72impl MatchedItem {}
73
74use std::cmp::Ordering as CmpOrd;
75
76impl PartialEq for MatchedItem {
77    fn eq(&self, other: &Self) -> bool {
78        self.rank.eq(&other.rank)
79    }
80}
81
82impl std::cmp::Eq for MatchedItem {}
83
84impl PartialOrd for MatchedItem {
85    fn partial_cmp(&self, other: &Self) -> Option<CmpOrd> {
86        self.rank.partial_cmp(&other.rank)
87    }
88}
89
90impl Ord for MatchedItem {
91    fn cmp(&self, other: &Self) -> CmpOrd {
92        self.rank.cmp(&other.rank)
93    }
94}
95
96//------------------------------------------------------------------------------
97const ITEM_POOL_CAPACITY: usize = 1024;
98
99pub struct ItemPool {
100    length: AtomicUsize,
101    pool: SpinLock<Vec<Arc<dyn SkimItem>>>,
102    /// number of items that was `take`n
103    taken: AtomicUsize,
104
105    /// reverse first N lines as header
106    reserved_items: SpinLock<Vec<Arc<dyn SkimItem>>>,
107    lines_to_reserve: usize,
108}
109
110impl ItemPool {
111    pub fn new() -> Self {
112        Self {
113            length: AtomicUsize::new(0),
114            pool: SpinLock::new(Vec::with_capacity(ITEM_POOL_CAPACITY)),
115            taken: AtomicUsize::new(0),
116            reserved_items: SpinLock::new(Vec::new()),
117            lines_to_reserve: 0,
118        }
119    }
120
121    pub fn lines_to_reserve(mut self, lines_to_reserve: usize) -> Self {
122        self.lines_to_reserve = lines_to_reserve;
123        self
124    }
125
126    pub fn len(&self) -> usize {
127        self.length.load(Ordering::SeqCst)
128    }
129
130    pub fn num_not_taken(&self) -> usize {
131        self.length.load(Ordering::SeqCst) - self.taken.load(Ordering::SeqCst)
132    }
133
134    pub fn num_taken(&self) -> usize {
135        self.taken.load(Ordering::SeqCst)
136    }
137
138    pub fn clear(&self) {
139        let mut items = self.pool.lock();
140        items.clear();
141        let mut header_items = self.reserved_items.lock();
142        header_items.clear();
143        self.taken.store(0, Ordering::SeqCst);
144        self.length.store(0, Ordering::SeqCst);
145    }
146
147    pub fn reset(&self) {
148        // lock to ensure consistency
149        let _items = self.pool.lock();
150        self.taken.store(0, Ordering::SeqCst);
151    }
152
153    /// append the items and return the new_size of the pool
154    pub fn append(&self, mut items: Vec<Arc<dyn SkimItem>>) -> usize {
155        let len = items.len();
156        trace!("item pool, append {} items", len);
157        let mut pool = self.pool.lock();
158        let mut header_items = self.reserved_items.lock();
159
160        let to_reserve = self.lines_to_reserve - header_items.len();
161        if to_reserve > 0 {
162            let to_reserve = min(to_reserve, items.len());
163            header_items.extend_from_slice(&items[..to_reserve]);
164            pool.extend_from_slice(&items[to_reserve..]);
165        } else {
166            pool.append(&mut items);
167        }
168        self.length.store(pool.len(), Ordering::SeqCst);
169        trace!("item pool, done append {} items", len);
170        pool.len()
171    }
172
173    pub fn take(&self) -> ItemPoolGuard<Arc<dyn SkimItem>> {
174        let guard = self.pool.lock();
175        let taken = self.taken.swap(guard.len(), Ordering::SeqCst);
176        ItemPoolGuard { guard, start: taken }
177    }
178
179    pub fn reserved(&self) -> ItemPoolGuard<Arc<dyn SkimItem>> {
180        let guard = self.reserved_items.lock();
181        ItemPoolGuard { guard, start: 0 }
182    }
183}
184
185pub struct ItemPoolGuard<'a, T: Sized + 'a> {
186    guard: SpinLockGuard<'a, Vec<T>>,
187    start: usize,
188}
189
190impl<'mutex, T: Sized> Deref for ItemPoolGuard<'mutex, T> {
191    type Target = [T];
192
193    fn deref(&self) -> &[T] {
194        &self.guard[self.start..]
195    }
196}
197
198//------------------------------------------------------------------------------
199#[derive(Debug, PartialEq, Eq, Clone, Copy)]
200pub enum RankCriteria {
201    Score,
202    Begin,
203    End,
204    NegScore,
205    NegBegin,
206    NegEnd,
207    Length,
208    NegLength,
209}
210
211pub fn parse_criteria(text: &str) -> Option<RankCriteria> {
212    match text.to_lowercase().as_ref() {
213        "score" => Some(RankCriteria::Score),
214        "begin" => Some(RankCriteria::Begin),
215        "end" => Some(RankCriteria::End),
216        "-score" => Some(RankCriteria::NegScore),
217        "-begin" => Some(RankCriteria::NegBegin),
218        "-end" => Some(RankCriteria::NegEnd),
219        "length" => Some(RankCriteria::Length),
220        "-length" => Some(RankCriteria::NegLength),
221        _ => None,
222    }
223}