1#[cfg(test)]
40mod tests;
41
42mod draw;
43mod item;
44mod layout;
45mod span;
46mod unicode;
47
48use std::{ops::Range, sync::Arc};
49
50use self::{
51 layout::{reset, resize, selection, update},
52 unicode::Span,
53};
54use crate::{Injector, Render, incremental::Incremental};
55
56use nucleo::{
57 self as nc,
58 pattern::{CaseMatching, Normalization},
59};
60
61#[derive(Debug, PartialEq, Eq)]
63#[non_exhaustive]
64pub enum MatchListEvent {
65 Up(usize),
67 Down(usize),
69 Reset,
71}
72
73pub trait ItemSize {
75 fn size(&self) -> usize;
77}
78
79pub trait ItemList {
81 type Item<'a>: ItemSize
83 where
84 Self: 'a;
85
86 fn total(&self) -> u32;
88
89 fn lower(&self, cursor: u32) -> impl DoubleEndedIterator<Item = Self::Item<'_>>;
91
92 fn lower_inclusive(&self, cursor: u32) -> impl DoubleEndedIterator<Item = Self::Item<'_>>;
94
95 fn higher(&self, cursor: u32) -> impl DoubleEndedIterator<Item = Self::Item<'_>>;
97
98 fn higher_inclusive(&self, selection: u32) -> impl DoubleEndedIterator<Item = Self::Item<'_>>;
100}
101
102trait ItemListExt: ItemList {
104 fn sizes_lower<'a>(
107 &self,
108 cursor: u32,
109 vec: &'a mut Vec<usize>,
110 ) -> Incremental<&'a mut Vec<usize>, impl Iterator<Item = usize>> {
111 vec.clear();
112 Incremental::new(vec, self.lower(cursor).map(|item| item.size()))
113 }
114
115 fn sizes_lower_inclusive<'a>(
118 &self,
119 cursor: u32,
120 vec: &'a mut Vec<usize>,
121 ) -> Incremental<&'a mut Vec<usize>, impl Iterator<Item = usize>> {
122 vec.clear();
123 Incremental::new(vec, self.lower_inclusive(cursor).map(|item| item.size()))
124 }
125
126 fn sizes_higher<'a>(
129 &self,
130 cursor: u32,
131 vec: &'a mut Vec<usize>,
132 ) -> Incremental<&'a mut Vec<usize>, impl Iterator<Item = usize>> {
133 vec.clear();
134 Incremental::new(vec, self.higher(cursor).map(|item| item.size()))
135 }
136
137 fn sizes_higher_inclusive<'a>(
140 &self,
141 cursor: u32,
142 vec: &'a mut Vec<usize>,
143 ) -> Incremental<&'a mut Vec<usize>, impl Iterator<Item = usize>> {
144 vec.clear();
145 Incremental::new(vec, self.higher_inclusive(cursor).map(|item| item.size()))
146 }
147}
148
149impl<B: ItemList> ItemListExt for B {}
150
151#[derive(Debug)]
153struct MatchListState {
154 selection: u32,
155 below: u16,
156 above: u16,
157 size: u16,
158}
159
160#[derive(Debug, Clone)]
162#[non_exhaustive]
163pub struct MatchListConfig {
164 pub highlight: bool,
166 pub reversed: bool,
168 pub highlight_padding: u16,
170 pub scroll_padding: u16,
172 pub case_matching: CaseMatching,
174 pub normalization: Normalization,
176}
177
178impl Default for MatchListConfig {
179 fn default() -> Self {
180 Self {
181 highlight: true,
182 reversed: false,
183 highlight_padding: 3,
184 scroll_padding: 3,
185 case_matching: CaseMatching::default(),
186 normalization: Normalization::default(),
187 }
188 }
189}
190
191pub struct IndexBuffer {
194 spans: Vec<Span>,
196 lines: Vec<Range<usize>>,
198 indices: Vec<u32>,
200}
201
202impl IndexBuffer {
203 pub fn new() -> Self {
205 Self {
206 spans: Vec::with_capacity(16),
207 lines: Vec::with_capacity(4),
208 indices: Vec::with_capacity(16),
209 }
210 }
211}
212
213pub struct MatchList<T: Send + Sync + 'static, R> {
219 selection: u32,
222 size: u16,
224 below: Vec<usize>,
226 above: Vec<usize>,
228 config: MatchListConfig,
230 nucleo: nc::Nucleo<T>,
232 scratch: IndexBuffer,
234 render: Arc<R>,
236 matcher: nc::Matcher,
238 prompt: String,
240}
241
242impl<T: Send + Sync + 'static, R: Render<T>> MatchList<T, R> {
243 pub fn new(
245 config: MatchListConfig,
246 nucleo_config: nc::Config,
247 nucleo: nc::Nucleo<T>,
248 render: Arc<R>,
249 ) -> Self {
250 Self {
251 size: 0,
252 selection: 0,
253 below: Vec::with_capacity(128),
254 above: Vec::with_capacity(128),
255 config,
256 nucleo,
257 matcher: nc::Matcher::new(nucleo_config),
258 render,
259 scratch: IndexBuffer::new(),
260 prompt: String::with_capacity(32),
261 }
262 }
263
264 pub fn reversed(&self) -> bool {
265 self.config.reversed
266 }
267
268 pub fn render<'a>(&self, item: &'a T) -> <R as Render<T>>::Str<'a> {
270 self.render.render(item)
271 }
272
273 pub fn reset_renderer(&mut self, render: R) {
275 self.restart();
276 self.render = render.into();
277 }
278
279 pub fn injector(&self) -> Injector<T, R> {
281 Injector::new(self.nucleo.injector(), self.render.clone())
282 }
283
284 pub fn restart(&mut self) {
286 self.nucleo.restart(true);
287 self.update_items();
288 }
289
290 pub fn update_nucleo_config(&mut self, config: nc::Config) {
292 self.nucleo.update_config(config);
293 }
294
295 fn state(&self) -> MatchListState {
298 let below = self.below.iter().sum::<usize>() as u16;
299 let above = self.above.iter().sum::<usize>() as u16;
300 MatchListState {
301 selection: self.selection,
302 below: self.size - above,
303 above: self.size - below,
304 size: self.size,
305 }
306 }
307
308 fn whitespace(&self) -> u16 {
310 self.size
311 - self.below.iter().sum::<usize>() as u16
312 - self.above.iter().sum::<usize>() as u16
313 }
314
315 pub fn padding(&self, size: u16) -> u16 {
317 self.config.scroll_padding.min(size.saturating_sub(1) / 2)
318 }
319
320 pub fn reparse(&mut self, new: &str) {
322 let appending = match new.strip_prefix(&self.prompt) {
325 Some(rest) => {
326 if rest.is_empty() {
327 return;
329 } else {
330 (self
332 .prompt
333 .bytes()
334 .rev()
335 .take_while(|ch| *ch == b'\\')
336 .count()
337 % 2)
338 == 0
339 }
340 }
341 None => false,
342 };
343 self.nucleo.pattern.reparse(
344 0,
345 new,
346 self.config.case_matching,
347 self.config.normalization,
348 appending,
349 );
350 self.prompt = new.to_owned();
351 }
352
353 pub fn is_empty(&self) -> bool {
355 self.nucleo.snapshot().matched_item_count() == 0
356 }
357
358 pub fn selection(&self) -> u32 {
359 self.selection
360 }
361
362 pub fn max_selection(&self) -> u32 {
363 self.nucleo
364 .snapshot()
365 .matched_item_count()
366 .saturating_sub(1)
367 }
368
369 pub fn get_item(&self, n: u32) -> Option<nc::Item<'_, T>> {
370 self.nucleo.snapshot().get_matched_item(n)
371 }
372
373 pub fn selection_range(&self) -> std::ops::RangeInclusive<u32> {
375 if self.config.reversed {
376 self.selection - self.above.len() as u32..=self.selection + self.below.len() as u32 - 1
377 } else {
378 self.selection + 1 - self.below.len() as u32..=self.selection + self.above.len() as u32
379 }
380 }
381
382 pub fn resize(&mut self, total_size: u16) {
384 if total_size == 0 {
386 self.size = 0;
387 self.above.clear();
388 self.below.clear();
389 return;
390 }
391
392 let buffer = self.nucleo.snapshot();
393
394 if buffer.total() == 0 {
396 self.size = total_size;
397 return;
398 }
399
400 let padding = self.padding(total_size);
401
402 let mut previous = self.state();
403
404 if self.config.reversed {
405 previous.below = previous.below.clamp(padding, total_size - padding - 1);
408
409 let sizes_below_incl = buffer.sizes_higher_inclusive(self.selection, &mut self.below);
410 let sizes_above = buffer.sizes_lower(self.selection, &mut self.above);
411
412 if self.size <= total_size {
413 resize::larger_rev(previous, total_size, padding, sizes_below_incl, sizes_above);
414 } else {
415 resize::smaller_rev(
416 previous,
417 total_size,
418 padding,
419 padding,
420 sizes_below_incl,
421 sizes_above,
422 );
423 }
424 } else {
425 previous.above = previous.above.clamp(padding, total_size - padding - 1);
428
429 let sizes_below_incl = buffer.sizes_lower_inclusive(self.selection, &mut self.below);
430 let sizes_above = buffer.sizes_higher(self.selection, &mut self.above);
431
432 if self.size <= total_size {
433 resize::larger(previous, total_size, sizes_below_incl, sizes_above);
434 } else {
435 resize::smaller(previous, total_size, padding, sizes_below_incl, sizes_above);
436 }
437 }
438
439 self.size = total_size;
440 }
441
442 pub fn update(&mut self, millis: u64) -> bool {
444 let status = self.nucleo.tick(millis);
445 if status.changed {
446 self.update_items();
447 }
448 status.changed
449 }
450
451 pub fn reset(&mut self) -> bool {
453 let buffer = self.nucleo.snapshot();
454 let padding = self.padding(self.size);
455 if self.selection != 0 {
456 if self.config.reversed {
457 let sizes_below_incl = buffer.sizes_higher_inclusive(0, &mut self.below);
458 self.above.clear();
459
460 reset::reset_rev(self.size, sizes_below_incl);
461 } else {
462 let sizes_below_incl = buffer.sizes_lower_inclusive(0, &mut self.below);
463 let sizes_above = buffer.sizes_higher(0, &mut self.above);
464
465 reset::reset(self.size, padding, sizes_below_incl, sizes_above);
466 }
467
468 self.selection = 0;
469 true
470 } else {
471 false
472 }
473 }
474
475 pub fn update_items(&mut self) {
477 let buffer = self.nucleo.snapshot();
478 self.selection = self.selection.min(buffer.total().saturating_sub(1));
480 let previous = self.state();
481 let padding = self.padding(self.size);
482
483 if buffer.total() > 0 {
484 if self.config.reversed {
485 let sizes_below_incl =
486 buffer.sizes_higher_inclusive(self.selection, &mut self.below);
487 let sizes_above = buffer.sizes_lower(self.selection, &mut self.above);
488
489 update::items_rev(previous, padding, sizes_below_incl, sizes_above);
490 } else {
491 let sizes_below_incl =
492 buffer.sizes_lower_inclusive(self.selection, &mut self.below);
493 let sizes_above = buffer.sizes_higher(self.selection, &mut self.above);
494
495 update::items(previous, padding, sizes_below_incl, sizes_above);
496 }
497 } else {
498 self.below.clear();
499 self.above.clear();
500 self.selection = 0;
501 }
502 }
503
504 #[inline]
505 pub fn set_selection(&mut self, new_selection: u32) -> bool {
506 let buffer = self.nucleo.snapshot();
507 let new_selection = new_selection.min(buffer.total().saturating_sub(1));
508
509 let previous = self.state();
510 let padding = self.padding(self.size);
511
512 if new_selection == 0 {
513 self.reset()
514 } else if new_selection > self.selection {
515 if self.config.reversed {
516 let sizes_below_incl =
517 buffer.sizes_higher_inclusive(new_selection, &mut self.below);
518 let sizes_above = buffer.sizes_lower(new_selection, &mut self.above);
519
520 selection::incr_rev(
521 previous,
522 new_selection,
523 padding,
524 padding,
525 sizes_below_incl,
526 sizes_above,
527 );
528 } else {
529 let sizes_below_incl = buffer.sizes_lower_inclusive(new_selection, &mut self.below);
530 let sizes_above = buffer.sizes_higher(new_selection, &mut self.above);
531
532 selection::incr(
533 previous,
534 new_selection,
535 padding,
536 sizes_below_incl,
537 sizes_above,
538 );
539 }
540
541 self.selection = new_selection;
542
543 true
544 } else if new_selection < self.selection {
545 if self.config.reversed {
546 let sizes_below_incl =
547 buffer.sizes_higher_inclusive(new_selection, &mut self.below);
548 let sizes_above = buffer.sizes_lower(new_selection, &mut self.above);
549
550 selection::decr_rev(
551 previous,
552 new_selection,
553 padding,
554 sizes_below_incl,
555 sizes_above,
556 );
557 } else {
558 let sizes_below_incl = buffer.sizes_lower_inclusive(new_selection, &mut self.below);
559 let sizes_above = buffer.sizes_higher(new_selection, &mut self.above);
560
561 selection::decr(
562 previous,
563 new_selection,
564 padding,
565 padding,
566 sizes_below_incl,
567 sizes_above,
568 );
569 }
570
571 self.selection = new_selection;
572
573 true
574 } else {
575 false
576 }
577 }
578
579 pub fn selection_incr(&mut self, increase: u32) -> bool {
581 let new_selection = self
582 .selection
583 .saturating_add(increase)
584 .min(self.nucleo.snapshot().total().saturating_sub(1));
585
586 self.set_selection(new_selection)
587 }
588
589 pub fn selection_decr(&mut self, decrease: u32) -> bool {
591 let new_selection = self.selection.saturating_sub(decrease);
592
593 self.set_selection(new_selection)
594 }
595}