1#![allow(unused)]
5
6use std::{
7 borrow::Cow,
8 sync::{
9 Arc,
10 atomic::{self, AtomicU32},
11 },
12};
13use super::{
14 Style,
15 Line, Span, Text,
16};
17use ratatui::style::Modifier;
18use unicode_segmentation::UnicodeSegmentation;
19use unicode_width::UnicodeWidthStr;
20
21use crate::{SSS, utils::text::{plain_text, wrap_text}};
22
23use super::{injector::WorkerInjector, query::PickerQuery};
24
25type ColumnFormatFn<T> = Box<dyn for<'a> Fn(&'a T) -> Text<'a> + Send + Sync>;
26pub struct Column<T> {
27 pub name: Arc<str>,
28 pub(super) format: ColumnFormatFn<T>,
29 pub(super) filter: bool,
31}
32
33impl<T> Column<T> {
34 pub fn new_boxed(name: impl Into<Arc<str>>, format: ColumnFormatFn<T>) -> Self {
35 Self {
36 name: name.into(),
37 format,
38 filter: true,
39 }
40 }
41
42 pub fn new<F>(name: impl Into<Arc<str>>, f: F) -> Self
43 where
44 F: for<'a> Fn(&'a T) -> Text<'a> + SSS,
45 {
46 Self {
47 name: name.into(),
48 format: Box::new(f),
49 filter: true,
50 }
51 }
52
53 pub fn without_filtering(mut self) -> Self {
54 self.filter = false;
55 self
56 }
57
58 pub(super) fn format<'a>(&self, item: &'a T) -> Text<'a> {
59 (self.format)(item)
60 }
61
62 pub(super) fn format_text<'a>(&self, item: &'a T) -> Cow<'a, str> {
63 Cow::Owned(plain_text(&(self.format)(item)))
64 }
65}
66
67pub struct Worker<T>
71where
72T: SSS,
73{
74 pub(crate) nucleo: nucleo::Nucleo<T>,
76 pub(super) query: PickerQuery,
78 pub(super) col_indices_buffer: Vec<u32>,
81 pub(crate) columns: Arc<[Column<T>]>,
82
83 pub(super) version: Arc<AtomicU32>,
85}
86
87impl<T> Worker<T>
88where
89T: SSS,
90{
91 pub fn new(
93 columns: impl IntoIterator<Item = Column<T>>,
94 default_column: usize,
95 ) -> Self {
96 let columns: Arc<[_]> = columns.into_iter().collect();
97 let matcher_columns = columns.iter().filter(|col| col.filter).count() as u32;
98
99 let inner = nucleo::Nucleo::new(
100 nucleo::Config::DEFAULT,
101 Arc::new(|| {}),
102 None,
103 matcher_columns,
104 );
105
106 Self {
107 nucleo: inner,
108 col_indices_buffer: Vec::with_capacity(128),
109 query: PickerQuery::new(columns.iter().map(|col| &col.name).cloned(), default_column),
110 columns,
111 version: Arc::new(AtomicU32::new(0)),
112 }
113 }
114
115 pub fn injector(&self) -> WorkerInjector<T> {
116 WorkerInjector {
117 inner: self.nucleo.injector(),
118 columns: self.columns.clone(),
119 version: self.version.load(atomic::Ordering::Relaxed),
120 picker_version: self.version.clone(),
121 }
122 }
123
124 pub fn find(&mut self, line: &str) {
125 let old_query = self.query.parse(line);
126 if self.query == old_query {
127 return;
128 }
129 for (i, column) in self
130 .columns
131 .iter()
132 .filter(|column| column.filter)
133 .enumerate()
134 {
135 let pattern = self
136 .query
137 .get(&column.name)
138 .map(|f| &**f)
139 .unwrap_or_default();
140
141 let old_pattern = old_query
142 .get(&column.name)
143 .map(|f| &**f)
144 .unwrap_or_default();
145 if pattern == old_pattern {
147 continue;
148 }
149 let is_append = pattern.starts_with(old_pattern);
150 self.nucleo.pattern.reparse(
151 i,
152 pattern,
153 nucleo::pattern::CaseMatching::Smart,
154 nucleo::pattern::Normalization::Smart,
155 is_append,
156 );
157 }
158 }
159
160 pub fn shutdown(&mut self) {
162 todo!()
163 }
164
165 pub fn restart(&mut self, clear_snapshot: bool) {
166 self.nucleo.restart(clear_snapshot);
167 }
168
169 pub fn get_nth(&self, n: u32) -> Option<&T> {
171 self.nucleo
172 .snapshot()
173 .get_matched_item(n)
174 .map(|item| item.data)
175 }
176
177 pub fn new_snapshot(nucleo: &mut nucleo::Nucleo<T>) -> (&nucleo::Snapshot<T>, Status) {
178 let nucleo::Status { changed, running } = nucleo.tick(10);
179 let snapshot = nucleo.snapshot();
180 (
181 snapshot,
182 Status {
183 item_count: snapshot.item_count(),
184 matched_count: snapshot.matched_item_count(),
185 running,
186 changed,
187 },
188 )
189 }
190
191 pub fn raw_results(&self) -> impl ExactSizeIterator<Item = &T> + DoubleEndedIterator + '_ {
192 let snapshot = self.nucleo.snapshot();
193 snapshot.matched_items(..).map(|item| item.data)
194 }
195
196 pub fn counts(&self) -> (u32, u32) {
198 let snapshot = self.nucleo.snapshot();
199 (snapshot.matched_item_count(), snapshot.item_count())
200 }
201}
202
203pub type WorkerResults<'a, T> = Vec<(Vec<Text<'a>>, &'a T, u16)>;
204
205#[derive(Debug, Default, Clone)]
206pub struct Status {
207 pub item_count: u32,
208 pub matched_count: u32,
209 pub running: bool,
210 pub changed: bool,
211}
212
213#[derive(Debug, thiserror::Error)]
214pub enum WorkerError {
215 #[error("the matcher injector has been shut down")]
216 InjectorShutdown,
217 #[error("{0}")]
218 Custom(&'static str),
219}
220
221impl<T: SSS> Worker<T> {
222 pub fn results(
223 &mut self,
224 start: u32,
225 end: u32,
226 width_limits: &[u16],
227 highlight_style: Style,
228 matcher: &mut nucleo::Matcher,
229 ) -> (WorkerResults<'_, T>, Vec<u16>, Status) {
230
231 let (snapshot, status) = Self::new_snapshot(&mut self.nucleo);
232
233 let mut widths = vec![0u16; self.columns.len()]; let iter =
236 snapshot.matched_items(start.min(status.matched_count)..end.min(status.matched_count));
237
238 let table = iter
239 .map(|item| {
240 let mut widths = widths.iter_mut();
241 let mut col_idx = 0;
242 let mut height = 0;
243
244 let row =
245 self.columns
246 .iter()
247 .zip(width_limits.iter().chain(std::iter::repeat(&u16::MAX)))
248 .map(|(column, &width_limit)| {
249
250 let max_width = widths.next().unwrap();
251 let cell = column.format(item.data);
252
253 if width_limit == 0 {
255 return Text::from("");
256 }
257
258 let (cell, width) = if column.filter && width_limit == u16::MAX {
259 let mut cell_width = 0;
260
261 let indices_buffer = &mut self.col_indices_buffer;
263 indices_buffer.clear();
264 snapshot.pattern().column_pattern(col_idx).indices(
265 item.matcher_columns[col_idx].slice(..),
266 matcher,
267 indices_buffer,
268 );
269 indices_buffer.sort_unstable();
270 indices_buffer.dedup();
271 let mut indices = indices_buffer.drain(..);
272
273 let mut lines = vec![];
274 let mut next_highlight_idx = indices.next().unwrap_or(u32::MAX);
275 let mut grapheme_idx = 0u32;
276
277 for line in cell {
278 let mut span_list = Vec::new();
279 let mut current_span = String::new();
280 let mut current_style = Style::default();
281 let mut width = 0;
282
283 for span in line {
284 for grapheme in span.content.graphemes(true) {
290 let style = if grapheme_idx == next_highlight_idx {
291 next_highlight_idx = indices.next().unwrap_or(u32::MAX);
292 span.style.patch(highlight_style)
293 } else {
294 span.style
295 };
296 if style != current_style {
297 if !current_span.is_empty() {
298 span_list
299 .push(Span::styled(current_span, current_style))
300 }
301 current_span = String::new();
302 current_style = style;
303 }
304 current_span.push_str(grapheme);
305 grapheme_idx += 1;
306 }
307 width += span.width();
308 }
309
310 span_list.push(Span::styled(current_span, current_style));
311 lines.push(Line::from(span_list));
312 cell_width = cell_width.max(width);
313 grapheme_idx += 1; }
315
316 col_idx += 1;
317 (Text::from(lines), cell_width)
318 } else if column.filter {
319 let mut cell_width = 0;
320 let mut wrapped = false;
321
322 let indices_buffer = &mut self.col_indices_buffer;
324 indices_buffer.clear();
325 snapshot.pattern().column_pattern(col_idx).indices(
326 item.matcher_columns[col_idx].slice(..),
327 matcher,
328 indices_buffer,
329 );
330 indices_buffer.sort_unstable();
331 indices_buffer.dedup();
332 let mut indices = indices_buffer.drain(..);
333
334
335 let mut lines: Vec<Line<'_>> = vec![];
336 let mut next_highlight_idx = indices.next().unwrap_or(u32::MAX);
337 let mut grapheme_idx = 0u32;
338
339 for line in cell {
340 let mut current_spans = Vec::new();
341 let mut current_span = String::new();
342 let mut current_style = Style::default();
343 let mut current_width = 0;
344
345 for span in line {
346 let mut graphemes = span.content.graphemes(true).peekable();
347 while let Some(grapheme) = graphemes.next() {
348 let grapheme_width = UnicodeWidthStr::width(grapheme);
349
350 if current_width + grapheme_width > (width_limit - 1) as usize &&
351 { grapheme_width > 1 || graphemes.peek().is_some() }
352 {
353 current_spans
354 .push(Span::styled(current_span, current_style));
355 current_spans.push(Span::styled("↵", Style::default().add_modifier(Modifier::DIM)));
356 lines.push(Line::from(current_spans));
357
358 current_spans = Vec::new();
359 current_span = String::new();
360 current_width = 0;
361 wrapped = true;
362 }
363
364 let style = if grapheme_idx == next_highlight_idx {
365 next_highlight_idx =
366 indices.next().unwrap_or(u32::MAX);
367 span.style.patch(highlight_style)
368 } else {
369 span.style
370 };
371
372 if style != current_style {
373 if !current_span.is_empty() {
374 current_spans
375 .push(Span::styled(current_span, current_style))
376 }
377 current_span = String::new();
378 current_style = style;
379 }
380 current_span.push_str(grapheme);
381 grapheme_idx += 1;
382 current_width += grapheme_width;
383 }
384 }
385
386 current_spans.push(Span::styled(current_span, current_style));
387 lines.push(Line::from(current_spans));
388 cell_width = cell_width.max(current_width);
389 grapheme_idx += 1; }
391
392 col_idx += 1;
393
394 (Text::from(lines), if wrapped { width_limit as usize } else { cell_width })
395 } else if width_limit != u16::MAX {
396 let (cell, wrapped) = wrap_text(cell, width_limit - 1);
397 let width = if wrapped { width_limit as usize } else { cell.width() };
398 (cell, width)
399 } else {
400 let width = cell.width();
401 (cell, width)
402 };
403
404 if width as u16 > *max_width {
406 *max_width = width as u16;
407 }
408
409 if cell.height() as u16 > height {
410 height = cell.height() as u16;
411 }
412
413 cell
414 });
415
416 (row.collect(), item.data, height)
417 })
418 .collect();
419
420 for (w, c) in widths.iter_mut().zip(self.columns.iter()) {
422 let name_width = c.name.width() as u16;
423 if *w != 0 {
424 *w = (*w).max(name_width);
425 }
426 }
427
428 (table, widths, status)
429 }
430}