1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use std::{cell::RefCell, rc::Rc};
use console::Key;
use crate::prompt::{cursor::StringCursor, interaction::State};
use crate::suggest::Suggest;
/// The list of items gathered (filtered) by interactive input using
/// `FilteredView::on` event in a selection prompt.
///
/// The filter keeps and tracks the list of items to be rendered, however,
/// the items list can be shrunk due to enabled filtering.
pub(crate) struct FilteredView<I: AsRef<str>> {
/// Enables the filtered view.
enabled: bool,
/// Collects the input from the user.
input: StringCursor,
/// Represents a view of the filtered items.
items: Vec<Rc<RefCell<I>>>,
}
impl<I> Default for FilteredView<I>
where
I: AsRef<str>,
{
fn default() -> Self {
Self {
enabled: false,
input: StringCursor::default(),
items: vec![],
}
}
}
impl<I> FilteredView<I>
where
I: AsRef<str>,
{
/// Sets the items to be filtered.
///
/// The reason of having this method is having an ability to support
/// a builder pattern of the selection prompt, where the items are added one by one,
/// and the filter can be enabled and initialized at different moment of time.
pub fn set(&mut self, items: Vec<Rc<RefCell<I>>>) {
self.items = items;
}
/// Sets a predefined set of items for the view.
pub fn enable(&mut self) {
self.enabled = true;
}
/// Returns the items in the view.
pub fn items(&self) -> &[Rc<RefCell<I>>] {
&self.items
}
/// Collects the input and filters the items from the list of suggestions.
pub fn on<T, S>(&mut self, key: &Key, all_items: &S) -> Option<State<T>>
where
S: Suggest<Result = Rc<RefCell<I>>>,
{
if !self.enabled {
// Pass over the control.
return None;
}
match key {
// Need further processing of simple "up" and "down" actions.
Key::ArrowDown | Key::ArrowUp => None,
// Need moving up and down if no input provided.
Key::ArrowLeft | Key::ArrowRight if self.input.is_empty() => None,
// Need to submit the selected item.
Key::Enter if !self.items.is_empty() => None,
// Otherwise, no items found.
Key::Enter => Some(State::Error("No items".into())),
// Ignore spaces passing through.
Key::Char(' ') => {
self.input.delete_left();
None
}
// Refresh the filtered items for the rest of the keys.
_ => {
self.items = all_items.suggest(&self.input.to_string());
Some(State::Active)
}
}
}
/// Returns the input cursor if the filter is enabled.
/// It makes the outer code to handle the input.
pub fn input(&mut self) -> Option<&mut StringCursor> {
if !self.enabled {
return None;
}
Some(&mut self.input)
}
}