Add command to check all the exercises

This allows for skipping repeating "next" when multiple exercises
are done at once, or when earlier exercises have been updated/changed
(and thus must be redone) while still working of the whole set (i.e.
the final check_all is not yet available to flag those undone exercises)
This commit is contained in:
Nahor 2024-10-02 13:40:32 -07:00
parent 26fd97a209
commit c52867eb8b
4 changed files with 51 additions and 4 deletions

View file

@ -396,8 +396,16 @@ impl AppState {
} }
// Return the exercise index of the first pending exercise found. // Return the exercise index of the first pending exercise found.
fn check_all_exercises(&mut self, stdout: &mut StdoutLock) -> Result<Option<usize>> { pub fn check_all_exercises(
stdout.write_all(FINAL_CHECK_MSG)?; &mut self,
stdout: &mut StdoutLock,
final_check: bool,
) -> Result<Option<usize>> {
if !final_check {
stdout.write_all(INTERMEDIATE_CHECK_MSG)?;
} else {
stdout.write_all(FINAL_CHECK_MSG)?;
}
let n_exercises = self.exercises.len(); let n_exercises = self.exercises.len();
let (mut checked_count, mut results) = thread::scope(|s| { let (mut checked_count, mut results) = thread::scope(|s| {
@ -513,7 +521,7 @@ impl AppState {
stdout.write_all(b"\n")?; stdout.write_all(b"\n")?;
} }
if let Some(pending_exercise_ind) = self.check_all_exercises(stdout)? { if let Some(pending_exercise_ind) = self.check_all_exercises(stdout, true)? {
stdout.write_all(b"\n\n")?; stdout.write_all(b"\n\n")?;
self.current_exercise_ind = pending_exercise_ind; self.current_exercise_ind = pending_exercise_ind;
@ -525,6 +533,12 @@ impl AppState {
// Write that the last exercise is done. // Write that the last exercise is done.
self.write()?; self.write()?;
self.render_final_message(stdout)?;
Ok(ExercisesProgress::AllDone)
}
pub fn render_final_message(&self, stdout: &mut StdoutLock) -> Result<()> {
clear_terminal(stdout)?; clear_terminal(stdout)?;
stdout.write_all(FENISH_LINE.as_bytes())?; stdout.write_all(FENISH_LINE.as_bytes())?;
@ -534,12 +548,14 @@ impl AppState {
stdout.write_all(b"\n")?; stdout.write_all(b"\n")?;
} }
Ok(ExercisesProgress::AllDone) Ok(())
} }
} }
const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises";
const STATE_FILE_HEADER: &[u8] = b"DON'T EDIT THIS FILE!\n\n"; const STATE_FILE_HEADER: &[u8] = b"DON'T EDIT THIS FILE!\n\n";
const INTERMEDIATE_CHECK_MSG: &[u8] = b"Checking all exercises
";
const FINAL_CHECK_MSG: &[u8] = b"All exercises seem to be done. const FINAL_CHECK_MSG: &[u8] = b"All exercises seem to be done.
Recompiling and running all exercises to make sure that all of them are actually done. Recompiling and running all exercises to make sure that all of them are actually done.
"; ";

View file

@ -103,6 +103,13 @@ fn run_watch(
WatchEvent::Input(InputEvent::Run) => watch_state.run_current_exercise(&mut stdout)?, 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::CheckAll) => match watch_state
.check_all_exercises(&mut stdout)?
{
ExercisesProgress::AllDone => break,
ExercisesProgress::NewPending => watch_state.run_current_exercise(&mut stdout)?,
ExercisesProgress::CurrentPending => (),
},
WatchEvent::Input(InputEvent::Reset) => watch_state.reset_exercise(&mut stdout)?, 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)?;

View file

@ -195,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"c")?;
stdout.queue(ResetColor)?;
stdout.write_all(b":check all / ")?;
stdout.queue(SetAttribute(Attribute::Bold))?; stdout.queue(SetAttribute(Attribute::Bold))?;
stdout.write_all(b"x")?; stdout.write_all(b"x")?;
stdout.queue(ResetColor)?; stdout.queue(ResetColor)?;
@ -274,6 +279,23 @@ impl<'a> WatchState<'a> {
Ok(()) Ok(())
} }
pub fn check_all_exercises(&mut self, stdout: &mut StdoutLock) -> Result<ExercisesProgress> {
stdout.write_all(b"\n")?;
if let Some(first_fail) = self.app_state.check_all_exercises(stdout, false)? {
// Only change exercise if the current one is done...
if self.app_state.current_exercise().done {
self.app_state.set_current_exercise_ind(first_fail)?;
}
// ...but always pretend it's a "new" anyway because that refreshes
// the display
Ok(ExercisesProgress::NewPending)
} else {
self.app_state.render_final_message(stdout)?;
Ok(ExercisesProgress::AllDone)
}
}
pub fn update_term_width(&mut self, width: u16, stdout: &mut StdoutLock) -> io::Result<()> { pub fn update_term_width(&mut self, width: u16, stdout: &mut StdoutLock) -> io::Result<()> {
if self.term_width != width { if self.term_width != width {
self.term_width = width; self.term_width = width;

View file

@ -11,6 +11,7 @@ pub enum InputEvent {
Run, Run,
Hint, Hint,
List, List,
CheckAll,
Reset, Reset,
Quit, Quit,
} }
@ -37,6 +38,7 @@ pub fn terminal_event_handler(
KeyCode::Char('r') if manual_run => InputEvent::Run, 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('c') => InputEvent::CheckAll,
KeyCode::Char('x') => { KeyCode::Char('x') => {
if sender.send(WatchEvent::Input(InputEvent::Reset)).is_err() { if sender.send(WatchEvent::Input(InputEvent::Reset)).is_err() {
return; return;