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() as u32;
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()) {
380 vacant_entry.insert(());
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 match self.limit {
399 None => {
400 for it in items {
401 match self.inner.entry(it) {
402 Entry::Vacant(vacant_entry) => {
403 toggled = true;
404 consumed += 1;
405 vacant_entry.insert(());
406 }
407 Entry::Occupied(_) => {
408 consumed += 1;
409 }
410 }
411 }
412 }
413 Some(l) => {
414 for it in items {
415 let current_len = self.inner.len();
416 match self.inner.entry(it) {
417 Entry::Vacant(vacant_entry) => {
418 if current_len < l.get() as usize {
419 toggled = true;
420 consumed += 1;
421 vacant_entry.insert(());
422 } else {
423 break;
424 }
425 }
426 Entry::Occupied(_) => {
427 consumed += 1;
428 }
429 }
430 }
431 }
432 }
433 (consumed, toggled)
434 }
435
436 #[inline]
437 fn is_queued(&self, idx: u32) -> bool {
438 self.inner.contains_key(&idx)
439 }
440
441 #[inline]
442 fn init(limit: Option<NonZero<u32>>) -> Self {
443 Self {
444 inner: BTreeMap::new(),
445 limit,
446 }
447 }
448
449 #[inline]
450 fn into_selection<'a, T: Send + Sync + 'static>(
451 self,
452 snapshot: &'a nucleo::Snapshot<T>,
453 ) -> Self::Output<'a, T> {
454 Self::Output {
455 snapshot,
456 queued: self,
457 }
458 }
459
460 #[inline]
461 fn into_only_selection<'a, T: Send + Sync + 'static>(
462 mut self,
463 snapshot: &'a nucleo::Snapshot<T>,
464 idx: u32,
465 ) -> Self::Output<'a, T> {
466 self.inner.insert(idx, ());
467 Self::Output {
468 snapshot,
469 queued: self,
470 }
471 }
472
473 #[inline]
474 fn count(&self, limit: Option<NonZero<u32>>) -> Option<(u32, Option<NonZero<u32>>)> {
475 Some((self.inner.len() as u32, limit))
476 }
477}
478
479pub struct SelectedIndices {
480 inner: BTreeMap<u32, ()>,
483 limit: Option<NonZero<u32>>,
484}
485
486pub struct Selection<'a, T: Send + Sync + 'static> {
495 snapshot: &'a nc::Snapshot<T>,
496 queued: SelectedIndices,
499}
500
501impl<'a, T: Send + Sync + 'static> Selection<'a, T> {
502 pub fn iter(&self) -> impl ExactSizeIterator<Item = &'a T> + DoubleEndedIterator {
511 self.queued.inner.keys().map(|idx| {
512 unsafe { self.snapshot.get_item_unchecked(*idx).data }
516 })
517 }
518
519 pub fn is_empty(&self) -> bool {
521 self.queued.inner.is_empty()
522 }
523
524 pub fn len(&self) -> usize {
526 self.queued.inner.len()
527 }
528}
529
530pub struct MatchList<T: Send + Sync + 'static, R> {
536 selection: u32,
539 size: u16,
541 below: Vec<usize>,
543 above: Vec<usize>,
545 config: MatchListConfig,
547 nucleo: nc::Nucleo<T>,
549 scratch: IndexBuffer,
551 render: Arc<R>,
553 matcher: nc::Matcher,
555 prompt: String,
557}
558
559impl<T: Send + Sync + 'static, R> MatchList<T, R> {
560 pub fn new(
562 config: MatchListConfig,
563 nucleo_config: nc::Config,
564 nucleo: nc::Nucleo<T>,
565 render: Arc<R>,
566 ) -> Self {
567 Self {
568 size: 0,
569 selection: 0,
570 below: Vec::with_capacity(128),
572 above: Vec::with_capacity(128),
573 config,
574 nucleo,
575 matcher: nc::Matcher::new(nucleo_config),
576 render,
577 scratch: IndexBuffer::new(),
578 prompt: String::with_capacity(32),
579 }
580 }
581
582 pub fn reversed(&self) -> bool {
583 self.config.reversed
584 }
585
586 pub fn render<'a>(&self, item: &'a T) -> <R as Render<T>>::Str<'a>
588 where
589 R: Render<T>,
590 {
591 self.render.render(item)
592 }
593
594 pub fn reset_renderer(&mut self, render: R) {
596 self.restart();
597 self.render = render.into();
598 }
599
600 pub fn injector(&self) -> Injector<T, R> {
602 Injector::new(self.nucleo.injector(), self.render.clone())
603 }
604
605 pub fn restart(&mut self) {
607 self.nucleo.restart(true);
608 self.update_items();
609 }
610
611 pub fn update_nucleo_config(&mut self, config: nc::Config) {
613 self.nucleo.update_config(config);
614 }
615
616 fn state(&self) -> MatchListState {
619 let below = self.below.iter().sum::<usize>() as u16;
620 let above = self.above.iter().sum::<usize>() as u16;
621 MatchListState {
622 selection: self.selection,
623 below: self.size - above,
624 above: self.size - below,
625 size: self.size,
626 }
627 }
628
629 fn whitespace(&self) -> u16 {
631 self.size
632 - self.below.iter().sum::<usize>() as u16
633 - self.above.iter().sum::<usize>() as u16
634 }
635
636 pub fn padding(&self, size: u16) -> u16 {
638 self.config.scroll_padding.min(size.saturating_sub(1) / 2)
639 }
640
641 pub fn reparse(&mut self, new: &str) {
643 let appending = match new.strip_prefix(&self.prompt) {
646 Some(rest) => {
647 if rest.is_empty() {
648 return;
650 } else {
651 true
652 }
653 }
654 None => false,
655 };
656 self.nucleo.pattern.reparse(
657 0,
658 new,
659 self.config.case_matching,
660 self.config.normalization,
661 appending,
662 );
663 self.prompt = new.to_owned();
664 }
665
666 pub fn is_empty(&self) -> bool {
668 self.nucleo.snapshot().matched_item_count() == 0
669 }
670
671 pub fn selection(&self) -> u32 {
672 self.selection
673 }
674
675 pub fn max_selection(&self) -> u32 {
676 self.nucleo
677 .snapshot()
678 .matched_item_count()
679 .saturating_sub(1)
680 }
681
682 fn idx_from_match_unchecked(&self, n: u32) -> u32 {
683 self.nucleo
684 .snapshot()
685 .matches()
686 .get(n as usize)
687 .unwrap()
688 .idx
689 }
690
691 pub fn unqueue_item<Q: Queued>(&mut self, queued_items: &mut Q, n: u32) -> bool {
692 queued_items.deselect(self.idx_from_match_unchecked(n))
693 }
694
695 pub fn queue_items_above<Q: Queued>(
696 &mut self,
697 queued_items: &mut Q,
698 n: u32,
699 ct: usize,
700 ) -> (usize, bool) {
701 let matches = self.nucleo.snapshot().matches();
702 let start = n as usize;
703 let end = (start + ct).min(matches.len() - 1);
704 queued_items.select(matches[start..=end].iter().map(|m| m.idx))
705 }
706
707 pub fn queue_items_below<Q: Queued>(
708 &mut self,
709 queued_items: &mut Q,
710 n: u32,
711 ct: usize,
712 ) -> (usize, bool) {
713 let matches = self.nucleo.snapshot().matches();
714 let start = n as usize;
715 let end = start.saturating_sub(ct);
716 queued_items.select(matches[end..=start].iter().rev().map(|m| m.idx))
717 }
718
719 pub fn queue_all<Q: Queued>(&mut self, queued_items: &mut Q) -> bool {
720 queued_items
721 .select(self.nucleo.snapshot().matches().iter().map(|m| m.idx))
722 .1
723 }
724
725 pub fn toggle_queued_item<Q: Queued>(&mut self, queued_items: &mut Q, n: u32) -> bool {
726 queued_items.toggle(self.idx_from_match_unchecked(n))
727 }
728
729 pub fn select_none<Q: Queued>(&self, mut queued_items: Q) -> Q::Output<'_, T> {
730 queued_items.clear();
731 self.select_queued(queued_items)
732 }
733
734 pub fn select_one<Q: Queued>(&self, queued_items: Q, n: u32) -> Q::Output<'_, T> {
735 let idx = self.idx_from_match_unchecked(n);
736 let snapshot = self.nucleo.snapshot();
737 queued_items.into_only_selection(snapshot, idx)
738 }
739
740 pub fn select_queued<Q: Queued>(&self, queued_items: Q) -> Q::Output<'_, T> {
741 let snapshot = self.nucleo.snapshot();
742 queued_items.into_selection(snapshot)
743 }
744
745 pub fn selection_range(&self) -> std::ops::RangeInclusive<usize> {
747 if self.config.reversed {
748 self.selection as usize - self.above.len()
749 ..=self.selection as usize + self.below.len() - 1
750 } else {
751 self.selection as usize + 1 - self.below.len()
752 ..=self.selection as usize + self.above.len()
753 }
754 }
755
756 pub fn resize(&mut self, total_size: u16) {
758 if total_size == 0 {
760 self.size = 0;
761 self.above.clear();
762 self.below.clear();
763 return;
764 }
765
766 let buffer = self.nucleo.snapshot();
767
768 if buffer.total() == 0 {
770 self.size = total_size;
771 return;
772 }
773
774 let padding = self.padding(total_size);
775
776 let mut previous = self.state();
777
778 if self.config.reversed {
779 previous.below = previous.below.clamp(padding, total_size - padding - 1);
782
783 let sizes_below_incl = buffer.sizes_higher_inclusive(self.selection, &mut self.below);
784 let sizes_above = buffer.sizes_lower(self.selection, &mut self.above);
785
786 if self.size <= total_size {
787 resize::larger_rev(previous, total_size, padding, sizes_below_incl, sizes_above);
788 } else {
789 resize::smaller_rev(
790 previous,
791 total_size,
792 padding,
793 padding,
794 sizes_below_incl,
795 sizes_above,
796 );
797 }
798 } else {
799 previous.above = previous.above.clamp(padding, total_size - padding - 1);
802
803 let sizes_below_incl = buffer.sizes_lower_inclusive(self.selection, &mut self.below);
804 let sizes_above = buffer.sizes_higher(self.selection, &mut self.above);
805
806 if self.size <= total_size {
807 resize::larger(previous, total_size, sizes_below_incl, sizes_above);
808 } else {
809 resize::smaller(previous, total_size, padding, sizes_below_incl, sizes_above);
810 }
811 }
812
813 self.size = total_size;
814 }
815
816 pub fn update(&mut self, millis: u64) -> bool {
818 let status = self.nucleo.tick(millis);
819 if status.changed {
820 self.update_items();
821 }
822 status.changed
823 }
824
825 pub fn reset(&mut self) -> bool {
827 let buffer = self.nucleo.snapshot();
828 let padding = self.padding(self.size);
829 if self.selection != 0 {
830 if self.config.reversed {
831 let sizes_below_incl = buffer.sizes_higher_inclusive(0, &mut self.below);
832 self.above.clear();
833
834 reset::reset_rev(self.size, sizes_below_incl);
835 } else {
836 let sizes_below_incl = buffer.sizes_lower_inclusive(0, &mut self.below);
837 let sizes_above = buffer.sizes_higher(0, &mut self.above);
838
839 reset::reset(self.size, padding, sizes_below_incl, sizes_above);
840 }
841
842 self.selection = 0;
843 true
844 } else {
845 false
846 }
847 }
848
849 pub fn update_items(&mut self) {
851 let buffer = self.nucleo.snapshot();
852 self.selection = self.selection.min(buffer.total().saturating_sub(1));
854 let previous = self.state();
855 let padding = self.padding(self.size);
856
857 if buffer.total() > 0 {
858 if self.config.reversed {
859 let sizes_below_incl =
860 buffer.sizes_higher_inclusive(self.selection, &mut self.below);
861 let sizes_above = buffer.sizes_lower(self.selection, &mut self.above);
862
863 update::items_rev(previous, padding, sizes_below_incl, sizes_above);
864 } else {
865 let sizes_below_incl =
866 buffer.sizes_lower_inclusive(self.selection, &mut self.below);
867 let sizes_above = buffer.sizes_higher(self.selection, &mut self.above);
868
869 update::items(previous, padding, sizes_below_incl, sizes_above);
870 }
871 } else {
872 self.below.clear();
873 self.above.clear();
874 self.selection = 0;
875 }
876 }
877
878 #[inline]
879 pub fn set_selection(&mut self, new_selection: u32) -> bool {
880 let buffer = self.nucleo.snapshot();
881 let new_selection = new_selection.min(buffer.total().saturating_sub(1));
882
883 let previous = self.state();
884 let padding = self.padding(self.size);
885
886 if new_selection == 0 {
887 self.reset()
888 } else if new_selection > self.selection {
889 if self.config.reversed {
890 let sizes_below_incl =
891 buffer.sizes_higher_inclusive(new_selection, &mut self.below);
892 let sizes_above = buffer.sizes_lower(new_selection, &mut self.above);
893
894 selection::incr_rev(
895 previous,
896 new_selection,
897 padding,
898 padding,
899 sizes_below_incl,
900 sizes_above,
901 );
902 } else {
903 let sizes_below_incl = buffer.sizes_lower_inclusive(new_selection, &mut self.below);
904 let sizes_above = buffer.sizes_higher(new_selection, &mut self.above);
905
906 selection::incr(
907 previous,
908 new_selection,
909 padding,
910 sizes_below_incl,
911 sizes_above,
912 );
913 }
914
915 self.selection = new_selection;
916
917 true
918 } else if new_selection < self.selection {
919 if self.config.reversed {
920 let sizes_below_incl =
921 buffer.sizes_higher_inclusive(new_selection, &mut self.below);
922 let sizes_above = buffer.sizes_lower(new_selection, &mut self.above);
923
924 selection::decr_rev(
925 previous,
926 new_selection,
927 padding,
928 sizes_below_incl,
929 sizes_above,
930 );
931 } else {
932 let sizes_below_incl = buffer.sizes_lower_inclusive(new_selection, &mut self.below);
933 let sizes_above = buffer.sizes_higher(new_selection, &mut self.above);
934
935 selection::decr(
936 previous,
937 new_selection,
938 padding,
939 padding,
940 sizes_below_incl,
941 sizes_above,
942 );
943 }
944
945 self.selection = new_selection;
946
947 true
948 } else {
949 false
950 }
951 }
952
953 #[cfg(test)]
955 pub fn selection_incr(&mut self, increase: u32) -> bool {
956 let new_selection = self
957 .selection
958 .saturating_add(increase)
959 .min(self.nucleo.snapshot().total().saturating_sub(1));
960
961 self.set_selection(new_selection)
962 }
963
964 #[cfg(test)]
966 pub fn selection_decr(&mut self, decrease: u32) -> bool {
967 let new_selection = self.selection.saturating_sub(decrease);
968
969 self.set_selection(new_selection)
970 }
971}