Merge pull request #1911 from mo8it/watch

Improvements to watch mode
This commit is contained in:
Mo 2024-03-27 14:42:17 +01:00 committed by GitHub
commit b13bafa13e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 59 additions and 38 deletions

7
Cargo.lock generated
View file

@ -570,6 +570,7 @@ dependencies = [
"regex", "regex",
"serde", "serde",
"serde_json", "serde_json",
"shlex",
"toml_edit", "toml_edit",
"which", "which",
] ]
@ -629,6 +630,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.11.0" version = "0.11.0"

View file

@ -17,6 +17,7 @@ notify-debouncer-mini = "0.4.1"
regex = "1.10.3" regex = "1.10.3"
serde_json = "1.0.114" serde_json = "1.0.114"
serde = { version = "1.0.197", features = ["derive"] } serde = { version = "1.0.197", features = ["derive"] }
shlex = "1.3.0"
toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] } toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] }
which = "6.0.1" which = "6.0.1"

View file

@ -6,6 +6,7 @@ use clap::{Parser, Subcommand};
use console::Emoji; use console::Emoji;
use notify_debouncer_mini::notify::{self, RecursiveMode}; use notify_debouncer_mini::notify::{self, RecursiveMode};
use notify_debouncer_mini::{new_debouncer, DebouncedEventKind}; use notify_debouncer_mini::{new_debouncer, DebouncedEventKind};
use shlex::Shlex;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fs; use std::fs;
use std::io::{self, prelude::*}; use std::io::{self, prelude::*};
@ -244,47 +245,49 @@ fn main() {
} }
fn spawn_watch_shell( fn spawn_watch_shell(
failed_exercise_hint: &Arc<Mutex<Option<String>>>, failed_exercise_hint: Arc<Mutex<Option<String>>>,
should_quit: Arc<AtomicBool>, should_quit: Arc<AtomicBool>,
) { ) {
let failed_exercise_hint = Arc::clone(failed_exercise_hint);
println!("Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here."); println!("Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.");
thread::spawn(move || loop {
let mut input = String::new(); thread::spawn(move || {
match io::stdin().read_line(&mut input) { let mut input = String::with_capacity(32);
Ok(_) => { let mut stdin = io::stdin().lock();
let input = input.trim();
if input == "hint" { loop {
if let Some(hint) = &*failed_exercise_hint.lock().unwrap() { // Recycle input buffer.
println!("{hint}"); input.clear();
}
} else if input == "clear" { if let Err(e) = stdin.read_line(&mut input) {
println!("\x1B[2J\x1B[1;1H"); println!("error reading command: {e}");
} else if input.eq("quit") { }
should_quit.store(true, Ordering::SeqCst);
println!("Bye!"); let input = input.trim();
} else if input.eq("help") { if input == "hint" {
println!("Commands available to you in watch mode:"); if let Some(hint) = &*failed_exercise_hint.lock().unwrap() {
println!(" hint - prints the current exercise's hint"); println!("{hint}");
println!(" clear - clears the screen"); }
println!(" quit - quits watch mode"); } else if input == "clear" {
println!(" !<cmd> - executes a command, like `!rustc --explain E0381`"); println!("\x1B[2J\x1B[1;1H");
println!(" help - displays this help message"); } else if input == "quit" {
println!(); should_quit.store(true, Ordering::SeqCst);
println!("Watch mode automatically re-evaluates the current exercise"); println!("Bye!");
println!("when you edit a file's contents.") } else if input == "help" {
} else if let Some(cmd) = input.strip_prefix('!') { println!("{WATCH_MODE_HELP_MESSAGE}");
let parts: Vec<&str> = cmd.split_whitespace().collect(); } else if let Some(cmd) = input.strip_prefix('!') {
if parts.is_empty() { let mut parts = Shlex::new(cmd);
println!("no command provided");
} else if let Err(e) = Command::new(parts[0]).args(&parts[1..]).status() { let Some(program) = parts.next() else {
println!("failed to execute command `{cmd}`: {e}"); println!("no command provided");
} continue;
} else { };
println!("unknown command: {input}");
} if let Err(e) = Command::new(program).args(parts).status() {
println!("failed to execute command `{cmd}`: {e}");
}
} else {
println!("unknown command: {input}\n{WATCH_MODE_HELP_MESSAGE}");
} }
Err(error) => println!("error reading command: {error}"),
} }
}); });
} }
@ -345,7 +348,7 @@ fn watch(
Ok(_) => return Ok(WatchStatus::Finished), Ok(_) => return Ok(WatchStatus::Finished),
Err(exercise) => Arc::new(Mutex::new(Some(exercise.hint.clone()))), Err(exercise) => Arc::new(Mutex::new(Some(exercise.hint.clone()))),
}; };
spawn_watch_shell(&failed_exercise_hint, Arc::clone(&should_quit)); spawn_watch_shell(Arc::clone(&failed_exercise_hint), Arc::clone(&should_quit));
loop { loop {
match rx.recv_timeout(Duration::from_secs(1)) { match rx.recv_timeout(Duration::from_secs(1)) {
Ok(event) => match event { Ok(event) => match event {
@ -462,3 +465,13 @@ const WELCOME: &str = r" welcome to...
| | | |_| \__ \ |_| | | | | | (_| \__ \ | | | |_| \__ \ |_| | | | | | (_| \__ \
|_| \__,_|___/\__|_|_|_| |_|\__, |___/ |_| \__,_|___/\__|_|_|_| |_|\__, |___/
|___/"; |___/";
const WATCH_MODE_HELP_MESSAGE: &str = "Commands available to you in watch mode:
hint - prints the current exercise's hint
clear - clears the screen
quit - quits watch mode
!<cmd> - executes a command, like `!rustc --explain E0381`
help - displays this help message
Watch mode automatically re-evaluates the current exercise
when you edit a file's contents.";