Skip to content

Commit 0c79f2e

Browse files
committed
Reset in prompt with confirmation
1 parent 0e9eb9e commit 0c79f2e

File tree

3 files changed

+89
-15
lines changed

3 files changed

+89
-15
lines changed

src/watch.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,14 @@ fn run_watch(
100100
ExercisesProgress::NewPending => watch_state.run_current_exercise(&mut stdout)?,
101101
ExercisesProgress::CurrentPending => (),
102102
},
103+
WatchEvent::Input(InputEvent::Run) => watch_state.run_current_exercise(&mut stdout)?,
103104
WatchEvent::Input(InputEvent::Hint) => watch_state.show_hint(&mut stdout)?,
104105
WatchEvent::Input(InputEvent::List) => return Ok(WatchExit::List),
106+
WatchEvent::Input(InputEvent::Reset) => watch_state.reset_exercise(&mut stdout)?,
105107
WatchEvent::Input(InputEvent::Quit) => {
106108
stdout.write_all(QUIT_MSG)?;
107109
break;
108110
}
109-
WatchEvent::Input(InputEvent::Run) => watch_state.run_current_exercise(&mut stdout)?,
110111
WatchEvent::FileChange { exercise_ind } => {
111112
watch_state.handle_file_change(exercise_ind, &mut stdout)?;
112113
}

src/watch/state.rs

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use crossterm::{
66
terminal, QueueableCommand,
77
};
88
use std::{
9-
io::{self, StdoutLock, Write},
10-
sync::mpsc::Sender,
9+
io::{self, Read, StdoutLock, Write},
10+
sync::mpsc::{sync_channel, Sender, SyncSender},
1111
thread,
1212
};
1313

@@ -34,6 +34,7 @@ pub struct WatchState<'a> {
3434
done_status: DoneStatus,
3535
manual_run: bool,
3636
term_width: u16,
37+
terminal_event_unpause_sender: SyncSender<()>,
3738
}
3839

