1#[cfg(test)]
40mod tests;
41
42mod draw;
43mod item;
44mod layout;
45mod span;
46mod unicode;
47
48use std::{
49 collections::{BTreeMap, btree_map::Entry},
50 num::NonZero,
51 ops::Range,
52 sync::Arc,
53};
54
55use self::{
56 layout::{reset, resize, selection, update},
57 unicode::Span,
58};
59use crate::{Injector, Render, incremental::Incremental};
60
61use nucleo::{
62 self as nc,
63 pattern::{CaseMatching as NucleoCaseMatching, Normalization as NucleoNormalization},
64};
65
66#[derive(Debug, PartialEq, Eq)]
85#[non_exhaustive]
86pub enum MatchListEvent {
87 Up(usize),
89 ToggleUp(usize),
91 Down(usize),
93 ToggleDown(usize),
95 QueueAbove(usize),
98 QueueBelow(usize),
101 QueueMatches,
103 Unqueue,
105 UnqueueAll,
107 Reset,
109}
110
111pub trait ItemSize {
113 fn size(&self) -> usize;
115}
116
117pub trait ItemList {
119 type Item<'a>: ItemSize
121 where
122 Self: 'a;
123
124 fn total(&self) -> u32;
126
127 fn lower(&self, cursor: u32) -> impl DoubleEndedIterator<Item = Self::Item<'_>>;
129
130 fn lower_inclusive(&self, cursor: u32) -> impl DoubleEndedIterator<Item = Self::Item<'_>>;
132
133 fn higher(&self, cursor: u32) -> impl DoubleEndedIterator<Item = Self::Item<'_>>;
135
136 fn higher_inclusive(&self, selection: u32) -> impl DoubleEndedIterator<Item = Self::Item<'_>>;
138}
139
140trait ItemListExt: ItemList {
142 fn sizes_lower<'a>(
145 &self,
146 cursor: u32,
147 vec: &'a mut Vec<usize>,
148 ) -> Incremental<&'a mut Vec<usize>, impl Iterator<Item = usize>> {
149 vec.clear();
150 Incremental::new(vec, self.lower(cursor).map(|item| item.size()))
151 }
152
153 fn sizes_lower_inclusive<'a>(
156 &self,
157 cursor: u32,
158 vec: &'a mut Vec<usize>,
159 ) -> Incremental<&'a mut Vec<usize>, impl Iterator<Item = usize>> {
160 vec.clear();
161 Incremental::new(vec, self.lower_inclusive(cursor).map(|item| item.size()))
162 }
163
164 fn sizes_higher<'a>(
167 &self,
168 cursor: u32,
169 vec: &'a mut Vec<usize>,
170 ) -> Incremental<&'a mut Vec<usize>, impl Iterator<Item = usize>> {
171 vec.clear();
172 Incremental::new(vec, self.higher(cursor).map(|item| item.size()))
173 }
174
175 fn sizes_higher_inclusive<'a>(
178 &self,
179 cursor: u32,
180 vec: &'a mut Vec<usize>,
181 ) -> Incremental<&'a mut Vec<usize>, impl Iterator<Item = usize>> {
182 vec.clear();
183 Incremental::new(vec, self.higher_inclusive(cursor).map(|item| item.size()))
184 }
185}
186
187impl<B: ItemList> ItemListExt for B {}
188
189#[derive(Debug)]
191struct MatchListState {
192 selection: u32,
193 below: u16,
194 above: u16,
195 size: u16,
196}
197
198#[derive(Debug, Clone)]
200#[non_exhaustive]
201pub struct MatchListConfig {
202 pub highlight: bool,
204 pub reversed: bool,
206 pub highlight_padding: u16,
208 pub scroll_padding: u16,
210 pub case_matching: NucleoCaseMatching,
212 pub normalization: NucleoNormalization,
214}
215
216impl MatchListConfig {
217 pub const fn new() -> Self {
218 Self {
219 highlight: true,
220 reversed: false,
221 highlight_padding: 3,
222 scroll_padding: 3,
223 case_matching: NucleoCaseMatching::Smart,
224 normalization: NucleoNormalization::Smart,
225 }
226 }
227}
228
229impl Default for MatchListConfig {
230 fn default() -> Self {
231 Self::new()
232 }
233}
234
235pub struct IndexBuffer {
238 spans: Vec<Span>,
240 lines: Vec<Range<usize>>,
242 indices: Vec<u32>,
244}
245
246impl IndexBuffer {
247 pub fn new() -> Self {
249 Self {
250 spans: Vec::with_capacity(16),
251 lines: Vec::with_capacity(4),
252 indices: Vec::with_capacity(16),
253 }
254 }
255}
256
257pub trait Queued {
258 type Output<'a, T: Send + Sync + 'static>;
259
260 fn is_empty(&self) -> bool;
261
262 fn clear(&mut self) -> bool;
263
264 fn deselect(&mut self, idx: u32) -> bool;
265
266 fn toggle(&mut self, idx: u32) -> bool;
267
268 fn select<I: IntoIterator<Item = u32>>(&mut self, items: I) -> (usize, bool);
274
275 fn is_queued(&self, idx: u32) -> bool;
276
277 fn count(&self, limit: Option<NonZero<u32>>) -> Option<(u32, Option<NonZero<u32>>)>;
278
279 fn init(limit: Option<NonZero<u32>>) -> Self;
280
281 fn into_only_selection<'a, T: Send + Sync + 'static>(
282 self,
283 snapshot: &'a nucleo::Snapshot<T>,
284 idx: u32,
285 ) -> Self::Output<'a, T>;
286
287 fn into_selection<'a, T: Send + Sync + 'static>(
288 self,
289 snapshot: &'a nucleo::Snapshot<T>,
290 ) -> Self::Output<'a, T>;
291}
292
293impl Queued for () {
294 type Output<'a, T: Send + Sync + 'static> = Option<&'a T>;
295
296 #[inline]
297 fn is_empty(&self) -> bool {
298 true
299 }
300
301 #[inline]
302 fn clear(&mut self) -> bool {
303 false
304 }
305
306 #[inline]
307 fn deselect(&mut self, _: u32) -> bool {
308 false
309 }
310
311 #[inline]
312 fn toggle(&mut self, _: u32) -> bool {
313 false
314 }
315
316 #[inline]
317 fn select<I: IntoIterator<Item = u32>>(&mut self, _: I) -> (usize, bool) {
318 (0, false)
319 }
320
321 #[inline]
322 fn is_queued(&self, _: u32) -> bool {
323 false
324 }
325
326 #[inline]
327 fn init(_: Option<NonZero<u32>>) -> Self {}
328
329 #[inline]
330 fn into_selection<'a, T: Send + Sync + 'static>(
331 self,
332 _: &'a nucleo::Snapshot<T>,
333 ) -> Self::Output<'a, T> {
334 None
335 }
336
337 #[inline]
338 fn into_only_selection<'a, T: Send + Sync + 'static>(
339 self,
340 snapshot: &'a nucleo::Snapshot<T>,
341 idx: u32,
342 ) -> Self::Output<'a, T> {
343 Some(snapshot.get_item(idx).unwrap().data)
344 }
345
346 #[inline]
347 fn count(&self, _: Option<NonZero<u32>>) -> Option<(u32, Option<NonZero<u32>>)> {
348 None
349 }
350}
351
352impl Queued for SelectedIndices {
353 type Output<'a, T: Send + Sync + 'static> = Selection<'a, T>;
354
355 #[inline]
356 fn is_empty(&self) -> bool {
357 self.inner.is_empty()
358 }
359
360 #[inline]
361 fn clear(&mut self) -> bool {
362 if self.is_empty() {
363 false
364 } else {
365 self.inner.clear();
366 true
367 }
368 }
369
370 #[inline]
371 fn toggle(&mut self, idx: u32) -> bool {
372 let n = self.inner.len();
373 match self.inner.entry(idx) {
374 Entry::Occupied(occupied_entry) => {
375 occupied_entry.remove_entry();
376 true
377 }
378 Entry::Vacant(vacant_entry) => {
379 if self.limit.is_none_or(|l| n < l.get() as usize) {
380 vacant_entry.insert(Self::next_order(&mut self.next_order));
381 true
382 } else {
383 false
384 }
385 }
386 }
387 }
388
389 #[inline]
390 fn deselect(&mut self, idx: u32) -> bool {
391 self.inner.remove(&idx).is_some()
392 }
393
394 fn select<I: IntoIterator<Item = u32>>(&mut self, items: I) -> (usize, bool) {
395 let mut toggled = false;
396 let mut consumed: usize = 0;
397
398 for it in items {
399 let current_len = self.inner.len();
400 match self.inner.entry(it) {
401 Entry::Vacant(vacant_entry)
402 if self.limit.is_none_or(|l| current_len < l.get() as usize) =>
403 {
404 toggled = true;
405 consumed += 1;
406 vacant_entry.insert(Self::next_order(&mut self.next_order));
407 }
408 Entry::Vacant(_) => break,
409 Entry::Occupied(_) => {
410 consumed += 1;
411 }
412 }
413 }
414
415 (consumed, toggled)
416 }
417
418 #[inline]
419 fn is_queued(&self, idx: u32) -> bool {
420 self.inner.contains_key(&idx)
421 }
422
423 #[inline]
424 fn init(limit: Option<NonZero<u32>>) -> Self {
425 Self {
426 inner: BTreeMap::new(),
427 next_order: 0,
428 limit,
429 }
430 }
431
432 #[inline]
433 fn into_selection<'a, T: Send + Sync + 'static>(
434 self,
435 snapshot: &'a nucleo::Snapshot<T>,
436 ) -> Self::Output<'a, T> {
437 Self::Output {
438 snapshot,
439 queued: self,
440 }
441 }
442
443 #[inline]
444 fn into_only_selection<'a, T: Send + Sync + 'static>(
445 mut self,
446 snapshot: &'a nucleo::Snapshot<T>,
447 idx: u32,
448 ) -> Self::Output<'a, T> {
449 self.insert(idx);
450 Self::Output {
451 snapshot,
452 queued: self,
453 }
454 }
455
456 #[inline]
457 fn count(&self, limit: Option<NonZero<u32>>) -> Option<(u32, Option<NonZero<u32>>)> {
458 Some((self.inner.len() as u32, limit))
459 }
460}
461
462pub struct SelectedIndices {
463 inner: BTreeMap<u32, u64>,
464 next_order: u64,
465 limit: Option<NonZero<u32>>,
466}
467
468impl SelectedIndices {
469 fn insert(&mut self, idx: u32) {
470 if let Entry::Vacant(entry) = self.inner.entry(idx) {
471 entry.insert(Self::next_order(&mut self.next_order));
472 }
473 }
474
475 fn next_order(next_order: &mut u64) -> u64 {
476 let order = *next_order;
477 *next_order += 1;
478 order
479 }
480}
481
482pub struct Selection<'a, T: Send + Sync + 'static> {
490 snapshot: &'a nc::Snapshot<T>,
491 queued: SelectedIndices,
492}
493
494impl<'a, T: Send + Sync + 'static> Selection<'a, T> {
495 pub fn iter(&self) -> impl ExactSizeIterator<Item = &'a T> + DoubleEndedIterator {
507 self.queued.inner.keys().map(|idx| {
508 unsafe { self.snapshot.get_item_unchecked(*idx).data }
512 })
513 }
514
515 pub fn iter_selected_order(
525 &self,
526 ) -> impl ExactSizeIterator<Item = &'a T> + DoubleEndedIterator {
527 let snapshot = self.snapshot;
528 let mut selected_items = self
529 .queued
530 .inner
531 .iter()
532 .map(|(&idx, &order)| (order, idx))
533 .collect::<Vec<_>>();
534 selected_items.sort_unstable_by_key(|&(order, _)| order);
535
536 selected_items.into_iter().map(move |(_, idx)| {
537 unsafe { snapshot.get_item_unchecked(idx).data }
541 })
542 }
543
544 pub fn is_empty(&self) -> bool {
546 self.queued.inner.is_empty()
547 }
548
549 pub fn len(&self) -> usize {
551 self.queued.inner.len()
552 }
553}
554
555pub struct MatchList<T: Send + Sync + 'static, R> {
561 selection: u32,
564 size: u16,
566 below: Vec<usize>,
568 above: Vec<usize>,
570 config: MatchListConfig,
572 nucleo: nc::Nucleo<T>,
574 scratch: IndexBuffer,
576 render: Arc<R>,
578 matcher: nc::Matcher,
580 prompt: String,
582}
583
584impl<T: Send + Sync + 'static, R> MatchList<T, R> {
585 pub fn new(
587 config: MatchListConfig,
588 nucleo_config: nc::Config,
589 nucleo: nc::Nucleo<T>,
590 render: Arc<R>,
591 ) -> Self {
592 Self {
593 size: 0,
594 selection: 0,
595 below: Vec::with_capacity(128),
597 above: Vec::with_capacity(128),
598 config,
599 nucleo,
600 matcher: nc::Matcher::new(nucleo_config),
601 render,
602 scratch: IndexBuffer::new(),
603 prompt: String::with_capacity(32),
604 }
605 }
606
607 pub fn reversed(&self) -> bool {
608 self.config.reversed
609 }
610
611 pub fn render<'a>(&self, item: &'a T) -> <R as Render<T>>::Str<'a>
613 where
614 R: Render<T>,
615 {
616 self.render.render(item)
617 }
618
619 pub fn reset_renderer(&mut self, render: R) {
621 self.restart();
622 self.render = render.into();
623 }
624
625 pub fn injector(&self) -> Injector<T, R> {
627 Injector::new(self.nucleo.injector(), self.render.clone())
628 }
629
630 pub fn restart(&mut self) {
632 self.nucleo.restart(true);
633 self.update_items();
634 }
635
636 pub fn update_nucleo_config(&mut self, config: nc::Config) {
638 self.nucleo.update_config(config);
639 }
640
641 fn state(&self) -> MatchListState {
644 let below = self.below.iter().sum::<usize>() as u16;
645 let above = self.above.iter().sum::<usize>() as u16;
646 MatchListState {
647 selection: self.selection,
648 below: self.size - above,
649 above: self.size - below,
650 size: self.size,
651 }
652 }
653
654 fn whitespace(&self) -> u16 {
656 self.size
657 - self.below.iter().sum::<usize>() as u16
658 - self.above.iter().sum::<usize>() as u16
659 }
660
661 pub fn padding(&self, size: u16) -> u16 {
663 self.config.scroll_padding.min(size.saturating_sub(1) / 2)
664 }
665
666 pub fn reparse(&mut self, new: &str) {
668 let appending = match new.strip_prefix(&self.prompt) {
671 Some(rest) => {
672 if rest.is_empty() {
673 return;
675 } else {
676 true
677 }
678 }
679 None => false,
680 };
681 self.nucleo.pattern.reparse(
682 0,
683 new,
684 self.config.case_matching,
685 self.config.normalization,
686 appending,
687 );
688 self.prompt = new.to_owned();
689 }
690
691 pub fn is_empty(&self) -> bool {
693 self.nucleo.snapshot().matched_item_count() == 0
694 }
695
696 pub fn selection(&self) -> u32 {
697 self.selection
698 }
699
700 pub fn max_selection(&self) -> u32 {
701 self.nucleo
702 .snapshot()
703 .matched_item_count()
704 .saturating_sub(1)
705 }
706
707 fn idx_from_match_unchecked(&self, n: u32) -> u32 {
708 self.nucleo
709 .snapshot()
710 .matches()
711 .get(n as usize)
712 .unwrap()
713 .idx
714 }
715
716 pub fn unqueue_item<Q: Queued>(&mut self, queued_items: &mut Q, n: u32) -> bool {
717 queued_items.deselect(self.idx_from_match_unchecked(n))
718 }
719
720 pub fn queue_items_above<Q: Queued>(
721 &mut self,
722 queued_items: &mut Q,
723 n: u32,
724 ct: usize,
725 ) -> (usize, bool) {
726 let matches = self.nucleo.snapshot().matches();
727 let start = n as usize;
728 let end = (start + ct).min(matches.len() - 1);
729 queued_items.select(matches[start..=end].iter().map(|m| m.idx))
730 }
731
732 pub fn queue_items_below<Q: Queued>(
733 &mut self,
734 queued_items: &mut Q,
735 n: u32,
736 ct: usize,
737 ) -> (usize, bool) {
738 let matches = self.nucleo.snapshot().matches();
739 let start = n as usize;
740 let end = start.saturating_sub(ct);
741 queued_items.select(matches[end..=start].iter().rev().map(|m| m.idx))
742 }
743
744 pub fn queue_all<Q: Queued>(&mut self, queued_items: &mut Q) -> bool {
745 queued_items
746 .select(self.nucleo.snapshot().matches().iter().map(|m| m.idx))
747 .1
748 }
749
750 pub fn toggle_queued_item<Q: Queued>(&mut self, queued_items: &mut Q, n: u32) -> bool {
751 queued_items.toggle(self.idx_from_match_unchecked(n))
752 }
753
754 pub fn select_none<Q: Queued>(&self, mut queued_items: Q) -> Q::Output<'_, T> {
755 queued_items.clear();
756 self.select_queued(queued_items)
757 }
758
759 pub fn select_one<Q: Queued>(&self, queued_items: Q, n: u32) -> Q::Output<'_, T> {
760 let idx = self.idx_from_match_unchecked(n);
761 let snapshot = self.nucleo.snapshot();
762 queued_items.into_only_selection(snapshot, idx)
763 }
764
765 pub fn select_queued<Q: Queued>(&self, queued_items: Q) -> Q::Output<'_, T> {
766 let snapshot = self.nucleo.snapshot();
767 queued_items.into_selection(snapshot)
768 }
769
770 pub fn selection_range(&self) -> std::ops::RangeInclusive<usize> {
772 if self.config.reversed {
773 self.selection as usize - self.above.len()
774 ..=self.selection as usize + self.below.len() - 1
775 } else {
776 self.selection as usize + 1 - self.below.len()
777 ..=self.selection as usize + self.above.len()
778 }
779 }
780
781 pub fn resize(&mut self, total_size: u16) {
783 if total_size == 0 {
785 self.size = 0;
786 self.above.clear();
787 self.below.clear();
788 return;
789 }
790
791 let buffer = self.nucleo.snapshot();
792
793 if buffer.total() == 0 {
795 self.size = total_size;
796 return;
797 }
798
799 let padding = self.padding(total_size);
800
801 let mut previous = self.state();
802
803 if self.config.reversed {
804 previous.below = previous.below.clamp(padding, total_size - padding - 1);
807
808 let sizes_below_incl = buffer.sizes_higher_inclusive(self.selection, &mut self.below);
809 let sizes_above = buffer.sizes_lower(self.selection, &mut self.above);
810
811 if self.size <= total_size {
812 resize::larger_rev(previous, total_size, padding, sizes_below_incl, sizes_above);
813 } else {
814 resize::smaller_rev(
815 previous,
816 total_size,
817 padding,
818 padding,
819 sizes_below_incl,
820 sizes_above,
821 );
822 }
823 } else {
824 previous.above = previous.above.clamp(padding, total_size - padding - 1);
827
828 let sizes_below_incl = buffer.sizes_lower_inclusive(self.selection, &mut self.below);
829 let sizes_above = buffer.sizes_higher(self.selection, &mut self.above);
830
831 if self.size <= total_size {
832 resize::larger(previous, total_size, sizes_below_incl, sizes_above);
833 } else {
834 resize::smaller(previous, total_size, padding, sizes_below_incl, sizes_above);
835 }
836 }
837
838 self.size = total_size;
839 }
840
841 pub fn update(&mut self, millis: u64) -> bool {
843 let status = self.nucleo.tick(millis);
844 if status.changed {
845 self.update_items();
846 }
847 status.changed
848 }
849
850 pub fn reset(&mut self) -> bool {
852 let buffer = self.nucleo.snapshot();
853 let padding = self.padding(self.size);
854 if self.selection != 0 {
855 if self.config.reversed {
856 let sizes_below_incl = buffer.sizes_higher_inclusive(0, &mut self.below);
857 self.above.clear();
858
859 reset::reset_rev(self.size, sizes_below_incl);
860 } else {
861 let sizes_below_incl = buffer.sizes_lower_inclusive(0, &mut self.below);
862 let sizes_above = buffer.sizes_higher(0, &mut self.above);
863
864 reset::reset(self.size, padding, sizes_below_incl, sizes_above);
865 }
866
867 self.selection = 0;
868 true
869 } else {
870 false
871 }
872 }
873
874 pub fn update_items(&mut self) {
876 let buffer = self.nucleo.snapshot();
877 self.selection = self.selection.min(buffer.total().saturating_sub(1));
879 let previous = self.state();
880 let padding = self.padding(self.size);
881
882 if buffer.total() > 0 {
883 if self.config.reversed {
884 let sizes_below_incl =
885 buffer.sizes_higher_inclusive(self.selection, &mut self.below);
886 let sizes_above = buffer.sizes_lower(self.selection, &mut self.above);
887
888 update::items_rev(previous, padding, sizes_below_incl, sizes_above);
889 } else {
890 let sizes_below_incl =
891 buffer.sizes_lower_inclusive(self.selection, &mut self.below);
892 let sizes_above = buffer.sizes_higher(self.selection, &mut self.above);
893
894 update::items(previous, padding, sizes_below_incl, sizes_above);
895 }
896 } else {
897 self.below.clear();
898 self.above.clear();
899 self.selection = 0;
900 }
901 }
902
903 #[inline]
904 pub fn set_selection(&mut self, new_selection: u32) -> bool {
905 let buffer = self.nucleo.snapshot();
906 let new_selection = new_selection.min(buffer.total().saturating_sub(1));
907
908 let previous = self.state();
909 let padding = self.padding(self.size);
910
911 if new_selection == 0 {
912 self.reset()
913 } else if new_selection > self.selection {
914 if self.config.reversed {
915 let sizes_below_incl =
916 buffer.sizes_higher_inclusive(new_selection, &mut self.below);
917 let sizes_above = buffer.sizes_lower(new_selection, &mut self.above);
918
919 selection::incr_rev(
920 previous,
921 new_selection,
922 padding,
923 padding,
924 sizes_below_incl,
925 sizes_above,
926 );
927 } else {
928 let sizes_below_incl = buffer.sizes_lower_inclusive(new_selection, &mut self.below);
929 let sizes_above = buffer.sizes_higher(new_selection, &mut self.above);
930
931 selection::incr(
932 previous,
933 new_selection,
934 padding,
935 sizes_below_incl,
936 sizes_above,
937 );
938 }
939
940 self.selection = new_selection;
941
942 true
943 } else if new_selection < self.selection {
944 if self.config.reversed {
945 let sizes_below_incl =
946 buffer.sizes_higher_inclusive(new_selection, &mut self.below);
947 let sizes_above = buffer.sizes_lower(new_selection, &mut self.above);
948
949 selection::decr_rev(
950 previous,
951 new_selection,
952 padding,
953 sizes_below_incl,
954 sizes_above,
955 );
956 } else {
957 let sizes_below_incl = buffer.sizes_lower_inclusive(new_selection, &mut self.below);
958 let sizes_above = buffer.sizes_higher(new_selection, &mut self.above);
959
960 selection::decr(
961 previous,
962 new_selection,
963 padding,
964 padding,
965 sizes_below_incl,
966 sizes_above,
967 );
968 }
969
970 self.selection = new_selection;
971
972 true
973 } else {
974 false
975 }
976 }
977
978 #[cfg(test)]
980 pub fn selection_incr(&mut self, increase: u32) -> bool {
981 let new_selection = self
982 .selection
983 .saturating_add(increase)
984 .min(self.nucleo.snapshot().total().saturating_sub(1));
985
986 self.set_selection(new_selection)
987 }
988
989 #[cfg(test)]
991 pub fn selection_decr(&mut self, decrease: u32) -> bool {
992 let new_selection = self.selection.saturating_sub(decrease);
993
994 self.set_selection(new_selection)
995 }
996}