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, Weak};
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, item_idx: 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        let item_idx = item_idx as i32;
44
45        self.criterion.iter().take(4).enumerate().for_each(|(index, criteria)| {
46            let value = match criteria {
47                RankCriteria::Index => item_idx,
48                RankCriteria::NegIndex => -item_idx,
49                RankCriteria::Score => -score,
50                RankCriteria::Begin => begin,
51                RankCriteria::End => end,
52                RankCriteria::NegScore => score,
53                RankCriteria::NegBegin => -begin,
54                RankCriteria::NegEnd => -end,
55                RankCriteria::Length => length,
56                RankCriteria::NegLength => -length,
57            };
58
59            rank[index] = value;
60        });
61
62        rank
63    }
64}
65
66//------------------------------------------------------------------------------
67#[derive(Clone)]
68pub struct MatchedItem {
69    pub item: Weak<dyn SkimItem>,
70    pub rank: Rank,
71    pub matched_range: Option<MatchRange>, // range of chars that matched the pattern
72    pub item_idx: u32,
73}
74
75impl MatchedItem {
76    pub fn upgrade_infallible(&self) -> Arc<dyn SkimItem> {
77        self.item.upgrade().unwrap_or(Arc::new(""))
78    }
79}
80
81use std::cmp::Ordering as CmpOrd;
82
83impl PartialEq for MatchedItem {
84    fn eq(&self, other: &Self) -> bool {
85        self.rank.eq(&other.rank)
86    }
87}
88
89impl std::cmp::Eq for MatchedItem {}
90
91impl PartialOrd for MatchedItem {
92    fn partial_cmp(&self, other: &Self) -> Option<CmpOrd> {
93        self.rank.partial_cmp(&other.rank)
94    }
95}
96
97impl Ord for MatchedItem {
98    fn cmp(&self, other: &Self) -> CmpOrd {
99        self.rank.cmp(&other.rank)
100    }
101}
102
103//------------------------------------------------------------------------------
104
105use crate::reader::ITEMS_INITIAL_CAPACITY;
106
107pub struct ItemPool {
108    length: AtomicUsize,
109    pool: SpinLock<Vec<Arc<dyn SkimItem>>>,
110    /// number of items that was `take`n
111    taken: AtomicUsize,
112
113    /// reverse first N lines as header
114    reserved_items: SpinLock<Vec<Weak<dyn SkimItem>>>,
115    lines_to_reserve: usize,
116}
117
118impl Drop for ItemPool {
119    fn drop(&mut self) {
120        let _reserved_items = std::mem::take(&mut *self.reserved_items.lock());
121        let _pool = std::mem::take(&mut *self.pool.lock());
122    }
123}
124
125impl Default for ItemPool {
126    fn default() -> Self {
127        ItemPool::new()
128    }
129}
130
131impl ItemPool {
132    pub fn new() -> Self {
133        Self {
134            length: AtomicUsize::new(0),
135            pool: SpinLock::new(Vec::with_capacity(ITEMS_INITIAL_CAPACITY)),
136            taken: AtomicUsize::new(0),
137            reserved_items: SpinLock::new(Vec::new()),
138            lines_to_reserve: 0,
139        }
140    }
141
142    pub fn lines_to_reserve(mut self, lines_to_reserve: usize) -> Self {
143        self.lines_to_reserve = lines_to_reserve;
144        self
145    }
146
147    pub fn len(&self) -> usize {
148        self.length.load(Ordering::SeqCst)
149    }
150
151    pub fn num_not_taken(&self) -> usize {
152        self.length.load(Ordering::SeqCst) - self.taken.load(Ordering::SeqCst)
153    }
154
155    pub fn num_taken(&self) -> usize {
156        self.taken.load(Ordering::SeqCst)
157    }
158
159    pub fn clear(&self) {
160        let mut items = self.pool.lock();
161        items.clear();
162        let mut header_items = self.reserved_items.lock();
163        header_items.clear();
164        self.taken.store(0, Ordering::SeqCst);
165        self.length.store(0, Ordering::SeqCst);
166    }
167
168    pub fn reset(&self) {
169        // lock to ensure consistency
170        let _items = self.pool.lock();
171        self.taken.store(0, Ordering::SeqCst);
172    }
173
174    /// append the items and return the new_size of the pool
175    pub fn append(&self, items: &mut Vec<Arc<dyn SkimItem>>) -> usize {
176        let len = items.len();
177        trace!("item pool, append {} items", len);
178        let mut pool = self.pool.lock();
179        let mut header_items = self.reserved_items.lock();
180
181        let to_reserve = self.lines_to_reserve - header_items.len();
182        if to_reserve > 0 {
183            let to_reserve = min(to_reserve, items.len());
184            let mut reserved_pool = items[..to_reserve].to_vec();
185            pool.append(&mut reserved_pool);
186            let mut reserved_header: Vec<Weak<dyn SkimItem>> = reserved_pool.iter().map(Arc::downgrade).collect();
187            header_items.append(&mut reserved_header);
188        } else {
189            pool.append(items);
190        }
191
192        self.length.store(pool.len(), Ordering::SeqCst);
193        trace!("item pool, done append {} items", len);
194        pool.len()
195    }
196
197    pub fn take(&self) -> ItemPoolGuard<'_, Arc<dyn SkimItem>> {
198        let guard = self.pool.lock();
199        let taken = self.taken.swap(guard.len(), Ordering::SeqCst);
200        ItemPoolGuard { guard, start: taken }
201    }
202
203    pub fn reserved(&self) -> ItemPoolGuard<'_, Weak<dyn SkimItem>> {
204        let guard = self.reserved_items.lock();
205        ItemPoolGuard { guard, start: 0 }
206    }
207}
208
209pub struct ItemPoolGuard<'a, T: Sized + 'a> {
210    guard: SpinLockGuard<'a, Vec<T>>,
211    start: usize,
212}
213
214impl<'mutex, T: Sized> Deref for ItemPoolGuard<'mutex, T> {
215    type Target = [T];
216
217    fn deref(&self) -> &[T] {
218        &self.guard[self.start..]
219    }
220}
221
222//------------------------------------------------------------------------------
223#[derive(Debug, PartialEq, Eq, Clone, Copy)]
224pub enum RankCriteria {
225    Index,
226    NegIndex,
227    Score,
228    Begin,
229    End,
230    NegScore,
231    NegBegin,
232    NegEnd,
233    Length,
234    NegLength,
235}
236
237pub fn parse_criteria(text: &str) -> Option<RankCriteria> {
238    match text.to_lowercase().as_ref() {
239        "score" => Some(RankCriteria::Score),
240        "begin" => Some(RankCriteria::Begin),
241        "end" => Some(RankCriteria::End),
242        "-score" => Some(RankCriteria::NegScore),
243        "-begin" => Some(RankCriteria::NegBegin),
244        "-end" => Some(RankCriteria::NegEnd),
245        "length" => Some(RankCriteria::Length),
246        "-length" => Some(RankCriteria::NegLength),
247        "index" => Some(RankCriteria::Index),
248        "-index" => Some(RankCriteria::NegIndex),
249        _ => None,
250    }
251}