Compare commits

..

3 commits

Author SHA1 Message Date
Kacper Poneta
445544f4ff
Merge 59e8f70e55 into 0c79f2ea3e 2024-09-27 17:17:42 +02:00
mo8it
0c79f2ea3e Reset in prompt with confirmation
Some checks failed
Rustlings Tests / clippy (push) Has been cancelled
Rustlings Tests / fmt (push) Has been cancelled
Rustlings Tests / test (macOS-latest) (push) Has been cancelled
Rustlings Tests / test (ubuntu-latest) (push) Has been cancelled
Rustlings Tests / test (windows-latest) (push) Has been cancelled
Rustlings Tests / dev-check (push) Has been cancelled
Web / Build and deploy site and docs (push) Has been cancelled
2024-09-26 18:15:45 +02:00
mo8it
0e9eb9e87e Replace three dots with dot in hint 2024-09-26 18:05:05 +02:00
4 changed files with 90 additions and 16 deletions

View file

@ -1155,7 +1155,7 @@ constants, but clippy recognizes those imprecise mathematical constants as a
source of potential error. source of potential error.
See the suggestions of the Clippy warning in the compile output and use the See the suggestions of the Clippy warning in the compile output and use the
appropriate replacement constant from `std::f32::consts`...""" appropriate replacement constant from `std::f32::consts`."""
[[exercises]] [[exercises]]
name = "clippy2" name = "clippy2"

View file

@ -100,13 +100,14 @@ fn run_watch(
ExercisesProgress::NewPending => watch_state.run_current_exercise(&mut stdout)?, ExercisesProgress::NewPending => watch_state.run_current_exercise(&mut stdout)?,
ExercisesProgress::CurrentPending => (), ExercisesProgress::CurrentPending => (),
}, },
WatchEvent::Input(InputEvent::Run) => watch_state.run_current_exercise(&mut stdout)?,
WatchEvent::Input(InputEvent::Hint) => watch_state.show_hint(&mut stdout)?, WatchEvent::Input(InputEvent::Hint) => watch_state.show_hint(&mut stdout)?,
WatchEvent::Input(InputEvent::List) => return Ok(WatchExit::List), WatchEvent::Input(InputEvent::List) => return Ok(WatchExit::List),
WatchEvent::Input(InputEvent::Reset) => watch_state.reset_exercise(&mut stdout)?,
WatchEvent::Input(InputEvent::Quit) => { WatchEvent::Input(InputEvent::Quit) => {
stdout.write_all(QUIT_MSG)?; stdout.write_all(QUIT_MSG)?;
break; break;
} }
WatchEvent::Input(InputEvent::Run) => watch_state.run_current_exercise(&mut stdout)?,
WatchEvent::FileChange { exercise_ind } => { WatchEvent::FileChange { exercise_ind } => {
watch_state.handle_file_change(exercise_ind, &mut stdout)?; watch_state.handle_file_change(exercise_ind, &mut stdout)?;
} }

View file