3940
impl<'a> WatchState<'a> {
@@ -46,8 +47,16 @@ impl<'a> WatchState<'a> {
4647
.context("Failed to get the terminal size")?
4748
.0;
4849

50+
let (terminal_event_unpause_sender, terminal_event_unpause_receiver) = sync_channel(0);
51+
4952
thread::Builder::new()
50-
.spawn(move || terminal_event_handler(watch_event_sender, manual_run))
53+
.spawn(move || {
54+
terminal_event_handler(
55+
watch_event_sender,
56+
terminal_event_unpause_receiver,
57+
manual_run,
58+
)
59+
})
5160
.context("Failed to spawn a thread to handle terminal events")?;
5261

5362
Ok(Self {
@@ -57,6 +66,7 @@ impl<'a> WatchState<'a> {
5766
done_status: DoneStatus::Pending,
5867
manual_run,
5968
term_width,
69+
terminal_event_unpause_sender,
6070
})
6171
}
6272

@@ -95,6 +105,44 @@ impl<'a> WatchState<'a> {
95105
Ok(())
96106
}
97107

108+
pub fn reset_exercise(&mut self, stdout: &mut StdoutLock) -> Result<()> {
109+
clear_terminal(stdout)?;
110+
111+
stdout.write_all(b"Resetting will undo all your changes to the file ")?;
112+
stdout.write_all(self.app_state.current_exercise().path.as_bytes())?;
113+
stdout.write_all(b"\nReset (y/n)? ")?;
114+
stdout.flush()?;
115+
116+
{
117+
let mut stdin = io::stdin().lock();
118+
let mut answer = [0];
119+
loop {
120+
stdin
121+
.read_exact(&mut answer)
122+
.context("Failed to read the user's input")?;
123+
124+
match answer[0] {
125+
b'y' | b'Y' => {
126+
self.app_state.reset_current_exercise()?;
127+
128+
// The file watcher reruns the exercise otherwise.
129+
if self.manual_run {
130+
self.run_current_exercise(stdout)?;
131+
}
132+
}
133+
b'n' | b'N' => self.render(stdout)?,
134+
_ => continue,
135+
}
136+
137+
break;
138+
}
139+
}
140+
141+
self.terminal_event_unpause_sender.send(())?;
142+
143+
Ok(())
144+
}
145+
98146
pub fn handle_file_change(
99147
&mut self,
100148
exercise_ind: usize,
@@ -117,13 +165,6 @@ impl<'a> WatchState<'a> {
117165
}
118166

119167
fn show_prompt(&self, stdout: &mut StdoutLock) -> io::Result<()> {
120-
if self.manual_run {
121-
stdout.queue(SetAttribute(Attribute::Bold))?;
122-
stdout.write_all(b"r")?;
123-
stdout.queue(ResetColor)?;
124-
stdout.write_all(b":run / ")?;
125-
}
126-
127168
if self.done_status != DoneStatus::Pending {
128169
stdout.queue(SetAttribute(Attribute::Bold))?;
129170
stdout.write_all(b"n")?;
@@ -135,6 +176,13 @@ impl<'a> WatchState<'a> {
135176
stdout.write_all(b" / ")?;
136177
}
137178

179+
if self.manual_run {
180+
stdout.queue(SetAttribute(Attribute::Bold))?;
181+
stdout.write_all(b"r")?;
182+
stdout.queue(ResetColor)?;
183+
stdout.write_all(b":run / ")?;
184+
}
185+
138186
if !self.show_hint {
139187
stdout.queue(SetAttribute(Attribute::Bold))?;
140188
stdout.write_all(b"h")?;
@@ -147,6 +195,11 @@ impl<'a> WatchState<'a> {
147195
stdout.queue(ResetColor)?;
148196
stdout.write_all(b":list / ")?;
149197

198+
stdout.queue(SetAttribute(Attribute::Bold))?;
199+
stdout.write_all(b"x")?;
200+
stdout.queue(ResetColor)?;
201+
stdout.write_all(b":reset / ")?;
202+
150203
stdout.queue(SetAttribute(Attribute::Bold))?;
151204
stdout.write_all(b"q")?;
152205
stdout.queue(ResetColor)?;

src/watch/terminal_event.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
11
use crossterm::event::{self, Event, KeyCode, KeyEventKind};
2-
use std::sync::{atomic::Ordering::Relaxed, mpsc::Sender};
2+
use std::sync::{
3+
atomic::Ordering::Relaxed,
4+
mpsc::{Receiver, Sender},
5+
};
36

47
use super::{WatchEvent, EXERCISE_RUNNING};
58

69
pub enum InputEvent {
7-
Run,
810
Next,
11+
Run,
912
Hint,
1013
List,
14+
Reset,
1115
Quit,
1216
}
1317

14-
pub fn terminal_event_handler(sender: Sender<WatchEvent>, manual_run: bool) {
18+
pub fn terminal_event_handler(
19+
sender: Sender<WatchEvent>,
20+
unpause_receiver: Receiver<()>,
21+
manual_run: bool,
22+
) {
1523
let last_watch_event = loop {
1624
match event::read() {
1725
Ok(Event::Key(key)) => {
@@ -26,10 +34,22 @@ pub fn terminal_event_handler(sender: Sender<WatchEvent>, manual_run: bool) {
2634

2735
let input_event = match key.code {
2836
KeyCode::Char('n') => InputEvent::Next,
37+
KeyCode::Char('r') if manual_run => InputEvent::Run,
2938
KeyCode::Char('h') => InputEvent::Hint,
3039
KeyCode::Char('l') => break WatchEvent::Input(InputEvent::List),
40+
KeyCode::Char('x') => {
41+
if sender.send(WatchEvent::Input(InputEvent::Reset)).is_err() {
42+
return;
43+
}
44+
45+
// Pause input until quitting the confirmation prompt.
46+
if unpause_receiver.recv().is_err() {
47+
return;
48+
};
49+
50+
continue;
51+
}
3152
KeyCode::Char('q') => break WatchEvent::Input(InputEvent::Quit),
32-
KeyCode::Char('r') if manual_run => InputEvent::Run,
3353
_ => continue,
3454
};
3555

0 commit comments

Comments
 (0)