-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathrestart_ext.rs
122 lines (109 loc) · 4.79 KB
/
restart_ext.rs
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//! # Picker with interactive restarts and slow event generation
//!
//! Generates a list of 1000 random `u32`s to be selected but with intentionally introduced delay
//! to imitate 'work' being done in the background.
//!
//! The user can either select one of the options, or press `ctrl + r` to receive a new list of
//! random integers. Try pressing `ctrl + r` very rapidly; new numbers will be generated even before
//! the previous list has finished rendering.
//!
//! This is a more complex version of the `restart` example; it is better to start there first. The
//! only documentation here is for the changes relative to the `restart` example.
use std::{
convert::Infallible,
io::{self, IsTerminal},
process::exit,
sync::mpsc::TryRecvError,
thread::{sleep, spawn},
time::Duration,
};
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
use nucleo_picker::{
event::{keybind_default, Event, StdinReader},
render::DisplayRenderer,
Picker,
};
use rand::random;
/// Generate a random `u32` but with a lot of extra delay to imitate an expensive computation.
fn slow_random() -> u32 {
sleep(Duration::from_millis(5));
random()
}
fn main() -> io::Result<()> {
let mut picker: Picker<u32, _> = Picker::new(DisplayRenderer);
let mut stderr = io::stderr().lock();
if !stderr.is_terminal() {
eprintln!("Failed to start picker: STDERR not interactive!");
exit(1);
}
// Do not initialize with an injector since we will send it ourself
let observer = picker.injector_observer(false);
let injector = picker.injector();
spawn(move || {
const NUM_ITEMS: usize = 1000;
// because of the delay, generating 1000 `u32`s will take about 5 seconds, which is way
// too long to be done in a single frame. Therefore after generating each random number
// we check for a restart event before continuing.
// In this example, coming up with the check frequency is quite easy since we know the
// delay is 5ms, which is approximately the frame interval. In practice, with
// computation-heavy item generation, tuning the check frequency to happen approximately
// twice per frame can be very challenging. Note that the `receiver.try_recv` call is quite
// cheap so it is better to err towards overeager checks than infrequent checks.
// the current active injector
let mut current_injector = injector;
let mut remaining_items = NUM_ITEMS;
loop {
match observer.try_recv() {
Ok(new_injector) => {
// we received a new injector so we should immediately start sending `u32`s to
// it instead
current_injector = new_injector;
remaining_items = NUM_ITEMS;
}
Err(TryRecvError::Empty) => {
if remaining_items > 0 {
// we still have remaining data to be sent; continue to send it to the
// picker
remaining_items -= 1;
current_injector.push(slow_random());
} else if let Ok(new_injector) = observer.recv() {
// we have sent all of the necessary data; but we cannot simply skip this
// branch or we will spin-loop and consume unnecessary CPU cycles. Instead,
// we should block and wait for the next restart event since we have
// nothing else to do. Once we receive the new injector, reset the state
// and begin generating again.
current_injector = new_injector;
remaining_items = NUM_ITEMS;
} else {
// observer.recv() returned an error, means the channel disconnected so we
// can shut down this thread
return;
}
}
Err(TryRecvError::Disconnected) => {
// the channel disconnected so we can shut down this thread
return;
}
}
}
});
let event_source = StdinReader::new(move |key_event| match key_event {
KeyEvent {
kind: KeyEventKind::Press,
modifiers: KeyModifiers::CONTROL,
code: KeyCode::Char('r'),
..
} => Some(Event::<Infallible>::Restart),
e => keybind_default(e),
});
match picker.pick_with_io(event_source, &mut stderr)? {
Some(num) => {
println!("Your favourite number is: {num}");
Ok(())
}
None => {
println!("You didn't like any of the numbers!");
exit(1);
}
}
}