@ -6,8 +6,8 @@ use crossterm::{
terminal, QueueableCommand, terminal, QueueableCommand,
}; };
use std::{ use std::{
io::{self, StdoutLock, Write}, io::{self, Read, StdoutLock, Write},
sync::mpsc::Sender, sync::mpsc::{sync_channel, Sender, SyncSender},
thread, thread,
}; };
@ -34,6 +34,7 @@ pub struct WatchState<'a> {
done_status: DoneStatus, done_status: DoneStatus,
manual_run: bool, manual_run: bool,
term_width: u16, term_width: u16,
terminal_event_unpause_sender: SyncSender<()>,
} }
impl<'a> WatchState<'a> { impl<'a> WatchState<'a> {
@ -46,8 +47,16 @@ impl<'a> WatchState<'a> {
.context("Failed to get the terminal size")? .context("Failed to get the terminal size")?
.0; .0;
let (terminal_event_unpause_sender, terminal_event_unpause_receiver) = sync_channel(0);
thread::Builder::new() thread::Builder::new()
.spawn(move || terminal_event_handler(watch_event_sender, manual_run)) .spawn(move || {
terminal_event_handler(
watch_event_sender,
terminal_event_unpause_receiver,
manual_run,
)
})
.context("Failed to spawn a thread to handle terminal events")?; .context("Failed to spawn a thread to handle terminal events")?;
Ok(Self { Ok(Self {
@ -57,6 +66,7 @@ impl<'a> WatchState<'a> {
done_status: DoneStatus::Pending, done_status: DoneStatus::Pending,
manual_run, manual_run,
term_width, term_width,
terminal_event_unpause_sender,
}) })
} }
@ -95,6 +105,44 @@ impl<'a> WatchState<'a> {
Ok(()) Ok(())
} }
pub fn reset_exercise(&mut self, stdout: &mut StdoutLock) -> Result<()> {
clear_terminal(stdout)?;
stdout.write_all(b"Resetting will undo all your changes to the file ")?;
stdout.write_all(self.app_state.current_exercise().path.as_bytes())?;
stdout.write_all(b"\nReset (y/n)? ")?;
stdout.flush()?;
{
let mut stdin = io::stdin().lock();
let mut answer = [0];
loop {
stdin
.read_exact(&mut answer)
.context("Failed to read the user's input")?;
match answer[0] {
b'y' | b'Y' => {
self.app_state.reset_current_exercise()?;
// The file watcher reruns the exercise otherwise.
if self.manual_run {
self.run_current_exercise(stdout)?;
}
}
b'n' | b'N' => self.render(stdout)?,
_ => continue,
}
break;
}
}
self.terminal_event_unpause_sender.send(())?;
Ok(())
}
pub fn handle_file_change( pub fn handle_file_change(
&mut self, &mut self,
exercise_ind: usize, exercise_ind: usize,
@ -117,13 +165,6 @@ impl<'a> WatchState<'a> {
} }
fn show_prompt(&self, stdout: &mut StdoutLock) -> io::Result<()> { fn show_prompt(&self, stdout: &mut StdoutLock) -> io::Result<()> {
if self.manual_run {
stdout.queue(SetAttribute(Attribute::Bold))?;
stdout.write_all(b"r")?;
stdout.queue(ResetColor)?;
stdout.write_all(b":run / ")?;
}
if self.done_status != DoneStatus::Pending { if self.done_status != DoneStatus::Pending {
stdout.queue(SetAttribute(Attribute::Bold))?; stdout.queue(SetAttribute(Attribute::Bold))?;
stdout.write_all(b"n")?; stdout.write_all(b"n")?;
@ -135,6 +176,13 @@ impl<'a> WatchState<'a> {
stdout.write_all(b" / ")?; stdout.write_all(b" / ")?;
} }
if self.manual_run {
stdout.queue(SetAttribute(Attribute::Bold))?;
stdout.write_all(b"r")?;
stdout.queue(ResetColor)?;
stdout.write_all(b":run / ")?;
}
if !self.show_hint { if !self.show_hint {
stdout.queue(SetAttribute(Attribute::Bold))?; stdout.queue(SetAttribute(Attribute::Bold))?;
stdout.write_all(b"h")?; stdout.write_all(b"h")?;
@ -147,6 +195,11 @@ impl<'a> WatchState<'a> {
stdout.queue(ResetColor)?; stdout.queue(ResetColor)?;
stdout.write_all(b":list / ")?; stdout.write_all(b":list / ")?;
stdout.queue(SetAttribute(Attribute::Bold))?;
stdout.write_all(b"x")?;
stdout.queue(ResetColor)?;
stdout.write_all(b":reset / ")?;
stdout.queue(SetAttribute(Attribute::Bold))?; stdout.queue(SetAttribute(Attribute::Bold))?;
stdout.write_all(b"q")?; stdout.write_all(b"q")?;
stdout.queue(ResetColor)?; stdout.queue(ResetColor)?;

View file

@ -1,17 +1,25 @@
use crossterm::event::{self, Event, KeyCode, KeyEventKind}; use crossterm::event::{self, Event, KeyCode, KeyEventKind};
use std::sync::{atomic::Ordering::Relaxed, mpsc::Sender}; use std::sync::{
atomic::Ordering::Relaxed,
mpsc::{Receiver, Sender},
};
use super::{WatchEvent, EXERCISE_RUNNING}; use super::{WatchEvent, EXERCISE_RUNNING};
pub enum InputEvent { pub enum InputEvent {
Run,
Next, Next,
Run,
Hint, Hint,
List, List,
Reset,
Quit, Quit,
} }
pub fn terminal_event_handler(sender: Sender<WatchEvent>, manual_run: bool) { pub fn terminal_event_handler(
sender: Sender<WatchEvent>,
unpause_receiver: Receiver<()>,
manual_run: bool,
) {
let last_watch_event = loop { let last_watch_event = loop {
match event::read() { match event::read() {
Ok(Event::Key(key)) => { Ok(Event::Key(key)) => {
@ -26,10 +34,22 @@ pub fn terminal_event_handler(sender: Sender<WatchEvent>, manual_run: bool) {
let input_event = match key.code { let input_event = match key.code {
KeyCode::Char('n') => InputEvent::Next, KeyCode::Char('n') => InputEvent::Next,
KeyCode::Char('r') if manual_run => InputEvent::Run,
KeyCode::Char('h') => InputEvent::Hint, KeyCode::Char('h') => InputEvent::Hint,
KeyCode::Char('l') => break WatchEvent::Input(InputEvent::List), KeyCode::Char('l') => break WatchEvent::Input(InputEvent::List),
KeyCode::Char('x') => {
if sender.send(WatchEvent::Input(InputEvent::Reset)).is_err() {
return;
}
// Pause input until quitting the confirmation prompt.
if unpause_receiver.recv().is_err() {
return;
};
continue;
}
KeyCode::Char('q') => break WatchEvent::Input(InputEvent::Quit), KeyCode::Char('q') => break WatchEvent::Input(InputEvent::Quit),
KeyCode::Char('r') if manual_run => InputEvent::Run,
_ => continue, _ => continue,
}; };